Index: sys/dev/uart/uart_cpu_fdt.c =================================================================== --- sys/dev/uart/uart_cpu_fdt.c +++ sys/dev/uart/uart_cpu_fdt.c @@ -57,6 +57,8 @@ #ifdef __aarch64__ extern bus_space_tag_t fdtbus_bs_tag; #endif +static int uart_fdt_remap_addr(phandle_t node, u_long start, u_long size, + u_long *uart_addr); /* * UART console routines. @@ -64,6 +66,13 @@ bus_space_tag_t uart_bus_space_io; bus_space_tag_t uart_bus_space_mem; +/* Used by range translation code */ +struct simplebus_range { + uint64_t bus; + uint64_t host; + uint64_t size; +}; + int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) { @@ -215,13 +224,124 @@ 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")) { + /* Remap UART physical address through simplebus ranges */ + if (uart_fdt_remap_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)); } + +/* Limit size of stack buffers */ +#define MAX_RANGES 8 +#define MAX_CELLS_PER_RANGE 6 + +static int +uart_fdt_remap_addr(phandle_t node, u_long start, u_long size, + u_long *uart_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 UART */ + node = OF_parent(node); + + /* Work from the immediate parent of UART 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); + } + } + + *uart_addr = start; + return (0); +}