Date: Wed, 16 May 2012 22:21:58 +0000 (UTC) From: Jakub Wojciech Klama <jceel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r235526 - in user/jceel/soc2012_armv6/sys: arm/ti dev/fdt dev/uart Message-ID: <201205162221.q4GMLw94065028@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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 **)®); @@ -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)); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201205162221.q4GMLw94065028>