grackle.c revision 293636
1249259Sdim/*-
2249259Sdim * Copyright 2003 by Peter Grehan. All rights reserved.
3249259Sdim *
4249259Sdim * Redistribution and use in source and binary forms, with or without
5249259Sdim * modification, are permitted provided that the following conditions
6249259Sdim * are met:
7249259Sdim * 1. Redistributions of source code must retain the above copyright
8249259Sdim *    notice, this list of conditions and the following disclaimer.
9249259Sdim * 2. Redistributions in binary form must reproduce the above copyright
10249259Sdim *    notice, this list of conditions and the following disclaimer in the
11249259Sdim *    documentation and/or other materials provided with the distribution.
12249259Sdim * 3. The name of the author may not be used to endorse or promote products
13249259Sdim *    derived from this software without specific prior written permission.
14249259Sdim *
15249259Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16251662Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17249259Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18249259Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19249259Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20249259Sdim * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21249259Sdim * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22249259Sdim * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23249259Sdim * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24249259Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25249259Sdim * SUCH DAMAGE.
26249259Sdim */
27249259Sdim
28249259Sdim#include <sys/cdefs.h>
29249259Sdim__FBSDID("$FreeBSD: head/sys/powerpc/powermac/grackle.c 293636 2016-01-10 16:42:14Z nwhitehorn $");
30249259Sdim
31249259Sdim#include <sys/param.h>
32249259Sdim#include <sys/systm.h>
33251662Sdim#include <sys/module.h>
34251662Sdim#include <sys/bus.h>
35249259Sdim#include <sys/conf.h>
36249259Sdim#include <sys/kernel.h>
37249259Sdim#include <sys/proc.h>
38249259Sdim
39249259Sdim#include <dev/ofw/openfirm.h>
40249259Sdim#include <dev/ofw/ofw_pci.h>
41249259Sdim#include <dev/ofw/ofw_bus.h>
42249259Sdim#include <dev/ofw/ofw_bus_subr.h>
43249259Sdim
44249259Sdim#include <dev/pci/pcivar.h>
45249259Sdim#include <dev/pci/pcireg.h>
46249259Sdim
47249259Sdim#include <machine/bus.h>
48249259Sdim#include <machine/intr_machdep.h>
49249259Sdim#include <machine/md_var.h>
50249259Sdim#include <machine/pio.h>
51249259Sdim#include <machine/resource.h>
52249259Sdim
53249259Sdim#include <sys/rman.h>
54249259Sdim
55249259Sdim#include <powerpc/ofw/ofw_pci.h>
56249259Sdim#include <powerpc/powermac/gracklevar.h>
57249259Sdim
58249259Sdim#include <vm/vm.h>
59249259Sdim#include <vm/pmap.h>
60249259Sdim
61249259Sdim#include "pcib_if.h"
62249259Sdim
63249259Sdim/*
64249259Sdim * Device interface.
65249259Sdim */
66249259Sdimstatic int		grackle_probe(device_t);
67249259Sdimstatic int		grackle_attach(device_t);
68249259Sdim
69249259Sdim/*
70249259Sdim * pcib interface.
71249259Sdim */
72249259Sdimstatic u_int32_t	grackle_read_config(device_t, u_int, u_int, u_int,
73249259Sdim			    u_int, int);
74249259Sdimstatic void		grackle_write_config(device_t, u_int, u_int, u_int,
75249259Sdim			    u_int, u_int32_t, int);
76249259Sdim
77249259Sdim/*
78249259Sdim * Local routines.
79249259Sdim */
80249259Sdimstatic int		grackle_enable_config(struct grackle_softc *, u_int,
81249259Sdim			    u_int, u_int, u_int);
82249259Sdimstatic void		grackle_disable_config(struct grackle_softc *);
83249259Sdimstatic int		badaddr(void *, size_t);
84249259Sdim
85249259Sdim/*
86249259Sdim * Driver methods.
87249259Sdim */
88249259Sdimstatic device_method_t	grackle_methods[] = {
89249259Sdim	/* Device interface */
90249259Sdim	DEVMETHOD(device_probe,		grackle_probe),
91249259Sdim	DEVMETHOD(device_attach,	grackle_attach),
92249259Sdim
93249259Sdim	/* pcib interface */
94249259Sdim	DEVMETHOD(pcib_read_config,	grackle_read_config),
95249259Sdim	DEVMETHOD(pcib_write_config,	grackle_write_config),
96249259Sdim
97249259Sdim	DEVMETHOD_END
98249259Sdim};
99249259Sdim
100249259Sdimstatic devclass_t	grackle_devclass;
101249259SdimDEFINE_CLASS_1(pcib, grackle_driver, grackle_methods,
102249259Sdim    sizeof(struct grackle_softc), ofw_pci_driver);
103249259SdimDRIVER_MODULE(grackle, ofwbus, grackle_driver, grackle_devclass, 0, 0);
104249259Sdim
105249259Sdimstatic int
106249259Sdimgrackle_probe(device_t dev)
107249259Sdim{
108249259Sdim	const char	*type, *compatible;
109249259Sdim
110249259Sdim	type = ofw_bus_get_type(dev);
111249259Sdim	compatible = ofw_bus_get_compat(dev);
112249259Sdim
113249259Sdim	if (type == NULL || compatible == NULL)
114249259Sdim		return (ENXIO);
115249259Sdim
116249259Sdim	if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0)
117249259Sdim		return (ENXIO);
118249259Sdim
119249259Sdim	device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge");
120249259Sdim	return (0);
121249259Sdim}
122249259Sdim
123249259Sdimstatic int
124249259Sdimgrackle_attach(device_t dev)
125249259Sdim{
126249259Sdim	struct		grackle_softc *sc;
127249259Sdim
128249259Sdim	sc = device_get_softc(dev);
129249259Sdim
130249259Sdim	/*
131249259Sdim	 * The Grackle PCI config addr/data registers are actually in
132249259Sdim	 * PCI space, but since they are needed to actually probe the
133249259Sdim	 * PCI bus, use the fact that they are also available directly
134249259Sdim	 * on the processor bus and map them
135249259Sdim	 */
136249259Sdim	sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE);
137249259Sdim	sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE);
138249259Sdim
139249259Sdim	return (ofw_pci_attach(dev));
140249259Sdim}
141249259Sdim
142249259Sdimstatic u_int32_t
143249259Sdimgrackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
144251662Sdim    int width)
145251662Sdim{
146251662Sdim	struct		grackle_softc *sc;
147251662Sdim	vm_offset_t	caoff;
148251662Sdim	u_int32_t	retval = 0xffffffff;
149251662Sdim
150251662Sdim	sc = device_get_softc(dev);
151251662Sdim	caoff = sc->sc_data + (reg & 0x03);
152251662Sdim
153251662Sdim	if (grackle_enable_config(sc, bus, slot, func, reg) != 0) {
154251662Sdim
155251662Sdim		/*
156251662Sdim		 * Config probes to non-existent devices on the
157251662Sdim		 * secondary bus generates machine checks. Be sure
158251662Sdim		 * to catch these.
159251662Sdim		 */
160251662Sdim		if (bus > 0) {
161251662Sdim		  if (badaddr((void *)sc->sc_data, 4)) {
162251662Sdim			  return (retval);
163251662Sdim		  }
164251662Sdim		}
165251662Sdim
166251662Sdim		switch (width) {
167251662Sdim		case 1:
168251662Sdim			retval = (in8rb(caoff));
169251662Sdim			break;
170251662Sdim		case 2:
171249259Sdim			retval = (in16rb(caoff));
172249259Sdim			break;
173249259Sdim		case 4:
174249259Sdim			retval = (in32rb(caoff));
175249259Sdim			break;
176249259Sdim		}
177249259Sdim	}
178249259Sdim	grackle_disable_config(sc);
179249259Sdim
180249259Sdim	return (retval);
181249259Sdim}
182249259Sdim
183249259Sdimstatic void
184249259Sdimgrackle_write_config(device_t dev, u_int bus, u_int slot, u_int func,
185249259Sdim    u_int reg, u_int32_t val, int width)
186249259Sdim{
187249259Sdim	struct		grackle_softc *sc;
188249259Sdim	vm_offset_t	caoff;
189249259Sdim
190249259Sdim	sc = device_get_softc(dev);
191249259Sdim	caoff = sc->sc_data + (reg & 0x03);
192249259Sdim
193249259Sdim	if (grackle_enable_config(sc, bus, slot, func, reg)) {
194249259Sdim		switch (width) {
195249259Sdim		case 1:
196249259Sdim			out8rb(caoff, val);
197249259Sdim			(void)in8rb(caoff);
198249259Sdim			break;
199249259Sdim		case 2:
200249259Sdim			out16rb(caoff, val);
201249259Sdim			(void)in16rb(caoff);
202249259Sdim			break;
203249259Sdim		case 4:
204249259Sdim			out32rb(caoff, val);
205249259Sdim			(void)in32rb(caoff);
206249259Sdim			break;
207249259Sdim		}
208249259Sdim	}
209249259Sdim	grackle_disable_config(sc);
210249259Sdim}
211249259Sdim
212249259Sdimstatic int
213249259Sdimgrackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot,
214249259Sdim    u_int func, u_int reg)
215251662Sdim{
216251662Sdim	u_int32_t	cfgval;
217249259Sdim
218249259Sdim	/*
219249259Sdim	 * Unlike UniNorth, the format of the config word is the same
220251662Sdim	 * for local (0) and remote busses.
221249259Sdim	 */
222251662Sdim	cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC)
223251662Sdim	    | GRACKLE_CFG_ENABLE;
224251662Sdim
225251662Sdim	out32rb(sc->sc_addr, cfgval);
226251662Sdim	(void) in32rb(sc->sc_addr);
227251662Sdim
228251662Sdim	return (1);
229249259Sdim}
230249259Sdim
231249259Sdimstatic void
232249259Sdimgrackle_disable_config(struct grackle_softc *sc)
233249259Sdim{
234249259Sdim	/*
235249259Sdim	 * Clear the GRACKLE_CFG_ENABLE bit to prevent stray
236249259Sdim	 * accesses from causing config cycles
237249259Sdim	 */
238249259Sdim	out32rb(sc->sc_addr, 0);
239249259Sdim}
240249259Sdim
241249259Sdimstatic int
242249259Sdimbadaddr(void *addr, size_t size)
243249259Sdim{
244249259Sdim	struct thread	*td;
245249259Sdim	jmp_buf		env, *oldfaultbuf;
246249259Sdim	int		x;
247249259Sdim
248249259Sdim	/* Get rid of any stale machine checks that have been waiting.  */
249249259Sdim	__asm __volatile ("sync; isync");
250249259Sdim
251249259Sdim	td = curthread;
252249259Sdim
253249259Sdim	oldfaultbuf = td->td_pcb->pcb_onfault;
254249259Sdim	td->td_pcb->pcb_onfault = &env;
255249259Sdim	if (setjmp(env)) {
256249259Sdim		td->td_pcb->pcb_onfault = oldfaultbuf;
257249259Sdim		__asm __volatile ("sync");
258249259Sdim		return 1;
259249259Sdim	}
260249259Sdim
261249259Sdim	__asm __volatile ("sync");
262249259Sdim
263249259Sdim	switch (size) {
264249259Sdim	case 1:
265249259Sdim		x = *(volatile int8_t *)addr;
266249259Sdim		break;
267249259Sdim	case 2:
268249259Sdim		x = *(volatile int16_t *)addr;
269249259Sdim		break;
270249259Sdim	case 4:
271249259Sdim		x = *(volatile int32_t *)addr;
272249259Sdim		break;
273249259Sdim	default:
274249259Sdim		panic("badaddr: invalid size (%zd)", size);
275249259Sdim	}
276249259Sdim
277249259Sdim	/* Make sure we took the machine check, if we caused one. */
278249259Sdim	__asm __volatile ("sync; isync");
279249259Sdim
280249259Sdim	td->td_pcb->pcb_onfault = oldfaultbuf;
281249259Sdim	__asm __volatile ("sync");	/* To be sure. */
282249259Sdim
283249259Sdim	return (0);
284249259Sdim}
285249259Sdim
286249259Sdim/*
287249259Sdim * Driver to swallow Grackle host bridges from the PCI bus side.
288249259Sdim */
289249259Sdimstatic int
290249259Sdimgrackle_hb_probe(device_t dev)
291249259Sdim{
292249259Sdim
293249259Sdim	if (pci_get_devid(dev) == 0x00021057) {
294249259Sdim		device_set_desc(dev, "Grackle Host to PCI bridge");
295249259Sdim		device_quiet(dev);
296249259Sdim		return (0);
297249259Sdim	}
298249259Sdim
299249259Sdim	return (ENXIO);
300249259Sdim}
301249259Sdim
302249259Sdimstatic int
303249259Sdimgrackle_hb_attach(device_t dev)
304249259Sdim{
305249259Sdim
306249259Sdim	return (0);
307249259Sdim}
308249259Sdim
309249259Sdimstatic device_method_t grackle_hb_methods[] = {
310249259Sdim	/* Device interface */
311249259Sdim	DEVMETHOD(device_probe,         grackle_hb_probe),
312249259Sdim	DEVMETHOD(device_attach,        grackle_hb_attach),
313249259Sdim
314249259Sdim	{ 0, 0 }
315249259Sdim};
316249259Sdim
317249259Sdimstatic driver_t grackle_hb_driver = {
318249259Sdim	"grackle_hb",
319249259Sdim	grackle_hb_methods,
320249259Sdim	1,
321249259Sdim};
322249259Sdimstatic devclass_t grackle_hb_devclass;
323249259Sdim
324249259SdimDRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0);
325249259Sdim