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_pci.c optional fdt pci dev/ofw/ofwbus.c optional fdt dev/ofw/openfirm.c optional fdt dev/ofw/openfirmio.c optional fdt Index: sys/dev/ofw/ofw_pci.h =================================================================== --- sys/dev/ofw/ofw_pci.h +++ sys/dev/ofw/ofw_pci.h @@ -35,6 +35,13 @@ #ifndef _DEV_OFW_OFW_PCI_H_ #define _DEV_OFW_OFW_PCI_H_ +#include + +#include + +#include +#include + /* * PCI Bus Binding to: * @@ -100,4 +107,26 @@ u_int32_t size_lo; }; +/* + * Parsed version of PCI range property + */ +struct ofw_pci_range { + bool nonrelocatable; + bool prefetchable; + bool aliased; + int space_code; /* In native format (not shifted) */ + /* use OFW_PCI_PHYS_HI_SPACE... */ + int bus; + int device; + int function; + int reg; + pci_addr_t pci_addr; /* PCI Address */ + bus_addr_t host_addr; /* Host bus address*/ + bus_size_t size; /* Range size */ +}; + +/* Get parsed 'ranges' property */ +int ofw_pci_get_ranges(device_t dev, phandle_t node, + struct ofw_pci_range **ranges); + #endif /* _DEV_OFW_OFW_PCI_H_ */ Index: sys/dev/ofw/ofw_pci.c =================================================================== --- /dev/null +++ sys/dev/ofw/ofw_pci.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2015 Michal Meloun + * 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. + */ + + #include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" +#include +#include +#include + +#include +#include + +/* + * Parse "ranges" property for given node. + * Returns number of ranges or -1 if error occurred. + */ +int +ofw_pci_get_ranges(device_t dev, phandle_t node, struct ofw_pci_range **ranges) +{ + int host_address_cells, pci_address_cells, size_cells; + pcell_t *base_ranges; + ssize_t nbase_ranges; + int nranges; + int i, j, k; + uint32_t flags; + uint64_t tmp; + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + + /* Get sizes. */ + if (OF_getencprop(OF_parent(node), "#address-cells", + &host_address_cells, sizeof(host_address_cells)) == -1) { + device_printf(dev, "Missing parent #address-cells " + "property, assuming <1>\n"); + host_address_cells = 1; + } + + if (OF_getencprop(node, "#address-cells", &pci_address_cells, + sizeof(pci_address_cells)) == -1) { + device_printf(dev, "Missing #address-cells " + "property, assuming <3>\n"); + pci_address_cells = 3; + } + if (OF_getencprop(node, "#size-cells", &size_cells, + sizeof(size_cells)) == -1) { + device_printf(dev, "Missing #size-cells " + "property, assuming <2>\n"); + size_cells = 2; + } + + /* + * Size checking is not trivial. For now, we only checks overflow + * of temporary variable. + * Note: It's possible to use 64-bit addressing scheme + * on 32-bit system if upper half of bits is zero. + */ + if (host_address_cells > (sizeof(tmp) / sizeof(pcell_t))) { + device_printf(dev, "Value of parent #address-cells " + "property is too big\n"); + return (-1); + } + /* One cell is used for flags */ + if (pci_address_cells > (sizeof(tmp) / sizeof(pcell_t) + 1)) { + device_printf(dev, "Value of #address-cells " + "property is too big\n"); + return (-1); + } + + if (size_cells > (sizeof(tmp) / sizeof(pcell_t))) { + device_printf(dev, "Value of #size-cells " + "property is too big\n"); + return (-1); + } + + nranges = nbase_ranges / sizeof(pcell_t) / + (pci_address_cells + host_address_cells + size_cells); + + *ranges = malloc(nranges * sizeof(struct ofw_pci_range), + M_OFWPROP, M_WAITOK); + base_ranges = malloc(nbase_ranges, M_OFWPROP, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < nranges; i++) { + flags = base_ranges[j++]; + (*ranges)[i].nonrelocatable = + flags & OFW_PCI_PHYS_HI_NONRELOCATABLE ? 1 : 0; + (*ranges)[i].prefetchable = + flags & OFW_PCI_PHYS_HI_PREFETCHABLE ? 1 : 0; + (*ranges)[i].aliased = + flags & OFW_PCI_PHYS_HI_ALIASED ? 1 : 0; + (*ranges)[i].space_code = flags & OFW_PCI_PHYS_HI_SPACEMASK; + (*ranges)[i].bus = OFW_PCI_PHYS_HI_BUS(flags); + (*ranges)[i].device = OFW_PCI_PHYS_HI_DEVICE(flags); + (*ranges)[i].function = OFW_PCI_PHYS_HI_FUNCTION(flags); + (*ranges)[i].reg = flags & OFW_PCI_PHYS_HI_REGISTERMASK; + + tmp = 0; + for (k = 0; k < pci_address_cells - 1; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + /* XXX overflow check? */ + (*ranges)[i].pci_addr = (pci_addr_t)tmp; + + tmp = 0; + for (k = 0; k < host_address_cells; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + /* XXX overflow check? */ + (*ranges)[i].host_addr = (bus_addr_t)tmp; + + tmp = 0; + for (k = 0; k < size_cells; k++) { + tmp <<= 32; + tmp |= base_ranges[j++]; + } + /* XXX overflow check? */ + (*ranges)[i].size = (bus_size_t)tmp; + } + + free(base_ranges, M_OFWPROP); + return (nranges); +}