1 /* 2 * Copyright (C) 2002 Benno Rice. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/bus.h> 31 #include <sys/conf.h> 32 #include <sys/kernel.h> 33 34 #include <dev/ofw/openfirm.h> 35 #include <dev/ofw/ofw_pci.h> 36 37 #include <dev/pci/pcivar.h> 38 #include <dev/pci/pcireg.h> 39 40 #include <machine/bus.h> 41 #include <machine/md_var.h> 42 #include <machine/nexusvar.h> 43 #include <machine/resource.h> 44 45 #include <sys/rman.h> 46 47 #include <powerpc/ofw/ofw_pci.h> 48 #include <powerpc/powermac/uninorthvar.h> 49 50 #include <vm/vm.h> 51 #include <vm/pmap.h> 52 53 #include "pcib_if.h" 54 55 #define UNINORTH_DEBUG 0 56 57 /* 58 * Device interface. 59 */ 60 static int uninorth_probe(device_t); 61 static int uninorth_attach(device_t); 62 63 /* 64 * Bus interface. 65 */ 66 static int uninorth_read_ivar(device_t, device_t, int, 67 uintptr_t *); 68 static struct resource * uninorth_alloc_resource(device_t bus, 69 device_t child, int type, int *rid, u_long start, 70 u_long end, u_long count, u_int flags); 71 static int uninorth_activate_resource(device_t bus, device_t child, 72 int type, int rid, struct resource *res); 73 74 /* 75 * pcib interface. 76 */ 77 static int uninorth_maxslots(device_t); 78 static u_int32_t uninorth_read_config(device_t, u_int, u_int, u_int, 79 u_int, int); 80 static void uninorth_write_config(device_t, u_int, u_int, u_int, 81 u_int, u_int32_t, int); 82 static int uninorth_route_interrupt(device_t, device_t, int); 83 84 /* 85 * Local routines. 86 */ 87 static int uninorth_enable_config(struct uninorth_softc *, u_int, 88 u_int, u_int, u_int); 89 90 /* 91 * Driver methods. 92 */ 93 static device_method_t uninorth_methods[] = { 94 /* Device interface */ 95 DEVMETHOD(device_probe, uninorth_probe), 96 DEVMETHOD(device_attach, uninorth_attach), 97 98 /* Bus interface */ 99 DEVMETHOD(bus_print_child, bus_generic_print_child), 100 DEVMETHOD(bus_read_ivar, uninorth_read_ivar), 101 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 102 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 103 DEVMETHOD(bus_alloc_resource, uninorth_alloc_resource), 104 DEVMETHOD(bus_activate_resource, uninorth_activate_resource), 105 106 /* pcib interface */ 107 DEVMETHOD(pcib_maxslots, uninorth_maxslots), 108 DEVMETHOD(pcib_read_config, uninorth_read_config), 109 DEVMETHOD(pcib_write_config, uninorth_write_config), 110 DEVMETHOD(pcib_route_interrupt, uninorth_route_interrupt), 111 112 { 0, 0 } 113 }; 114 115 static driver_t uninorth_driver = { 116 "pcib", 117 uninorth_methods, 118 sizeof(struct uninorth_softc) 119 }; 120 121 static devclass_t uninorth_devclass; 122 123 DRIVER_MODULE(uninorth, nexus, uninorth_driver, uninorth_devclass, 0, 0); 124 125 static int 126 uninorth_probe(device_t dev) 127 { 128 char *type, *compatible; 129 130 type = nexus_get_device_type(dev); 131 compatible = nexus_get_compatible(dev); 132 133 if (type == NULL || compatible == NULL) 134 return (ENXIO); 135 136 if (strcmp(type, "pci") != 0 || strcmp(compatible, "uni-north") != 0) 137 return (ENXIO); 138 139 device_set_desc(dev, "Apple UniNorth Host-PCI bridge"); 140 return (0); 141 } 142 143 static int 144 uninorth_attach(device_t dev) 145 { 146 struct uninorth_softc *sc; 147 phandle_t node; 148 u_int32_t reg[2], busrange[2]; 149 struct uninorth_range *rp, *io, *mem[2]; 150 int nmem, i; 151 152 node = nexus_get_node(dev); 153 sc = device_get_softc(dev); 154 155 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) 156 return (ENXIO); 157 158 if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) 159 return (ENXIO); 160 161 sc->sc_dev = dev; 162 sc->sc_node = node; 163 sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[0] + 0x800000, PAGE_SIZE); 164 sc->sc_data = (vm_offset_t)pmap_mapdev(reg[0] + 0xc00000, PAGE_SIZE); 165 sc->sc_bus = busrange[0]; 166 167 ofw_pci_fixup(dev, sc->sc_bus, node); 168 169 bzero(sc->sc_range, sizeof(sc->sc_range)); 170 sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, 171 sizeof(sc->sc_range)); 172 173 if (sc->sc_nrange == -1) { 174 device_printf(dev, "could not get ranges\n"); 175 return (ENXIO); 176 } 177 178 sc->sc_range[6].pci_hi = 0; 179 io = NULL; 180 nmem = 0; 181 182 for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { 183 switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { 184 case OFW_PCI_PHYS_HI_SPACE_CONFIG: 185 break; 186 case OFW_PCI_PHYS_HI_SPACE_IO: 187 io = rp; 188 break; 189 case OFW_PCI_PHYS_HI_SPACE_MEM32: 190 mem[nmem] = rp; 191 nmem++; 192 break; 193 case OFW_PCI_PHYS_HI_SPACE_MEM64: 194 break; 195 } 196 } 197 198 if (io == NULL) { 199 device_printf(dev, "can't find io range\n"); 200 return (ENXIO); 201 } 202 sc->sc_io_rman.rm_type = RMAN_ARRAY; 203 sc->sc_io_rman.rm_descr = "UniNorth PCI I/O Ports"; 204 if (rman_init(&sc->sc_io_rman) != 0 || 205 rman_manage_region(&sc->sc_io_rman, io->pci_lo, 206 io->pci_lo + io->size_lo) != 0) { 207 device_printf(dev, "failed to set up io range management\n"); 208 return (ENXIO); 209 } 210 211 if (nmem == 0) { 212 device_printf(dev, "can't find mem ranges\n"); 213 return (ENXIO); 214 } 215 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 216 sc->sc_mem_rman.rm_descr = "UniNorth PCI Memory"; 217 if (rman_init(&sc->sc_mem_rman) != 0) { 218 device_printf(dev, 219 "failed to init mem range resources\n"); 220 return (ENXIO); 221 } 222 for (i = 0; i < nmem; i++) { 223 if (rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, 224 mem[i]->pci_lo + mem[i]->size_lo) != 0) { 225 device_printf(dev, 226 "failed to set up memory range management\n"); 227 return (ENXIO); 228 } 229 } 230 231 device_add_child(dev, "pci", device_get_unit(dev)); 232 return (bus_generic_attach(dev)); 233 } 234 235 static int 236 uninorth_maxslots(device_t dev) 237 { 238 239 return (PCI_SLOTMAX); 240 } 241 242 static u_int32_t 243 uninorth_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 244 int width) 245 { 246 struct uninorth_softc *sc; 247 vm_offset_t caoff; 248 249 sc = device_get_softc(dev); 250 caoff = sc->sc_data + (reg & 0x07); 251 252 if (uninorth_enable_config(sc, bus, slot, func, reg) != 0) { 253 switch (width) { 254 case 1: 255 return (in8rb(caoff)); 256 break; 257 case 2: 258 return (in16rb(caoff)); 259 break; 260 case 4: 261 return (in32rb(caoff)); 262 break; 263 } 264 } 265 266 return (0xffffffff); 267 } 268 269 static void 270 uninorth_write_config(device_t dev, u_int bus, u_int slot, u_int func, 271 u_int reg, u_int32_t val, int width) 272 { 273 struct uninorth_softc *sc; 274 vm_offset_t caoff; 275 276 sc = device_get_softc(dev); 277 caoff = sc->sc_data + (reg & 0x07); 278 279 if (uninorth_enable_config(sc, bus, slot, func, reg)) { 280 switch (width) { 281 case 1: 282 out8rb(caoff, val); 283 (void)in8rb(caoff); 284 break; 285 case 2: 286 out16rb(caoff, val); 287 (void)in16rb(caoff); 288 break; 289 case 4: 290 out32rb(caoff, val); 291 (void)in32rb(caoff); 292 break; 293 } 294 } 295 } 296 297 static int 298 uninorth_route_interrupt(device_t bus, device_t dev, int pin) 299 { 300 301 return (0); 302 } 303 304 static int 305 uninorth_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 306 { 307 struct uninorth_softc *sc; 308 309 sc = device_get_softc(dev); 310 311 switch (which) { 312 case PCIB_IVAR_BUS: 313 *result = sc->sc_bus; 314 return (0); 315 break; 316 } 317 318 return (ENOENT); 319 } 320 321 static struct resource * 322 uninorth_alloc_resource(device_t bus, device_t child, int type, int *rid, 323 u_long start, u_long end, u_long count, u_int flags) 324 { 325 struct uninorth_softc *sc; 326 struct resource *rv; 327 struct rman *rm; 328 bus_space_tag_t bt; 329 int needactivate; 330 331 needactivate = flags & RF_ACTIVE; 332 flags &= ~RF_ACTIVE; 333 334 sc = device_get_softc(bus); 335 336 switch (type) { 337 case SYS_RES_MEMORY: 338 rm = &sc->sc_mem_rman; 339 bt = sc->sc_memt; 340 break; 341 case SYS_RES_IRQ: 342 return (bus_alloc_resource(bus, type, rid, start, end, count, 343 flags)); 344 break; 345 default: 346 device_printf(bus, "unknown resource request from %s\n", 347 device_get_nameunit(child)); 348 return (NULL); 349 } 350 351 rv = rman_reserve_resource(rm, start, end, count, flags, child); 352 if (rv == NULL) { 353 device_printf(bus, "failed to reserve resource for %s\n", 354 device_get_nameunit(child)); 355 return (NULL); 356 } 357 358 rman_set_bustag(rv, bt); 359 rman_set_bushandle(rv, rman_get_start(rv)); 360 361 if (needactivate) { 362 if (bus_activate_resource(child, type, *rid, rv) != 0) { 363 device_printf(bus, 364 "failed to activate resource for %s\n", 365 device_get_nameunit(child)); 366 rman_release_resource(rv); 367 return (NULL); 368 } 369 } 370 371 return (rv); 372 } 373 374 static int 375 uninorth_activate_resource(device_t bus, device_t child, int type, int rid, 376 struct resource *res) 377 { 378 void *p; 379 380 if (type == SYS_RES_IRQ) 381 return (bus_activate_resource(bus, type, rid, res)); 382 383 if (type == SYS_RES_MEMORY) { 384 p = pmap_mapdev((vm_offset_t)rman_get_start(res), 385 (vm_size_t)rman_get_size(res)); 386 if (p == NULL) 387 return (ENOMEM); 388 rman_set_virtual(res, p); 389 rman_set_bushandle(res, (u_long)p); 390 } 391 392 return (rman_activate_resource(res)); 393 } 394 395 static int 396 uninorth_enable_config(struct uninorth_softc *sc, u_int bus, u_int slot, 397 u_int func, u_int reg) 398 { 399 u_int32_t cfgval; 400 401 if (sc->sc_bus == bus) { 402 /* 403 * No slots less than 11 on the primary bus 404 */ 405 if (slot < 11) 406 return (0); 407 408 cfgval = (1 << slot) | (func << 8) | (reg & 0xfc); 409 } else { 410 cfgval = (bus << 16) | (slot << 11) | (func << 8) | 411 (reg & 0xfc) | 1; 412 } 413 414 do { 415 out32rb(sc->sc_addr, cfgval); 416 } while (in32rb(sc->sc_addr) != cfgval); 417 418 return (1); 419 } 420 421 /* 422 * Driver to swallow UniNorth host bridges from the PCI bus side. 423 */ 424 static int 425 unhb_probe(device_t dev) 426 { 427 428 if (pci_get_class(dev) == PCIC_BRIDGE && 429 pci_get_subclass(dev) == PCIS_BRIDGE_HOST) { 430 device_set_desc(dev, "Host to PCI bridge"); 431 device_quiet(dev); 432 return (-10000); 433 } 434 435 return (ENXIO); 436 } 437 438 static int 439 unhb_attach(device_t dev) 440 { 441 442 return (0); 443 } 444 445 static device_method_t unhb_methods[] = { 446 /* Device interface */ 447 DEVMETHOD(device_probe, unhb_probe), 448 DEVMETHOD(device_attach, unhb_attach), 449 450 { 0, 0 } 451 }; 452 453 static driver_t unhb_driver = { 454 "unhb", 455 unhb_methods, 456 1, 457 }; 458 static devclass_t unhb_devclass; 459 460 DRIVER_MODULE(unhb, pci, unhb_driver, unhb_devclass, 0, 0); 461