1139825Simp/*- 2116964Sgrehan * Copyright 2003 by Peter Grehan. All rights reserved. 3116964Sgrehan * 4116964Sgrehan * Redistribution and use in source and binary forms, with or without 5116964Sgrehan * modification, are permitted provided that the following conditions 6116964Sgrehan * are met: 7116964Sgrehan * 1. Redistributions of source code must retain the above copyright 8116964Sgrehan * notice, this list of conditions and the following disclaimer. 9116964Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 10116964Sgrehan * notice, this list of conditions and the following disclaimer in the 11116964Sgrehan * documentation and/or other materials provided with the distribution. 12116964Sgrehan * 3. The name of the author may not be used to endorse or promote products 13116964Sgrehan * derived from this software without specific prior written permission. 14116964Sgrehan * 15116964Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16116964Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17116964Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18116964Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19116964Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20116964Sgrehan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21116964Sgrehan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22116964Sgrehan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23116964Sgrehan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24116964Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25116964Sgrehan * SUCH DAMAGE. 26116964Sgrehan */ 27116964Sgrehan 28227843Smarius#include <sys/cdefs.h> 29227843Smarius__FBSDID("$FreeBSD$"); 30227843Smarius 31116964Sgrehan#include <sys/param.h> 32116964Sgrehan#include <sys/systm.h> 33131102Sgrehan#include <sys/module.h> 34116964Sgrehan#include <sys/bus.h> 35116964Sgrehan#include <sys/conf.h> 36116964Sgrehan#include <sys/kernel.h> 37258696Snwhitehorn#include <sys/proc.h> 38297392Szbb#include <sys/rman.h> 39116964Sgrehan 40116964Sgrehan#include <dev/ofw/openfirm.h> 41295837Szbb#include <dev/ofw/ofw_pci.h> 42183882Snwhitehorn#include <dev/ofw/ofw_bus.h> 43186128Snwhitehorn#include <dev/ofw/ofw_bus_subr.h> 44297392Szbb#include <dev/ofw/ofwpci.h> 45116964Sgrehan 46116964Sgrehan#include <dev/pci/pcivar.h> 47116964Sgrehan#include <dev/pci/pcireg.h> 48116964Sgrehan 49116964Sgrehan#include <machine/bus.h> 50209298Snwhitehorn#include <machine/intr_machdep.h> 51116964Sgrehan#include <machine/md_var.h> 52174782Smarcel#include <machine/pio.h> 53116964Sgrehan#include <machine/resource.h> 54116964Sgrehan 55116964Sgrehan#include <powerpc/powermac/gracklevar.h> 56116964Sgrehan 57116964Sgrehan#include <vm/vm.h> 58116964Sgrehan#include <vm/pmap.h> 59116964Sgrehan 60116964Sgrehan#include "pcib_if.h" 61116964Sgrehan 62116964Sgrehan/* 63116964Sgrehan * Device interface. 64116964Sgrehan */ 65116964Sgrehanstatic int grackle_probe(device_t); 66116964Sgrehanstatic int grackle_attach(device_t); 67116964Sgrehan 68116964Sgrehan/* 69116964Sgrehan * pcib interface. 70116964Sgrehan */ 71116964Sgrehanstatic u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, 72116964Sgrehan u_int, int); 73116964Sgrehanstatic void grackle_write_config(device_t, u_int, u_int, u_int, 74116964Sgrehan u_int, u_int32_t, int); 75116964Sgrehan 76116964Sgrehan/* 77116964Sgrehan * Local routines. 78116964Sgrehan */ 79116964Sgrehanstatic int grackle_enable_config(struct grackle_softc *, u_int, 80116964Sgrehan u_int, u_int, u_int); 81116964Sgrehanstatic void grackle_disable_config(struct grackle_softc *); 82258696Snwhitehornstatic int badaddr(void *, size_t); 83116964Sgrehan 84116964Sgrehan/* 85116964Sgrehan * Driver methods. 86116964Sgrehan */ 87116964Sgrehanstatic device_method_t grackle_methods[] = { 88116964Sgrehan /* Device interface */ 89116964Sgrehan DEVMETHOD(device_probe, grackle_probe), 90116964Sgrehan DEVMETHOD(device_attach, grackle_attach), 91116964Sgrehan 92116964Sgrehan /* pcib interface */ 93116964Sgrehan DEVMETHOD(pcib_read_config, grackle_read_config), 94116964Sgrehan DEVMETHOD(pcib_write_config, grackle_write_config), 95116964Sgrehan 96227843Smarius DEVMETHOD_END 97116964Sgrehan}; 98116964Sgrehan 99116964Sgrehanstatic devclass_t grackle_devclass; 100230994SnwhitehornDEFINE_CLASS_1(pcib, grackle_driver, grackle_methods, 101230993Snwhitehorn sizeof(struct grackle_softc), ofw_pci_driver); 102261513SnwhitehornDRIVER_MODULE(grackle, ofwbus, grackle_driver, grackle_devclass, 0, 0); 103116964Sgrehan 104116964Sgrehanstatic int 105116964Sgrehangrackle_probe(device_t dev) 106116964Sgrehan{ 107183882Snwhitehorn const char *type, *compatible; 108116964Sgrehan 109183882Snwhitehorn type = ofw_bus_get_type(dev); 110183882Snwhitehorn compatible = ofw_bus_get_compat(dev); 111116964Sgrehan 112116964Sgrehan if (type == NULL || compatible == NULL) 113116964Sgrehan return (ENXIO); 114116964Sgrehan 115116964Sgrehan if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) 116116964Sgrehan return (ENXIO); 117116964Sgrehan 118116964Sgrehan device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); 119116964Sgrehan return (0); 120116964Sgrehan} 121116964Sgrehan 122116964Sgrehanstatic int 123116964Sgrehangrackle_attach(device_t dev) 124116964Sgrehan{ 125116964Sgrehan struct grackle_softc *sc; 126116964Sgrehan 127116964Sgrehan sc = device_get_softc(dev); 128116964Sgrehan 129116964Sgrehan /* 130116964Sgrehan * The Grackle PCI config addr/data registers are actually in 131116964Sgrehan * PCI space, but since they are needed to actually probe the 132116964Sgrehan * PCI bus, use the fact that they are also available directly 133116964Sgrehan * on the processor bus and map them 134116964Sgrehan */ 135116964Sgrehan sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); 136116964Sgrehan sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); 137116964Sgrehan 138230993Snwhitehorn return (ofw_pci_attach(dev)); 139116964Sgrehan} 140116964Sgrehan 141116964Sgrehanstatic u_int32_t 142116964Sgrehangrackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 143116964Sgrehan int width) 144116964Sgrehan{ 145116964Sgrehan struct grackle_softc *sc; 146116964Sgrehan vm_offset_t caoff; 147116964Sgrehan u_int32_t retval = 0xffffffff; 148116964Sgrehan 149116964Sgrehan sc = device_get_softc(dev); 150116964Sgrehan caoff = sc->sc_data + (reg & 0x03); 151116964Sgrehan 152116964Sgrehan if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { 153116964Sgrehan 154116964Sgrehan /* 155116964Sgrehan * Config probes to non-existent devices on the 156116964Sgrehan * secondary bus generates machine checks. Be sure 157116964Sgrehan * to catch these. 158116964Sgrehan */ 159116964Sgrehan if (bus > 0) { 160125688Sgrehan if (badaddr((void *)sc->sc_data, 4)) { 161116964Sgrehan return (retval); 162116964Sgrehan } 163116964Sgrehan } 164125688Sgrehan 165116964Sgrehan switch (width) { 166116964Sgrehan case 1: 167116964Sgrehan retval = (in8rb(caoff)); 168116964Sgrehan break; 169116964Sgrehan case 2: 170116964Sgrehan retval = (in16rb(caoff)); 171116964Sgrehan break; 172116964Sgrehan case 4: 173116964Sgrehan retval = (in32rb(caoff)); 174116964Sgrehan break; 175116964Sgrehan } 176116964Sgrehan } 177116964Sgrehan grackle_disable_config(sc); 178116964Sgrehan 179116964Sgrehan return (retval); 180116964Sgrehan} 181116964Sgrehan 182116964Sgrehanstatic void 183116964Sgrehangrackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, 184116964Sgrehan u_int reg, u_int32_t val, int width) 185116964Sgrehan{ 186116964Sgrehan struct grackle_softc *sc; 187116964Sgrehan vm_offset_t caoff; 188116964Sgrehan 189116964Sgrehan sc = device_get_softc(dev); 190116964Sgrehan caoff = sc->sc_data + (reg & 0x03); 191116964Sgrehan 192116964Sgrehan if (grackle_enable_config(sc, bus, slot, func, reg)) { 193116964Sgrehan switch (width) { 194116964Sgrehan case 1: 195116964Sgrehan out8rb(caoff, val); 196116964Sgrehan (void)in8rb(caoff); 197116964Sgrehan break; 198116964Sgrehan case 2: 199116964Sgrehan out16rb(caoff, val); 200116964Sgrehan (void)in16rb(caoff); 201116964Sgrehan break; 202116964Sgrehan case 4: 203116964Sgrehan out32rb(caoff, val); 204116964Sgrehan (void)in32rb(caoff); 205116964Sgrehan break; 206116964Sgrehan } 207116964Sgrehan } 208116964Sgrehan grackle_disable_config(sc); 209116964Sgrehan} 210116964Sgrehan 211116964Sgrehanstatic int 212116964Sgrehangrackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, 213116964Sgrehan u_int func, u_int reg) 214116964Sgrehan{ 215116964Sgrehan u_int32_t cfgval; 216116964Sgrehan 217116964Sgrehan /* 218116964Sgrehan * Unlike UniNorth, the format of the config word is the same 219116964Sgrehan * for local (0) and remote busses. 220116964Sgrehan */ 221116964Sgrehan cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) 222116964Sgrehan | GRACKLE_CFG_ENABLE; 223125688Sgrehan 224116964Sgrehan out32rb(sc->sc_addr, cfgval); 225116964Sgrehan (void) in32rb(sc->sc_addr); 226116964Sgrehan 227116964Sgrehan return (1); 228116964Sgrehan} 229116964Sgrehan 230116964Sgrehanstatic void 231116964Sgrehangrackle_disable_config(struct grackle_softc *sc) 232116964Sgrehan{ 233116964Sgrehan /* 234116964Sgrehan * Clear the GRACKLE_CFG_ENABLE bit to prevent stray 235116964Sgrehan * accesses from causing config cycles 236116964Sgrehan */ 237116964Sgrehan out32rb(sc->sc_addr, 0); 238116964Sgrehan} 239116964Sgrehan 240258696Snwhitehornstatic int 241258696Snwhitehornbadaddr(void *addr, size_t size) 242258696Snwhitehorn{ 243258696Snwhitehorn struct thread *td; 244293636Snwhitehorn jmp_buf env, *oldfaultbuf; 245258696Snwhitehorn int x; 246258696Snwhitehorn 247258696Snwhitehorn /* Get rid of any stale machine checks that have been waiting. */ 248258696Snwhitehorn __asm __volatile ("sync; isync"); 249258696Snwhitehorn 250258696Snwhitehorn td = curthread; 251258696Snwhitehorn 252258696Snwhitehorn oldfaultbuf = td->td_pcb->pcb_onfault; 253293636Snwhitehorn td->td_pcb->pcb_onfault = &env; 254293636Snwhitehorn if (setjmp(env)) { 255258696Snwhitehorn td->td_pcb->pcb_onfault = oldfaultbuf; 256258696Snwhitehorn __asm __volatile ("sync"); 257258696Snwhitehorn return 1; 258258696Snwhitehorn } 259258696Snwhitehorn 260258696Snwhitehorn __asm __volatile ("sync"); 261258696Snwhitehorn 262258696Snwhitehorn switch (size) { 263258696Snwhitehorn case 1: 264258696Snwhitehorn x = *(volatile int8_t *)addr; 265258696Snwhitehorn break; 266258696Snwhitehorn case 2: 267258696Snwhitehorn x = *(volatile int16_t *)addr; 268258696Snwhitehorn break; 269258696Snwhitehorn case 4: 270258696Snwhitehorn x = *(volatile int32_t *)addr; 271258696Snwhitehorn break; 272258696Snwhitehorn default: 273258696Snwhitehorn panic("badaddr: invalid size (%zd)", size); 274258696Snwhitehorn } 275258696Snwhitehorn 276258696Snwhitehorn /* Make sure we took the machine check, if we caused one. */ 277258696Snwhitehorn __asm __volatile ("sync; isync"); 278258696Snwhitehorn 279258696Snwhitehorn td->td_pcb->pcb_onfault = oldfaultbuf; 280258696Snwhitehorn __asm __volatile ("sync"); /* To be sure. */ 281258696Snwhitehorn 282258696Snwhitehorn return (0); 283258696Snwhitehorn} 284258696Snwhitehorn 285116964Sgrehan/* 286116964Sgrehan * Driver to swallow Grackle host bridges from the PCI bus side. 287116964Sgrehan */ 288116964Sgrehanstatic int 289116964Sgrehangrackle_hb_probe(device_t dev) 290116964Sgrehan{ 291116964Sgrehan 292127703Sgrehan if (pci_get_devid(dev) == 0x00021057) { 293127703Sgrehan device_set_desc(dev, "Grackle Host to PCI bridge"); 294116964Sgrehan device_quiet(dev); 295127703Sgrehan return (0); 296116964Sgrehan } 297116964Sgrehan 298116964Sgrehan return (ENXIO); 299116964Sgrehan} 300116964Sgrehan 301116964Sgrehanstatic int 302116964Sgrehangrackle_hb_attach(device_t dev) 303116964Sgrehan{ 304116964Sgrehan 305116964Sgrehan return (0); 306116964Sgrehan} 307116964Sgrehan 308116964Sgrehanstatic device_method_t grackle_hb_methods[] = { 309116964Sgrehan /* Device interface */ 310116964Sgrehan DEVMETHOD(device_probe, grackle_hb_probe), 311116964Sgrehan DEVMETHOD(device_attach, grackle_hb_attach), 312116964Sgrehan 313116964Sgrehan { 0, 0 } 314116964Sgrehan}; 315116964Sgrehan 316116964Sgrehanstatic driver_t grackle_hb_driver = { 317116964Sgrehan "grackle_hb", 318116964Sgrehan grackle_hb_methods, 319116964Sgrehan 1, 320116964Sgrehan}; 321116964Sgrehanstatic devclass_t grackle_hb_devclass; 322116964Sgrehan 323116964SgrehanDRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); 324