Index: sys/arm/arm/ofw_machdep.c =================================================================== --- sys/arm/arm/ofw_machdep.c +++ sys/arm/arm/ofw_machdep.c @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 2015 Ian Lepore + * All rights excluded. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include + +#include +#include + +int +OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, + bus_space_handle_t *handle) +{ + bus_addr_t addr; + bus_size_t size; + pcell_t pci_hi; + int flags, res; + + res = ofw_reg_to_paddr(dev, regno, &addr, &size, &pci_hi); + if (res < 0) + return (res); + + /* + * Nothing special to do for PCI busses right now. + * This may need to be handled per-platform when it does come up. + */ +#ifdef notyet + if (pci_hi == OFW_PADDR_NOT_PCI) { + *tag = fdtbus_bs_tag; + flags = 0; + } else { + *tag = fdtbus_bs_tag; + flags = (pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) ? + BUS_SPACE_MAP_PREFETCHABLE: 0; + } +#else + *tag = fdtbus_bs_tag; + flags = 0; +#endif + return (bus_space_map(*tag, addr, size, flags, handle)); +} + Index: sys/arm/include/ofw_machdep.h =================================================================== --- sys/arm/include/ofw_machdep.h +++ sys/arm/include/ofw_machdep.h @@ -32,6 +32,9 @@ #ifndef _MACHINE_OFW_MACHDEP_H_ #define _MACHINE_OFW_MACHDEP_H_ +#include +#include +#include #include typedef uint32_t cell_t; Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2094,6 +2094,7 @@ dev/ofw/ofw_fdt.c optional fdt dev/ofw/ofw_if.m optional fdt dev/ofw/ofw_iicbus.c optional fdt iicbus +dev/ofw/ofw_subr.c optional fdt dev/ofw/ofwbus.c optional fdt dev/ofw/openfirm.c optional fdt dev/ofw/openfirmio.c optional fdt Index: sys/conf/files.arm =================================================================== --- sys/conf/files.arm +++ sys/conf/files.arm @@ -55,6 +55,7 @@ arm/arm/mp_machdep.c optional smp arm/arm/mpcore_timer.c optional mpcore_timer arm/arm/nexus.c standard +arm/arm/ofw_machdep.c optional fdt arm/arm/physmem.c standard kern/pic_if.m optional arm_intrng arm/arm/pl190.c optional pl190 Index: sys/conf/files.powerpc =================================================================== --- sys/conf/files.powerpc +++ sys/conf/files.powerpc @@ -58,6 +58,7 @@ dev/ofw/ofw_iicbus.c optional iicbus aim dev/ofw/ofwbus.c optional aim | fdt dev/ofw/ofw_standard.c optional aim powerpc +dev/ofw/ofw_subr.c optional aim powerpc dev/powermac_nvram/powermac_nvram.c optional powermac_nvram powermac dev/quicc/quicc_bfe_fdt.c optional quicc mpc85xx dev/scc/scc_bfe_macio.c optional scc powermac Index: sys/dev/ofw/ofw_subr.h =================================================================== --- sys/dev/ofw/ofw_subr.h +++ sys/dev/ofw/ofw_subr.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2015 Ian Lepore + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_OFW_OFW_SUBR_H_ +#define _DEV_OFW_OFW_SUBR_H_ + +/* + * Translate an address from the Nth tuple of a device node's reg properties to + * a physical memory address, by applying the range mappings from all ancestors. + * This assumes that all ancestor ranges are simple numerical offsets for which + * addition and subtraction operations will perform the required mapping (the + * bit-options in the high word of standard PCI properties are also handled). + * After the call, *pci_hi (if non-NULL) contains the phys.hi cell of the + * device's parent PCI bus, or OFW_PADDR_NOT_PCI if no PCI bus is involved. + * + * This is intended to be a helper function called by the platform-specific + * implementation of OF_decode_addr(), and not for direct use by device drivers. + */ +#define OFW_PADDR_NOT_PCI (~0) + +int ofw_reg_to_paddr(phandle_t _dev, int _regno, bus_addr_t *_paddr, + bus_size_t *_size, pcell_t *_pci_hi); + +#endif Index: sys/dev/ofw/ofw_subr.c =================================================================== --- sys/dev/ofw/ofw_subr.c +++ sys/dev/ofw/ofw_subr.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 2015 Ian Lepore + * All rights excluded. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +static void +get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip) +{ + char type[64]; + uint32_t addr, size; + int pci, res; + + res = OF_getencprop(node, "#address-cells", &addr, sizeof(addr)); + if (res == -1) + addr = 2; + res = OF_getencprop(node, "#size-cells", &size, sizeof(size)); + if (res == -1) + size = 1; + pci = 0; + if (addr == 3 && size == 2) { + res = OF_getprop(node, "device_type", type, sizeof(type)); + if (res != -1) { + type[sizeof(type) - 1] = '\0'; + pci = (strcmp(type, "pci") == 0) ? 1 : 0; + } + } + if (addrp != NULL) + *addrp = addr; + if (sizep != NULL) + *sizep = size; + if (pcip != NULL) + *pcip = pci; +} + +int +ofw_reg_to_paddr(phandle_t dev, int regno, bus_addr_t *paddr, + bus_size_t *psize, pcell_t *ppci_hi) +{ + pcell_t cell[32], pci_hi; + bus_addr_t addr, raddr, baddr; + bus_size_t size, rsize; + uint32_t c, nbridge, naddr, nsize; + phandle_t bridge, parent; + u_int spc, rspc; + int pci, pcib, res; + + /* Sanity checking. */ + if (dev == 0) + return (EINVAL); + bridge = OF_parent(dev); + if (bridge == 0) + return (EINVAL); + if (regno < 0) + return (EINVAL); + if (paddr == NULL || psize == NULL) + return (EINVAL); + + get_addr_props(bridge, &naddr, &nsize, &pci); + res = OF_getencprop(dev, (pci) ? "assigned-addresses" : "reg", + cell, sizeof(cell)); + if (res == -1) + return (ENXIO); + if (res % sizeof(cell[0])) + return (ENXIO); + res /= sizeof(cell[0]); + regno *= naddr + nsize; + if (regno + naddr + nsize > res) + return (EINVAL); + pci_hi = pci ? cell[regno] : OFW_PADDR_NOT_PCI; + spc = pci_hi & OFW_PCI_PHYS_HI_SPACEMASK; + addr = 0; + for (c = 0; c < naddr; c++) + addr = ((uint64_t)addr << 32) | cell[regno++]; + size = 0; + for (c = 0; c < nsize; c++) + size = ((uint64_t)size << 32) | cell[regno++]; + /* + * Map the address range in the bridge's decoding window as given + * by the "ranges" property. If a node doesn't have such property + * or the property is empty, we assume an identity mapping. The + * standard says a missing property indicates no possible mapping. + * This code is more liberal since the intended use is to get a + * console running early, and a printf to warn of malformed data + * is probably futile before the console is fully set up. + */ + parent = OF_parent(bridge); + while (parent != 0) { + get_addr_props(parent, &nbridge, NULL, &pcib); + res = OF_getencprop(bridge, "ranges", cell, sizeof(cell)); + if (res < 1) + goto next; + if (res % sizeof(cell[0])) + return (ENXIO); + /* Capture pci_hi if we just transitioned onto a PCI bus. */ + if (pcib && pci_hi == OFW_PADDR_NOT_PCI) { + pci_hi = cell[0]; + spc = pci_hi & OFW_PCI_PHYS_HI_SPACEMASK; + } + res /= sizeof(cell[0]); + regno = 0; + while (regno < res) { + rspc = (pci) + ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK + : OFW_PADDR_NOT_PCI; + if (rspc != spc) { + regno += naddr + nbridge + nsize; + continue; + } + raddr = 0; + for (c = 0; c < naddr; c++) + raddr = ((uint64_t)raddr << 32) | cell[regno++]; + rspc = (pcib) + ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK + : OFW_PADDR_NOT_PCI; + baddr = 0; + for (c = 0; c < nbridge; c++) + baddr = ((uint64_t)baddr << 32) | cell[regno++]; + rsize = 0; + for (c = 0; c < nsize; c++) + rsize = ((uint64_t)rsize << 32) | cell[regno++]; + if (addr < raddr || addr >= raddr + rsize) + continue; + addr = addr - raddr + baddr; + if (rspc != OFW_PADDR_NOT_PCI) + spc = rspc; + } + next: + bridge = parent; + parent = OF_parent(bridge); + get_addr_props(bridge, &naddr, &nsize, &pci); + } + + *paddr = addr; + *psize = size; + if (ppci_hi != NULL) + *ppci_hi = pci_hi; + + return (0); +} Index: sys/dev/ofw/openfirm.h =================================================================== --- sys/dev/ofw/openfirm.h +++ sys/dev/ofw/openfirm.h @@ -167,5 +167,16 @@ /* User interface functions */ int OF_interpret(const char *cmd, int nreturns, ...); +/* + * Decode the Nth register property of the given device node and create a bus + * space tag and handle for accessing it. This is for use in setting up things + * like early console output before newbus is available. The implementation is + * machine-dependent, and sparc uses a different function signature as well. + */ +#ifndef __sparc64__ +int OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *ptag, + bus_space_handle_t *phandle); +#endif + #endif /* _KERNEL */ #endif /* _DEV_OPENFIRM_H_ */ Index: sys/powerpc/include/ofw_machdep.h =================================================================== --- sys/powerpc/include/ofw_machdep.h +++ sys/powerpc/include/ofw_machdep.h @@ -37,7 +37,6 @@ typedef uint32_t cell_t; -int OF_decode_addr(phandle_t, int, bus_space_tag_t *, bus_space_handle_t *); void OF_getetheraddr(device_t dev, u_char *addr); void OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *)); Index: sys/powerpc/ofw/ofw_machdep.c =================================================================== --- sys/powerpc/ofw/ofw_machdep.c +++ sys/powerpc/ofw/ofw_machdep.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -565,135 +566,28 @@ * register in the address space of its parent and recursively walk * the device tree upward this way. */ -static void -OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip) -{ - char type[64]; - uint32_t addr, size; - int pci, res; - - res = OF_getencprop(node, "#address-cells", &addr, sizeof(addr)); - if (res == -1) - addr = 2; - res = OF_getencprop(node, "#size-cells", &size, sizeof(size)); - if (res == -1) - size = 1; - pci = 0; - if (addr == 3 && size == 2) { - res = OF_getprop(node, "device_type", type, sizeof(type)); - if (res != -1) { - type[sizeof(type) - 1] = '\0'; - pci = (strcmp(type, "pci") == 0) ? 1 : 0; - } - } - if (addrp != NULL) - *addrp = addr; - if (sizep != NULL) - *sizep = size; - if (pcip != NULL) - *pcip = pci; -} - int OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, bus_space_handle_t *handle) { - uint32_t cell[32]; - bus_addr_t addr, raddr, baddr; - bus_size_t size, rsize; - uint32_t c, nbridge, naddr, nsize; - phandle_t bridge, parent; - u_int spc, rspc, prefetch; - int pci, pcib, res; - - /* Sanity checking. */ - if (dev == 0) - return (EINVAL); - bridge = OF_parent(dev); - if (bridge == 0) - return (EINVAL); - if (regno < 0) - return (EINVAL); - if (tag == NULL || handle == NULL) - return (EINVAL); - - /* Assume big-endian unless we find a PCI device */ - *tag = &bs_be_tag; - - /* Get the requested register. */ - OF_get_addr_props(bridge, &naddr, &nsize, &pci); - if (pci) + bus_addr_t addr; + bus_size_t size; + pcell_t pci_hi; + int flags, res; + + res = ofw_reg_to_paddr(dev, regno, &addr, &size, &pci_hi); + if (res < 0) + return (res); + + if (pci_hi == OFW_PADDR_NOT_PCI) { + *tag = &bs_be_tag; + flags = 0; + } else { *tag = &bs_le_tag; - res = OF_getencprop(dev, (pci) ? "assigned-addresses" : "reg", - cell, sizeof(cell)); - if (res == -1) - return (ENXIO); - if (res % sizeof(cell[0])) - return (ENXIO); - res /= sizeof(cell[0]); - regno *= naddr + nsize; - if (regno + naddr + nsize > res) - return (EINVAL); - spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0; - prefetch = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_PREFETCHABLE : 0; - addr = 0; - for (c = 0; c < naddr; c++) - addr = ((uint64_t)addr << 32) | cell[regno++]; - size = 0; - for (c = 0; c < nsize; c++) - size = ((uint64_t)size << 32) | cell[regno++]; - - /* - * Map the address range in the bridge's decoding window as given - * by the "ranges" property. If a node doesn't have such property - * then no mapping is done. - */ - parent = OF_parent(bridge); - while (parent != 0) { - OF_get_addr_props(parent, &nbridge, NULL, &pcib); - if (pcib) - *tag = &bs_le_tag; - res = OF_getencprop(bridge, "ranges", cell, sizeof(cell)); - if (res == -1) - goto next; - if (res % sizeof(cell[0])) - return (ENXIO); - res /= sizeof(cell[0]); - regno = 0; - while (regno < res) { - rspc = (pci) - ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK - : ~0; - if (rspc != spc) { - regno += naddr + nbridge + nsize; - continue; - } - raddr = 0; - for (c = 0; c < naddr; c++) - raddr = ((uint64_t)raddr << 32) | cell[regno++]; - rspc = (pcib) - ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK - : ~0; - baddr = 0; - for (c = 0; c < nbridge; c++) - baddr = ((uint64_t)baddr << 32) | cell[regno++]; - rsize = 0; - for (c = 0; c < nsize; c++) - rsize = ((uint64_t)rsize << 32) | cell[regno++]; - if (addr < raddr || addr >= raddr + rsize) - continue; - addr = addr - raddr + baddr; - if (rspc != ~0) - spc = rspc; - } - - next: - bridge = parent; - parent = OF_parent(bridge); - OF_get_addr_props(bridge, &naddr, &nsize, &pci); + flags = (pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) ? + BUS_SPACE_MAP_PREFETCHABLE: 0; } - return (bus_space_map(*tag, addr, size, - prefetch ? BUS_SPACE_MAP_PREFETCHABLE : 0, handle)); + return (bus_space_map(*tag, addr, size, flags, handle)); }