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: releng/10.3/sys/powerpc/powermac/grackle.c 266160 2014-05-15 17:30:16Z ian $"); 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> 37266019Sian#include <sys/proc.h> 38116964Sgrehan 39116964Sgrehan#include <dev/ofw/openfirm.h> 40116964Sgrehan#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 55230993Snwhitehorn#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 *); 83266019Sianstatic int badaddr(void *, size_t); 84116964Sgrehan 85266019Sianint setfault(faultbuf); /* defined in locore.S */ 86266019Sian 87116964Sgrehan/* 88116964Sgrehan * Driver methods. 89116964Sgrehan */ 90116964Sgrehanstatic device_method_t grackle_methods[] = { 91116964Sgrehan /* Device interface */ 92116964Sgrehan DEVMETHOD(device_probe, grackle_probe), 93116964Sgrehan DEVMETHOD(device_attach, grackle_attach), 94116964Sgrehan 95116964Sgrehan /* pcib interface */ 96116964Sgrehan DEVMETHOD(pcib_read_config, grackle_read_config), 97116964Sgrehan DEVMETHOD(pcib_write_config, grackle_write_config), 98116964Sgrehan 99227843Smarius DEVMETHOD_END 100116964Sgrehan}; 101116964Sgrehan 102116964Sgrehanstatic devclass_t grackle_devclass; 103230994SnwhitehornDEFINE_CLASS_1(pcib, grackle_driver, grackle_methods, 104230993Snwhitehorn sizeof(struct grackle_softc), ofw_pci_driver); 105266160SianDRIVER_MODULE(grackle, ofwbus, grackle_driver, grackle_devclass, 0, 0); 106116964Sgrehan 107116964Sgrehanstatic int 108116964Sgrehangrackle_probe(device_t dev) 109116964Sgrehan{ 110183882Snwhitehorn const char *type, *compatible; 111116964Sgrehan 112183882Snwhitehorn type = ofw_bus_get_type(dev); 113183882Snwhitehorn compatible = ofw_bus_get_compat(dev); 114116964Sgrehan 115116964Sgrehan if (type == NULL || compatible == NULL) 116116964Sgrehan return (ENXIO); 117116964Sgrehan 118116964Sgrehan if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) 119116964Sgrehan return (ENXIO); 120116964Sgrehan 121116964Sgrehan device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); 122116964Sgrehan return (0); 123116964Sgrehan} 124116964Sgrehan 125116964Sgrehanstatic int 126116964Sgrehangrackle_attach(device_t dev) 127116964Sgrehan{ 128116964Sgrehan struct grackle_softc *sc; 129116964Sgrehan 130116964Sgrehan sc = device_get_softc(dev); 131116964Sgrehan 132116964Sgrehan /* 133116964Sgrehan * The Grackle PCI config addr/data registers are actually in 134116964Sgrehan * PCI space, but since they are needed to actually probe the 135116964Sgrehan * PCI bus, use the fact that they are also available directly 136116964Sgrehan * on the processor bus and map them 137116964Sgrehan */ 138116964Sgrehan sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); 139116964Sgrehan sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); 140116964Sgrehan 141230993Snwhitehorn return (ofw_pci_attach(dev)); 142116964Sgrehan} 143116964Sgrehan 144116964Sgrehanstatic u_int32_t 145116964Sgrehangrackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 146116964Sgrehan int width) 147116964Sgrehan{ 148116964Sgrehan struct grackle_softc *sc; 149116964Sgrehan vm_offset_t caoff; 150116964Sgrehan u_int32_t retval = 0xffffffff; 151116964Sgrehan 152116964Sgrehan sc = device_get_softc(dev); 153116964Sgrehan caoff = sc->sc_data + (reg & 0x03); 154116964Sgrehan 155116964Sgrehan if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { 156116964Sgrehan 157116964Sgrehan /* 158116964Sgrehan * Config probes to non-existent devices on the 159116964Sgrehan * secondary bus generates machine checks. Be sure 160116964Sgrehan * to catch these. 161116964Sgrehan */ 162116964Sgrehan if (bus > 0) { 163125688Sgrehan if (badaddr((void *)sc->sc_data, 4)) { 164116964Sgrehan return (retval); 165116964Sgrehan } 166116964Sgrehan } 167125688Sgrehan 168116964Sgrehan switch (width) { 169116964Sgrehan case 1: 170116964Sgrehan retval = (in8rb(caoff)); 171116964Sgrehan break; 172116964Sgrehan case 2: 173116964Sgrehan retval = (in16rb(caoff)); 174116964Sgrehan break; 175116964Sgrehan case 4: 176116964Sgrehan retval = (in32rb(caoff)); 177116964Sgrehan break; 178116964Sgrehan } 179116964Sgrehan } 180116964Sgrehan grackle_disable_config(sc); 181116964Sgrehan 182116964Sgrehan return (retval); 183116964Sgrehan} 184116964Sgrehan 185116964Sgrehanstatic void 186116964Sgrehangrackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, 187116964Sgrehan u_int reg, u_int32_t val, int width) 188116964Sgrehan{ 189116964Sgrehan struct grackle_softc *sc; 190116964Sgrehan vm_offset_t caoff; 191116964Sgrehan 192116964Sgrehan sc = device_get_softc(dev); 193116964Sgrehan caoff = sc->sc_data + (reg & 0x03); 194116964Sgrehan 195116964Sgrehan if (grackle_enable_config(sc, bus, slot, func, reg)) { 196116964Sgrehan switch (width) { 197116964Sgrehan case 1: 198116964Sgrehan out8rb(caoff, val); 199116964Sgrehan (void)in8rb(caoff); 200116964Sgrehan break; 201116964Sgrehan case 2: 202116964Sgrehan out16rb(caoff, val); 203116964Sgrehan (void)in16rb(caoff); 204116964Sgrehan break; 205116964Sgrehan case 4: 206116964Sgrehan out32rb(caoff, val); 207116964Sgrehan (void)in32rb(caoff); 208116964Sgrehan break; 209116964Sgrehan } 210116964Sgrehan } 211116964Sgrehan grackle_disable_config(sc); 212116964Sgrehan} 213116964Sgrehan 214116964Sgrehanstatic int 215116964Sgrehangrackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, 216116964Sgrehan u_int func, u_int reg) 217116964Sgrehan{ 218116964Sgrehan u_int32_t cfgval; 219116964Sgrehan 220116964Sgrehan /* 221116964Sgrehan * Unlike UniNorth, the format of the config word is the same 222116964Sgrehan * for local (0) and remote busses. 223116964Sgrehan */ 224116964Sgrehan cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) 225116964Sgrehan | GRACKLE_CFG_ENABLE; 226125688Sgrehan 227116964Sgrehan out32rb(sc->sc_addr, cfgval); 228116964Sgrehan (void) in32rb(sc->sc_addr); 229116964Sgrehan 230116964Sgrehan return (1); 231116964Sgrehan} 232116964Sgrehan 233116964Sgrehanstatic void 234116964Sgrehangrackle_disable_config(struct grackle_softc *sc) 235116964Sgrehan{ 236116964Sgrehan /* 237116964Sgrehan * Clear the GRACKLE_CFG_ENABLE bit to prevent stray 238116964Sgrehan * accesses from causing config cycles 239116964Sgrehan */ 240116964Sgrehan out32rb(sc->sc_addr, 0); 241116964Sgrehan} 242116964Sgrehan 243266019Sianstatic int 244266019Sianbadaddr(void *addr, size_t size) 245266019Sian{ 246266019Sian struct thread *td; 247266019Sian faultbuf env, *oldfaultbuf; 248266019Sian int x; 249266019Sian 250266019Sian /* Get rid of any stale machine checks that have been waiting. */ 251266019Sian __asm __volatile ("sync; isync"); 252266019Sian 253266019Sian td = curthread; 254266019Sian 255266019Sian oldfaultbuf = td->td_pcb->pcb_onfault; 256266019Sian if (setfault(env)) { 257266019Sian td->td_pcb->pcb_onfault = oldfaultbuf; 258266019Sian __asm __volatile ("sync"); 259266019Sian return 1; 260266019Sian } 261266019Sian 262266019Sian __asm __volatile ("sync"); 263266019Sian 264266019Sian switch (size) { 265266019Sian case 1: 266266019Sian x = *(volatile int8_t *)addr; 267266019Sian break; 268266019Sian case 2: 269266019Sian x = *(volatile int16_t *)addr; 270266019Sian break; 271266019Sian case 4: 272266019Sian x = *(volatile int32_t *)addr; 273266019Sian break; 274266019Sian default: 275266019Sian panic("badaddr: invalid size (%zd)", size); 276266019Sian } 277266019Sian 278266019Sian /* Make sure we took the machine check, if we caused one. */ 279266019Sian __asm __volatile ("sync; isync"); 280266019Sian 281266019Sian td->td_pcb->pcb_onfault = oldfaultbuf; 282266019Sian __asm __volatile ("sync"); /* To be sure. */ 283266019Sian 284266019Sian return (0); 285266019Sian} 286266019Sian 287116964Sgrehan/* 288116964Sgrehan * Driver to swallow Grackle host bridges from the PCI bus side. 289116964Sgrehan */ 290116964Sgrehanstatic int 291116964Sgrehangrackle_hb_probe(device_t dev) 292116964Sgrehan{ 293116964Sgrehan 294127703Sgrehan if (pci_get_devid(dev) == 0x00021057) { 295127703Sgrehan device_set_desc(dev, "Grackle Host to PCI bridge"); 296116964Sgrehan device_quiet(dev); 297127703Sgrehan return (0); 298116964Sgrehan } 299116964Sgrehan 300116964Sgrehan return (ENXIO); 301116964Sgrehan} 302116964Sgrehan 303116964Sgrehanstatic int 304116964Sgrehangrackle_hb_attach(device_t dev) 305116964Sgrehan{ 306116964Sgrehan 307116964Sgrehan return (0); 308116964Sgrehan} 309116964Sgrehan 310116964Sgrehanstatic device_method_t grackle_hb_methods[] = { 311116964Sgrehan /* Device interface */ 312116964Sgrehan DEVMETHOD(device_probe, grackle_hb_probe), 313116964Sgrehan DEVMETHOD(device_attach, grackle_hb_attach), 314116964Sgrehan 315116964Sgrehan { 0, 0 } 316116964Sgrehan}; 317116964Sgrehan 318116964Sgrehanstatic driver_t grackle_hb_driver = { 319116964Sgrehan "grackle_hb", 320116964Sgrehan grackle_hb_methods, 321116964Sgrehan 1, 322116964Sgrehan}; 323116964Sgrehanstatic devclass_t grackle_hb_devclass; 324116964Sgrehan 325116964SgrehanDRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); 326