grackle.c revision 230994
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 230994 2012-02-04 20:04:35Z nwhitehorn $"); 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> 37116964Sgrehan 38116964Sgrehan#include <dev/ofw/openfirm.h> 39116964Sgrehan#include <dev/ofw/ofw_pci.h> 40183882Snwhitehorn#include <dev/ofw/ofw_bus.h> 41186128Snwhitehorn#include <dev/ofw/ofw_bus_subr.h> 42116964Sgrehan 43116964Sgrehan#include <dev/pci/pcivar.h> 44116964Sgrehan#include <dev/pci/pcireg.h> 45116964Sgrehan 46116964Sgrehan#include <machine/bus.h> 47209298Snwhitehorn#include <machine/intr_machdep.h> 48116964Sgrehan#include <machine/md_var.h> 49174782Smarcel#include <machine/pio.h> 50116964Sgrehan#include <machine/resource.h> 51116964Sgrehan 52116964Sgrehan#include <sys/rman.h> 53116964Sgrehan 54230993Snwhitehorn#include <powerpc/ofw/ofw_pci.h> 55116964Sgrehan#include <powerpc/powermac/gracklevar.h> 56116964Sgrehan 57116964Sgrehan#include <vm/vm.h> 58116964Sgrehan#include <vm/pmap.h> 59116964Sgrehan 60116964Sgrehan#include "pcib_if.h" 61116964Sgrehan 62125688Sgrehanint badaddr(void *, size_t); /* XXX */ 63125688Sgrehan 64116964Sgrehan/* 65116964Sgrehan * Device interface. 66116964Sgrehan */ 67116964Sgrehanstatic int grackle_probe(device_t); 68116964Sgrehanstatic int grackle_attach(device_t); 69116964Sgrehan 70116964Sgrehan/* 71116964Sgrehan * pcib interface. 72116964Sgrehan */ 73116964Sgrehanstatic u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, 74116964Sgrehan u_int, int); 75116964Sgrehanstatic void grackle_write_config(device_t, u_int, u_int, u_int, 76116964Sgrehan u_int, u_int32_t, int); 77116964Sgrehan 78116964Sgrehan/* 79116964Sgrehan * Local routines. 80116964Sgrehan */ 81116964Sgrehanstatic int grackle_enable_config(struct grackle_softc *, u_int, 82116964Sgrehan u_int, u_int, u_int); 83116964Sgrehanstatic void grackle_disable_config(struct grackle_softc *); 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); 103116964SgrehanDRIVER_MODULE(grackle, nexus, 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 241116964Sgrehan/* 242116964Sgrehan * Driver to swallow Grackle host bridges from the PCI bus side. 243116964Sgrehan */ 244116964Sgrehanstatic int 245116964Sgrehangrackle_hb_probe(device_t dev) 246116964Sgrehan{ 247116964Sgrehan 248127703Sgrehan if (pci_get_devid(dev) == 0x00021057) { 249127703Sgrehan device_set_desc(dev, "Grackle Host to PCI bridge"); 250116964Sgrehan device_quiet(dev); 251127703Sgrehan return (0); 252116964Sgrehan } 253116964Sgrehan 254116964Sgrehan return (ENXIO); 255116964Sgrehan} 256116964Sgrehan 257116964Sgrehanstatic int 258116964Sgrehangrackle_hb_attach(device_t dev) 259116964Sgrehan{ 260116964Sgrehan 261116964Sgrehan return (0); 262116964Sgrehan} 263116964Sgrehan 264116964Sgrehanstatic device_method_t grackle_hb_methods[] = { 265116964Sgrehan /* Device interface */ 266116964Sgrehan DEVMETHOD(device_probe, grackle_hb_probe), 267116964Sgrehan DEVMETHOD(device_attach, grackle_hb_attach), 268116964Sgrehan 269116964Sgrehan { 0, 0 } 270116964Sgrehan}; 271116964Sgrehan 272116964Sgrehanstatic driver_t grackle_hb_driver = { 273116964Sgrehan "grackle_hb", 274116964Sgrehan grackle_hb_methods, 275116964Sgrehan 1, 276116964Sgrehan}; 277116964Sgrehanstatic devclass_t grackle_hb_devclass; 278116964Sgrehan 279116964SgrehanDRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); 280