Index: sys/dev/fdt/simplebus.h =================================================================== --- sys/dev/fdt/simplebus.h +++ sys/dev/fdt/simplebus.h @@ -61,4 +61,6 @@ const char *name, int unit, struct simplebus_devinfo *di); struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev, phandle_t node, struct simplebus_devinfo *di); +int simplebus_get_base_addr(phandle_t node, u_long start, u_long size, + u_long *phys_addr); #endif /* _FDT_SIMPLEBUS_H */ Index: sys/dev/fdt/simplebus.c =================================================================== --- sys/dev/fdt/simplebus.c +++ sys/dev/fdt/simplebus.c @@ -39,6 +39,7 @@ #include #include +#include /* * Bus interface. @@ -421,3 +422,108 @@ rv += bus_print_child_footer(bus, child); return (rv); } + +/* Limit size of stack buffers */ +#define MAX_RANGES 8 +#define MAX_CELLS_PER_RANGE 6 + +/* Calculate absolute physical base address of a device node */ +int +simplebus_get_base_addr(phandle_t node, u_long start, u_long size, + u_long *phys_addr) +{ + struct simplebus_range ranges[MAX_RANGES]; + cell_t base_ranges[MAX_CELLS_PER_RANGE * MAX_RANGES]; + ssize_t nbase_ranges, rv; + int i, j, k, nranges; + pcell_t addr_cells, host_address_cells, size_cells; + u_long end; + + end = start + size - 1; + /* Get parent bus of the node */ + node = OF_parent(node); + + /* Work from the immediate parent of the device node up to the root node + * translating the base address through 'ranges' properties of every + * simple-bus parent, the same way simplebus_alloc_resource() does it. + */ + for (; node != 0; node = OF_parent(node)) { + if (!fdt_is_compatible(node, "simple-bus")) + continue; + + rv = OF_getencprop(node, "#address-cells", &addr_cells, + sizeof(addr_cells)); + if (rv <= 0) + addr_cells = 2; /* Default size used by simplebus */ + + rv = OF_getencprop(OF_parent(node), "#address-cells", + &host_address_cells, sizeof(host_address_cells)); + if (rv <= 0) + host_address_cells = 2; /* Default size */ + + rv = OF_getencprop(node, "#size-cells", &size_cells, + sizeof(size_cells)); + if (rv <= 0) + size_cells = 1; /* Default size */ + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges < 0) + return (-1); + + if (nbase_ranges == 0) { + /* Empty ranges; property means 1:1 mapping */ + continue; + } + + nranges = nbase_ranges / sizeof(cell_t) / + (addr_cells + host_address_cells + size_cells); + + if (nranges == 0 || (nranges > MAX_RANGES)) + return (-1); + + /* Copy ranges data into local buffer */ + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + /* Parse bus and host addresses along with sizes */ + for (i = 0, j = 0; i < nranges; i++) { + ranges[i].bus = 0; + for (k = 0; k < addr_cells; k++) { + ranges[i].bus <<= 32; + ranges[i].bus |= base_ranges[j++]; + } + ranges[i].host = 0; + for (k = 0; k < host_address_cells; k++) { + ranges[i].host <<= 32; + ranges[i].host |= base_ranges[j++]; + } + ranges[i].size = 0; + for (k = 0; k < size_cells; k++) { + ranges[i].size <<= 32; + ranges[i].size |= base_ranges[j++]; + } + } + + /* Remap through ranges property */ + for (i = 0; i < nranges; i++) { + if (start >= ranges[i].bus && end < + (ranges[i].bus + ranges[i].size)) { + /* Found matching range entry */ + start -= ranges[i].bus; + start += ranges[i].host; + end -= ranges[i].bus; + end += ranges[i].host; + break; + } + } + + if (i == nranges) { + /* Could not find corresponding range entry */ + printf("%s: address space translation error\n", + __func__); + return (-1); + } + } + + *phys_addr = start; + return (0); +} Index: sys/dev/uart/uart_cpu_fdt.c =================================================================== --- sys/dev/uart/uart_cpu_fdt.c +++ sys/dev/uart/uart_cpu_fdt.c @@ -47,6 +47,7 @@ #endif #include +#include #include #include #include @@ -215,13 +216,20 @@ di->bas.bst = uart_bus_space_mem; err = fdt_regsize(node, &start, &size); - if (err) + if (err != 0) return (ENXIO); - err = fdt_get_range(OF_parent(node), 0, &pbase, &psize); - if (err) - pbase = 0; - start += pbase; + if (fdt_is_compatible(OF_parent(node), "simple-bus")) { + /* Get the physical base address of UART registers */ + if (simplebus_get_base_addr(node, start, size, &start)) + return (ENXIO); + } else { + err = fdt_get_range(OF_parent(node), 0, &pbase, &psize); + if (err != 0) + pbase = 0; + + start += pbase; + } return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh)); }