vbus.c revision 1.5
1/*	$OpenBSD: vbus.c,v 1.5 2010/11/11 17:58:23 miod Exp $	*/
2/*
3 * Copyright (c) 2008 Mark Kettenis
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/device.h>
20#include <sys/malloc.h>
21#include <sys/systm.h>
22
23#include <machine/autoconf.h>
24#include <machine/hypervisor.h>
25#include <machine/openfirm.h>
26
27#include <sparc64/dev/vbusvar.h>
28
29#include <dev/clock_subr.h>
30extern todr_chip_handle_t todr_handle;
31
32struct vbus_softc {
33	struct device		sc_dv;
34	bus_space_tag_t		sc_bustag;
35	bus_dma_tag_t		sc_dmatag;
36};
37
38int	vbus_cmp_cells(int *, int *, int *, int);
39int	vbus_match(struct device *, void *, void *);
40void	vbus_attach(struct device *, struct device *, void *);
41int	vbus_print(void *, const char *);
42
43struct cfattach vbus_ca = {
44	sizeof(struct vbus_softc), vbus_match, vbus_attach
45};
46
47struct cfdriver vbus_cd = {
48	NULL, "vbus", DV_DULL
49};
50
51void	*vbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
52    int (*)(void *), void *, const char *);
53void	vbus_intr_ack(struct intrhand *);
54bus_space_tag_t vbus_alloc_bus_tag(struct vbus_softc *, bus_space_tag_t);
55
56int
57vbus_match(struct device *parent, void *match, void *aux)
58{
59	struct mainbus_attach_args *ma = aux;
60
61	if (strcmp(ma->ma_name, "virtual-devices") == 0)
62		return (1);
63
64	return (0);
65}
66
67void
68vbus_attach(struct device *parent, struct device *self, void *aux)
69{
70	struct vbus_softc *sc = (struct vbus_softc *)self;
71	struct mainbus_attach_args *ma = aux;
72	int node;
73
74	sc->sc_bustag = vbus_alloc_bus_tag(sc, ma->ma_bustag);
75	sc->sc_dmatag = ma->ma_dmatag;
76	printf("\n");
77
78	for (node = OF_child(ma->ma_node); node; node = OF_peer(node)) {
79		struct vbus_attach_args va;
80		char buf[32];
81
82		bzero(&va, sizeof(va));
83		va.va_node = node;
84		if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
85			continue;
86		va.va_name = buf;
87		va.va_bustag = sc->sc_bustag;
88		va.va_dmatag = sc->sc_dmatag;
89		getprop(node, "reg", sizeof(*va.va_reg),
90		    &va.va_nreg, (void **)&va.va_reg);
91		getprop(node, "interrupts", sizeof(*va.va_intr),
92		    &va.va_nintr, (void **)&va.va_intr);
93		config_found(self, &va, vbus_print);
94	}
95
96	if (todr_handle == NULL) {
97		struct vbus_attach_args va;
98
99		bzero(&va, sizeof(va));
100		va.va_name = "rtc";
101		config_found(self, &va, vbus_print);
102	}
103}
104
105int
106vbus_print(void *aux, const char *name)
107{
108	struct vbus_attach_args *va = aux;
109
110	if (name)
111		printf("\"%s\" at %s", va->va_name, name);
112	return (UNCONF);
113}
114
115/*
116 * Compare a sequence of cells with a mask, return 1 if they match and
117 * 0 if they don't.
118 */
119int
120vbus_cmp_cells(int *cell1, int *cell2, int *mask, int ncells)
121{
122	int i;
123
124	for (i = 0; i < ncells; i++) {
125		if (((cell1[i] ^ cell2[i]) & mask[i]) != 0)
126			return (0);
127	}
128	return (1);
129}
130
131int
132vbus_intr_map(int node, int ino, uint64_t *sysino)
133{
134	int *imap = NULL, nimap;
135	int *reg = NULL, nreg;
136	int *imap_mask;
137	int parent;
138	int address_cells, interrupt_cells;
139	uint64_t devhandle;
140	uint64_t devino;
141	int len;
142	int err;
143
144	parent = OF_parent(node);
145
146	address_cells = getpropint(parent, "#address-cells", 2);
147	interrupt_cells = getpropint(parent, "#interrupt-cells", 1);
148	KASSERT(interrupt_cells == 1);
149
150	len = OF_getproplen(parent, "interrupt-map-mask");
151	if (len < (address_cells + interrupt_cells) * sizeof(int))
152		return (-1);
153	imap_mask = malloc(len, M_DEVBUF, M_NOWAIT);
154	if (imap_mask == NULL)
155		return (-1);
156	if (OF_getprop(parent, "interrupt-map-mask", imap_mask, len) != len)
157		return (-1);
158
159	getprop(parent, "interrupt-map", sizeof(int), &nimap, (void **)&imap);
160	getprop(node, "reg", sizeof(*reg), &nreg, (void **)&reg);
161	if (nreg < address_cells)
162		return (-1);
163
164	while (nimap >= address_cells + interrupt_cells + 2) {
165		if (vbus_cmp_cells(imap, reg, imap_mask, address_cells) &&
166		    vbus_cmp_cells(&imap[address_cells], &ino,
167		    &imap_mask[address_cells], interrupt_cells)) {
168			node = imap[address_cells + interrupt_cells];
169			devino = imap[address_cells + interrupt_cells + 1];
170
171			free(reg, M_DEVBUF);
172			reg = NULL;
173
174			getprop(node, "reg", sizeof(*reg), &nreg, (void **)&reg);
175			devhandle = reg[0] & 0x0fffffff;
176
177			err = hv_intr_devino_to_sysino(devhandle, devino, sysino);
178			if (err != H_EOK)
179				return (-1);
180
181			KASSERT(*sysino == INTVEC(*sysino));
182			return (0);
183		}
184		imap += address_cells + interrupt_cells + 2;
185		nimap -= address_cells + interrupt_cells + 2;
186	}
187
188	return (-1);
189}
190
191void *
192vbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
193    int level, int flags, int (*handler)(void *), void *arg, const char *what)
194{
195	uint64_t sysino = INTVEC(ihandle);
196	struct intrhand *ih;
197	int err;
198
199	ih = bus_intr_allocate(t0, handler, arg, ihandle, level,
200	    NULL, NULL, what);
201	if (ih == NULL)
202		return (NULL);
203
204	intr_establish(ih->ih_pil, ih);
205	ih->ih_ack = vbus_intr_ack;
206
207	err = hv_intr_settarget(sysino, cpus->ci_upaid);
208	if (err != H_EOK)
209		return (NULL);
210
211	/* Clear pending interrupts. */
212	err = hv_intr_setstate(sysino, INTR_IDLE);
213	if (err != H_EOK)
214		return (NULL);
215
216	err = hv_intr_setenabled(sysino, INTR_ENABLED);
217	if (err != H_EOK)
218		return (NULL);
219
220	return (ih);
221}
222
223void
224vbus_intr_ack(struct intrhand *ih)
225{
226	hv_intr_setstate(ih->ih_number, INTR_IDLE);
227}
228
229bus_space_tag_t
230vbus_alloc_bus_tag(struct vbus_softc *sc, bus_space_tag_t parent)
231{
232	struct sparc_bus_space_tag *bt;
233
234	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
235	if (bt == NULL)
236		panic("could not allocate vbus bus tag");
237
238	strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name));
239	bt->cookie = sc;
240	bt->parent = parent;
241	bt->asi = parent->asi;
242	bt->sasi = parent->sasi;
243	bt->sparc_bus_map = parent->sparc_bus_map;
244	bt->sparc_intr_establish = vbus_intr_establish;
245
246	return (bt);
247}
248