xref: /freebsd-src/sys/powerpc/powermac/grackle.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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