1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright 2003 by Peter Grehan. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/module.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/kernel.h> 36 #include <sys/proc.h> 37 #include <sys/rman.h> 38 39 #include <dev/ofw/openfirm.h> 40 #include <dev/ofw/ofw_pci.h> 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 #include <dev/ofw/ofwpci.h> 44 45 #include <dev/pci/pcivar.h> 46 #include <dev/pci/pcireg.h> 47 48 #include <machine/bus.h> 49 #include <machine/intr_machdep.h> 50 #include <machine/md_var.h> 51 #include <machine/pio.h> 52 #include <machine/resource.h> 53 54 #include <powerpc/powermac/gracklevar.h> 55 56 #include <vm/vm.h> 57 #include <vm/pmap.h> 58 59 #include "pcib_if.h" 60 61 /* 62 * Device interface. 63 */ 64 static int grackle_probe(device_t); 65 static int grackle_attach(device_t); 66 67 /* 68 * pcib interface. 69 */ 70 static u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, 71 u_int, int); 72 static void grackle_write_config(device_t, u_int, u_int, u_int, 73 u_int, u_int32_t, int); 74 75 /* 76 * Local routines. 77 */ 78 static int grackle_enable_config(struct grackle_softc *, u_int, 79 u_int, u_int, u_int); 80 static void grackle_disable_config(struct grackle_softc *); 81 static int badaddr(void *, size_t); 82 83 /* 84 * Driver methods. 85 */ 86 static device_method_t grackle_methods[] = { 87 /* Device interface */ 88 DEVMETHOD(device_probe, grackle_probe), 89 DEVMETHOD(device_attach, grackle_attach), 90 91 /* pcib interface */ 92 DEVMETHOD(pcib_read_config, grackle_read_config), 93 DEVMETHOD(pcib_write_config, grackle_write_config), 94 95 DEVMETHOD_END 96 }; 97 98 DEFINE_CLASS_1(pcib, grackle_driver, grackle_methods, 99 sizeof(struct grackle_softc), ofw_pcib_driver); 100 DRIVER_MODULE(grackle, ofwbus, grackle_driver, 0, 0); 101 102 static int 103 grackle_probe(device_t dev) 104 { 105 const char *type, *compatible; 106 107 type = ofw_bus_get_type(dev); 108 compatible = ofw_bus_get_compat(dev); 109 110 if (type == NULL || compatible == NULL) 111 return (ENXIO); 112 113 if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) 114 return (ENXIO); 115 116 device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); 117 return (0); 118 } 119 120 static int 121 grackle_attach(device_t dev) 122 { 123 struct grackle_softc *sc; 124 125 sc = device_get_softc(dev); 126 127 /* 128 * The Grackle PCI config addr/data registers are actually in 129 * PCI space, but since they are needed to actually probe the 130 * PCI bus, use the fact that they are also available directly 131 * on the processor bus and map them 132 */ 133 sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); 134 sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); 135 136 return (ofw_pcib_attach(dev)); 137 } 138 139 static u_int32_t 140 grackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 141 int width) 142 { 143 struct grackle_softc *sc; 144 vm_offset_t caoff; 145 u_int32_t retval = 0xffffffff; 146 147 sc = device_get_softc(dev); 148 caoff = sc->sc_data + (reg & 0x03); 149 150 if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { 151 /* 152 * Config probes to non-existent devices on the 153 * secondary bus generates machine checks. Be sure 154 * to catch these. 155 */ 156 if (bus > 0) { 157 if (badaddr((void *)sc->sc_data, 4)) { 158 return (retval); 159 } 160 } 161 162 switch (width) { 163 case 1: 164 retval = (in8rb(caoff)); 165 break; 166 case 2: 167 retval = (in16rb(caoff)); 168 break; 169 case 4: 170 retval = (in32rb(caoff)); 171 break; 172 } 173 } 174 grackle_disable_config(sc); 175 176 return (retval); 177 } 178 179 static void 180 grackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, 181 u_int reg, u_int32_t val, int width) 182 { 183 struct grackle_softc *sc; 184 vm_offset_t caoff; 185 186 sc = device_get_softc(dev); 187 caoff = sc->sc_data + (reg & 0x03); 188 189 if (grackle_enable_config(sc, bus, slot, func, reg)) { 190 switch (width) { 191 case 1: 192 out8rb(caoff, val); 193 (void)in8rb(caoff); 194 break; 195 case 2: 196 out16rb(caoff, val); 197 (void)in16rb(caoff); 198 break; 199 case 4: 200 out32rb(caoff, val); 201 (void)in32rb(caoff); 202 break; 203 } 204 } 205 grackle_disable_config(sc); 206 } 207 208 static int 209 grackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, 210 u_int func, u_int reg) 211 { 212 u_int32_t cfgval; 213 214 /* 215 * Unlike UniNorth, the format of the config word is the same 216 * for local (0) and remote busses. 217 */ 218 cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) 219 | GRACKLE_CFG_ENABLE; 220 221 out32rb(sc->sc_addr, cfgval); 222 (void) in32rb(sc->sc_addr); 223 224 return (1); 225 } 226 227 static void 228 grackle_disable_config(struct grackle_softc *sc) 229 { 230 /* 231 * Clear the GRACKLE_CFG_ENABLE bit to prevent stray 232 * accesses from causing config cycles 233 */ 234 out32rb(sc->sc_addr, 0); 235 } 236 237 static int 238 badaddr(void *addr, size_t size) 239 { 240 struct thread *td; 241 jmp_buf env, *oldfaultbuf; 242 243 /* Get rid of any stale machine checks that have been waiting. */ 244 __asm __volatile ("sync; isync"); 245 246 td = curthread; 247 248 oldfaultbuf = td->td_pcb->pcb_onfault; 249 td->td_pcb->pcb_onfault = &env; 250 if (setjmp(env)) { 251 td->td_pcb->pcb_onfault = oldfaultbuf; 252 __asm __volatile ("sync"); 253 return 1; 254 } 255 256 __asm __volatile ("sync"); 257 258 switch (size) { 259 case 1: 260 (void)*(volatile int8_t *)addr; 261 break; 262 case 2: 263 (void)*(volatile int16_t *)addr; 264 break; 265 case 4: 266 (void)*(volatile int32_t *)addr; 267 break; 268 default: 269 panic("badaddr: invalid size (%zd)", size); 270 } 271 272 /* Make sure we took the machine check, if we caused one. */ 273 __asm __volatile ("sync; isync"); 274 275 td->td_pcb->pcb_onfault = oldfaultbuf; 276 __asm __volatile ("sync"); /* To be sure. */ 277 278 return (0); 279 } 280 281 /* 282 * Driver to swallow Grackle host bridges from the PCI bus side. 283 */ 284 static int 285 grackle_hb_probe(device_t dev) 286 { 287 288 if (pci_get_devid(dev) == 0x00021057) { 289 device_set_desc(dev, "Grackle Host to PCI bridge"); 290 device_quiet(dev); 291 return (0); 292 } 293 294 return (ENXIO); 295 } 296 297 static int 298 grackle_hb_attach(device_t dev) 299 { 300 301 return (0); 302 } 303 304 static device_method_t grackle_hb_methods[] = { 305 /* Device interface */ 306 DEVMETHOD(device_probe, grackle_hb_probe), 307 DEVMETHOD(device_attach, grackle_hb_attach), 308 { 0, 0 } 309 }; 310 311 static driver_t grackle_hb_driver = { 312 "grackle_hb", 313 grackle_hb_methods, 314 1, 315 }; 316 317 DRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, 0, 0); 318