160727d8bSWarner Losh /*-
271e3c308SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
371e3c308SPedro F. Giffuni *
44b7624a5SPeter Grehan * Copyright 2003 by Peter Grehan. All rights reserved.
54b7624a5SPeter Grehan *
64b7624a5SPeter Grehan * Redistribution and use in source and binary forms, with or without
74b7624a5SPeter Grehan * modification, are permitted provided that the following conditions
84b7624a5SPeter Grehan * are met:
94b7624a5SPeter Grehan * 1. Redistributions of source code must retain the above copyright
104b7624a5SPeter Grehan * notice, this list of conditions and the following disclaimer.
114b7624a5SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright
124b7624a5SPeter Grehan * notice, this list of conditions and the following disclaimer in the
134b7624a5SPeter Grehan * documentation and/or other materials provided with the distribution.
144b7624a5SPeter Grehan * 3. The name of the author may not be used to endorse or promote products
154b7624a5SPeter Grehan * derived from this software without specific prior written permission.
164b7624a5SPeter Grehan *
174b7624a5SPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
184b7624a5SPeter Grehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194b7624a5SPeter Grehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
204b7624a5SPeter Grehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
214b7624a5SPeter Grehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
224b7624a5SPeter Grehan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
234b7624a5SPeter Grehan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
244b7624a5SPeter Grehan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
254b7624a5SPeter Grehan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
264b7624a5SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
274b7624a5SPeter Grehan * SUCH DAMAGE.
284b7624a5SPeter Grehan */
294b7624a5SPeter Grehan
304b7624a5SPeter Grehan #include <sys/param.h>
314b7624a5SPeter Grehan #include <sys/systm.h>
3240cdee9dSPeter Grehan #include <sys/module.h>
334b7624a5SPeter Grehan #include <sys/bus.h>
344b7624a5SPeter Grehan #include <sys/conf.h>
354b7624a5SPeter Grehan #include <sys/kernel.h>
36a0d0d6d8SNathan Whitehorn #include <sys/proc.h>
37c43a8674SZbigniew Bodek #include <sys/rman.h>
384b7624a5SPeter Grehan
394b7624a5SPeter Grehan #include <dev/ofw/openfirm.h>
4036e9c2ceSZbigniew Bodek #include <dev/ofw/ofw_pci.h>
4151d163d3SNathan Whitehorn #include <dev/ofw/ofw_bus.h>
4294b4a038SNathan Whitehorn #include <dev/ofw/ofw_bus_subr.h>
43c43a8674SZbigniew Bodek #include <dev/ofw/ofwpci.h>
444b7624a5SPeter Grehan
454b7624a5SPeter Grehan #include <dev/pci/pcivar.h>
464b7624a5SPeter Grehan #include <dev/pci/pcireg.h>
474b7624a5SPeter Grehan
484b7624a5SPeter Grehan #include <machine/bus.h>
49eaef5f0aSNathan Whitehorn #include <machine/intr_machdep.h>
504b7624a5SPeter Grehan #include <machine/md_var.h>
51de2fa7b8SMarcel Moolenaar #include <machine/pio.h>
524b7624a5SPeter Grehan #include <machine/resource.h>
534b7624a5SPeter Grehan
544b7624a5SPeter Grehan #include <powerpc/powermac/gracklevar.h>
554b7624a5SPeter Grehan
564b7624a5SPeter Grehan #include <vm/vm.h>
574b7624a5SPeter Grehan #include <vm/pmap.h>
584b7624a5SPeter Grehan
594b7624a5SPeter Grehan #include "pcib_if.h"
604b7624a5SPeter Grehan
614b7624a5SPeter Grehan /*
624b7624a5SPeter Grehan * Device interface.
634b7624a5SPeter Grehan */
644b7624a5SPeter Grehan static int grackle_probe(device_t);
654b7624a5SPeter Grehan static int grackle_attach(device_t);
664b7624a5SPeter Grehan
674b7624a5SPeter Grehan /*
684b7624a5SPeter Grehan * pcib interface.
694b7624a5SPeter Grehan */
704b7624a5SPeter Grehan static u_int32_t grackle_read_config(device_t, u_int, u_int, u_int,
714b7624a5SPeter Grehan u_int, int);
724b7624a5SPeter Grehan static void grackle_write_config(device_t, u_int, u_int, u_int,
734b7624a5SPeter Grehan u_int, u_int32_t, int);
7451d163d3SNathan Whitehorn
7551d163d3SNathan Whitehorn /*
764b7624a5SPeter Grehan * Local routines.
774b7624a5SPeter Grehan */
784b7624a5SPeter Grehan static int grackle_enable_config(struct grackle_softc *, u_int,
794b7624a5SPeter Grehan u_int, u_int, u_int);
804b7624a5SPeter Grehan static void grackle_disable_config(struct grackle_softc *);
81a0d0d6d8SNathan Whitehorn static int badaddr(void *, size_t);
82a0d0d6d8SNathan Whitehorn
834b7624a5SPeter Grehan /*
844b7624a5SPeter Grehan * Driver methods.
854b7624a5SPeter Grehan */
864b7624a5SPeter Grehan static device_method_t grackle_methods[] = {
874b7624a5SPeter Grehan /* Device interface */
884b7624a5SPeter Grehan DEVMETHOD(device_probe, grackle_probe),
894b7624a5SPeter Grehan DEVMETHOD(device_attach, grackle_attach),
904b7624a5SPeter Grehan
914b7624a5SPeter Grehan /* pcib interface */
924b7624a5SPeter Grehan DEVMETHOD(pcib_read_config, grackle_read_config),
934b7624a5SPeter Grehan DEVMETHOD(pcib_write_config, grackle_write_config),
9451d163d3SNathan Whitehorn
954b7ec270SMarius Strobl DEVMETHOD_END
964b7624a5SPeter Grehan };
974b7624a5SPeter Grehan
9864f4cd75SNathan Whitehorn DEFINE_CLASS_1(pcib, grackle_driver, grackle_methods,
9924042910SMarcin Wojtas sizeof(struct grackle_softc), ofw_pcib_driver);
100*992ae60bSJohn Baldwin DRIVER_MODULE(grackle, ofwbus, grackle_driver, 0, 0);
1014b7624a5SPeter Grehan
1024b7624a5SPeter Grehan static int
grackle_probe(device_t dev)1034b7624a5SPeter Grehan grackle_probe(device_t dev)
1044b7624a5SPeter Grehan {
10551d163d3SNathan Whitehorn const char *type, *compatible;
1064b7624a5SPeter Grehan
10751d163d3SNathan Whitehorn type = ofw_bus_get_type(dev);
10851d163d3SNathan Whitehorn compatible = ofw_bus_get_compat(dev);
1094b7624a5SPeter Grehan
1104b7624a5SPeter Grehan if (type == NULL || compatible == NULL)
1114b7624a5SPeter Grehan return (ENXIO);
1124b7624a5SPeter Grehan
1134b7624a5SPeter Grehan if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0)
1144b7624a5SPeter Grehan return (ENXIO);
1154b7624a5SPeter Grehan
1164b7624a5SPeter Grehan device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge");
1174b7624a5SPeter Grehan return (0);
1184b7624a5SPeter Grehan }
1194b7624a5SPeter Grehan
1204b7624a5SPeter Grehan static int
grackle_attach(device_t dev)1214b7624a5SPeter Grehan grackle_attach(device_t dev)
1224b7624a5SPeter Grehan {
1234b7624a5SPeter Grehan struct grackle_softc *sc;
1244b7624a5SPeter Grehan
1254b7624a5SPeter Grehan sc = device_get_softc(dev);
1264b7624a5SPeter Grehan
1274b7624a5SPeter Grehan /*
1284b7624a5SPeter Grehan * The Grackle PCI config addr/data registers are actually in
1294b7624a5SPeter Grehan * PCI space, but since they are needed to actually probe the
1304b7624a5SPeter Grehan * PCI bus, use the fact that they are also available directly
1314b7624a5SPeter Grehan * on the processor bus and map them
1324b7624a5SPeter Grehan */
1334b7624a5SPeter Grehan sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE);
1344b7624a5SPeter Grehan sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE);
1354b7624a5SPeter Grehan
13624042910SMarcin Wojtas return (ofw_pcib_attach(dev));
1374b7624a5SPeter Grehan }
1384b7624a5SPeter Grehan
1394b7624a5SPeter Grehan static u_int32_t
grackle_read_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,int width)1404b7624a5SPeter Grehan grackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
1414b7624a5SPeter Grehan int width)
1424b7624a5SPeter Grehan {
1434b7624a5SPeter Grehan struct grackle_softc *sc;
1444b7624a5SPeter Grehan vm_offset_t caoff;
1454b7624a5SPeter Grehan u_int32_t retval = 0xffffffff;
1464b7624a5SPeter Grehan
1474b7624a5SPeter Grehan sc = device_get_softc(dev);
1484b7624a5SPeter Grehan caoff = sc->sc_data + (reg & 0x03);
1494b7624a5SPeter Grehan
1504b7624a5SPeter Grehan if (grackle_enable_config(sc, bus, slot, func, reg) != 0) {
1514b7624a5SPeter Grehan /*
1524b7624a5SPeter Grehan * Config probes to non-existent devices on the
1534b7624a5SPeter Grehan * secondary bus generates machine checks. Be sure
1544b7624a5SPeter Grehan * to catch these.
1554b7624a5SPeter Grehan */
1564b7624a5SPeter Grehan if (bus > 0) {
157475a183dSPeter Grehan if (badaddr((void *)sc->sc_data, 4)) {
1584b7624a5SPeter Grehan return (retval);
1594b7624a5SPeter Grehan }
1604b7624a5SPeter Grehan }
1614b7624a5SPeter Grehan
1624b7624a5SPeter Grehan switch (width) {
1634b7624a5SPeter Grehan case 1:
1644b7624a5SPeter Grehan retval = (in8rb(caoff));
1654b7624a5SPeter Grehan break;
1664b7624a5SPeter Grehan case 2:
1674b7624a5SPeter Grehan retval = (in16rb(caoff));
1684b7624a5SPeter Grehan break;
1694b7624a5SPeter Grehan case 4:
1704b7624a5SPeter Grehan retval = (in32rb(caoff));
1714b7624a5SPeter Grehan break;
1724b7624a5SPeter Grehan }
1734b7624a5SPeter Grehan }
1744b7624a5SPeter Grehan grackle_disable_config(sc);
1754b7624a5SPeter Grehan
1764b7624a5SPeter Grehan return (retval);
1774b7624a5SPeter Grehan }
1784b7624a5SPeter Grehan
1794b7624a5SPeter Grehan static void
grackle_write_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,u_int32_t val,int width)1804b7624a5SPeter Grehan grackle_write_config(device_t dev, u_int bus, u_int slot, u_int func,
1814b7624a5SPeter Grehan u_int reg, u_int32_t val, int width)
1824b7624a5SPeter Grehan {
1834b7624a5SPeter Grehan struct grackle_softc *sc;
1844b7624a5SPeter Grehan vm_offset_t caoff;
1854b7624a5SPeter Grehan
1864b7624a5SPeter Grehan sc = device_get_softc(dev);
1874b7624a5SPeter Grehan caoff = sc->sc_data + (reg & 0x03);
1884b7624a5SPeter Grehan
1894b7624a5SPeter Grehan if (grackle_enable_config(sc, bus, slot, func, reg)) {
1904b7624a5SPeter Grehan switch (width) {
1914b7624a5SPeter Grehan case 1:
1924b7624a5SPeter Grehan out8rb(caoff, val);
1934b7624a5SPeter Grehan (void)in8rb(caoff);
1944b7624a5SPeter Grehan break;
1954b7624a5SPeter Grehan case 2:
1964b7624a5SPeter Grehan out16rb(caoff, val);
1974b7624a5SPeter Grehan (void)in16rb(caoff);
1984b7624a5SPeter Grehan break;
1994b7624a5SPeter Grehan case 4:
2004b7624a5SPeter Grehan out32rb(caoff, val);
2014b7624a5SPeter Grehan (void)in32rb(caoff);
2024b7624a5SPeter Grehan break;
2034b7624a5SPeter Grehan }
2044b7624a5SPeter Grehan }
2054b7624a5SPeter Grehan grackle_disable_config(sc);
2064b7624a5SPeter Grehan }
2074b7624a5SPeter Grehan
2084b7624a5SPeter Grehan static int
grackle_enable_config(struct grackle_softc * sc,u_int bus,u_int slot,u_int func,u_int reg)2094b7624a5SPeter Grehan grackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot,
2104b7624a5SPeter Grehan u_int func, u_int reg)
2114b7624a5SPeter Grehan {
2124b7624a5SPeter Grehan u_int32_t cfgval;
2134b7624a5SPeter Grehan
2144b7624a5SPeter Grehan /*
2154b7624a5SPeter Grehan * Unlike UniNorth, the format of the config word is the same
2164b7624a5SPeter Grehan * for local (0) and remote busses.
2174b7624a5SPeter Grehan */
2184b7624a5SPeter Grehan cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC)
2194b7624a5SPeter Grehan | GRACKLE_CFG_ENABLE;
2204b7624a5SPeter Grehan
2214b7624a5SPeter Grehan out32rb(sc->sc_addr, cfgval);
2224b7624a5SPeter Grehan (void) in32rb(sc->sc_addr);
2234b7624a5SPeter Grehan
2244b7624a5SPeter Grehan return (1);
2254b7624a5SPeter Grehan }
2264b7624a5SPeter Grehan
2274b7624a5SPeter Grehan static void
grackle_disable_config(struct grackle_softc * sc)2284b7624a5SPeter Grehan grackle_disable_config(struct grackle_softc *sc)
2294b7624a5SPeter Grehan {
2304b7624a5SPeter Grehan /*
2314b7624a5SPeter Grehan * Clear the GRACKLE_CFG_ENABLE bit to prevent stray
2324b7624a5SPeter Grehan * accesses from causing config cycles
2334b7624a5SPeter Grehan */
2344b7624a5SPeter Grehan out32rb(sc->sc_addr, 0);
2354b7624a5SPeter Grehan }
2364b7624a5SPeter Grehan
237a0d0d6d8SNathan Whitehorn static int
badaddr(void * addr,size_t size)238a0d0d6d8SNathan Whitehorn badaddr(void *addr, size_t size)
239a0d0d6d8SNathan Whitehorn {
240a0d0d6d8SNathan Whitehorn struct thread *td;
241a18c313eSNathan Whitehorn jmp_buf env, *oldfaultbuf;
242a0d0d6d8SNathan Whitehorn
243a0d0d6d8SNathan Whitehorn /* Get rid of any stale machine checks that have been waiting. */
244a0d0d6d8SNathan Whitehorn __asm __volatile ("sync; isync");
245a0d0d6d8SNathan Whitehorn
246a0d0d6d8SNathan Whitehorn td = curthread;
247a0d0d6d8SNathan Whitehorn
248a0d0d6d8SNathan Whitehorn oldfaultbuf = td->td_pcb->pcb_onfault;
249a18c313eSNathan Whitehorn td->td_pcb->pcb_onfault = &env;
250a18c313eSNathan Whitehorn if (setjmp(env)) {
251a0d0d6d8SNathan Whitehorn td->td_pcb->pcb_onfault = oldfaultbuf;
252a0d0d6d8SNathan Whitehorn __asm __volatile ("sync");
253a0d0d6d8SNathan Whitehorn return 1;
254a0d0d6d8SNathan Whitehorn }
255a0d0d6d8SNathan Whitehorn
256a0d0d6d8SNathan Whitehorn __asm __volatile ("sync");
257a0d0d6d8SNathan Whitehorn
258a0d0d6d8SNathan Whitehorn switch (size) {
259a0d0d6d8SNathan Whitehorn case 1:
2602ad93a4fSJohn Baldwin (void)*(volatile int8_t *)addr;
261a0d0d6d8SNathan Whitehorn break;
262a0d0d6d8SNathan Whitehorn case 2:
2632ad93a4fSJohn Baldwin (void)*(volatile int16_t *)addr;
264a0d0d6d8SNathan Whitehorn break;
265a0d0d6d8SNathan Whitehorn case 4:
2662ad93a4fSJohn Baldwin (void)*(volatile int32_t *)addr;
267a0d0d6d8SNathan Whitehorn break;
268a0d0d6d8SNathan Whitehorn default:
269a0d0d6d8SNathan Whitehorn panic("badaddr: invalid size (%zd)", size);
270a0d0d6d8SNathan Whitehorn }
271a0d0d6d8SNathan Whitehorn
272a0d0d6d8SNathan Whitehorn /* Make sure we took the machine check, if we caused one. */
273a0d0d6d8SNathan Whitehorn __asm __volatile ("sync; isync");
274a0d0d6d8SNathan Whitehorn
275a0d0d6d8SNathan Whitehorn td->td_pcb->pcb_onfault = oldfaultbuf;
276a0d0d6d8SNathan Whitehorn __asm __volatile ("sync"); /* To be sure. */
277a0d0d6d8SNathan Whitehorn
278a0d0d6d8SNathan Whitehorn return (0);
279a0d0d6d8SNathan Whitehorn }
280a0d0d6d8SNathan Whitehorn
2814b7624a5SPeter Grehan /*
2824b7624a5SPeter Grehan * Driver to swallow Grackle host bridges from the PCI bus side.
2834b7624a5SPeter Grehan */
2844b7624a5SPeter Grehan static int
grackle_hb_probe(device_t dev)2854b7624a5SPeter Grehan grackle_hb_probe(device_t dev)
2864b7624a5SPeter Grehan {
2874b7624a5SPeter Grehan
2886611d669SPeter Grehan if (pci_get_devid(dev) == 0x00021057) {
2896611d669SPeter Grehan device_set_desc(dev, "Grackle Host to PCI bridge");
2904b7624a5SPeter Grehan device_quiet(dev);
2916611d669SPeter Grehan return (0);
2924b7624a5SPeter Grehan }
2934b7624a5SPeter Grehan
2944b7624a5SPeter Grehan return (ENXIO);
2954b7624a5SPeter Grehan }
2964b7624a5SPeter Grehan
2974b7624a5SPeter Grehan static int
grackle_hb_attach(device_t dev)2984b7624a5SPeter Grehan grackle_hb_attach(device_t dev)
2994b7624a5SPeter Grehan {
3004b7624a5SPeter Grehan
3014b7624a5SPeter Grehan return (0);
3024b7624a5SPeter Grehan }
3034b7624a5SPeter Grehan
3044b7624a5SPeter Grehan static device_method_t grackle_hb_methods[] = {
3054b7624a5SPeter Grehan /* Device interface */
3064b7624a5SPeter Grehan DEVMETHOD(device_probe, grackle_hb_probe),
3074b7624a5SPeter Grehan DEVMETHOD(device_attach, grackle_hb_attach),
3084b7624a5SPeter Grehan { 0, 0 }
3094b7624a5SPeter Grehan };
3104b7624a5SPeter Grehan
3114b7624a5SPeter Grehan static driver_t grackle_hb_driver = {
3124b7624a5SPeter Grehan "grackle_hb",
3134b7624a5SPeter Grehan grackle_hb_methods,
3144b7624a5SPeter Grehan 1,
3154b7624a5SPeter Grehan };
3164b7624a5SPeter Grehan
317*992ae60bSJohn Baldwin DRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, 0, 0);
318