grackle.c revision 295837
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: head/sys/powerpc/powermac/grackle.c 295837 2016-02-20 12:28:20Z zbb $"); 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> 38116964Sgrehan 39116964Sgrehan#include <dev/ofw/openfirm.h> 40295837Szbb#include <dev/ofw/ofw_pci.h> 41183882Snwhitehorn#include <dev/ofw/ofw_bus.h> 42186128Snwhitehorn#include <dev/ofw/ofw_bus_subr.h> 43116964Sgrehan 44116964Sgrehan#include <dev/pci/pcivar.h> 45116964Sgrehan#include <dev/pci/pcireg.h> 46116964Sgrehan 47116964Sgrehan#include <machine/bus.h> 48209298Snwhitehorn#include <machine/intr_machdep.h> 49116964Sgrehan#include <machine/md_var.h> 50174782Smarcel#include <machine/pio.h> 51116964Sgrehan#include <machine/resource.h> 52116964Sgrehan 53116964Sgrehan#include <sys/rman.h> 54116964Sgrehan 55295837Szbb#include <powerpc/ofw/ofw_pci.h> 56116964Sgrehan#include <powerpc/powermac/gracklevar.h> 57116964Sgrehan 58116964Sgrehan#include <vm/vm.h> 59116964Sgrehan#include <vm/pmap.h> 60116964Sgrehan 61116964Sgrehan#include "pcib_if.h" 62116964Sgrehan 63116964Sgrehan/* 64116964Sgrehan * Device interface. 65116964Sgrehan */ 66116964Sgrehanstatic int grackle_probe(device_t); 67116964Sgrehanstatic int grackle_attach(device_t); 68116964Sgrehan 69116964Sgrehan/* 70116964Sgrehan * pcib interface. 71116964Sgrehan */ 72116964Sgrehanstatic u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, 73116964Sgrehan u_int, int); 74116964Sgrehanstatic void grackle_write_config(device_t, u_int, u_int, u_int, 75116964Sgrehan u_int, u_int32_t, int); 76116964Sgrehan 77116964Sgrehan/* 78116964Sgrehan * Local routines. 79116964Sgrehan */ 80116964Sgrehanstatic int grackle_enable_config(struct grackle_softc *, u_int, 81116964Sgrehan u_int, u_int, u_int); 82116964Sgrehanstatic void grackle_disable_config(struct grackle_softc *); 83258696Snwhitehornstatic int badaddr(void *, size_t); 84116964Sgrehan 85116964Sgrehan/* 86116964Sgrehan * Driver methods. 87116964Sgrehan */ 88116964Sgrehanstatic device_method_t grackle_methods[] = { 89116964Sgrehan /* Device interface */ 90116964Sgrehan DEVMETHOD(device_probe, grackle_probe), 91116964Sgrehan DEVMETHOD(device_attach, grackle_attach), 92116964Sgrehan 93116964Sgrehan /* pcib interface */ 94116964Sgrehan DEVMETHOD(pcib_read_config, grackle_read_config), 95116964Sgrehan DEVMETHOD(pcib_write_config, grackle_write_config), 96116964Sgrehan 97227843Smarius DEVMETHOD_END 98116964Sgrehan}; 99116964Sgrehan 100116964Sgrehanstatic devclass_t grackle_devclass; 101230994SnwhitehornDEFINE_CLASS_1(pcib, grackle_driver, grackle_methods, 102230993Snwhitehorn sizeof(struct grackle_softc), ofw_pci_driver); 103261513SnwhitehornDRIVER_MODULE(grackle, ofwbus, grackle_driver, grackle_devclass, 0, 0); 104116964Sgrehan 105116964Sgrehanstatic int 106116964Sgrehangrackle_probe(device_t dev) 107116964Sgrehan{ 108183882Snwhitehorn const char *type, *compatible; 109116964Sgrehan 110183882Snwhitehorn type = ofw_bus_get_type(dev); 111183882Snwhitehorn compatible = ofw_bus_get_compat(dev); 112116964Sgrehan 113116964Sgrehan if (type == NULL || compatible == NULL) 114116964Sgrehan return (ENXIO); 115116964Sgrehan 116116964Sgrehan if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) 117116964Sgrehan return (ENXIO); 118116964Sgrehan 119116964Sgrehan device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); 120116964Sgrehan return (0); 121116964Sgrehan} 122116964Sgrehan 123116964Sgrehanstatic int 124116964Sgrehangrackle_attach(device_t dev) 125116964Sgrehan{ 126116964Sgrehan struct grackle_softc *sc; 127116964Sgrehan 128116964Sgrehan sc = device_get_softc(dev); 129116964Sgrehan 130116964Sgrehan /* 131116964Sgrehan * The Grackle PCI config addr/data registers are actually in 132116964Sgrehan * PCI space, but since they are needed to actually probe the 133116964Sgrehan * PCI bus, use the fact that they are also available directly 134116964Sgrehan * on the processor bus and map them 135116964Sgrehan */ 136116964Sgrehan sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); 137116964Sgrehan sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); 138116964Sgrehan 139230993Snwhitehorn return (ofw_pci_attach(dev)); 140116964Sgrehan} 141116964Sgrehan 142116964Sgrehanstatic u_int32_t 143116964Sgrehangrackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 144116964Sgrehan int width) 145116964Sgrehan{ 146116964Sgrehan struct grackle_softc *sc; 147116964Sgrehan vm_offset_t caoff; 148116964Sgrehan u_int32_t retval = 0xffffffff; 149116964Sgrehan 150116964Sgrehan sc = device_get_softc(dev); 151116964Sgrehan caoff = sc->sc_data + (reg & 0x03); 152116964Sgrehan 153116964Sgrehan if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { 154116964Sgrehan 155116964Sgrehan /* 156116964Sgrehan * Config probes to non-existent devices on the 157116964Sgrehan * secondary bus generates machine checks. Be sure 158116964Sgrehan * to catch these. 159116964Sgrehan */ 160116964Sgrehan if (bus > 0) { 161125688Sgrehan if (badaddr((void *)sc->sc_data, 4)) { 162116964Sgrehan return (retval); 163116964Sgrehan } 164116964Sgrehan } 165125688Sgrehan 166116964Sgrehan switch (width) { 167116964Sgrehan case 1: 168116964Sgrehan retval = (in8rb(caoff)); 169116964Sgrehan break; 170116964Sgrehan case 2: 171116964Sgrehan retval = (in16rb(caoff)); 172116964Sgrehan break; 173116964Sgrehan case 4: 174116964Sgrehan retval = (in32rb(caoff)); 175116964Sgrehan break; 176116964Sgrehan } 177116964Sgrehan } 178116964Sgrehan grackle_disable_config(sc); 179116964Sgrehan 180116964Sgrehan return (retval); 181116964Sgrehan} 182116964Sgrehan 183116964Sgrehanstatic void 184116964Sgrehangrackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, 185116964Sgrehan u_int reg, u_int32_t val, int width) 186116964Sgrehan{ 187116964Sgrehan struct grackle_softc *sc; 188116964Sgrehan vm_offset_t caoff; 189116964Sgrehan 190116964Sgrehan sc = device_get_softc(dev); 191116964Sgrehan caoff = sc->sc_data + (reg & 0x03); 192116964Sgrehan 193116964Sgrehan if (grackle_enable_config(sc, bus, slot, func, reg)) { 194116964Sgrehan switch (width) { 195116964Sgrehan case 1: 196116964Sgrehan out8rb(caoff, val); 197116964Sgrehan (void)in8rb(caoff); 198116964Sgrehan break; 199116964Sgrehan case 2: 200116964Sgrehan out16rb(caoff, val); 201116964Sgrehan (void)in16rb(caoff); 202116964Sgrehan break; 203116964Sgrehan case 4: 204116964Sgrehan out32rb(caoff, val); 205116964Sgrehan (void)in32rb(caoff); 206116964Sgrehan break; 207116964Sgrehan } 208116964Sgrehan } 209116964Sgrehan grackle_disable_config(sc); 210116964Sgrehan} 211116964Sgrehan 212116964Sgrehanstatic int 213116964Sgrehangrackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, 214116964Sgrehan u_int func, u_int reg) 215116964Sgrehan{ 216116964Sgrehan u_int32_t cfgval; 217116964Sgrehan 218116964Sgrehan /* 219116964Sgrehan * Unlike UniNorth, the format of the config word is the same 220116964Sgrehan * for local (0) and remote busses. 221116964Sgrehan */ 222116964Sgrehan cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) 223116964Sgrehan | GRACKLE_CFG_ENABLE; 224125688Sgrehan 225116964Sgrehan out32rb(sc->sc_addr, cfgval); 226116964Sgrehan (void) in32rb(sc->sc_addr); 227116964Sgrehan 228116964Sgrehan return (1); 229116964Sgrehan} 230116964Sgrehan 231116964Sgrehanstatic void 232116964Sgrehangrackle_disable_config(struct grackle_softc *sc) 233116964Sgrehan{ 234116964Sgrehan /* 235116964Sgrehan * Clear the GRACKLE_CFG_ENABLE bit to prevent stray 236116964Sgrehan * accesses from causing config cycles 237116964Sgrehan */ 238116964Sgrehan out32rb(sc->sc_addr, 0); 239116964Sgrehan} 240116964Sgrehan 241258696Snwhitehornstatic int 242258696Snwhitehornbadaddr(void *addr, size_t size) 243258696Snwhitehorn{ 244258696Snwhitehorn struct thread *td; 245293636Snwhitehorn jmp_buf env, *oldfaultbuf; 246258696Snwhitehorn int x; 247258696Snwhitehorn 248258696Snwhitehorn /* Get rid of any stale machine checks that have been waiting. */ 249258696Snwhitehorn __asm __volatile ("sync; isync"); 250258696Snwhitehorn 251258696Snwhitehorn td = curthread; 252258696Snwhitehorn 253258696Snwhitehorn oldfaultbuf = td->td_pcb->pcb_onfault; 254293636Snwhitehorn td->td_pcb->pcb_onfault = &env; 255293636Snwhitehorn if (setjmp(env)) { 256258696Snwhitehorn td->td_pcb->pcb_onfault = oldfaultbuf; 257258696Snwhitehorn __asm __volatile ("sync"); 258258696Snwhitehorn return 1; 259258696Snwhitehorn } 260258696Snwhitehorn 261258696Snwhitehorn __asm __volatile ("sync"); 262258696Snwhitehorn 263258696Snwhitehorn switch (size) { 264258696Snwhitehorn case 1: 265258696Snwhitehorn x = *(volatile int8_t *)addr; 266258696Snwhitehorn break; 267258696Snwhitehorn case 2: 268258696Snwhitehorn x = *(volatile int16_t *)addr; 269258696Snwhitehorn break; 270258696Snwhitehorn case 4: 271258696Snwhitehorn x = *(volatile int32_t *)addr; 272258696Snwhitehorn break; 273258696Snwhitehorn default: 274258696Snwhitehorn panic("badaddr: invalid size (%zd)", size); 275258696Snwhitehorn } 276258696Snwhitehorn 277258696Snwhitehorn /* Make sure we took the machine check, if we caused one. */ 278258696Snwhitehorn __asm __volatile ("sync; isync"); 279258696Snwhitehorn 280258696Snwhitehorn td->td_pcb->pcb_onfault = oldfaultbuf; 281258696Snwhitehorn __asm __volatile ("sync"); /* To be sure. */ 282258696Snwhitehorn 283258696Snwhitehorn return (0); 284258696Snwhitehorn} 285258696Snwhitehorn 286116964Sgrehan/* 287116964Sgrehan * Driver to swallow Grackle host bridges from the PCI bus side. 288116964Sgrehan */ 289116964Sgrehanstatic int 290116964Sgrehangrackle_hb_probe(device_t dev) 291116964Sgrehan{ 292116964Sgrehan 293127703Sgrehan if (pci_get_devid(dev) == 0x00021057) { 294127703Sgrehan device_set_desc(dev, "Grackle Host to PCI bridge"); 295116964Sgrehan device_quiet(dev); 296127703Sgrehan return (0); 297116964Sgrehan } 298116964Sgrehan 299116964Sgrehan return (ENXIO); 300116964Sgrehan} 301116964Sgrehan 302116964Sgrehanstatic int 303116964Sgrehangrackle_hb_attach(device_t dev) 304116964Sgrehan{ 305116964Sgrehan 306116964Sgrehan return (0); 307116964Sgrehan} 308116964Sgrehan 309116964Sgrehanstatic device_method_t grackle_hb_methods[] = { 310116964Sgrehan /* Device interface */ 311116964Sgrehan DEVMETHOD(device_probe, grackle_hb_probe), 312116964Sgrehan DEVMETHOD(device_attach, grackle_hb_attach), 313116964Sgrehan 314116964Sgrehan { 0, 0 } 315116964Sgrehan}; 316116964Sgrehan 317116964Sgrehanstatic driver_t grackle_hb_driver = { 318116964Sgrehan "grackle_hb", 319116964Sgrehan grackle_hb_methods, 320116964Sgrehan 1, 321116964Sgrehan}; 322116964Sgrehanstatic devclass_t grackle_hb_devclass; 323116964Sgrehan 324116964SgrehanDRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); 325