xref: /openbsd-src/sys/arch/sparc64/dev/cbus.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: cbus.c,v 1.11 2013/07/16 19:45:37 kettenis 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/mdesc.h>
26 #include <machine/openfirm.h>
27 
28 #include <sparc64/dev/cbusvar.h>
29 #include <sparc64/dev/vbusvar.h>
30 
31 #define CBUS_HANDLE(x) ((x) & ~0xff)
32 #define CBUS_INO(x) ((x) & 0xff)
33 
34 struct cbus_softc {
35 	struct device		sc_dv;
36 	bus_space_tag_t		sc_bustag;
37 	bus_dma_tag_t		sc_dmatag;
38 
39 	/* Machine description. */
40 	int			sc_idx;
41 };
42 
43 int	cbus_match(struct device *, void *, void *);
44 void	cbus_attach(struct device *, struct device *, void *);
45 int	cbus_print(void *, const char *);
46 
47 struct cfattach cbus_ca = {
48 	sizeof(struct cbus_softc), cbus_match, cbus_attach
49 };
50 
51 struct cfdriver cbus_cd = {
52 	NULL, "cbus", DV_DULL
53 };
54 
55 void	*cbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int,
56     int (*)(void *), void *, const char *);
57 void	cbus_intr_ack(struct intrhand *);
58 bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t);
59 
60 int	cbus_get_channel_endpoint(struct cbus_softc *,
61 	    struct cbus_attach_args *);
62 
63 int
64 cbus_match(struct device *parent, void *match, void *aux)
65 {
66 	struct vbus_attach_args *va = aux;
67 
68 	if (strcmp(va->va_name, "channel-devices") == 0)
69 		return (1);
70 
71 	return (0);
72 }
73 
74 void
75 cbus_attach(struct device *parent, struct device *self, void *aux)
76 {
77 	struct cbus_softc *sc = (struct cbus_softc *)self;
78 	struct vbus_attach_args *va = aux;
79 	int node;
80 
81 	sc->sc_bustag = cbus_alloc_bus_tag(sc, va->va_bustag);
82 	sc->sc_dmatag = va->va_dmatag;
83 
84 	printf("\n");
85 
86 	sc->sc_idx = mdesc_find(va->va_name, va->va_reg[0]);
87 	if (sc->sc_idx == -1)
88 		return;
89 
90 	for (node = OF_child(va->va_node); node; node = OF_peer(node)) {
91 		struct cbus_attach_args ca;
92 		char buf[32];
93 
94 		bzero(&ca, sizeof(ca));
95 		ca.ca_node = node;
96 		if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
97 			continue;
98 		ca.ca_name = buf;
99 		ca.ca_bustag = sc->sc_bustag;
100 		ca.ca_dmatag = sc->sc_dmatag;
101 		getprop(node, "reg", sizeof(*ca.ca_reg),
102 		    &ca.ca_nreg, (void **)&ca.ca_reg);
103 		if (cbus_get_channel_endpoint(sc, &ca) != 0)
104 			continue;
105 
106 		config_found(self, &ca, cbus_print);
107 	}
108 }
109 
110 int
111 cbus_print(void *aux, const char *name)
112 {
113 	struct cbus_attach_args *ca = aux;
114 
115 	if (name)
116 		printf("\"%s\" at %s", ca->ca_name, name);
117 	if (ca->ca_id != -1)
118 		printf(" chan 0x%llx", ca->ca_id);
119 	return (UNCONF);
120 }
121 
122 int
123 cbus_intr_map(int node, int ino, uint64_t *sysino)
124 {
125 	int parent;
126 	int reg;
127 	int err;
128 
129 	parent = OF_parent(node);
130 	if (OF_getprop(parent, "reg", &reg, sizeof(reg)) != sizeof(reg))
131 		return (-1);
132 
133 	*sysino = CBUS_HANDLE(reg) | CBUS_INO(ino);
134 	err = hv_vintr_setcookie(reg, ino, *sysino);
135 	if (err != H_EOK)
136 		return (-1);
137 
138 	return (0);
139 }
140 
141 int
142 cbus_intr_setstate(uint64_t sysino, uint64_t state)
143 {
144 	uint64_t devhandle = CBUS_HANDLE(sysino);
145 	uint64_t devino = CBUS_INO(sysino);
146 	int err;
147 
148 	err = hv_vintr_setstate(devhandle, devino, state);
149 	if (err != H_EOK)
150 		return (-1);
151 
152 	return (0);
153 }
154 
155 int
156 cbus_intr_setenabled(uint64_t sysino, uint64_t enabled)
157 {
158 	uint64_t devhandle = CBUS_HANDLE(sysino);
159 	uint64_t devino = CBUS_INO(sysino);
160 	int err;
161 
162 	err = hv_vintr_setenabled(devhandle, devino, enabled);
163 	if (err != H_EOK)
164 		return (-1);
165 
166 	return (0);
167 }
168 
169 void *
170 cbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
171     int level, int flags, int (*handler)(void *), void *arg, const char *what)
172 {
173 	uint64_t devhandle = CBUS_HANDLE(ihandle);
174 	uint64_t devino = CBUS_INO(ihandle);
175 	struct intrhand *ih;
176 	int err;
177 
178 	ih = bus_intr_allocate(t0, handler, arg, ihandle, level,
179 	    NULL, NULL, what);
180 	if (ih == NULL)
181 		return (NULL);
182 
183 	err = hv_vintr_setenabled(devhandle, devino, INTR_DISABLED);
184 	if (err != H_EOK) {
185 		printf("hv_vintr_setenabled: %d\n", err);
186 		return (NULL);
187 	}
188 
189 	if (flags & BUS_INTR_ESTABLISH_MPSAFE)
190 		ih->ih_mpsafe = 1;
191 
192 	intr_establish(ih->ih_pil, ih);
193 	ih->ih_ack = cbus_intr_ack;
194 
195 	err = hv_vintr_settarget(devhandle, devino, cpus->ci_upaid);
196 	if (err != H_EOK) {
197 		printf("hv_vintr_settarget: %d\n", err);
198 		return (NULL);
199 	}
200 
201 	/* Clear pending interrupts. */
202 	err = hv_vintr_setstate(devhandle, devino, INTR_IDLE);
203 	if (err != H_EOK) {
204 		printf("hv_vintr_setstate: %d\n", err);
205 		return (NULL);
206 	}
207 
208 	return (ih);
209 }
210 
211 void
212 cbus_intr_ack(struct intrhand *ih)
213 {
214 	uint64_t devhandle = CBUS_HANDLE(ih->ih_number);
215 	uint64_t devino = CBUS_INO(ih->ih_number);
216 
217 	hv_vintr_setstate(devhandle, devino, INTR_IDLE);
218 }
219 
220 bus_space_tag_t
221 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent)
222 {
223 	struct sparc_bus_space_tag *bt;
224 
225 	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
226 	if (bt == NULL)
227 		panic("could not allocate cbus bus tag");
228 
229 	strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name));
230 	bt->cookie = sc;
231 	bt->parent = parent;
232 	bt->asi = parent->asi;
233 	bt->sasi = parent->sasi;
234 	bt->sparc_bus_map = parent->sparc_bus_map;
235 	bt->sparc_intr_establish = cbus_intr_establish;
236 
237 	return (bt);
238 }
239 
240 int
241 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca)
242 {
243 	struct md_header *hdr;
244 	struct md_element *elem;
245 	const char *name_blk;
246 	const char *str;
247 	int idx;
248 	int arc;
249 
250 	idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]);
251 	if (idx == -1)
252 		return (ENOENT);
253 
254 	hdr = (struct md_header *)mdesc;
255 	elem = (struct md_element *)(mdesc + sizeof(struct md_header));
256 	name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz;
257 
258 	ca->ca_idx = idx;
259 
260 	ca->ca_id = -1;
261 	ca->ca_tx_ino = -1;
262 	ca->ca_rx_ino = -1;
263 
264 	if (strcmp(ca->ca_name, "disk") != 0 &&
265 	    strcmp(ca->ca_name, "network") != 0)
266 		return (0);
267 
268 	for (; elem[idx].tag != 'E'; idx++) {
269 		str = name_blk + elem[idx].name_offset;
270 		if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
271 			continue;
272 
273 		arc = elem[idx].d.val;
274 		str = name_blk + elem[arc].name_offset;
275 		if (strcmp(str, "virtual-device-port") == 0) {
276 			idx = arc;
277 			continue;
278 		}
279 
280 		if (strcmp(str, "channel-endpoint") == 0) {
281 			ca->ca_id = mdesc_get_prop_val(arc, "id");
282 			ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino");
283 			ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino");
284 			return (0);
285 		}
286 	}
287 
288 	return (0);
289 }
290