psycho.c revision 119737
1275970Scy/*
2275970Scy * Copyright (c) 1999, 2000 Matthew R. Green
3275970Scy * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>
4275970Scy * All rights reserved.
5275970Scy *
6275970Scy * Redistribution and use in source and binary forms, with or without
7275970Scy * modification, are permitted provided that the following conditions
8275970Scy * are met:
9290000Sglebius * 1. Redistributions of source code must retain the above copyright
10290000Sglebius *    notice, this list of conditions and the following disclaimer.
11290000Sglebius * 2. Redistributions in binary form must reproduce the above copyright
12275970Scy *    notice, this list of conditions and the following disclaimer in the
13275970Scy *    documentation and/or other materials provided with the distribution.
14275970Scy * 3. The name of the author may not be used to endorse or promote products
15275970Scy *    derived from this software without specific prior written permission.
16275970Scy *
17275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18290000Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19290000Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20290000Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22275970Scy * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23275970Scy * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24275970Scy * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25275970Scy * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26290000Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27290000Sglebius * SUCH DAMAGE.
28290000Sglebius *
29290000Sglebius *	from: NetBSD: psycho.c,v 1.39 2001/10/07 20:30:41 eeh Exp
30290000Sglebius *
31290000Sglebius * $FreeBSD: head/sys/sparc64/pci/psycho.c 119737 2003-09-04 15:25:10Z tmm $
32290000Sglebius */
33290000Sglebius
34290000Sglebius/*
35290000Sglebius * Support for `psycho' and `psycho+' UPA to PCI bridge and
36290000Sglebius * UltraSPARC IIi and IIe `sabre' PCI controllers.
37290000Sglebius */
38290000Sglebius
39290000Sglebius#include "opt_ofw_pci.h"
40290000Sglebius#include "opt_psycho.h"
41290000Sglebius
42290000Sglebius#include <sys/param.h>
43290000Sglebius#include <sys/systm.h>
44290000Sglebius#include <sys/bus.h>
45290000Sglebius#include <sys/kernel.h>
46290000Sglebius#include <sys/malloc.h>
47290000Sglebius#include <sys/pcpu.h>
48290000Sglebius
49290000Sglebius#include <dev/ofw/openfirm.h>
50290000Sglebius#include <dev/ofw/ofw_pci.h>
51290000Sglebius
52290000Sglebius#include <machine/bus.h>
53290000Sglebius#include <machine/bus_private.h>
54290000Sglebius#include <machine/iommureg.h>
55290000Sglebius#include <machine/bus_common.h>
56290000Sglebius#include <machine/frame.h>
57290000Sglebius#include <machine/intr_machdep.h>
58290000Sglebius#include <machine/nexusvar.h>
59290000Sglebius#include <machine/ofw_bus.h>
60290000Sglebius#include <machine/ofw_upa.h>
61290000Sglebius#include <machine/resource.h>
62290000Sglebius#include <machine/cpu.h>
63290000Sglebius
64290000Sglebius#include <sys/rman.h>
65290000Sglebius
66290000Sglebius#include <machine/iommuvar.h>
67290000Sglebius
68290000Sglebius#include <dev/pci/pcivar.h>
69290000Sglebius#include <dev/pci/pcireg.h>
70290000Sglebius
71290000Sglebius#include <sparc64/pci/ofw_pci.h>
72290000Sglebius#include <sparc64/pci/psychoreg.h>
73290000Sglebius#include <sparc64/pci/psychovar.h>
74290000Sglebius
75275970Scy#include "pcib_if.h"
76275970Scy
77290000Sglebiusstatic void psycho_get_ranges(phandle_t, struct upa_ranges **, int *);
78275970Scystatic void psycho_set_intr(struct psycho_softc *, int, device_t, bus_addr_t,
79275970Scy    int, driver_intr_t);
80275970Scystatic int psycho_find_intrmap(struct psycho_softc *, int, bus_addr_t *,
81275970Scy    bus_addr_t *, u_long *);
82275970Scystatic void psycho_intr_stub(void *);
83275970Scystatic bus_space_tag_t psycho_alloc_bus_tag(struct psycho_softc *, int);
84290000Sglebius#ifndef OFW_NEWPCI
85275970Scystatic ofw_pci_binit_t psycho_binit;
86275970Scy#endif
87290000Sglebius
88290000Sglebius/* Interrupt handlers */
89275970Scystatic void psycho_ue(void *);
90290000Sglebiusstatic void psycho_ce(void *);
91275970Scystatic void psycho_bus_a(void *);
92275970Scystatic void psycho_bus_b(void *);
93275970Scystatic void psycho_powerfail(void *);
94290000Sglebius#ifdef PSYCHO_MAP_WAKEUP
95290000Sglebiusstatic void psycho_wakeup(void *);
96290000Sglebius#endif
97290000Sglebius
98290000Sglebius/* IOMMU support */
99290000Sglebiusstatic void psycho_iommu_init(struct psycho_softc *, int);
100290000Sglebius
101290000Sglebius/*
102290000Sglebius * Methods.
103290000Sglebius */
104290000Sglebiusstatic device_probe_t psycho_probe;
105290000Sglebiusstatic device_attach_t psycho_attach;
106290000Sglebiusstatic bus_read_ivar_t psycho_read_ivar;
107290000Sglebiusstatic bus_setup_intr_t psycho_setup_intr;
108290000Sglebiusstatic bus_teardown_intr_t psycho_teardown_intr;
109290000Sglebiusstatic bus_alloc_resource_t psycho_alloc_resource;
110290000Sglebiusstatic bus_activate_resource_t psycho_activate_resource;
111290000Sglebiusstatic bus_deactivate_resource_t psycho_deactivate_resource;
112290000Sglebiusstatic bus_release_resource_t psycho_release_resource;
113290000Sglebiusstatic pcib_maxslots_t psycho_maxslots;
114290000Sglebiusstatic pcib_read_config_t psycho_read_config;
115290000Sglebiusstatic pcib_write_config_t psycho_write_config;
116290000Sglebiusstatic pcib_route_interrupt_t psycho_route_interrupt;
117290000Sglebiusstatic ofw_pci_intr_pending_t psycho_intr_pending;
118290000Sglebius#ifndef OFW_NEWPCI
119290000Sglebiusstatic ofw_pci_guess_ino_t psycho_guess_ino;
120290000Sglebius#endif
121290000Sglebiusstatic ofw_pci_get_bus_handle_t psycho_get_bus_handle;
122290000Sglebius#ifdef OFW_NEWPCI
123290000Sglebiusstatic ofw_pci_get_node_t psycho_get_node;
124275970Scystatic ofw_pci_adjust_busrange_t psycho_adjust_busrange;
125275970Scy#endif
126275970Scy
127275970Scystatic device_method_t psycho_methods[] = {
128275970Scy	/* Device interface */
129275970Scy	DEVMETHOD(device_probe,		psycho_probe),
130275970Scy	DEVMETHOD(device_attach,	psycho_attach),
131275970Scy
132290000Sglebius	/* Bus interface */
133275970Scy	DEVMETHOD(bus_print_child,	bus_generic_print_child),
134275970Scy	DEVMETHOD(bus_read_ivar,	psycho_read_ivar),
135275970Scy	DEVMETHOD(bus_setup_intr, 	psycho_setup_intr),
136275970Scy	DEVMETHOD(bus_teardown_intr,	psycho_teardown_intr),
137275970Scy	DEVMETHOD(bus_alloc_resource,	psycho_alloc_resource),
138275970Scy	DEVMETHOD(bus_activate_resource,	psycho_activate_resource),
139275970Scy	DEVMETHOD(bus_deactivate_resource,	psycho_deactivate_resource),
140275970Scy	DEVMETHOD(bus_release_resource,	psycho_release_resource),
141280849Scy
142275970Scy	/* pcib interface */
143275970Scy	DEVMETHOD(pcib_maxslots,	psycho_maxslots),
144275970Scy	DEVMETHOD(pcib_read_config,	psycho_read_config),
145275970Scy	DEVMETHOD(pcib_write_config,	psycho_write_config),
146275970Scy	DEVMETHOD(pcib_route_interrupt,	psycho_route_interrupt),
147275970Scy
148275970Scy	/* ofw_pci interface */
149275970Scy	DEVMETHOD(ofw_pci_intr_pending,	psycho_intr_pending),
150275970Scy#ifndef OFW_NEWPCI
151275970Scy	DEVMETHOD(ofw_pci_guess_ino,	psycho_guess_ino),
152275970Scy#endif
153275970Scy	DEVMETHOD(ofw_pci_get_bus_handle,	psycho_get_bus_handle),
154275970Scy#ifdef OFW_NEWPCI
155290000Sglebius	DEVMETHOD(ofw_pci_get_node,	psycho_get_node),
156290000Sglebius	DEVMETHOD(ofw_pci_adjust_busrange,	psycho_adjust_busrange),
157290000Sglebius#endif
158290000Sglebius
159290000Sglebius	{ 0, 0 }
160290000Sglebius};
161290000Sglebius
162290000Sglebiusstatic driver_t psycho_driver = {
163290000Sglebius	"pcib",
164290000Sglebius	psycho_methods,
165290000Sglebius	sizeof(struct psycho_softc),
166290000Sglebius};
167290000Sglebius
168290000Sglebiusstatic devclass_t psycho_devclass;
169290000Sglebius
170290000SglebiusDRIVER_MODULE(psycho, nexus, psycho_driver, psycho_devclass, 0, 0);
171290000Sglebius
172290000SglebiusSLIST_HEAD(, psycho_softc) psycho_softcs =
173290000Sglebius    SLIST_HEAD_INITIALIZER(psycho_softcs);
174290000Sglebius
175290000Sglebiusstruct psycho_clr {
176290000Sglebius	struct psycho_softc	*pci_sc;
177290000Sglebius	bus_addr_t	pci_clr;	/* clear register */
178290000Sglebius	driver_intr_t	*pci_handler;	/* handler to call */
179290000Sglebius	void		*pci_arg;	/* argument for the handler */
180290000Sglebius	void		*pci_cookie;	/* interrupt cookie of parent bus */
181290000Sglebius};
182290000Sglebius
183290000Sglebiusstruct psycho_strayclr {
184290000Sglebius	struct psycho_softc	*psc_sc;
185290000Sglebius	bus_addr_t	psc_clr;	/* clear register */
186290000Sglebius};
187290000Sglebius
188290000Sglebius#define	PSYCHO_READ8(sc, off) \
189290000Sglebius	bus_space_read_8((sc)->sc_bustag, (sc)->sc_bushandle, (off))
190290000Sglebius#define	PSYCHO_WRITE8(sc, off, v) \
191275970Scy	bus_space_write_8((sc)->sc_bustag, (sc)->sc_bushandle, (off), (v))
192275970Scy#define	PCICTL_READ8(sc, off) \
193275970Scy	PSYCHO_READ8((sc), (sc)->sc_pcictl + (off))
194275970Scy#define	PCICTL_WRITE8(sc, off, v) \
195275970Scy	PSYCHO_WRITE8((sc), (sc)->sc_pcictl + (off), (v))
196275970Scy
197275970Scy/*
198275970Scy * "sabre" is the UltraSPARC IIi onboard UPA to PCI bridge.  It manages a
199275970Scy * single PCI bus and does not have a streaming buffer.  It often has an APB
200275970Scy * (advanced PCI bridge) connected to it, which was designed specifically for
201290000Sglebius * the IIi.  The APB let's the IIi handle two independednt PCI buses, and
202290000Sglebius * appears as two "simba"'s underneath the sabre.
203290000Sglebius *
204290000Sglebius * "psycho" and "psycho+" is a dual UPA to PCI bridge.  It sits on the UPA bus
205290000Sglebius * and manages two PCI buses.  "psycho" has two 64-bit 33MHz buses, while
206290000Sglebius * "psycho+" controls both a 64-bit 33Mhz and a 64-bit 66Mhz PCI bus.  You
207290000Sglebius * will usually find a "psycho+" since I don't think the original "psycho"
208290000Sglebius * ever shipped, and if it did it would be in the U30.
209290000Sglebius *
210290000Sglebius * Each "psycho" PCI bus appears as a separate OFW node, but since they are
211290000Sglebius * both part of the same IC, they only have a single register space.  As such,
212290000Sglebius * they need to be configured together, even though the autoconfiguration will
213290000Sglebius * attach them separately.
214275970Scy *
215290000Sglebius * On UltraIIi machines, "sabre" itself usually takes pci0, with "simba" often
216290000Sglebius * as pci1 and pci2, although they have been implemented with other PCI bus
217290000Sglebius * numbers on some machines.
218290000Sglebius *
219290000Sglebius * On UltraII machines, there can be any number of "psycho+" ICs, each
220275970Scy * providing two PCI buses.
221275970Scy *
222275970Scy *
223275970Scy * XXXX The psycho/sabre node has an `interrupts' attribute.  They contain
224275970Scy * the values of the following interrupts in this order:
225275970Scy *
226275970Scy * PCI Bus Error	(30)
227275970Scy * DMA UE		(2e)
228275970Scy * DMA CE		(2f)
229275970Scy * Power Fail		(25)
230290000Sglebius *
231290000Sglebius * We really should attach handlers for each.
232290000Sglebius */
233290000Sglebius#ifdef DEBUGGER_ON_POWERFAIL
234290000Sglebius#define	PSYCHO_PWRFAIL_INT_FLAGS	INTR_FAST
235275970Scy#else
236275970Scy#define	PSYCHO_PWRFAIL_INT_FLAGS	0
237275970Scy#endif
238275970Scy
239275970Scy#define	OFW_PCI_TYPE		"pci"
240275970Scy
241275970Scystruct psycho_desc {
242275970Scy	char	*pd_string;
243275970Scy	int	pd_mode;
244275970Scy	char	*pd_name;
245275970Scy};
246275970Scy
247275970Scystatic struct psycho_desc psycho_compats[] = {
248275970Scy	{ "pci108e,8000", PSYCHO_MODE_PSYCHO,	"Psycho compatible" },
249275970Scy	{ "pci108e,a000", PSYCHO_MODE_SABRE,	"Sabre (US-IIi) compatible" },
250275970Scy	{ "pci108e,a001", PSYCHO_MODE_SABRE,	"Sabre (US-IIe) compatible" },
251275970Scy	{ NULL,		  0,			NULL }
252275970Scy};
253275970Scy
254275970Scystatic struct psycho_desc psycho_models[] = {
255275970Scy	{ "SUNW,psycho",  PSYCHO_MODE_PSYCHO,	"Psycho" },
256275970Scy	{ "SUNW,sabre",   PSYCHO_MODE_SABRE,	"Sabre" },
257275970Scy	{ NULL,		  0,			NULL }
258275970Scy};
259275970Scy
260275970Scystatic struct psycho_desc *
261290000Sglebiuspsycho_find_desc(struct psycho_desc *table, char *string)
262275970Scy{
263290000Sglebius	struct psycho_desc *desc;
264290000Sglebius
265290000Sglebius	for (desc = table; desc->pd_string != NULL; desc++) {
266275970Scy		if (strcmp(desc->pd_string, string) == 0)
267275970Scy			return (desc);
268275970Scy	}
269275970Scy	return (NULL);
270275970Scy}
271275970Scy
272275970Scystatic struct psycho_desc *
273275970Scypsycho_get_desc(phandle_t node, char *model)
274275970Scy{
275275970Scy	struct psycho_desc *rv;
276275970Scy	char compat[32];
277275970Scy
278275970Scy	rv = NULL;
279290000Sglebius	if (model != NULL)
280290000Sglebius		rv = psycho_find_desc(psycho_models, model);
281290000Sglebius	if (rv == NULL &&
282290000Sglebius	    OF_getprop(node, "compatible", compat, sizeof(compat)) != -1)
283290000Sglebius		rv = psycho_find_desc(psycho_compats, compat);
284290000Sglebius	return (rv);
285290000Sglebius}
286290000Sglebius
287290000Sglebiusstatic int
288290000Sglebiuspsycho_probe(device_t dev)
289290000Sglebius{
290290000Sglebius	phandle_t node;
291290000Sglebius	char *dtype;
292290000Sglebius
293290000Sglebius	node = nexus_get_node(dev);
294290000Sglebius	dtype = nexus_get_device_type(dev);
295290000Sglebius	if (nexus_get_reg(dev) != NULL && dtype != NULL &&
296275970Scy	    strcmp(dtype, OFW_PCI_TYPE) == 0 &&
297290000Sglebius	    psycho_get_desc(node, nexus_get_model(dev)) != NULL) {
298275970Scy		device_set_desc(dev, "U2P UPA-PCI bridge");
299290000Sglebius		return (0);
300275970Scy	}
301275970Scy
302275970Scy	return (ENXIO);
303290000Sglebius}
304290000Sglebius
305290000Sglebius/*
306290000Sglebius * SUNW,psycho initialisation ..
307275970Scy *	- find the per-psycho registers
308290000Sglebius *	- figure out the IGN.
309290000Sglebius *	- find our partner psycho
310290000Sglebius *	- configure ourselves
311290000Sglebius *	- bus range, bus,
312275970Scy *	- interrupt map,
313290000Sglebius *	- setup the chipsets.
314290000Sglebius *	- if we're the first of the pair, initialise the IOMMU, otherwise
315290000Sglebius *	  just copy it's tags and addresses.
316290000Sglebius */
317275970Scystatic int
318290000Sglebiuspsycho_attach(device_t dev)
319290000Sglebius{
320290000Sglebius	struct psycho_softc *sc;
321275970Scy	struct psycho_softc *osc = NULL;
322275970Scy	struct psycho_softc *asc;
323290000Sglebius	struct upa_regs *reg;
324290000Sglebius#ifndef OFW_NEWPCI
325275970Scy	struct ofw_pci_bdesc obd;
326290000Sglebius#endif
327290000Sglebius	struct psycho_desc *desc;
328275970Scy	phandle_t node;
329275970Scy	u_int64_t csr;
330290000Sglebius	u_long mlen;
331290000Sglebius	int psycho_br[2];
332290000Sglebius	int n, i, nreg, rid;
333275970Scy#ifdef PSYCHO_DEBUG
334275970Scy	bus_addr_t map, clr;
335275970Scy	u_int64_t mr;
336275970Scy#endif
337275970Scy
338275970Scy	node = nexus_get_node(dev);
339275970Scy	sc = device_get_softc(dev);
340275970Scy	desc = psycho_get_desc(node, nexus_get_model(dev));
341275970Scy
342290000Sglebius	sc->sc_node = node;
343290000Sglebius	sc->sc_dev = dev;
344290000Sglebius	sc->sc_dmatag = nexus_get_dmatag(dev);
345290000Sglebius	sc->sc_mode = desc->pd_mode;
346290000Sglebius
347290000Sglebius	/*
348275970Scy	 * The psycho gets three register banks:
349275970Scy	 * (0) per-PBM configuration and status registers
350275970Scy	 * (1) per-PBM PCI configuration space, containing only the
351275970Scy	 *     PBM 256-byte PCI header
352290000Sglebius	 * (2) the shared psycho configuration registers (struct psychoreg)
353290000Sglebius	 */
354290000Sglebius	reg = nexus_get_reg(dev);
355290000Sglebius	nreg = nexus_get_nreg(dev);
356275970Scy	/* Register layouts are different.  stuupid. */
357275970Scy	if (sc->sc_mode == PSYCHO_MODE_PSYCHO) {
358290000Sglebius		if (nreg <= 2)
359275970Scy			panic("psycho_attach: %d not enough registers", nreg);
360275970Scy		sc->sc_basepaddr = (vm_paddr_t)UPA_REG_PHYS(&reg[2]);
361275970Scy		mlen = UPA_REG_SIZE(&reg[2]);
362275970Scy		sc->sc_pcictl = UPA_REG_PHYS(&reg[0]) - sc->sc_basepaddr;
363275970Scy		switch (sc->sc_pcictl) {
364275970Scy		case PSR_PCICTL0:
365275970Scy			sc->sc_half = 0;
366275970Scy			break;
367275970Scy		case PSR_PCICTL1:
368275970Scy			sc->sc_half = 1;
369275970Scy			break;
370275970Scy		default:
371275970Scy			panic("psycho_attach: bogus pci control register "
372290000Sglebius			    "location");
373290000Sglebius		}
374290000Sglebius	} else {
375275970Scy		if (nreg <= 0)
376275970Scy			panic("psycho_attach: %d not enough registers", nreg);
377275970Scy		sc->sc_basepaddr = (vm_paddr_t)UPA_REG_PHYS(&reg[0]);
378275970Scy		mlen = UPA_REG_SIZE(reg);
379275970Scy		sc->sc_pcictl = PSR_PCICTL0;
380290000Sglebius		sc->sc_half = 0;
381290000Sglebius	}
382290000Sglebius
383290000Sglebius	/*
384290000Sglebius	 * Match other psycho's that are already configured against
385290000Sglebius	 * the base physical address. This will be the same for a
386290000Sglebius	 * pair of devices that share register space.
387290000Sglebius	 */
388290000Sglebius	SLIST_FOREACH(asc, &psycho_softcs, sc_link) {
389290000Sglebius		if (asc->sc_basepaddr == sc->sc_basepaddr) {
390290000Sglebius			/* Found partner */
391290000Sglebius			osc = asc;
392290000Sglebius			break;
393275970Scy		}
394290000Sglebius	}
395290000Sglebius
396275970Scy	if (osc == NULL) {
397290000Sglebius		rid = 0;
398290000Sglebius		sc->sc_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
399290000Sglebius		    sc->sc_basepaddr, sc->sc_basepaddr + mlen - 1, mlen,
400290000Sglebius		    RF_ACTIVE);
401290000Sglebius		if (sc->sc_mem_res == NULL ||
402290000Sglebius		    rman_get_start(sc->sc_mem_res) != sc->sc_basepaddr)
403290000Sglebius			panic("psycho_attach: can't allocate device memory");
404290000Sglebius		sc->sc_bustag = rman_get_bustag(sc->sc_mem_res);
405290000Sglebius		sc->sc_bushandle = rman_get_bushandle(sc->sc_mem_res);
406290000Sglebius	} else {
407290000Sglebius		/*
408290000Sglebius		 * There's another psycho using the same register space. Copy the
409290000Sglebius		 * relevant stuff.
410290000Sglebius		 */
411275970Scy		sc->sc_mem_res = NULL;
412275970Scy		sc->sc_bustag = osc->sc_bustag;
413275970Scy		sc->sc_bushandle = osc->sc_bushandle;
414275970Scy	}
415275970Scy	csr = PSYCHO_READ8(sc, PSR_CS);
416275970Scy	sc->sc_ign = 0x7c0; /* APB IGN is always 0x7c */
417275970Scy	if (sc->sc_mode == PSYCHO_MODE_PSYCHO)
418275970Scy		sc->sc_ign = PSYCHO_GCSR_IGN(csr) << 6;
419275970Scy
420275970Scy	device_printf(dev, "%s, impl %d, version %d, ign %#x, bus %c\n",
421275970Scy	    desc->pd_name, (int)PSYCHO_GCSR_IMPL(csr),
422275970Scy	    (int)PSYCHO_GCSR_VERS(csr), sc->sc_ign, 'A' + sc->sc_half);
423275970Scy
424275970Scy	/*
425275970Scy	 * Setup the PCI control register
426275970Scy	 */
427275970Scy	csr = PCICTL_READ8(sc, PCR_CS);
428275970Scy	csr |= PCICTL_MRLM | PCICTL_ARB_PARK | PCICTL_ERRINTEN | PCICTL_4ENABLE;
429275970Scy	csr &= ~(PCICTL_SERR | PCICTL_CPU_PRIO | PCICTL_ARB_PRIO |
430275970Scy	    PCICTL_RTRYWAIT);
431275970Scy	PCICTL_WRITE8(sc, PCR_CS, csr);
432275970Scy
433275970Scy	if (sc->sc_mode == PSYCHO_MODE_SABRE) {
434275970Scy		/*
435275970Scy		 * Use the PROM preset for now.
436275970Scy		 */
437290000Sglebius		csr = PCICTL_READ8(sc, PCR_TAS);
438290000Sglebius		if (csr == 0)
439290000Sglebius			panic("psycho_attach: sabre TAS not initialized.");
440290000Sglebius		sc->sc_dvmabase = (ffs(csr) - 1) << PCITAS_ADDR_SHIFT;
441275970Scy	} else
442275970Scy		sc->sc_dvmabase = -1;
443275970Scy
444275970Scy	/* Grab the psycho ranges */
445275970Scy	psycho_get_ranges(sc->sc_node, &sc->sc_range, &sc->sc_nrange);
446290000Sglebius
447290000Sglebius	/* Initialize memory and i/o rmans */
448290000Sglebius	sc->sc_io_rman.rm_type = RMAN_ARRAY;
449290000Sglebius	sc->sc_io_rman.rm_descr = "Psycho PCI I/O Ports";
450290000Sglebius	if (rman_init(&sc->sc_io_rman) != 0 ||
451290000Sglebius	    rman_manage_region(&sc->sc_io_rman, 0, PSYCHO_IO_SIZE) != 0)
452275970Scy		panic("psycho_probe: failed to set up i/o rman");
453290000Sglebius	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
454290000Sglebius	sc->sc_mem_rman.rm_descr = "Psycho PCI Memory";
455275970Scy	if (rman_init(&sc->sc_mem_rman) != 0 ||
456275970Scy	    rman_manage_region(&sc->sc_mem_rman, 0, PSYCHO_MEM_SIZE) != 0)
457275970Scy		panic("psycho_probe: failed to set up memory rman");
458290000Sglebius	/*
459290000Sglebius	 * Find the addresses of the various bus spaces.
460290000Sglebius	 * There should not be multiple ones of one kind.
461290000Sglebius	 * The physical start addresses of the ranges are the configuration,
462290000Sglebius	 * memory and IO handles.
463290000Sglebius	 */
464290000Sglebius	for (n = 0; n < sc->sc_nrange; n++) {
465290000Sglebius		i = UPA_RANGE_CS(&sc->sc_range[n]);
466290000Sglebius		if (sc->sc_bh[i] != 0)
467290000Sglebius			panic("psycho_attach: duplicate range for space %d", i);
468290000Sglebius		sc->sc_bh[i] = UPA_RANGE_PHYS(&sc->sc_range[n]);
469290000Sglebius	}
470290000Sglebius	/*
471290000Sglebius	 * Check that all needed handles are present. The PCI_CS_MEM64 one is
472290000Sglebius	 * not currently used.
473290000Sglebius	 */
474290000Sglebius	for (n = 0; n < 3; n++) {
475290000Sglebius		if (sc->sc_bh[n] == 0)
476290000Sglebius			panic("psycho_attach: range %d missing", n);
477290000Sglebius	}
478290000Sglebius
479290000Sglebius	/* Register the softc, this is needed for paired psychos. */
480290000Sglebius	SLIST_INSERT_HEAD(&psycho_softcs, sc, sc_link);
481290000Sglebius
482290000Sglebius	/*
483290000Sglebius	 * If we're a sabre or the first of a pair of psycho's to arrive here,
484290000Sglebius	 * start up the IOMMU.
485290000Sglebius	 */
486290000Sglebius	if (osc == NULL) {
487290000Sglebius		/*
488290000Sglebius		 * Establish handlers for interesting interrupts....
489290000Sglebius		 *
490290000Sglebius		 * XXX We need to remember these and remove this to support
491290000Sglebius		 * hotplug on the UPA/FHC bus.
492290000Sglebius		 *
493290000Sglebius		 * XXX Not all controllers have these, but installing them
494290000Sglebius		 * is better than trying to sort through this mess.
495275970Scy		 */
496275970Scy		psycho_set_intr(sc, 0, dev, PSR_UE_INT_MAP, INTR_FAST,
497275970Scy		    psycho_ue);
498275970Scy		psycho_set_intr(sc, 1, dev, PSR_CE_INT_MAP, 0, psycho_ce);
499275970Scy		psycho_set_intr(sc, 2, dev, PSR_PCIAERR_INT_MAP, INTR_FAST,
500275970Scy		    psycho_bus_a);
501275970Scy		psycho_set_intr(sc, 4, dev, PSR_POWER_INT_MAP,
502290000Sglebius		    PSYCHO_PWRFAIL_INT_FLAGS, psycho_powerfail);
503290000Sglebius		/* Psycho-specific initialization. */
504290000Sglebius		if (sc->sc_mode == PSYCHO_MODE_PSYCHO) {
505275970Scy			/*
506275970Scy			 * Sabres do not have the following two interrupts.
507275970Scy			 */
508290000Sglebius			psycho_set_intr(sc, 3, dev, PSR_PCIBERR_INT_MAP,
509290000Sglebius			    INTR_FAST, psycho_bus_b);
510290000Sglebius#ifdef PSYCHO_MAP_WAKEUP
511275970Scy			/*
512290000Sglebius			 * psycho_wakeup() doesn't do anything useful right
513290000Sglebius			 * now.
514290000Sglebius			 */
515290000Sglebius			psycho_set_intr(sc, 5, dev, PSR_PWRMGT_INT_MAP, 0,
516290000Sglebius			    psycho_wakeup);
517290000Sglebius#endif /* PSYCHO_MAP_WAKEUP */
518290000Sglebius
519290000Sglebius			/* Initialize the counter-timer. */
520290000Sglebius			sparc64_counter_init(sc->sc_bustag, sc->sc_bushandle,
521290000Sglebius			    PSR_TC0);
522290000Sglebius		}
523290000Sglebius
524290000Sglebius		/*
525290000Sglebius		 * Setup IOMMU and PCI configuration if we're the first
526290000Sglebius		 * of a pair of psycho's to arrive here.
527290000Sglebius		 *
528290000Sglebius		 * We should calculate a TSB size based on amount of RAM
529290000Sglebius		 * and number of bus controllers and number and type of
530290000Sglebius		 * child devices.
531290000Sglebius		 *
532290000Sglebius		 * For the moment, 32KB should be more than enough.
533290000Sglebius		 */
534290000Sglebius		sc->sc_is = malloc(sizeof(struct iommu_state), M_DEVBUF,
535290000Sglebius		    M_NOWAIT);
536290000Sglebius		if (sc->sc_is == NULL)
537290000Sglebius			panic("psycho_attach: malloc iommu_state failed");
538290000Sglebius		sc->sc_is->is_sb[0] = 0;
539290000Sglebius		sc->sc_is->is_sb[1] = 0;
540290000Sglebius		if (OF_getproplen(sc->sc_node, "no-streaming-cache") < 0)
541290000Sglebius			sc->sc_is->is_sb[0] = sc->sc_pcictl + PCR_STRBUF;
542290000Sglebius		psycho_iommu_init(sc, 3);
543290000Sglebius	} else {
544290000Sglebius		/* Just copy IOMMU state, config tag and address */
545290000Sglebius		sc->sc_is = osc->sc_is;
546290000Sglebius		if (OF_getproplen(sc->sc_node, "no-streaming-cache") < 0)
547290000Sglebius			sc->sc_is->is_sb[1] = sc->sc_pcictl + PCR_STRBUF;
548290000Sglebius		iommu_reset(sc->sc_is);
549290000Sglebius	}
550275970Scy
551275970Scy	/* Allocate our tags. */
552290000Sglebius	sc->sc_memt = psycho_alloc_bus_tag(sc, PCI_MEMORY_BUS_SPACE);
553275970Scy	sc->sc_iot = psycho_alloc_bus_tag(sc, PCI_IO_BUS_SPACE);
554275970Scy	sc->sc_cfgt = psycho_alloc_bus_tag(sc, PCI_CONFIG_BUS_SPACE);
555275970Scy	if (bus_dma_tag_create(sc->sc_dmatag, 8, 1, 0, 0x3ffffffff, NULL, NULL,
556275970Scy	    0x3ffffffff, 0xff, 0xffffffff, 0, NULL, NULL, &sc->sc_dmat) != 0)
557275970Scy		panic("psycho_attach: bus_dma_tag_create failed");
558275970Scy	/* Customize the tag. */
559275970Scy	sc->sc_dmat->dt_cookie = sc->sc_is;
560275970Scy	sc->sc_dmat->dt_mt = &iommu_dma_methods;
561275970Scy	/* XXX: register as root dma tag (kludge). */
562290000Sglebius	sparc64_root_dma_tag = sc->sc_dmat;
563290000Sglebius
564290000Sglebius#ifdef PSYCHO_DEBUG
565290000Sglebius	/*
566275970Scy	 * Enable all interrupts and clear all interrupt states.
567290000Sglebius	 * This aids the debugging of interrupt routing problems.
568290000Sglebius	 */
569290000Sglebius	for (map = PSR_PCIA0_INT_MAP, clr = PSR_PCIA0_INT_CLR, n = 0;
570290000Sglebius	     map <= PSR_PCIB3_INT_MAP; map += 8, clr += 32, n++) {
571290000Sglebius		mr = PSYCHO_READ8(sc, map);
572275970Scy		device_printf(dev, "intr map (pci) %d: %#lx\n", n, (u_long)mr);
573290000Sglebius		PSYCHO_WRITE8(sc, map, mr & ~INTMAP_V);
574275970Scy		for (i = 0; i < 4; i++)
575275970Scy			PCICTL_WRITE8(sc, clr + i * 8, 0);
576275970Scy		PSYCHO_WRITE8(sc, map, INTMAP_ENABLE(mr, PCPU_GET(mid)));
577290000Sglebius	}
578290000Sglebius	for (map = PSR_SCSI_INT_MAP, clr = PSR_SCSI_INT_CLR, n = 0;
579290000Sglebius	     map <= PSR_SERIAL_INT_MAP; map += 8, clr += 8, n++) {
580290000Sglebius		mr = PSYCHO_READ8(sc, map);
581290000Sglebius		device_printf(dev, "intr map (obio) %d: %#lx, clr: %#lx\n", n,
582290000Sglebius		    (u_long)mr, (u_long)clr);
583290000Sglebius		PSYCHO_WRITE8(sc, map, mr & ~INTMAP_V);
584275970Scy		PSYCHO_WRITE8(sc, clr, 0);
585275970Scy		PSYCHO_WRITE8(sc, map, INTMAP_ENABLE(mr, PCPU_GET(mid)));
586275970Scy	}
587275970Scy#endif /* PSYCHO_DEBUG */
588275970Scy
589275970Scy	/*
590290000Sglebius	 * Get the bus range from the firmware; it is used solely for obtaining
591290000Sglebius	 * the inital bus number, and cannot be trusted on all machines.
592290000Sglebius	 */
593290000Sglebius	n = OF_getprop(node, "bus-range", (void *)psycho_br, sizeof(psycho_br));
594290000Sglebius	if (n == -1)
595275970Scy		panic("could not get psycho bus-range");
596275970Scy	if (n != sizeof(psycho_br))
597275970Scy		panic("broken psycho bus-range (%d)", n);
598275970Scy
599275970Scy	sc->sc_secbus = sc->sc_subbus = ofw_pci_alloc_busno(sc->sc_node);
600275970Scy	/*
601275970Scy	 * Program the bus range registers.
602275970Scy	 * NOTE: the psycho, the second write changes the bus number the
603275970Scy	 * psycho itself uses for it's configuration space, so these
604275970Scy	 * writes must be kept in this order!
605275970Scy	 * The sabre always uses bus 0, but there only can be one sabre per
606275970Scy	 * machine.
607275970Scy	 */
608275970Scy	PCIB_WRITE_CONFIG(dev, psycho_br[0], PCS_DEVICE, PCS_FUNC, PCSR_SUBBUS,
609290000Sglebius	    sc->sc_subbus, 1);
610275970Scy	PCIB_WRITE_CONFIG(dev, psycho_br[0], PCS_DEVICE, PCS_FUNC, PCSR_SECBUS,
611275970Scy	    sc->sc_secbus, 1);
612275970Scy
613290000Sglebius#ifdef OFW_NEWPCI
614290000Sglebius	ofw_bus_setup_iinfo(node, &sc->sc_iinfo, sizeof(ofw_pci_intr_t));
615290000Sglebius#else
616290000Sglebius	obd.obd_bus = obd.obd_secbus = sc->sc_secbus;
617290000Sglebius	obd.obd_subbus = sc->sc_subbus;
618290000Sglebius	obd.obd_slot = PCS_DEVICE;
619290000Sglebius	obd.obd_func = PCS_FUNC;
620290000Sglebius	obd.obd_init = psycho_binit;
621290000Sglebius	obd.obd_super = NULL;
622290000Sglebius
623290000Sglebius	/*
624290000Sglebius	 * Initialize the interrupt registers of all devices hanging from
625290000Sglebius	 * the host bridge directly or indirectly via PCI-PCI bridges.
626290000Sglebius	 * The MI code (and the PCI spec) assume that this is done during
627275970Scy	 * system initialization, however the firmware does not do this
628290000Sglebius	 * at least on some models, and we probably shouldn't trust that
629290000Sglebius	 * the firmware uses the same model as this driver if it does.
630290000Sglebius	 * Additionally, set up the bus numbers and ranges.
631290000Sglebius	 */
632290000Sglebius	ofw_pci_init(dev, sc->sc_node, sc->sc_ign, &obd);
633290000Sglebius#endif /* OFW_NEWPCI */
634290000Sglebius
635290000Sglebius	device_add_child(dev, "pci", sc->sc_secbus);
636290000Sglebius	return (bus_generic_attach(dev));
637290000Sglebius}
638290000Sglebius
639290000Sglebiusstatic void
640275970Scypsycho_set_intr(struct psycho_softc *sc, int index,
641275970Scy    device_t dev, bus_addr_t map, int iflags, driver_intr_t handler)
642275970Scy{
643275970Scy	int rid, vec;
644275970Scy	u_int64_t mr;
645275970Scy
646275970Scy	mr = PSYCHO_READ8(sc, map);
647275970Scy	vec = INTVEC(mr);
648275970Scy	sc->sc_irq_res[index] = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
649275970Scy	    vec, vec, 1, RF_ACTIVE);
650275970Scy	if (sc->sc_irq_res[index] == NULL)
651275970Scy		panic("psycho_set_intr: failed to get interrupt");
652275970Scy	bus_setup_intr(dev, sc->sc_irq_res[index], INTR_TYPE_MISC | iflags,
653275970Scy	    handler, sc, &sc->sc_ihand[index]);
654290000Sglebius	PSYCHO_WRITE8(sc, map, INTMAP_ENABLE(mr, PCPU_GET(mid)));
655275970Scy}
656275970Scy
657275970Scystatic int
658275970Scypsycho_find_intrmap(struct psycho_softc *sc, int ino, bus_addr_t *intrmapptr,
659290000Sglebius    bus_addr_t *intrclrptr, bus_addr_t *intrdiagptr)
660290000Sglebius{
661290000Sglebius	bus_addr_t intrmap, intrclr;
662290000Sglebius	u_int64_t im;
663290000Sglebius	u_long diag;
664275970Scy	int found;
665275970Scy
666275970Scy	found = 0;
667275970Scy	/* Hunt thru obio first */
668275970Scy	diag = PSYCHO_READ8(sc, PSR_OBIO_INT_DIAG);
669275970Scy	for (intrmap = PSR_SCSI_INT_MAP, intrclr = PSR_SCSI_INT_CLR;
670275970Scy	     intrmap <= PSR_SERIAL_INT_MAP; intrmap += 8, intrclr += 8,
671275970Scy	     diag >>= 2) {
672275970Scy		im = PSYCHO_READ8(sc, intrmap);
673275970Scy		if (INTINO(im) == ino) {
674275970Scy			diag &= 2;
675275970Scy			found = 1;
676275970Scy			break;
677275970Scy		}
678275970Scy	}
679275970Scy
680275970Scy	if (!found) {
681275970Scy		diag = PSYCHO_READ8(sc, PSR_PCI_INT_DIAG);
682275970Scy		/* Now do PCI interrupts */
683275970Scy		for (intrmap = PSR_PCIA0_INT_MAP, intrclr = PSR_PCIA0_INT_CLR;
684275970Scy		     intrmap <= PSR_PCIB3_INT_MAP; intrmap += 8, intrclr += 32,
685290000Sglebius		     diag >>= 8) {
686275970Scy			if (sc->sc_mode == PSYCHO_MODE_PSYCHO &&
687275970Scy			    (intrmap == PSR_PCIA2_INT_MAP ||
688275970Scy			     intrmap ==  PSR_PCIA3_INT_MAP))
689275970Scy				continue;
690275970Scy			im = PSYCHO_READ8(sc, intrmap);
691275970Scy			if (((im ^ ino) & 0x3c) == 0) {
692275970Scy				intrclr += 8 * (ino & 3);
693275970Scy				diag = (diag >> ((ino & 3) * 2)) & 2;
694275970Scy				found = 1;
695275970Scy				break;
696275970Scy			}
697275970Scy		}
698275970Scy	}
699275970Scy	if (intrmapptr != NULL)
700275970Scy		*intrmapptr = intrmap;
701290000Sglebius	if (intrclrptr != NULL)
702290000Sglebius		*intrclrptr = intrclr;
703290000Sglebius	if (intrdiagptr != NULL)
704290000Sglebius		*intrdiagptr = diag;
705275970Scy	return (found);
706275970Scy}
707275970Scy
708275970Scy/* grovel the OBP for various psycho properties */
709290000Sglebiusstatic void
710275970Scypsycho_get_ranges(phandle_t node, struct upa_ranges **rp, int *np)
711275970Scy{
712290000Sglebius
713290000Sglebius	*np = OF_getprop_alloc(node, "ranges", sizeof(**rp), (void **)rp);
714290000Sglebius	if (*np == -1)
715290000Sglebius		panic("could not get psycho ranges");
716290000Sglebius}
717275970Scy
718290000Sglebius/*
719275970Scy * Interrupt handlers.
720290000Sglebius */
721275970Scystatic void
722290000Sglebiuspsycho_ue(void *arg)
723275970Scy{
724275970Scy	struct psycho_softc *sc = (struct psycho_softc *)arg;
725275970Scy	u_int64_t afar, afsr;
726275970Scy
727290000Sglebius	afar = PSYCHO_READ8(sc, PSR_UE_AFA);
728290000Sglebius	afsr = PSYCHO_READ8(sc, PSR_UE_AFS);
729290000Sglebius	/*
730290000Sglebius	 * On the UltraSPARC-IIi/IIe, IOMMU misses/protection faults cause
731290000Sglebius	 * the AFAR to be set to the physical address of the TTE entry that
732290000Sglebius	 * was invalid/write protected. Call into the iommu code to have
733275970Scy	 * them decoded to virtual IO addresses.
734275970Scy	 */
735290000Sglebius	if ((afsr & UEAFSR_P_DTE) != 0)
736290000Sglebius		iommu_decode_fault(sc->sc_is, afar);
737290000Sglebius	/* It's uncorrectable.  Dump the regs and panic. */
738290000Sglebius	panic("%s: uncorrectable DMA error AFAR %#lx AFSR %#lx",
739290000Sglebius	    device_get_name(sc->sc_dev), (u_long)afar, (u_long)afsr);
740290000Sglebius}
741290000Sglebius
742275970Scystatic void
743275970Scypsycho_ce(void *arg)
744290000Sglebius{
745290000Sglebius	struct psycho_softc *sc = (struct psycho_softc *)arg;
746290000Sglebius	u_int64_t afar, afsr;
747290000Sglebius
748290000Sglebius	afar = PSYCHO_READ8(sc, PSR_CE_AFA);
749290000Sglebius	afsr = PSYCHO_READ8(sc, PSR_CE_AFS);
750290000Sglebius	/* It's correctable.  Dump the regs and continue. */
751290000Sglebius	device_printf(sc->sc_dev, "correctable DMA error AFAR %#lx "
752290000Sglebius	    "AFSR %#lx\n", (u_long)afar, (u_long)afsr);
753290000Sglebius	/* Clear the error bits that we caught. */
754290000Sglebius	PSYCHO_WRITE8(sc, PSR_CE_AFS, afsr & CEAFSR_ERRMASK);
755290000Sglebius	PSYCHO_WRITE8(sc, PSR_CE_INT_CLR, 0);
756290000Sglebius}
757290000Sglebius
758290000Sglebiusstatic void
759290000Sglebiuspsycho_bus_a(void *arg)
760290000Sglebius{
761290000Sglebius	struct psycho_softc *sc = (struct psycho_softc *)arg;
762290000Sglebius	u_int64_t afar, afsr;
763290000Sglebius
764290000Sglebius	afar = PSYCHO_READ8(sc, PSR_PCICTL0 + PCR_AFA);
765290000Sglebius	afsr = PSYCHO_READ8(sc, PSR_PCICTL0 + PCR_AFS);
766290000Sglebius	/* It's uncorrectable.  Dump the regs and panic. */
767290000Sglebius	panic("%s: PCI bus A error AFAR %#lx AFSR %#lx",
768290000Sglebius	    device_get_name(sc->sc_dev), (u_long)afar, (u_long)afsr);
769290000Sglebius}
770290000Sglebius
771290000Sglebiusstatic void
772290000Sglebiuspsycho_bus_b(void *arg)
773290000Sglebius{
774290000Sglebius	struct psycho_softc *sc = (struct psycho_softc *)arg;
775290000Sglebius	u_int64_t afar, afsr;
776290000Sglebius
777275970Scy	afar = PSYCHO_READ8(sc, PSR_PCICTL1 + PCR_AFA);
778275970Scy	afsr = PSYCHO_READ8(sc, PSR_PCICTL1 + PCR_AFS);
779275970Scy	/* It's uncorrectable.  Dump the regs and panic. */
780275970Scy	panic("%s: PCI bus B error AFAR %#lx AFSR %#lx",
781275970Scy	    device_get_name(sc->sc_dev), (u_long)afar, (u_long)afsr);
782275970Scy}
783275970Scy
784275970Scystatic void
785275970Scypsycho_powerfail(void *arg)
786275970Scy{
787275970Scy
788275970Scy	/* We lost power.  Try to shut down NOW. */
789290000Sglebius#ifdef DEBUGGER_ON_POWERFAIL
790290000Sglebius	struct psycho_softc *sc = (struct psycho_softc *)arg;
791290000Sglebius
792290000Sglebius	Debugger("powerfail");
793290000Sglebius	PSYCHO_WRITE8(sc, PSR_POWER_INT_CLR, 0);
794290000Sglebius#else
795290000Sglebius	printf("Power Failure Detected: Shutting down NOW.\n");
796290000Sglebius	shutdown_nice(0);
797275970Scy#endif
798290000Sglebius}
799290000Sglebius
800290000Sglebius#ifdef PSYCHO_MAP_WAKEUP
801290000Sglebiusstatic void
802290000Sglebiuspsycho_wakeup(void *arg)
803290000Sglebius{
804290000Sglebius	struct psycho_softc *sc = (struct psycho_softc *)arg;
805290000Sglebius
806275970Scy	PSYCHO_WRITE8(sc, PSR_PWRMGT_INT_CLR, 0);
807275970Scy	/* Gee, we don't really have a framework to deal with this properly. */
808275970Scy	device_printf(sc->sc_dev, "power management wakeup\n");
809290000Sglebius}
810290000Sglebius#endif /* PSYCHO_MAP_WAKEUP */
811290000Sglebius
812290000Sglebius/* initialise the IOMMU... */
813275970Scyvoid
814290000Sglebiuspsycho_iommu_init(struct psycho_softc *sc, int tsbsize)
815275970Scy{
816275970Scy	char *name;
817275970Scy	struct iommu_state *is = sc->sc_is;
818275970Scy
819275970Scy	/* punch in our copies */
820275970Scy	is->is_bustag = sc->sc_bustag;
821275970Scy	is->is_bushandle = sc->sc_bushandle;
822275970Scy	is->is_iommu = PSR_IOMMU;
823275970Scy	is->is_dtag = PSR_IOMMU_TLB_TAG_DIAG;
824275970Scy	is->is_ddram = PSR_IOMMU_TLB_DATA_DIAG;
825275970Scy	is->is_dqueue = PSR_IOMMU_QUEUE_DIAG;
826275970Scy	is->is_dva = PSR_IOMMU_SVADIAG;
827275970Scy	is->is_dtcmp = PSR_IOMMU_TLB_CMP_DIAG;
828275970Scy
829275970Scy	/* give us a nice name.. */
830290000Sglebius	name = (char *)malloc(32, M_DEVBUF, M_NOWAIT);
831290000Sglebius	if (name == 0)
832290000Sglebius		panic("couldn't malloc iommu name");
833275970Scy	snprintf(name, 32, "%s dvma", device_get_name(sc->sc_dev));
834275970Scy
835290000Sglebius	iommu_init(name, is, tsbsize, sc->sc_dvmabase, 0);
836290000Sglebius}
837290000Sglebius
838290000Sglebius#ifndef OFW_NEWPCI
839290000Sglebiusstatic void
840290000Sglebiuspsycho_binit(device_t busdev, struct ofw_pci_bdesc *obd)
841275970Scy{
842275970Scy
843275970Scy#ifdef PSYCHO_DEBUG
844275970Scy	printf("psycho at %u/%u/%u: setting bus #s to %u/%u/%u\n",
845275970Scy	    obd->obd_bus, obd->obd_slot, obd->obd_func, obd->obd_bus,
846275970Scy	    obd->obd_secbus, obd->obd_subbus);
847275970Scy#endif /* PSYCHO_DEBUG */
848275970Scy	PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func,
849275970Scy	    PCSR_SUBBUS, obd->obd_subbus, 1);
850275970Scy}
851275970Scy#endif
852275970Scy
853275970Scystatic int
854275970Scypsycho_maxslots(device_t dev)
855275970Scy{
856275970Scy
857275970Scy	/* XXX: is this correct? */
858275970Scy	return (PCI_SLOTMAX);
859290000Sglebius}
860275970Scy
861290000Sglebius#ifndef OFW_NEWPCI
862290000Sglebius/*
863290000Sglebius * Keep a table of quirky PCI devices that need fixups before the MI PCI code
864290000Sglebius * creates the resource lists. This needs to be moved around once other bus
865290000Sglebius * drivers are added. Moving it to the MI code should maybe be reconsidered
866290000Sglebius * if one of these devices appear in non-sparc64 boxen. It's likely that not
867290000Sglebius * all BIOSes/firmwares can deal with them.
868290000Sglebius */
869290000Sglebiusstruct psycho_dquirk {
870290000Sglebius	u_int32_t	dq_devid;
871290000Sglebius	int		dq_quirk;
872290000Sglebius};
873290000Sglebius
874290000Sglebius/* Quirk types. May be or'ed together. */
875290000Sglebius#define	DQT_BAD_INTPIN	1	/* Intpin reg 0, but intpin used */
876275970Scy
877290000Sglebiusstatic struct psycho_dquirk dquirks[] = {
878275970Scy	{ 0x1001108e, DQT_BAD_INTPIN },	/* Sun HME (PCIO func. 1) */
879275970Scy	{ 0x1101108e, DQT_BAD_INTPIN },	/* Sun GEM (PCIO2 func. 1) */
880290000Sglebius	{ 0x1102108e, DQT_BAD_INTPIN },	/* Sun FireWire ctl. (PCIO2 func. 2) */
881290000Sglebius	{ 0x1103108e, DQT_BAD_INTPIN },	/* Sun USB ctl. (PCIO2 func. 3) */
882290000Sglebius};
883290000Sglebius#endif /* !OFW_NEWPCI */
884290000Sglebius
885290000Sglebius#define	NDQUIRKS	(sizeof(dquirks) / sizeof(dquirks[0]))
886290000Sglebius
887290000Sglebiusstatic u_int32_t
888290000Sglebiuspsycho_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
889290000Sglebius	int width)
890290000Sglebius{
891290000Sglebius	struct psycho_softc *sc;
892290000Sglebius	bus_space_handle_t bh;
893290000Sglebius	u_long offset = 0;
894275970Scy#ifndef OFW_NEWPCI
895290000Sglebius	u_int32_t devid;
896290000Sglebius#endif
897290000Sglebius	u_int8_t byte;
898290000Sglebius	u_int16_t shrt;
899290000Sglebius	u_int32_t wrd;
900290000Sglebius	u_int32_t r;
901290000Sglebius	int i;
902290000Sglebius
903290000Sglebius	sc = (struct psycho_softc *)device_get_softc(dev);
904290000Sglebius	offset = PSYCHO_CONF_OFF(bus, slot, func, reg);
905290000Sglebius	bh = sc->sc_bh[PCI_CS_CONFIG];
906290000Sglebius	switch (width) {
907290000Sglebius	case 1:
908290000Sglebius		i = bus_space_peek_1(sc->sc_cfgt, bh, offset, &byte);
909290000Sglebius		r = byte;
910290000Sglebius		break;
911290000Sglebius	case 2:
912290000Sglebius		i = bus_space_peek_2(sc->sc_cfgt, bh, offset, &shrt);
913290000Sglebius		r = shrt;
914290000Sglebius		break;
915290000Sglebius	case 4:
916290000Sglebius		i = bus_space_peek_4(sc->sc_cfgt, bh, offset, &wrd);
917290000Sglebius		r = wrd;
918290000Sglebius		break;
919290000Sglebius	default:
920290000Sglebius		panic("psycho_read_config: bad width");
921290000Sglebius	}
922290000Sglebius
923290000Sglebius	if (i) {
924290000Sglebius#ifdef PSYCHO_DEBUG
925290000Sglebius		printf("psycho read data error reading: %d.%d.%d: 0x%x\n",
926290000Sglebius		    bus, slot, func, reg);
927290000Sglebius#endif
928290000Sglebius		r = -1;
929290000Sglebius	}
930290000Sglebius
931290000Sglebius#ifndef OFW_NEWPCI
932290000Sglebius	if (reg == PCIR_INTPIN && r == 0) {
933290000Sglebius		/* Check for DQT_BAD_INTPIN quirk. */
934290000Sglebius		devid = psycho_read_config(dev, bus, slot, func,
935290000Sglebius		    PCIR_DEVVENDOR, 4);
936290000Sglebius		for (i = 0; i < NDQUIRKS; i++) {
937290000Sglebius			if (dquirks[i].dq_devid == devid) {
938290000Sglebius				/*
939290000Sglebius				 * Need to set the intpin to a value != 0 so
940290000Sglebius				 * that the MI code will think that this device
941290000Sglebius				 * has an interrupt.
942290000Sglebius				 * Just use 1 (intpin a) for now. This is, of
943290000Sglebius				 * course, bogus, but since interrupts are
944290000Sglebius				 * routed in advance, this does not really
945290000Sglebius				 * matter.
946290000Sglebius				 */
947290000Sglebius				if ((dquirks[i].dq_quirk & DQT_BAD_INTPIN) != 0)
948290000Sglebius					r = 1;
949290000Sglebius				break;
950290000Sglebius			}
951290000Sglebius		}
952310419Sdelphij	}
953290000Sglebius#endif /* !OFW_NEWPCI */
954290000Sglebius	return (r);
955290000Sglebius}
956290000Sglebius
957290000Sglebiusstatic void
958290000Sglebiuspsycho_write_config(device_t dev, u_int bus, u_int slot, u_int func,
959290000Sglebius	u_int reg, u_int32_t val, int width)
960290000Sglebius{
961290000Sglebius	struct psycho_softc *sc;
962290000Sglebius	bus_space_handle_t bh;
963290000Sglebius	u_long offset = 0;
964290000Sglebius
965290000Sglebius	sc = (struct psycho_softc *)device_get_softc(dev);
966290000Sglebius	offset = PSYCHO_CONF_OFF(bus, slot, func, reg);
967290000Sglebius	bh = sc->sc_bh[PCI_CS_CONFIG];
968290000Sglebius	switch (width) {
969290000Sglebius	case 1:
970290000Sglebius		bus_space_write_1(sc->sc_cfgt, bh, offset, val);
971290000Sglebius		break;
972290000Sglebius	case 2:
973290000Sglebius		bus_space_write_2(sc->sc_cfgt, bh, offset, val);
974290000Sglebius		break;
975290000Sglebius	case 4:
976290000Sglebius		bus_space_write_4(sc->sc_cfgt, bh, offset, val);
977290000Sglebius		break;
978290000Sglebius	default:
979290000Sglebius		panic("psycho_write_config: bad width");
980290000Sglebius	}
981290000Sglebius}
982290000Sglebius
983290000Sglebiusstatic int
984290000Sglebiuspsycho_route_interrupt(device_t bridge, device_t dev, int pin)
985290000Sglebius{
986290000Sglebius#ifdef OFW_NEWPCI
987290000Sglebius	struct psycho_softc *sc = device_get_softc(bridge);
988290000Sglebius	struct ofw_pci_register reg;
989290000Sglebius	bus_addr_t intrmap;
990290000Sglebius	phandle_t node = ofw_pci_get_node(dev);
991290000Sglebius	ofw_pci_intr_t pintr, mintr;
992290000Sglebius	u_int8_t maskbuf[sizeof(reg) + sizeof(pintr)];
993290000Sglebius
994290000Sglebius	pintr = pin;
995290000Sglebius	if (ofw_bus_lookup_imap(node, &sc->sc_iinfo, &reg, sizeof(reg),
996290000Sglebius	    &pintr, sizeof(pintr), &mintr, sizeof(mintr), maskbuf))
997290000Sglebius		return (mintr);
998290000Sglebius	/*
999290000Sglebius	 * If this is outside of the range for an intpin, it's likely a full
1000290000Sglebius	 * INO, and no mapping is required at all; this happens on the u30,
1001290000Sglebius	 * where there's no interrupt map at the psycho node. Fortunately,
1002290000Sglebius	 * there seem to be no INOs in the intpin range on this boxen, so
1003290000Sglebius	 * this easy heuristics will do.
1004290000Sglebius	 */
1005290000Sglebius	if (pin > 4)
1006290000Sglebius		return (pin);
1007290000Sglebius	/*
1008290000Sglebius	 * Guess the INO; we always assume that this is a non-OBIO
1009290000Sglebius	 * device, and that pin is a "real" intpin number. Determine
1010290000Sglebius	 * the mapping register to be used by the slot number.
1011290000Sglebius	 * We only need to do this on e450s, it seems; here, the slot numbers
1012290000Sglebius	 * for bus A are one-based, while those for bus B seemingly have an
1013290000Sglebius	 * offset of 2 (hence the factor of 3 below).
1014290000Sglebius	 */
1015290000Sglebius	intrmap = PSR_PCIA0_INT_MAP +
1016290000Sglebius	    8 * (pci_get_slot(dev) - 1 + 3 * sc->sc_half);
1017290000Sglebius	mintr = INTINO(PSYCHO_READ8(sc, intrmap)) + pin - 1;
1018290000Sglebius	device_printf(bridge, "guessing interrupt %d for device %d/%d pin %d\n",
1019290000Sglebius	    (int)mintr, pci_get_slot(dev), pci_get_function(dev), pin);
1020290000Sglebius	return (mintr);
1021290000Sglebius#else
1022290000Sglebius	/*
1023290000Sglebius	 * XXX: ugly loathsome hack:
1024290000Sglebius	 * We can't use ofw_pci_route_intr() here; the device passed may be
1025290000Sglebius	 * the one of a bridge, so the original device can't be recovered.
1026290000Sglebius	 *
1027290000Sglebius	 * We need to use the firmware to route interrupts, however it has
1028290000Sglebius	 * no interface which could be used to interpret intpins; instead,
1029290000Sglebius	 * all assignments are done by device.
1030290000Sglebius	 *
1031290000Sglebius	 * The MI pci code will try to reroute interrupts of 0, although they
1032290000Sglebius	 * are correct; all other interrupts are preinitialized, so if we
1033290000Sglebius	 * get here, the intline is either 0 (so return 0), or we hit a
1034290000Sglebius	 * device which was not preinitialized (e.g. hotplugged stuff), in
1035290000Sglebius	 * which case we are lost.
1036290000Sglebius	 */
1037290000Sglebius	return (0);
1038290000Sglebius#endif /* OFW_NEWPCI */
1039290000Sglebius}
1040290000Sglebius
1041290000Sglebiusstatic int
1042290000Sglebiuspsycho_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
1043290000Sglebius{
1044290000Sglebius	struct psycho_softc *sc;
1045290000Sglebius
1046290000Sglebius	sc = (struct psycho_softc *)device_get_softc(dev);
1047290000Sglebius	switch (which) {
1048290000Sglebius	case PCIB_IVAR_BUS:
1049290000Sglebius		*result = sc->sc_secbus;
1050290000Sglebius		return (0);
1051290000Sglebius	}
1052290000Sglebius	return (ENOENT);
1053290000Sglebius}
1054290000Sglebius
1055290000Sglebius/* Write to the correct clr register, and call the actual handler. */
1056290000Sglebiusstatic void
1057290000Sglebiuspsycho_intr_stub(void *arg)
1058290000Sglebius{
1059290000Sglebius	struct psycho_clr *pc;
1060290000Sglebius
1061290000Sglebius	pc = (struct psycho_clr *)arg;
1062290000Sglebius	pc->pci_handler(pc->pci_arg);
1063275970Scy	PSYCHO_WRITE8(pc->pci_sc, pc->pci_clr, 0);
1064275970Scy}
1065275970Scy
1066290000Sglebiusstatic int
1067290000Sglebiuspsycho_setup_intr(device_t dev, device_t child,
1068290000Sglebius    struct resource *ires,  int flags, driver_intr_t *intr, void *arg,
1069290000Sglebius    void **cookiep)
1070290000Sglebius{
1071290000Sglebius	struct psycho_softc *sc;
1072290000Sglebius	struct psycho_clr *pc;
1073290000Sglebius	bus_addr_t intrmapptr, intrclrptr;
1074290000Sglebius	long vec = rman_get_start(ires);
1075290000Sglebius	u_int64_t mr;
1076290000Sglebius	int ino, error;
1077290000Sglebius
1078290000Sglebius	sc = (struct psycho_softc *)device_get_softc(dev);
1079275970Scy	pc = (struct psycho_clr *)malloc(sizeof(*pc), M_DEVBUF, M_NOWAIT);
1080290000Sglebius	if (pc == NULL)
1081290000Sglebius		return (NULL);
1082275970Scy
1083290000Sglebius	/*
1084290000Sglebius	 * Hunt through all the interrupt mapping regs to look for our
1085275970Scy	 * interrupt vector.
1086290000Sglebius	 *
1087290000Sglebius	 * XXX We only compare INOs rather than IGNs since the firmware may
1088290000Sglebius	 * not provide the IGN and the IGN is constant for all device on that
1089290000Sglebius	 * PCI controller.  This could cause problems for the FFB/external
1090290000Sglebius	 * interrupt which has a full vector that can be set arbitrarily.
1091290000Sglebius	 */
1092290000Sglebius	ino = INTINO(vec);
1093290000Sglebius
1094290000Sglebius	if (!psycho_find_intrmap(sc, ino, &intrmapptr, &intrclrptr, NULL)) {
1095290000Sglebius		device_printf(dev, "Cannot find interrupt vector %lx\n", vec);
1096290000Sglebius		free(pc, M_DEVBUF);
1097290000Sglebius		return (NULL);
1098290000Sglebius	}
1099290000Sglebius
1100290000Sglebius#ifdef PSYCHO_DEBUG
1101275970Scy	device_printf(dev, "psycho_setup_intr: INO %d, map %#lx, clr %#lx\n",
1102290000Sglebius	    ino, (u_long)intrmapptr, (u_long)intrclrptr);
1103290000Sglebius#endif
1104290000Sglebius	pc->pci_sc = sc;
1105290000Sglebius	pc->pci_arg = arg;
1106290000Sglebius	pc->pci_handler = intr;
1107290000Sglebius	pc->pci_clr = intrclrptr;
1108290000Sglebius	/* Disable the interrupt while we fiddle with it */
1109290000Sglebius	mr = PSYCHO_READ8(sc, intrmapptr);
1110290000Sglebius	PSYCHO_WRITE8(sc, intrmapptr, mr & ~INTMAP_V);
1111290000Sglebius	error = BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags,
1112290000Sglebius	    psycho_intr_stub, pc, cookiep);
1113290000Sglebius	if (error != 0) {
1114290000Sglebius		free(pc, M_DEVBUF);
1115290000Sglebius		return (error);
1116290000Sglebius	}
1117290000Sglebius	pc->pci_cookie = *cookiep;
1118290000Sglebius	*cookiep = pc;
1119290000Sglebius
1120290000Sglebius	/*
1121290000Sglebius	 * Clear the interrupt, it might have been triggered before it was
1122290000Sglebius	 * set up.
1123290000Sglebius	 */
1124290000Sglebius	PSYCHO_WRITE8(sc, intrclrptr, 0);
1125290000Sglebius	/*
1126290000Sglebius	 * Enable the interrupt and program the target module now we have the
1127290000Sglebius	 * handler installed.
1128290000Sglebius	 */
1129290000Sglebius	PSYCHO_WRITE8(sc, intrmapptr, INTMAP_ENABLE(mr, PCPU_GET(mid)));
1130290000Sglebius	return (error);
1131290000Sglebius}
1132275970Scy
1133275970Scystatic int
1134275970Scypsycho_teardown_intr(device_t dev, device_t child,
1135275970Scy    struct resource *vec, void *cookie)
1136275970Scy{
1137275970Scy	struct psycho_clr *pc;
1138275970Scy	int error;
1139294904Sdelphij
1140290000Sglebius	pc = (struct psycho_clr *)cookie;
1141290000Sglebius	error = BUS_TEARDOWN_INTR(device_get_parent(dev), child, vec,
1142290000Sglebius	    pc->pci_cookie);
1143290000Sglebius	/*
1144290000Sglebius	 * Don't disable the interrupt for now, so that stray interupts get
1145290000Sglebius	 * detected...
1146290000Sglebius	 */
1147290000Sglebius	if (error != 0)
1148290000Sglebius		free(pc, M_DEVBUF);
1149290000Sglebius	return (error);
1150290000Sglebius}
1151290000Sglebius
1152290000Sglebiusstatic struct resource *
1153290000Sglebiuspsycho_alloc_resource(device_t bus, device_t child, int type, int *rid,
1154290000Sglebius    u_long start, u_long end, u_long count, u_int flags)
1155290000Sglebius{
1156290000Sglebius	struct psycho_softc *sc;
1157290000Sglebius	struct resource *rv;
1158275970Scy	struct rman *rm;
1159290000Sglebius	bus_space_tag_t bt;
1160290000Sglebius	bus_space_handle_t bh;
1161290000Sglebius	int needactivate = flags & RF_ACTIVE;
1162290000Sglebius
1163290000Sglebius	flags &= ~RF_ACTIVE;
1164294904Sdelphij
1165294904Sdelphij	sc = (struct psycho_softc *)device_get_softc(bus);
1166294904Sdelphij	if (type == SYS_RES_IRQ) {
1167294904Sdelphij		/*
1168290000Sglebius		 * XXX: Don't accept blank ranges for now, only single
1169290000Sglebius		 * interrupts. The other case should not happen with the MI pci
1170275970Scy		 * code...
1171275970Scy		 * XXX: This may return a resource that is out of the range
1172290000Sglebius		 * that was specified. Is this correct...?
1173275970Scy		 */
1174275970Scy		if (start != end)
1175275970Scy			panic("psycho_alloc_resource: XXX: interrupt range");
1176275970Scy		start = end |= sc->sc_ign;
1177290000Sglebius		return (bus_alloc_resource(bus, type, rid, start, end,
1178290000Sglebius		    count, flags));
1179290000Sglebius	}
1180290000Sglebius	switch (type) {
1181275970Scy	case SYS_RES_MEMORY:
1182275970Scy		rm = &sc->sc_mem_rman;
1183275970Scy		bt = sc->sc_memt;
1184290000Sglebius		bh = sc->sc_bh[PCI_CS_MEM32];
1185290000Sglebius		break;
1186275970Scy	case SYS_RES_IOPORT:
1187290000Sglebius		rm = &sc->sc_io_rman;
1188290000Sglebius		bt = sc->sc_iot;
1189290000Sglebius		bh = sc->sc_bh[PCI_CS_IO];
1190290000Sglebius		break;
1191290000Sglebius	default:
1192290000Sglebius		return (NULL);
1193290000Sglebius	}
1194290000Sglebius
1195294904Sdelphij	rv = rman_reserve_resource(rm, start, end, count, flags, child);
1196275970Scy	if (rv == NULL)
1197290000Sglebius		return (NULL);
1198290000Sglebius
1199290000Sglebius	bh += rman_get_start(rv);
1200290000Sglebius	rman_set_bustag(rv, bt);
1201290000Sglebius	rman_set_bushandle(rv, bh);
1202290000Sglebius
1203275970Scy	if (needactivate) {
1204275970Scy		if (bus_activate_resource(child, type, *rid, rv)) {
1205275970Scy			rman_release_resource(rv);
1206275970Scy			return (NULL);
1207275970Scy		}
1208275970Scy	}
1209275970Scy
1210290000Sglebius	return (rv);
1211290000Sglebius}
1212290000Sglebius
1213290000Sglebiusstatic int
1214290000Sglebiuspsycho_activate_resource(device_t bus, device_t child, int type, int rid,
1215290000Sglebius    struct resource *r)
1216290000Sglebius{
1217290000Sglebius	void *p;
1218290000Sglebius	int error;
1219290000Sglebius
1220290000Sglebius	if (type == SYS_RES_IRQ)
1221290000Sglebius		return (bus_activate_resource(bus, type, rid, r));
1222290000Sglebius	if (type == SYS_RES_MEMORY) {
1223290000Sglebius		/*
1224290000Sglebius		 * Need to memory-map the device space, as some drivers depend
1225290000Sglebius		 * on the virtual address being set and useable.
1226290000Sglebius		 */
1227290000Sglebius		error = sparc64_bus_mem_map(rman_get_bustag(r),
1228290000Sglebius		    rman_get_bushandle(r), rman_get_size(r), 0, NULL, &p);
1229290000Sglebius		if (error != 0)
1230290000Sglebius			return (error);
1231290000Sglebius		rman_set_virtual(r, p);
1232290000Sglebius	}
1233290000Sglebius	return (rman_activate_resource(r));
1234290000Sglebius}
1235290000Sglebius
1236290000Sglebiusstatic int
1237290000Sglebiuspsycho_deactivate_resource(device_t bus, device_t child, int type, int rid,
1238290000Sglebius    struct resource *r)
1239290000Sglebius{
1240290000Sglebius
1241290000Sglebius	if (type == SYS_RES_IRQ)
1242290000Sglebius		return (bus_deactivate_resource(bus, type, rid, r));
1243275970Scy	if (type == SYS_RES_MEMORY) {
1244275970Scy		sparc64_bus_mem_unmap(rman_get_virtual(r), rman_get_size(r));
1245275970Scy		rman_set_virtual(r, NULL);
1246275970Scy	}
1247275970Scy	return (rman_deactivate_resource(r));
1248290000Sglebius}
1249290000Sglebius
1250290000Sglebiusstatic int
1251275970Scypsycho_release_resource(device_t bus, device_t child, int type, int rid,
1252275970Scy    struct resource *r)
1253275970Scy{
1254275970Scy	int error;
1255275970Scy
1256275970Scy	if (type == SYS_RES_IRQ)
1257275970Scy		return (bus_release_resource(bus, type, rid, r));
1258275970Scy	if (rman_get_flags(r) & RF_ACTIVE) {
1259275970Scy		error = bus_deactivate_resource(child, type, rid, r);
1260275970Scy		if (error)
1261290000Sglebius			return error;
1262290000Sglebius	}
1263290000Sglebius	return (rman_release_resource(r));
1264290000Sglebius}
1265275970Scy
1266275970Scystatic int
1267275970Scypsycho_intr_pending(device_t dev, ofw_pci_intr_t intr)
1268275970Scy{
1269275970Scy	struct psycho_softc *sc;
1270275970Scy	u_long diag;
1271275970Scy
1272275970Scy	sc = (struct psycho_softc *)device_get_softc(dev);
1273275970Scy	if (!psycho_find_intrmap(sc, intr, NULL, NULL, &diag)) {
1274275970Scy		device_printf(dev, "psycho_intr_pending: mapping not found for"
1275290000Sglebius		    " %d\n", intr);
1276290000Sglebius		return (0);
1277290000Sglebius	}
1278275970Scy	return (diag != 0);
1279290000Sglebius}
1280290000Sglebius
1281290000Sglebius#ifndef OFW_NEWPCI
1282290000Sglebiusstatic ofw_pci_intr_t
1283290000Sglebiuspsycho_guess_ino(device_t dev, phandle_t node, u_int slot, u_int pin)
1284290000Sglebius{
1285290000Sglebius	struct psycho_softc *sc = (struct psycho_softc *)device_get_softc(dev);
1286290000Sglebius	bus_addr_t intrmap;
1287275970Scy
1288275970Scy	/*
1289275970Scy	 * If this is not for one of our direct children (i.e. we are mapping
1290275970Scy	 * at our node), tell the interrupt mapper to go on - we need the
1291275970Scy	 * slot number of the device or it's topmost parent bridge to guess
1292275970Scy	 * the INO.
1293275970Scy	 */
1294275970Scy	if (node != sc->sc_node)
1295275970Scy		return (PCI_INVALID_IRQ);
1296275970Scy	/*
1297290000Sglebius	 * Actually guess the INO. We always assume that this is a non-OBIO
1298290000Sglebius	 * device, and use from the slot number to determine it.
1299290000Sglebius	 * We only need to do this on e450s, it seems; here, the slot numbers
1300290000Sglebius	 * for bus A are one-based, while those for bus B seemingly have an
1301290000Sglebius	 * offset of 2 (hence the factor of 3 below).
1302290000Sglebius	 */
1303290000Sglebius	intrmap = PSR_PCIA0_INT_MAP + 8 * (slot - 1 + 3 * sc->sc_half);
1304290000Sglebius	return (INTINO(PSYCHO_READ8(sc, intrmap)) + pin - 1);
1305290000Sglebius}
1306290000Sglebius#endif /* !OFW_NEWPCI */
1307290000Sglebius
1308275970Scystatic bus_space_handle_t
1309275970Scypsycho_get_bus_handle(device_t dev, int type, bus_space_handle_t childhdl,
1310275970Scy    bus_space_tag_t *tag)
1311290000Sglebius{
1312275970Scy	struct psycho_softc *sc;
1313275970Scy
1314275970Scy	sc = (struct psycho_softc *)device_get_softc(dev);
1315275970Scy	switch (type) {
1316275970Scy	case SYS_RES_IOPORT:
1317275970Scy		*tag = sc->sc_iot;
1318290000Sglebius		return (sc->sc_bh[PCI_CS_IO] + childhdl);
1319290000Sglebius	case SYS_RES_MEMORY:
1320290000Sglebius		*tag = sc->sc_memt;
1321275970Scy		return (sc->sc_bh[PCI_CS_MEM32] + childhdl);
1322290000Sglebius	default:
1323290000Sglebius		panic("psycho_get_bus_handle: illegal space\n");
1324290000Sglebius	}
1325290000Sglebius}
1326290000Sglebius
1327290000Sglebius#ifdef OFW_NEWPCI
1328290000Sglebiusstatic phandle_t
1329290000Sglebiuspsycho_get_node(device_t bus, device_t dev)
1330275970Scy{
1331275970Scy	struct psycho_softc *sc = device_get_softc(bus);
1332290000Sglebius
1333275970Scy	/* We only have one child, the PCI bus, which needs our own node. */
1334275970Scy	return (sc->sc_node);
1335275970Scy}
1336275970Scy
1337275970Scystatic void
1338275970Scypsycho_adjust_busrange(device_t dev, u_int subbus)
1339275970Scy{
1340275970Scy	struct psycho_softc *sc = device_get_softc(dev);
1341290000Sglebius
1342290000Sglebius	/* If necessary, adjust the subordinate bus number register. */
1343290000Sglebius	if (subbus > sc->sc_subbus) {
1344290000Sglebius#ifdef PSYCHO_DEBUG
1345290000Sglebius		device_printf(dev,
1346290000Sglebius		    "adjusting secondary bus number from %d to %d\n",
1347290000Sglebius		    sc->sc_subbus, subbus);
1348290000Sglebius#endif
1349290000Sglebius		sc->sc_subbus = subbus;
1350290000Sglebius		PCIB_WRITE_CONFIG(dev, sc->sc_secbus, PCS_DEVICE, PCS_FUNC,
1351290000Sglebius		    PCSR_SUBBUS, subbus, 1);
1352275970Scy	}
1353275970Scy}
1354275970Scy#endif
1355275970Scy
1356275970Scy/*
1357275970Scy * below here is bus space and bus dma support
1358275970Scy */
1359290000Sglebiusstatic bus_space_tag_t
1360290000Sglebiuspsycho_alloc_bus_tag(struct psycho_softc *sc, int type)
1361275970Scy{
1362275970Scy	bus_space_tag_t bt;
1363275970Scy
1364275970Scy	bt = (bus_space_tag_t)malloc(sizeof(struct bus_space_tag), M_DEVBUF,
1365275970Scy	    M_NOWAIT | M_ZERO);
1366290000Sglebius	if (bt == NULL)
1367290000Sglebius		panic("psycho_alloc_bus_tag: out of memory");
1368290000Sglebius
1369275970Scy	bzero(bt, sizeof *bt);
1370290000Sglebius	bt->bst_cookie = sc;
1371275970Scy	bt->bst_parent = sc->sc_bustag;
1372290000Sglebius	bt->bst_type = type;
1373290000Sglebius	return (bt);
1374290000Sglebius}
1375275970Scy