grackle.c revision 293636
1249259Sdim/*- 2249259Sdim * Copyright 2003 by Peter Grehan. All rights reserved. 3249259Sdim * 4249259Sdim * Redistribution and use in source and binary forms, with or without 5249259Sdim * modification, are permitted provided that the following conditions 6249259Sdim * are met: 7249259Sdim * 1. Redistributions of source code must retain the above copyright 8249259Sdim * notice, this list of conditions and the following disclaimer. 9249259Sdim * 2. Redistributions in binary form must reproduce the above copyright 10249259Sdim * notice, this list of conditions and the following disclaimer in the 11249259Sdim * documentation and/or other materials provided with the distribution. 12249259Sdim * 3. The name of the author may not be used to endorse or promote products 13249259Sdim * derived from this software without specific prior written permission. 14249259Sdim * 15249259Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16251662Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17249259Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18249259Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19249259Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20249259Sdim * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21249259Sdim * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22249259Sdim * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23249259Sdim * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24249259Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25249259Sdim * SUCH DAMAGE. 26249259Sdim */ 27249259Sdim 28249259Sdim#include <sys/cdefs.h> 29249259Sdim__FBSDID("$FreeBSD: head/sys/powerpc/powermac/grackle.c 293636 2016-01-10 16:42:14Z nwhitehorn $"); 30249259Sdim 31249259Sdim#include <sys/param.h> 32249259Sdim#include <sys/systm.h> 33251662Sdim#include <sys/module.h> 34251662Sdim#include <sys/bus.h> 35249259Sdim#include <sys/conf.h> 36249259Sdim#include <sys/kernel.h> 37249259Sdim#include <sys/proc.h> 38249259Sdim 39249259Sdim#include <dev/ofw/openfirm.h> 40249259Sdim#include <dev/ofw/ofw_pci.h> 41249259Sdim#include <dev/ofw/ofw_bus.h> 42249259Sdim#include <dev/ofw/ofw_bus_subr.h> 43249259Sdim 44249259Sdim#include <dev/pci/pcivar.h> 45249259Sdim#include <dev/pci/pcireg.h> 46249259Sdim 47249259Sdim#include <machine/bus.h> 48249259Sdim#include <machine/intr_machdep.h> 49249259Sdim#include <machine/md_var.h> 50249259Sdim#include <machine/pio.h> 51249259Sdim#include <machine/resource.h> 52249259Sdim 53249259Sdim#include <sys/rman.h> 54249259Sdim 55249259Sdim#include <powerpc/ofw/ofw_pci.h> 56249259Sdim#include <powerpc/powermac/gracklevar.h> 57249259Sdim 58249259Sdim#include <vm/vm.h> 59249259Sdim#include <vm/pmap.h> 60249259Sdim 61249259Sdim#include "pcib_if.h" 62249259Sdim 63249259Sdim/* 64249259Sdim * Device interface. 65249259Sdim */ 66249259Sdimstatic int grackle_probe(device_t); 67249259Sdimstatic int grackle_attach(device_t); 68249259Sdim 69249259Sdim/* 70249259Sdim * pcib interface. 71249259Sdim */ 72249259Sdimstatic u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, 73249259Sdim u_int, int); 74249259Sdimstatic void grackle_write_config(device_t, u_int, u_int, u_int, 75249259Sdim u_int, u_int32_t, int); 76249259Sdim 77249259Sdim/* 78249259Sdim * Local routines. 79249259Sdim */ 80249259Sdimstatic int grackle_enable_config(struct grackle_softc *, u_int, 81249259Sdim u_int, u_int, u_int); 82249259Sdimstatic void grackle_disable_config(struct grackle_softc *); 83249259Sdimstatic int badaddr(void *, size_t); 84249259Sdim 85249259Sdim/* 86249259Sdim * Driver methods. 87249259Sdim */ 88249259Sdimstatic device_method_t grackle_methods[] = { 89249259Sdim /* Device interface */ 90249259Sdim DEVMETHOD(device_probe, grackle_probe), 91249259Sdim DEVMETHOD(device_attach, grackle_attach), 92249259Sdim 93249259Sdim /* pcib interface */ 94249259Sdim DEVMETHOD(pcib_read_config, grackle_read_config), 95249259Sdim DEVMETHOD(pcib_write_config, grackle_write_config), 96249259Sdim 97249259Sdim DEVMETHOD_END 98249259Sdim}; 99249259Sdim 100249259Sdimstatic devclass_t grackle_devclass; 101249259SdimDEFINE_CLASS_1(pcib, grackle_driver, grackle_methods, 102249259Sdim sizeof(struct grackle_softc), ofw_pci_driver); 103249259SdimDRIVER_MODULE(grackle, ofwbus, grackle_driver, grackle_devclass, 0, 0); 104249259Sdim 105249259Sdimstatic int 106249259Sdimgrackle_probe(device_t dev) 107249259Sdim{ 108249259Sdim const char *type, *compatible; 109249259Sdim 110249259Sdim type = ofw_bus_get_type(dev); 111249259Sdim compatible = ofw_bus_get_compat(dev); 112249259Sdim 113249259Sdim if (type == NULL || compatible == NULL) 114249259Sdim return (ENXIO); 115249259Sdim 116249259Sdim if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) 117249259Sdim return (ENXIO); 118249259Sdim 119249259Sdim device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); 120249259Sdim return (0); 121249259Sdim} 122249259Sdim 123249259Sdimstatic int 124249259Sdimgrackle_attach(device_t dev) 125249259Sdim{ 126249259Sdim struct grackle_softc *sc; 127249259Sdim 128249259Sdim sc = device_get_softc(dev); 129249259Sdim 130249259Sdim /* 131249259Sdim * The Grackle PCI config addr/data registers are actually in 132249259Sdim * PCI space, but since they are needed to actually probe the 133249259Sdim * PCI bus, use the fact that they are also available directly 134249259Sdim * on the processor bus and map them 135249259Sdim */ 136249259Sdim sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); 137249259Sdim sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); 138249259Sdim 139249259Sdim return (ofw_pci_attach(dev)); 140249259Sdim} 141249259Sdim 142249259Sdimstatic u_int32_t 143249259Sdimgrackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 144251662Sdim int width) 145251662Sdim{ 146251662Sdim struct grackle_softc *sc; 147251662Sdim vm_offset_t caoff; 148251662Sdim u_int32_t retval = 0xffffffff; 149251662Sdim 150251662Sdim sc = device_get_softc(dev); 151251662Sdim caoff = sc->sc_data + (reg & 0x03); 152251662Sdim 153251662Sdim if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { 154251662Sdim 155251662Sdim /* 156251662Sdim * Config probes to non-existent devices on the 157251662Sdim * secondary bus generates machine checks. Be sure 158251662Sdim * to catch these. 159251662Sdim */ 160251662Sdim if (bus > 0) { 161251662Sdim if (badaddr((void *)sc->sc_data, 4)) { 162251662Sdim return (retval); 163251662Sdim } 164251662Sdim } 165251662Sdim 166251662Sdim switch (width) { 167251662Sdim case 1: 168251662Sdim retval = (in8rb(caoff)); 169251662Sdim break; 170251662Sdim case 2: 171249259Sdim retval = (in16rb(caoff)); 172249259Sdim break; 173249259Sdim case 4: 174249259Sdim retval = (in32rb(caoff)); 175249259Sdim break; 176249259Sdim } 177249259Sdim } 178249259Sdim grackle_disable_config(sc); 179249259Sdim 180249259Sdim return (retval); 181249259Sdim} 182249259Sdim 183249259Sdimstatic void 184249259Sdimgrackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, 185249259Sdim u_int reg, u_int32_t val, int width) 186249259Sdim{ 187249259Sdim struct grackle_softc *sc; 188249259Sdim vm_offset_t caoff; 189249259Sdim 190249259Sdim sc = device_get_softc(dev); 191249259Sdim caoff = sc->sc_data + (reg & 0x03); 192249259Sdim 193249259Sdim if (grackle_enable_config(sc, bus, slot, func, reg)) { 194249259Sdim switch (width) { 195249259Sdim case 1: 196249259Sdim out8rb(caoff, val); 197249259Sdim (void)in8rb(caoff); 198249259Sdim break; 199249259Sdim case 2: 200249259Sdim out16rb(caoff, val); 201249259Sdim (void)in16rb(caoff); 202249259Sdim break; 203249259Sdim case 4: 204249259Sdim out32rb(caoff, val); 205249259Sdim (void)in32rb(caoff); 206249259Sdim break; 207249259Sdim } 208249259Sdim } 209249259Sdim grackle_disable_config(sc); 210249259Sdim} 211249259Sdim 212249259Sdimstatic int 213249259Sdimgrackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, 214249259Sdim u_int func, u_int reg) 215251662Sdim{ 216251662Sdim u_int32_t cfgval; 217249259Sdim 218249259Sdim /* 219249259Sdim * Unlike UniNorth, the format of the config word is the same 220251662Sdim * for local (0) and remote busses. 221249259Sdim */ 222251662Sdim cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) 223251662Sdim | GRACKLE_CFG_ENABLE; 224251662Sdim 225251662Sdim out32rb(sc->sc_addr, cfgval); 226251662Sdim (void) in32rb(sc->sc_addr); 227251662Sdim 228251662Sdim return (1); 229249259Sdim} 230249259Sdim 231249259Sdimstatic void 232249259Sdimgrackle_disable_config(struct grackle_softc *sc) 233249259Sdim{ 234249259Sdim /* 235249259Sdim * Clear the GRACKLE_CFG_ENABLE bit to prevent stray 236249259Sdim * accesses from causing config cycles 237249259Sdim */ 238249259Sdim out32rb(sc->sc_addr, 0); 239249259Sdim} 240249259Sdim 241249259Sdimstatic int 242249259Sdimbadaddr(void *addr, size_t size) 243249259Sdim{ 244249259Sdim struct thread *td; 245249259Sdim jmp_buf env, *oldfaultbuf; 246249259Sdim int x; 247249259Sdim 248249259Sdim /* Get rid of any stale machine checks that have been waiting. */ 249249259Sdim __asm __volatile ("sync; isync"); 250249259Sdim 251249259Sdim td = curthread; 252249259Sdim 253249259Sdim oldfaultbuf = td->td_pcb->pcb_onfault; 254249259Sdim td->td_pcb->pcb_onfault = &env; 255249259Sdim if (setjmp(env)) { 256249259Sdim td->td_pcb->pcb_onfault = oldfaultbuf; 257249259Sdim __asm __volatile ("sync"); 258249259Sdim return 1; 259249259Sdim } 260249259Sdim 261249259Sdim __asm __volatile ("sync"); 262249259Sdim 263249259Sdim switch (size) { 264249259Sdim case 1: 265249259Sdim x = *(volatile int8_t *)addr; 266249259Sdim break; 267249259Sdim case 2: 268249259Sdim x = *(volatile int16_t *)addr; 269249259Sdim break; 270249259Sdim case 4: 271249259Sdim x = *(volatile int32_t *)addr; 272249259Sdim break; 273249259Sdim default: 274249259Sdim panic("badaddr: invalid size (%zd)", size); 275249259Sdim } 276249259Sdim 277249259Sdim /* Make sure we took the machine check, if we caused one. */ 278249259Sdim __asm __volatile ("sync; isync"); 279249259Sdim 280249259Sdim td->td_pcb->pcb_onfault = oldfaultbuf; 281249259Sdim __asm __volatile ("sync"); /* To be sure. */ 282249259Sdim 283249259Sdim return (0); 284249259Sdim} 285249259Sdim 286249259Sdim/* 287249259Sdim * Driver to swallow Grackle host bridges from the PCI bus side. 288249259Sdim */ 289249259Sdimstatic int 290249259Sdimgrackle_hb_probe(device_t dev) 291249259Sdim{ 292249259Sdim 293249259Sdim if (pci_get_devid(dev) == 0x00021057) { 294249259Sdim device_set_desc(dev, "Grackle Host to PCI bridge"); 295249259Sdim device_quiet(dev); 296249259Sdim return (0); 297249259Sdim } 298249259Sdim 299249259Sdim return (ENXIO); 300249259Sdim} 301249259Sdim 302249259Sdimstatic int 303249259Sdimgrackle_hb_attach(device_t dev) 304249259Sdim{ 305249259Sdim 306249259Sdim return (0); 307249259Sdim} 308249259Sdim 309249259Sdimstatic device_method_t grackle_hb_methods[] = { 310249259Sdim /* Device interface */ 311249259Sdim DEVMETHOD(device_probe, grackle_hb_probe), 312249259Sdim DEVMETHOD(device_attach, grackle_hb_attach), 313249259Sdim 314249259Sdim { 0, 0 } 315249259Sdim}; 316249259Sdim 317249259Sdimstatic driver_t grackle_hb_driver = { 318249259Sdim "grackle_hb", 319249259Sdim grackle_hb_methods, 320249259Sdim 1, 321249259Sdim}; 322249259Sdimstatic devclass_t grackle_hb_devclass; 323249259Sdim 324249259SdimDRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); 325249259Sdim