ebus_mainbus.c revision 1.1
1/*	$OpenBSD: ebus_mainbus.c,v 1.7 2010/11/11 17:58:23 miod Exp $	*/
2
3/*
4 * Copyright (c) 2007 Mark Kettenis
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#ifdef DEBUG
20#define	EDB_PROM	0x01
21#define EDB_CHILD	0x02
22#define	EDB_INTRMAP	0x04
23#define EDB_BUSMAP	0x08
24#define EDB_BUSDMA	0x10
25#define EDB_INTR	0x20
26extern int ebus_debug;
27#define DPRINTF(l, s)   do { if (ebus_debug & l) printf s; } while (0)
28#else
29#define DPRINTF(l, s)
30#endif
31
32#include <sys/param.h>
33#include <sys/conf.h>
34#include <sys/device.h>
35#include <sys/errno.h>
36#include <sys/extent.h>
37#include <sys/malloc.h>
38#include <sys/systm.h>
39#include <sys/time.h>
40
41#define _SPARC_BUS_DMA_PRIVATE
42#include <machine/bus.h>
43#include <machine/autoconf.h>
44#include <machine/hypervisor.h>
45#include <machine/openfirm.h>
46
47#include <dev/pci/pcivar.h>
48
49#include <sparc64/dev/iommureg.h>
50#include <sparc64/dev/ebusreg.h>
51#include <sparc64/dev/ebusvar.h>
52#include <sparc64/dev/pyrovar.h>
53
54extern struct cfdriver pyro_cd;
55
56int	ebus_mainbus_match(struct device *, void *, void *);
57void	ebus_mainbus_attach(struct device *, struct device *, void *);
58
59struct cfattach ebus_mainbus_ca = {
60	sizeof(struct ebus_softc), ebus_mainbus_match, ebus_mainbus_attach
61};
62
63
64int ebus_mainbus_bus_map(bus_space_tag_t, bus_space_tag_t,
65    bus_addr_t, bus_size_t, int, bus_space_handle_t *);
66void *ebus_mainbus_intr_establish(bus_space_tag_t, bus_space_tag_t,
67    int, int, int, int (*)(void *), void *, const char *);
68bus_space_tag_t ebus_alloc_bus_tag(struct ebus_softc *, bus_space_tag_t);
69void ebus_mainbus_intr_ack(struct intrhand *);
70
71int
72ebus_mainbus_match(struct device *parent, void *match, void *aux)
73{
74	struct mainbus_attach_args *ma = aux;
75
76	if (strcmp(ma->ma_name, "ebus") == 0)
77		return (1);
78	return (0);
79}
80
81void
82ebus_mainbus_attach(struct device *parent, struct device *self, void *aux)
83{
84	struct ebus_softc *sc = (struct ebus_softc *)self;
85	struct mainbus_attach_args *ma = aux;
86	struct ebus_attach_args eba;
87	struct ebus_interrupt_map_mask *immp;
88	int node, nmapmask, error;
89	struct pyro_softc *psc;
90	int i;
91
92	sc->sc_node = node = ma->ma_node;
93	sc->sc_ign = INTIGN((ma->ma_upaid) << INTMAP_IGN_SHIFT);
94
95	if (CPU_ISSUN4U) {
96		printf(": ign %x", sc->sc_ign);
97
98		for (i = 0; i < pyro_cd.cd_ndevs; i++) {
99			psc = pyro_cd.cd_devs[i];
100			if (psc && psc->sc_ign == sc->sc_ign) {
101				sc->sc_bust = psc->sc_bust;
102				sc->sc_csr = psc->sc_csr;
103				sc->sc_csrh = psc->sc_csrh;
104				break;
105			}
106		}
107
108		if (sc->sc_csr == 0) {
109			printf(": can't find matching host bridge leaf\n");
110			return;
111		}
112	}
113
114	printf("\n");
115
116	sc->sc_memtag = ebus_alloc_bus_tag(sc, ma->ma_bustag);
117	sc->sc_iotag = ebus_alloc_bus_tag(sc, ma->ma_bustag);
118	sc->sc_dmatag = ebus_alloc_dma_tag(sc, ma->ma_dmatag);
119
120	/*
121	 * fill in our softc with information from the prom
122	 */
123	sc->sc_intmap = NULL;
124	sc->sc_range = NULL;
125	error = getprop(node, "interrupt-map",
126			sizeof(struct ebus_interrupt_map),
127			&sc->sc_nintmap, (void **)&sc->sc_intmap);
128	switch (error) {
129	case 0:
130		immp = &sc->sc_intmapmask;
131		error = getprop(node, "interrupt-map-mask",
132			    sizeof(struct ebus_interrupt_map_mask), &nmapmask,
133			    (void **)&immp);
134		if (error)
135			panic("could not get ebus interrupt-map-mask");
136		if (nmapmask != 1)
137			panic("ebus interrupt-map-mask is broken");
138		break;
139	case ENOENT:
140		break;
141	default:
142		panic("ebus interrupt-map: error %d", error);
143		break;
144	}
145
146	error = getprop(node, "ranges", sizeof(struct ebus_mainbus_ranges),
147	    &sc->sc_nrange, (void **)&sc->sc_range);
148	if (error)
149		panic("ebus ranges: error %d", error);
150
151	/*
152	 * now attach all our children
153	 */
154	DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
155	for (node = firstchild(node); node; node = nextsibling(node)) {
156		if (ebus_setup_attach_args(sc, node, &eba) != 0) {
157			DPRINTF(EDB_CHILD,
158			    ("ebus_mainbus_attach: %s: incomplete\n",
159			    getpropstring(node, "name")));
160			continue;
161		} else {
162			DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
163			    eba.ea_name));
164			(void)config_found(self, &eba, ebus_print);
165		}
166		ebus_destroy_attach_args(&eba);
167	}
168}
169
170bus_space_tag_t
171ebus_alloc_bus_tag(struct ebus_softc *sc, bus_space_tag_t parent)
172{
173	struct sparc_bus_space_tag *bt;
174
175	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
176	if (bt == NULL)
177		panic("could not allocate ebus bus tag");
178
179	strlcpy(bt->name, sc->sc_dev.dv_xname, sizeof(bt->name));
180	bt->cookie = sc;
181	bt->parent = parent;
182	bt->asi = parent->asi;
183	bt->sasi = parent->sasi;
184	bt->sparc_bus_map = ebus_mainbus_bus_map;
185	bt->sparc_intr_establish = ebus_mainbus_intr_establish;
186
187	return (bt);
188}
189
190int
191ebus_mainbus_bus_map(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t offset,
192    bus_size_t size, int flags, bus_space_handle_t *hp)
193{
194	struct ebus_softc *sc = t->cookie;
195	struct ebus_mainbus_ranges *range;
196	bus_addr_t hi, lo;
197	int i;
198
199	DPRINTF(EDB_BUSMAP,
200	    ("\n_ebus_mainbus_bus_map: off %016llx sz %x flags %d",
201	    (unsigned long long)offset, (int)size, (int)flags));
202
203	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
204		printf("\n_ebus_mainbus_bus_map: invalid parent");
205		return (EINVAL);
206	}
207
208	t = t->parent;
209
210	if (flags & BUS_SPACE_MAP_PROMADDRESS) {
211		return ((*t->sparc_bus_map)
212		    (t, t0, offset, size, flags, hp));
213	}
214
215	hi = offset >> 32UL;
216	lo = offset & 0xffffffff;
217	range = (struct ebus_mainbus_ranges *)sc->sc_range;
218
219	DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
220	for (i = 0; i < sc->sc_nrange; i++) {
221		bus_addr_t addr;
222
223		if (hi != range[i].child_hi)
224			continue;
225		if (lo < range[i].child_lo ||
226		    (lo + size) > (range[i].child_lo + range[i].size))
227			continue;
228
229		addr = ((bus_addr_t)range[i].phys_hi << 32UL) |
230				    range[i].phys_lo;
231		addr += lo;
232		DPRINTF(EDB_BUSMAP,
233		    ("\n_ebus_mainbus_bus_map: paddr offset %qx addr %qx\n",
234		    (unsigned long long)offset, (unsigned long long)addr));
235                return ((*t->sparc_bus_map)(t, t0, addr, size, flags, hp));
236	}
237	DPRINTF(EDB_BUSMAP, (": FAILED\n"));
238	return (EINVAL);
239}
240
241void *
242ebus_mainbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
243    int level, int flags, int (*handler)(void *), void *arg, const char *what)
244{
245	struct ebus_softc *sc = t->cookie;
246	struct intrhand *ih = NULL;
247	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
248	int ino;
249
250#ifdef SUN4V
251	if (CPU_ISSUN4V) {
252		struct upa_reg reg;
253		u_int64_t devhandle, devino = INTINO(ihandle);
254		u_int64_t sysino;
255		int node = -1;
256		int i, err;
257
258		for (i = 0; i < sc->sc_nintmap; i++) {
259			if (sc->sc_intmap[i].cintr == ihandle) {
260				node = sc->sc_intmap[i].cnode;
261				break;
262			}
263		}
264		if (node == -1)
265			return (NULL);
266
267		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
268			return (NULL);
269		devhandle = (reg.ur_paddr >> 32) & 0x0fffffff;
270
271		err = hv_intr_devino_to_sysino(devhandle, devino, &sysino);
272		if (err != H_EOK)
273			return (NULL);
274
275		KASSERT(sysino == INTVEC(sysino));
276		ih = bus_intr_allocate(t0, handler, arg, sysino, level,
277		    NULL, NULL, what);
278		if (ih == NULL)
279			return (NULL);
280
281		intr_establish(ih->ih_pil, ih);
282		ih->ih_ack = ebus_mainbus_intr_ack;
283
284		err = hv_intr_settarget(sysino, cpus->ci_upaid);
285		if (err != H_EOK)
286			return (NULL);
287
288		/* Clear pending interrupts. */
289		err = hv_intr_setstate(sysino, INTR_IDLE);
290		if (err != H_EOK)
291			return (NULL);
292
293		err = hv_intr_setenabled(sysino, INTR_ENABLED);
294		if (err != H_EOK)
295			return (NULL);
296
297		return (ih);
298	}
299#endif
300
301	ihandle |= sc->sc_ign;
302	ino = INTINO(ihandle);
303
304	if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) {
305		u_int64_t *imap, *iclr;
306
307		/* XXX */
308		imap = bus_space_vaddr(sc->sc_bust, sc->sc_csrh) + 0x1000;
309		iclr = bus_space_vaddr(sc->sc_bust, sc->sc_csrh) + 0x1400;
310		intrmapptr = &imap[ino];
311		intrclrptr = &iclr[ino];
312		ino |= INTVEC(ihandle);
313	}
314
315	ih = bus_intr_allocate(t0, handler, arg, ino, level, intrmapptr,
316	    intrclrptr, what);
317	if (ih == NULL)
318		return (NULL);
319
320	intr_establish(ih->ih_pil, ih);
321
322	if (intrmapptr != NULL) {
323		u_int64_t intrmap;
324
325		intrmap = *intrmapptr;
326		intrmap |= (1LL << 6);
327		intrmap |= INTMAP_V;
328		*intrmapptr = intrmap;
329		intrmap = *intrmapptr;
330		ih->ih_number |= intrmap & INTMAP_INR;
331	}
332
333	return (ih);
334}
335
336#ifdef SUN4V
337
338void
339ebus_mainbus_intr_ack(struct intrhand *ih)
340{
341	hv_intr_setstate(ih->ih_number, INTR_IDLE);
342}
343
344#endif
345