1 /* 2 * Copyright (C) 2016 Cavium Inc. 3 * All rights reserved. 4 * 5 * Developed by Semihalf. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 #include "opt_platform.h" 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/malloc.h> 36 #include <sys/types.h> 37 #include <sys/sysctl.h> 38 #include <sys/kernel.h> 39 #include <sys/rman.h> 40 #include <sys/module.h> 41 #include <sys/bus.h> 42 #include <sys/endian.h> 43 #include <sys/cpuset.h> 44 45 #include <dev/ofw/openfirm.h> 46 #include <dev/ofw/ofw_bus.h> 47 #include <dev/ofw/ofw_bus_subr.h> 48 49 #include "thunder_pcie_common.h" 50 51 #define OFW_CELL_TO_UINT64(cell) \ 52 (((uint64_t)(*(cell)) << 32) | (uint64_t)(*((cell) + 1))) 53 54 #define SPACE_CODE_SHIFT 24 55 #define SPACE_CODE_MASK 0x3 56 #define SPACE_CODE_IO_SPACE 0x1 57 #define PROPS_CELL_SIZE 1 58 #define PCI_ADDR_CELL_SIZE 2 59 60 static int thunder_pcie_fdt_probe(device_t); 61 static int thunder_pcie_fdt_attach(device_t); 62 63 static struct resource * thunder_pcie_ofw_bus_alloc_res(device_t, device_t, 64 int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); 65 static int thunder_pcie_ofw_bus_rel_res(device_t, device_t, int, int, 66 struct resource *); 67 68 static const struct ofw_bus_devinfo *thunder_pcie_ofw_get_devinfo(device_t, 69 device_t); 70 71 static device_method_t thunder_pcie_fdt_methods[] = { 72 /* Device interface */ 73 DEVMETHOD(device_probe, thunder_pcie_fdt_probe), 74 DEVMETHOD(device_attach, thunder_pcie_fdt_attach), 75 76 /* Bus interface */ 77 DEVMETHOD(bus_alloc_resource, thunder_pcie_ofw_bus_alloc_res), 78 DEVMETHOD(bus_release_resource, thunder_pcie_ofw_bus_rel_res), 79 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 80 81 /* ofw_bus interface */ 82 DEVMETHOD(ofw_bus_get_devinfo, thunder_pcie_ofw_get_devinfo), 83 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 84 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 85 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 86 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 87 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 88 89 /* End */ 90 DEVMETHOD_END 91 }; 92 93 DEFINE_CLASS_1(pcib, thunder_pcie_fdt_driver, thunder_pcie_fdt_methods, 94 sizeof(struct thunder_pcie_softc), thunder_pcie_driver); 95 96 static devclass_t thunder_pcie_fdt_devclass; 97 98 DRIVER_MODULE(thunder_pcib, simplebus, thunder_pcie_fdt_driver, 99 thunder_pcie_fdt_devclass, 0, 0); 100 DRIVER_MODULE(thunder_pcib, ofwbus, thunder_pcie_fdt_driver, 101 thunder_pcie_fdt_devclass, 0, 0); 102 103 static int thunder_pcie_fdt_ranges(device_t); 104 static int thunder_pcie_ofw_bus_attach(device_t); 105 106 static int 107 thunder_pcie_fdt_probe(device_t dev) 108 { 109 110 if (!ofw_bus_status_okay(dev)) 111 return (ENXIO); 112 113 if (ofw_bus_is_compatible(dev, "cavium,thunder-pcie") || 114 ofw_bus_is_compatible(dev, "cavium,pci-host-thunder-ecam")) { 115 device_set_desc(dev, "Cavium Integrated PCI/PCI-E Controller"); 116 return (BUS_PROBE_DEFAULT); 117 } 118 119 return (ENXIO); 120 } 121 122 static int 123 thunder_pcie_fdt_attach(device_t dev) 124 { 125 int err; 126 127 /* Retrieve 'ranges' property from FDT */ 128 if (thunder_pcie_fdt_ranges(dev) != 0) 129 return (ENXIO); 130 131 err = thunder_pcie_ofw_bus_attach(dev); 132 if (err != 0) 133 return (err); 134 135 return (thunder_pcie_attach(dev)); 136 } 137 138 static __inline void 139 get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells) 140 { 141 142 *addr_cells = 2; 143 /* Find address cells if present */ 144 OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells)); 145 146 *size_cells = 2; 147 /* Find size cells if present */ 148 OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells)); 149 } 150 151 static int 152 thunder_pcie_fdt_ranges(device_t dev) 153 { 154 struct thunder_pcie_softc *sc; 155 phandle_t node; 156 pcell_t pci_addr_cells, parent_addr_cells, size_cells; 157 pcell_t attributes; 158 pcell_t *ranges_buf, *cell_ptr; 159 int cells_count, tuples_count; 160 int tuple; 161 int rv; 162 163 sc = device_get_softc(dev); 164 node = ofw_bus_get_node(dev); 165 166 get_addr_size_cells(node, &pci_addr_cells, &size_cells); 167 168 /* Find parent address cells if present */ 169 if (OF_getencprop(OF_parent(node), "#address-cells", 170 &parent_addr_cells, sizeof(parent_addr_cells)) < sizeof(parent_addr_cells)) 171 parent_addr_cells = 2; 172 173 /* Check if FDT format matches driver requirements */ 174 if ((parent_addr_cells != 2) || (pci_addr_cells != 3) || 175 (size_cells != 2)) { 176 device_printf(dev, 177 "Unexpected number of address or size cells in FDT " 178 " %d:%d:%d\n", 179 parent_addr_cells, pci_addr_cells, size_cells); 180 return (ENXIO); 181 } 182 183 cells_count = OF_getencprop_alloc(node, "ranges", 184 sizeof(pcell_t), (void **)&ranges_buf); 185 if (cells_count == -1) { 186 device_printf(dev, "Error parsing FDT 'ranges' property\n"); 187 return (ENXIO); 188 } 189 190 tuples_count = cells_count / 191 (pci_addr_cells + parent_addr_cells + size_cells); 192 if (tuples_count > RANGES_TUPLES_MAX) { 193 device_printf(dev, 194 "Unexpected number of 'ranges' tuples in FDT\n"); 195 rv = ENXIO; 196 goto out; 197 } 198 199 cell_ptr = ranges_buf; 200 201 for (tuple = 0; tuple < tuples_count; tuple++) { 202 /* 203 * TUPLE FORMAT: 204 * attributes - 32-bit attributes field 205 * PCI address - bus address combined of two cells in 206 * a following format: 207 * <ADDR MSB> <ADDR LSB> 208 * PA address - physical address combined of two cells in 209 * a following format: 210 * <ADDR MSB> <ADDR LSB> 211 * size - range size combined of two cells in 212 * a following format: 213 * <ADDR MSB> <ADDR LSB> 214 */ 215 attributes = *cell_ptr; 216 attributes = (attributes >> SPACE_CODE_SHIFT) & SPACE_CODE_MASK; 217 if (attributes == SPACE_CODE_IO_SPACE) { 218 /* Internal PCIe does not support IO space, ignore. */ 219 sc->ranges[tuple].phys_base = 0; 220 sc->ranges[tuple].size = 0; 221 cell_ptr += 222 (pci_addr_cells + parent_addr_cells + size_cells); 223 continue; 224 } 225 cell_ptr += PROPS_CELL_SIZE; 226 sc->ranges[tuple].pci_base = OFW_CELL_TO_UINT64(cell_ptr); 227 cell_ptr += PCI_ADDR_CELL_SIZE; 228 sc->ranges[tuple].phys_base = OFW_CELL_TO_UINT64(cell_ptr); 229 cell_ptr += parent_addr_cells; 230 sc->ranges[tuple].size = OFW_CELL_TO_UINT64(cell_ptr); 231 cell_ptr += size_cells; 232 233 if (bootverbose) { 234 device_printf(dev, 235 "\tPCI addr: 0x%jx, CPU addr: 0x%jx, Size: 0x%jx\n", 236 sc->ranges[tuple].pci_base, 237 sc->ranges[tuple].phys_base, 238 sc->ranges[tuple].size); 239 } 240 241 } 242 for (; tuple < RANGES_TUPLES_MAX; tuple++) { 243 /* zero-fill remaining tuples to mark empty elements in array */ 244 sc->ranges[tuple].phys_base = 0; 245 sc->ranges[tuple].size = 0; 246 } 247 248 rv = 0; 249 out: 250 free(ranges_buf, M_OFWPROP); 251 return (rv); 252 } 253 254 /* OFW bus interface */ 255 struct thunder_pcie_ofw_devinfo { 256 struct ofw_bus_devinfo di_dinfo; 257 struct resource_list di_rl; 258 }; 259 260 static const struct ofw_bus_devinfo * 261 thunder_pcie_ofw_get_devinfo(device_t bus __unused, device_t child) 262 { 263 struct thunder_pcie_ofw_devinfo *di; 264 265 di = device_get_ivars(child); 266 return (&di->di_dinfo); 267 } 268 269 static struct resource * 270 thunder_pcie_ofw_bus_alloc_res(device_t bus, device_t child, int type, int *rid, 271 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 272 { 273 struct thunder_pcie_softc *sc; 274 struct thunder_pcie_ofw_devinfo *di; 275 struct resource_list_entry *rle; 276 int i; 277 278 /* For PCIe devices that do not have FDT nodes, use PCIB method */ 279 if ((int)ofw_bus_get_node(child) <= 0) { 280 return (thunder_pcie_alloc_resource(bus, child, type, rid, 281 start, end, count, flags)); 282 } 283 284 sc = device_get_softc(bus); 285 286 if ((start == 0UL) && (end == ~0UL)) { 287 if ((di = device_get_ivars(child)) == NULL) 288 return (NULL); 289 if (type == SYS_RES_IOPORT) 290 type = SYS_RES_MEMORY; 291 292 /* Find defaults for this rid */ 293 rle = resource_list_find(&di->di_rl, type, *rid); 294 if (rle == NULL) 295 return (NULL); 296 297 start = rle->start; 298 end = rle->end; 299 count = rle->count; 300 } 301 302 if (type == SYS_RES_MEMORY) { 303 /* Remap through ranges property */ 304 for (i = 0; i < RANGES_TUPLES_MAX; i++) { 305 if (start >= sc->ranges[i].phys_base && end < 306 sc->ranges[i].pci_base + sc->ranges[i].size) { 307 start -= sc->ranges[i].phys_base; 308 start += sc->ranges[i].pci_base; 309 end -= sc->ranges[i].phys_base; 310 end += sc->ranges[i].pci_base; 311 break; 312 } 313 } 314 315 if (i == RANGES_TUPLES_MAX) { 316 device_printf(bus, "Could not map resource " 317 "%#lx-%#lx\n", start, end); 318 return (NULL); 319 } 320 } 321 322 return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 323 count, flags)); 324 } 325 326 static int 327 thunder_pcie_ofw_bus_rel_res(device_t bus, device_t child, int type, int rid, 328 struct resource *res) 329 { 330 331 /* For PCIe devices that do not have FDT nodes, use PCIB method */ 332 if ((int)ofw_bus_get_node(child) <= 0) { 333 return (thunder_pcie_release_resource(bus, 334 child, type, rid, res)); 335 } 336 337 return (bus_generic_release_resource(bus, child, type, rid, res)); 338 } 339 340 /* Helper functions */ 341 342 static int 343 thunder_pcie_ofw_bus_attach(device_t dev) 344 { 345 struct thunder_pcie_ofw_devinfo *di; 346 device_t child; 347 phandle_t parent, node; 348 pcell_t addr_cells, size_cells; 349 350 parent = ofw_bus_get_node(dev); 351 if (parent > 0) { 352 get_addr_size_cells(parent, &addr_cells, &size_cells); 353 /* Iterate through all bus subordinates */ 354 for (node = OF_child(parent); node > 0; node = OF_peer(node)) { 355 /* Allocate and populate devinfo. */ 356 di = malloc(sizeof(*di), M_THUNDER_PCIE, M_WAITOK | M_ZERO); 357 if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { 358 free(di, M_THUNDER_PCIE); 359 continue; 360 } 361 362 /* Initialize and populate resource list. */ 363 resource_list_init(&di->di_rl); 364 ofw_bus_reg_to_rl(dev, node, addr_cells, size_cells, 365 &di->di_rl); 366 ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL); 367 368 /* Add newbus device for this FDT node */ 369 child = device_add_child(dev, NULL, -1); 370 if (child == NULL) { 371 resource_list_free(&di->di_rl); 372 ofw_bus_gen_destroy_devinfo(&di->di_dinfo); 373 free(di, M_THUNDER_PCIE); 374 continue; 375 } 376 377 device_set_ivars(child, di); 378 } 379 } 380 381 return (0); 382 } 383