svn commit: r235526 - in user/jceel/soc2012_armv6/sys: arm/ti dev/fdt dev/uart

Jakub Wojciech Klama jceel at FreeBSD.org
Wed May 16 22:21:58 UTC 2012


Author: jceel
Date: Wed May 16 22:21:57 2012
New Revision: 235526
URL: http://svn.freebsd.org/changeset/base/235526

Log:
  Rewrite FDT bus ranges calculation:
  
  - bus ranges from DTS files are correctly read and used for translation
    of device register addresses. this includes empty "ranges;" property
    as well as encoded ranges tuples.
  - simplebuses can be freely nested (except for system console UART which
    should be placed in toplevel simplebus)
  - base addresses in resources (rman) are now physical. translation or
    mapping to virtual address is done via bus_space_map() and can be
    accessed via rman_get_bushandle().
  - ti_mmchs driver is corrected to use physical registers base address
    from rman instead of using vtophys().

Modified:
  user/jceel/soc2012_armv6/sys/arm/ti/ti_mmchs.c
  user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.c
  user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.h
  user/jceel/soc2012_armv6/sys/dev/fdt/fdtbus.c
  user/jceel/soc2012_armv6/sys/dev/fdt/simplebus.c
  user/jceel/soc2012_armv6/sys/dev/uart/uart_bus_fdt.c
Directory Properties:
  user/jceel/soc2012_armv6/   (props changed)

Modified: user/jceel/soc2012_armv6/sys/arm/ti/ti_mmchs.c
==============================================================================
--- user/jceel/soc2012_armv6/sys/arm/ti/ti_mmchs.c	Wed May 16 22:19:22 2012	(r235525)
+++ user/jceel/soc2012_armv6/sys/arm/ti/ti_mmchs.c	Wed May 16 22:21:57 2012	(r235526)
@@ -1630,7 +1630,7 @@ ti_mmchs_activate(device_t dev)
 		panic("Unknown OMAP device\n");
 
 	/* Get the physical address of the MMC data register, needed for DMA */
-	addr = vtophys(rman_get_start(sc->sc_mem_res));
+	addr = rman_get_start(sc->sc_mem_res);
 	sc->sc_data_reg_paddr = addr + sc->sc_reg_off + MMCHS_DATA;
 
 	/* Set the initial power state to off */

Modified: user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.c
==============================================================================
--- user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.c	Wed May 16 22:19:22 2012	(r235525)
+++ user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.c	Wed May 16 22:21:57 2012	(r235526)
@@ -64,12 +64,30 @@ vm_offset_t fdt_immr_va;
 vm_offset_t fdt_immr_size;
 
 int
-fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size)
+fdt_immr_addr(vm_offset_t immr_va)
 {
 	pcell_t ranges[6], *rangesptr;
+	phandle_t node;
+	u_long base, size;
 	pcell_t addr_cells, size_cells, par_addr_cells;
 	int len, tuple_size, tuples;
 
+	/*
+	 * Try to access the SOC node directly i.e. through /aliases/.
+	 */
+	if ((node = OF_finddevice("soc")) != -1)
+		if (fdt_is_compatible_strict(node, "simple-bus"))
+			goto moveon;
+	/*
+	 * Find the node the long way.
+	 */
+	if ((node = OF_finddevice("/")) == -1)
+		return (ENXIO);
+
+	if ((node = fdt_find_compatible(node, "simple-bus", 1)) == 0)
+		return (ENXIO);
+
+moveon:
 	if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
 		return (ENXIO);
 	/*
@@ -82,14 +100,6 @@ fdt_get_range(phandle_t node, int range_
 	len = OF_getproplen(node, "ranges");
 	if (len > sizeof(ranges))
 		return (ENOMEM);
-	if (len == 0) {
-		*base = 0;
-		*size = ULONG_MAX;
-		return (0);
-	}
-
-	if (!(range_id < len))
-		return (ERANGE);
 
 	if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
 		return (EINVAL);
@@ -102,48 +112,81 @@ fdt_get_range(phandle_t node, int range_
 	    addr_cells, size_cells)) {
 		return (ERANGE);
 	}
-	*base = 0;
-	*size = 0;
-	rangesptr = &ranges[range_id];
+	base = 0;
+	size = 0;
+	rangesptr = &ranges[0];
 
-	*base = fdt_data_get((void *)rangesptr, addr_cells);
+	base = fdt_data_get((void *)rangesptr, addr_cells);
 	rangesptr += addr_cells;
-	*base += fdt_data_get((void *)rangesptr, par_addr_cells);
+	base += fdt_data_get((void *)rangesptr, par_addr_cells);
 	rangesptr += par_addr_cells;
-	*size = fdt_data_get((void *)rangesptr, size_cells);
+	size = fdt_data_get((void *)rangesptr, size_cells);
+
+	fdt_immr_pa = base;
+	fdt_immr_va = immr_va;
+	fdt_immr_size = size;
+
 	return (0);
 }
 
 int
-fdt_immr_addr(vm_offset_t immr_va)
+fdt_read_ranges(phandle_t node, struct fdt_range **ranges, int addr_cells,
+    int par_addr_cells, int size_cells)
 {
-	phandle_t node;
-	u_long base, size;
-	int r;
-
-	/*
-	 * Try to access the SOC node directly i.e. through /aliases/.
-	 */
-	if ((node = OF_finddevice("soc")) != 0)
-		if (fdt_is_compatible_strict(node, "simple-bus"))
-			goto moveon;
-	/*
-	 * Find the node the long way.
-	 */
-	if ((node = OF_finddevice("/")) == 0)
-		return (ENXIO);
-
-	if ((node = fdt_find_compatible(node, "simple-bus", 1)) == 0)
-		return (ENXIO);
+	static pcell_t data[128];
+	pcell_t *ptr;
+	int i, len, tuple_size, ranges_count;
+	
+	len = OF_getprop(node, "ranges", (void *)&data, sizeof data);
+	tuple_size = addr_cells + par_addr_cells + size_cells;
+	ranges_count = len != 0 ? len / tuple_size / sizeof(pcell_t) : 1;
+
+	debugf("tuple_size=%d ranges_count=%d\n", tuple_size, ranges_count);
+	
+	ptr = data;
+
+	if (*ranges == NULL)
+		*ranges = malloc(sizeof(struct fdt_range) * ranges_count, 
+		    M_TEMP, M_WAITOK | M_ZERO);
+	
+	/* empty ranges case */
+	if (len == 0) {
+		(*ranges)[0].base = 0l;
+		(*ranges)[0].parent = 0l;
+		(*ranges)[0].size = ~0l;
+		return (1);
+	}
 
-moveon:
-	if ((r = fdt_get_range(node, 0, &base, &size)) == 0) {
-		fdt_immr_pa = base;
-		fdt_immr_va = immr_va;
-		fdt_immr_size = size;
+	for (i = 0; i < ranges_count; i++) {
+		(*ranges)[i].base = fdt_data_get((void *)ptr, addr_cells);
+		ptr += addr_cells;
+		(*ranges)[i].parent = fdt_data_get((void *)ptr, par_addr_cells);
+		ptr += par_addr_cells;
+		(*ranges)[i].size = fdt_data_get((void *)ptr, size_cells); 
+		ptr += size_cells;
+		
+		debugf("new range: base=%lx parent=%lx size=%lx\n",
+		    (*ranges)[i].base, (*ranges[i]).parent, 
+		    (*ranges)[i].size);
+	}
+	
+	return (ranges_count);
+}
+
+__inline u_long
+fdt_ranges_lookup(struct fdt_range *ranges, int nranges, u_long addr,
+    u_long size)
+{
+	int n;
+
+	for (n = 0; n < nranges; n++) {
+		if (ranges[n].base <= addr && (ranges[n].base + 
+		    ranges[n].size >= addr + size - 1)) {
+			return ranges[n].parent;
+		}
 	}
 
-	return (r);
+	return 0;
 }
 
 /*
@@ -419,19 +462,17 @@ fdt_regsize(phandle_t node, u_long *base
 }
 
 int
-fdt_reg_to_rl(phandle_t node, struct resource_list *rl)
+fdt_reg_to_rl(phandle_t node, struct resource_list *rl,
+    struct fdt_range *ranges, int ranges_count)
 {
-	u_long start, end, count;
+	u_long start, end, count, parent;
 	pcell_t *reg, *regptr;
 	pcell_t addr_cells, size_cells;
 	int tuple_size, tuples;
 	int i, rv;
-	long vaddr;
-	long busaddr, bussize;
 
 	if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
 		return (ENXIO);
-	fdt_get_range(OF_parent(node), 0, &busaddr, &bussize);
 
 	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
 	tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)&reg);
@@ -453,15 +494,22 @@ fdt_reg_to_rl(phandle_t node, struct res
 		reg += addr_cells + size_cells;
 
 		/* Calculate address range relative to base. */
-		start += busaddr;
-		if (bus_space_map(fdtbus_bs_tag, start, count, 0, &vaddr) != 0)
-			panic("Couldn't map the device memory");
-		end = vaddr + count - 1;
+		parent = 0;
+
+		if (ranges == NULL)
+			goto moveon;
+
+		parent = fdt_ranges_lookup(ranges, ranges_count, start, count);
+
+moveon:
+
+		start = parent + start;
+		end = start + count - 1;
 
-		debugf("reg addr start = %lx, end = %lx, count = %lx\n", vaddr,
+		debugf("reg addr start = %lx, end = %lx, count = %lx\n", start,
 		    end, count);
 
-		resource_list_add(rl, SYS_RES_MEMORY, i, vaddr, end,
+		resource_list_add(rl, SYS_RES_MEMORY, i, start, end,
 		    count);
 	}
 	rv = 0;

Modified: user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.h
==============================================================================
--- user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.h	Wed May 16 22:19:22 2012	(r235525)
+++ user/jceel/soc2012_armv6/sys/dev/fdt/fdt_common.h	Wed May 16 22:21:57 2012	(r235526)
@@ -40,6 +40,12 @@
 
 #define DI_MAX_INTR_NUM	8
 
+struct fdt_range {
+	u_long	base;
+	u_long	parent;
+	u_long	size;
+};
+
 struct fdt_pci_range {
 	u_long	base_pci;
 	u_long	base_parent;
@@ -90,8 +96,11 @@ int fdt_data_verify(void *, int);
 phandle_t fdt_find_compatible(phandle_t, const char *, int);
 int fdt_get_mem_regions(struct mem_region *, int *, uint32_t *);
 int fdt_get_phyaddr(phandle_t, device_t, int *, void **);
-int fdt_get_range(phandle_t, int, u_long *, u_long *);
 int fdt_immr_addr(vm_offset_t);
+int fdt_read_ranges(phandle_t node, struct fdt_range **ranges, int addr_cells,
+    int par_addr_cells, int size_cels);
+u_long fdt_ranges_lookup(struct fdt_range *ranges, int nranges, u_long addr,
+    u_long size);
 int fdt_regsize(phandle_t, u_long *, u_long *);
 int fdt_intr_decode(phandle_t, pcell_t *, int *, int *, int *);
 int fdt_intr_to_rl(phandle_t, struct resource_list *, struct fdt_sense_level *);
@@ -107,7 +116,7 @@ int fdt_pci_ranges_decode(phandle_t, str
     struct fdt_pci_range *);
 int fdt_pci_route_intr(int, int, int, int, struct fdt_pci_intr *, int *);
 int fdt_ranges_verify(pcell_t *, int, int, int, int);
-int fdt_reg_to_rl(phandle_t, struct resource_list *);
+int fdt_reg_to_rl(phandle_t, struct resource_list *, struct fdt_range *, int);
 int fdt_pm(phandle_t);
 
 #endif /* _FDT_COMMON_H_ */

Modified: user/jceel/soc2012_armv6/sys/dev/fdt/fdtbus.c
==============================================================================
--- user/jceel/soc2012_armv6/sys/dev/fdt/fdtbus.c	Wed May 16 22:19:22 2012	(r235525)
+++ user/jceel/soc2012_armv6/sys/dev/fdt/fdtbus.c	Wed May 16 22:21:57 2012	(r235526)
@@ -290,7 +290,7 @@ newbus_device_create(device_t dev_par, p
 
 	resource_list_init(&di->di_res);
 
-	if (fdt_reg_to_rl(node, &di->di_res)) {
+	if (fdt_reg_to_rl(node, &di->di_res, NULL, 0)) {
 		device_printf(child, "could not process 'reg' property\n");
 		newbus_device_destroy(child);
 		child = NULL;
@@ -378,9 +378,7 @@ newbus_pci_create(device_t dev_par, phan
 	}
 
 	/* Calculate address range relative to base. */
-	par_base &= 0x000ffffful;
-	start &= 0x000ffffful;
-	start += par_base + fdt_immr_va;
+	start += par_base;
 	if (count == 0)
 		count = par_size;
 	end = start + count - 1;
@@ -487,6 +485,7 @@ fdtbus_alloc_resource(device_t bus, devi
 	struct rman *rm;
 	struct fdtbus_devinfo *di;
 	struct resource_list_entry *rle;
+	bus_space_handle_t bsh;
 	int needactivate;
 
 	/*
@@ -541,8 +540,16 @@ fdtbus_alloc_resource(device_t bus, devi
 
 	if (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY) {
 		/* XXX endianess should be set based on SOC node */
+		if (bus_space_map(fdtbus_bs_tag, rman_get_start(res),
+		    rman_get_size(res), 0, &bsh))
+			printf("cannot map memory on 0x%lx\n",
+			    rman_get_start(res));
+
 		rman_set_bustag(res, fdtbus_bs_tag);
-		rman_set_bushandle(res, rman_get_start(res));
+		rman_set_bushandle(res, bsh);
+
+		debugf("%s: virtual register space: 0x%lx\n",
+		    device_get_name(child), bsh);
 	}
 
 	if (needactivate)

Modified: user/jceel/soc2012_armv6/sys/dev/fdt/simplebus.c
==============================================================================
--- user/jceel/soc2012_armv6/sys/dev/fdt/simplebus.c	Wed May 16 22:19:22 2012	(r235525)
+++ user/jceel/soc2012_armv6/sys/dev/fdt/simplebus.c	Wed May 16 22:21:57 2012	(r235526)
@@ -59,8 +59,10 @@ __FBSDID("$FreeBSD$");
 static MALLOC_DEFINE(M_SIMPLEBUS, "simplebus", "simplebus devices information");
 
 struct simplebus_softc {
-	int	sc_addr_cells;
-	int	sc_size_cells;
+	int			sc_addr_cells;
+	int			sc_size_cells;
+	struct fdt_range *	sc_ranges;
+	int			sc_ranges_count;
 };
 
 struct simplebus_devinfo {
@@ -129,6 +131,7 @@ static driver_t simplebus_driver = {
 devclass_t simplebus_devclass;
 
 DRIVER_MODULE(simplebus, fdtbus, simplebus_driver, simplebus_devclass, 0, 0);
+DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 0, 0);
 
 static int
 simplebus_probe(device_t dev)
@@ -146,11 +149,48 @@ static int
 simplebus_attach(device_t dev)
 {
 	device_t dev_child;
+	device_t parent;
 	struct simplebus_devinfo *di;
 	struct simplebus_softc *sc;
+	struct simplebus_softc *parent_sc;
 	phandle_t dt_node, dt_child;
+	int i, par_addr_cells;
 
 	sc = device_get_softc(dev);
+	dt_node = ofw_bus_get_node(dev);
+
+	if ((fdt_addrsize_cells(dt_node, &sc->sc_addr_cells, &sc->sc_size_cells)) != 0)
+		return (ENXIO);	
+
+	/*
+	 * Process 'ranges' property
+	 */
+	par_addr_cells = fdt_parent_addr_cells(dt_node);
+	if (par_addr_cells > 2)
+		return (ERANGE);
+
+	sc->sc_ranges_count = fdt_read_ranges(dt_node, &sc->sc_ranges,
+	    sc->sc_addr_cells, par_addr_cells, sc->sc_size_cells);
+	if (sc->sc_ranges_count <= 0)
+		device_printf(dev, "WARNING: could not read bus ranges.");
+
+	/*
+	 * Check if we're nested. If so, look up parent simplebus
+	 * ranges and merge it with ours.
+	 */
+	parent = device_get_parent(dev);
+
+	if (device_get_devclass(parent) == simplebus_devclass) {
+		parent_sc = device_get_softc(parent);
+		for (i = 0; i < sc->sc_ranges_count; i++) {
+			sc->sc_ranges[i].parent += fdt_ranges_lookup(
+			    parent_sc->sc_ranges,
+			    parent_sc->sc_ranges_count,
+			    sc->sc_ranges[i].parent,
+			    sc->sc_ranges[i].size);
+		}
+	}
+
 
 	/*
 	 * Walk simple-bus and add direct subordinates as our children.
@@ -175,7 +215,9 @@ simplebus_attach(device_t dev)
 		}
 
 		resource_list_init(&di->di_res);
-		if (fdt_reg_to_rl(dt_child, &di->di_res)) {
+
+		if (fdt_reg_to_rl(dt_child, &di->di_res, sc->sc_ranges,
+		    sc->sc_ranges_count)) {
 			device_printf(dev,
 			    "%s: could not process 'reg' "
 			    "property\n", di->di_ofw.obd_name);

Modified: user/jceel/soc2012_armv6/sys/dev/uart/uart_bus_fdt.c
==============================================================================
--- user/jceel/soc2012_armv6/sys/dev/uart/uart_bus_fdt.c	Wed May 16 22:19:22 2012	(r235525)
+++ user/jceel/soc2012_armv6/sys/dev/uart/uart_bus_fdt.c	Wed May 16 22:21:57 2012	(r235526)
@@ -137,10 +137,13 @@ uart_cpu_getdev(int devtype, struct uart
 {
 	char buf[64];
 	struct uart_class *class;
-	phandle_t node, chosen;
+	phandle_t node, parent, chosen;
 	pcell_t shift, br, rclk;
-	u_long start, size, pbase, psize;
-	int err;
+	u_long start, size;
+	struct fdt_range ranges[8];
+	struct fdt_range *rptr = ranges;
+	int err, addr_cells, par_addr_cells, size_cells;
+	int nranges;
 
 	uart_bus_space_mem = fdtbus_bs_tag;
 	uart_bus_space_io = NULL;
@@ -168,6 +171,21 @@ uart_cpu_getdev(int devtype, struct uart
 	if (OF_finddevice(buf) != node)
 		/* Only stdin == stdout is supported. */
 		return (ENXIO);
+
+	/*
+	 * Retrieve UART device parent bus
+	 */
+	if ((parent = OF_parent(node)) <= 0)
+		return (ENXIO);
+	if (fdt_addrsize_cells(parent, &addr_cells, &size_cells))
+		return (ENXIO);
+	if ((par_addr_cells = fdt_parent_addr_cells(parent)) > 2)
+		return (ENXIO);
+	nranges = fdt_read_ranges(parent, &rptr, addr_cells, 
+	    par_addr_cells, size_cells);
+	if (nranges <= 0)
+		return (ENXIO);
+
 	/*
 	 * Retrieve serial attributes.
 	 */
@@ -203,8 +221,11 @@ uart_cpu_getdev(int devtype, struct uart
 	if (err)
 		return (ENXIO);
 
-	fdt_get_range(OF_parent(node), 0, &pbase, &psize);
-	start += pbase;
+	/* 
+	 * XXX this will not work with uart sitting on
+	 * simplebus nested in other simplebus.
+	 */
+	start += fdt_ranges_lookup(ranges, nranges, start, size);
 
 	return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh));
 }


More information about the svn-src-user mailing list