xref: /netbsd-src/sys/arch/sparc64/dev/cbus.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: cbus.c,v 1.2 2016/07/18 19:32:44 palle Exp $	*/
2 /*	$OpenBSD: cbus.c,v 1.15 2015/09/27 11:29:20 kettenis Exp $	*/
3 /*
4  * Copyright (c) 2008 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 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23 
24 #include <machine/autoconf.h>
25 #include <machine/hypervisor.h>
26 #include <machine/openfirm.h>
27 #include <machine/mdesc.h>
28 
29 #include <sparc64/dev/cbusvar.h>
30 #include <sparc64/dev/vbusvar.h>
31 
32 #include <sparc64/dev/iommureg.h>
33 
34 #ifdef DEBUG
35 #define CBUSDB_AC               0x01
36 #define CBUSDB_INTR             0x02
37 int cbus_debug = 0x00;
38 #define DPRINTF(l, s)   do { if (cbus_debug & l) printf s; } while (0)
39 #else
40 #define DPRINTF(l, s)
41 #endif
42 
43 struct cbus_softc {
44 	device_t		sc_dv;
45 	bus_space_tag_t		sc_bustag;
46 	bus_dma_tag_t		sc_dmatag;
47 
48 	uint64_t		sc_devhandle;
49 
50 	/* Machine description. */
51 	int			sc_idx;
52 };
53 
54 int	cbus_match(device_t, cfdata_t, void *);
55 void	cbus_attach(device_t, device_t, void *);
56 int	cbus_print(void *, const char *);
57 
58 CFATTACH_DECL_NEW(cbus, sizeof(struct cbus_softc),
59     cbus_match, cbus_attach, NULL, NULL);
60 
61 
62 void *cbus_intr_establish(bus_space_tag_t, int, int,
63     int (*)(void *), void *, void (*)(void));
64 void cbus_intr_ack(struct intrhand *);
65 bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t);
66 
67 int cbus_get_channel_endpoint(struct cbus_softc *,
68 			      struct cbus_attach_args *);
69 
70 int
71 cbus_match(device_t parent, cfdata_t match, void *aux)
72 {
73 	struct vbus_attach_args *va = aux;
74 
75 	if (strcmp(va->va_name, "channel-devices") == 0)
76 		return (1);
77 
78 	return (0);
79 }
80 
81 void
82 cbus_attach(device_t parent, device_t self, void *aux)
83 {
84         struct cbus_softc *sc = device_private(self);
85 	struct vbus_attach_args *va = aux;
86 	int node;
87 	int reg;
88 
89 	sc->sc_bustag = cbus_alloc_bus_tag(sc, va->va_bustag);
90 	sc->sc_dmatag = va->va_dmatag;
91 
92 	if (OF_getprop(va->va_node, "reg", &reg, sizeof(reg)) != sizeof(reg))
93 		return;
94 	sc->sc_devhandle = reg;
95 
96 	printf("\n");
97 
98 	sc->sc_idx = mdesc_find(va->va_name, va->va_reg[0]);
99 	if (sc->sc_idx == -1) {
100 	  DPRINTF(CBUSDB_AC, ("cbus_attach() - no idx\n"));
101 	  return;
102 	}
103 
104 	for (node = OF_child(va->va_node); node; node = OF_peer(node)) {
105 		struct cbus_attach_args ca;
106 		char buf[32];
107 
108 		bzero(&ca, sizeof(ca));
109 		ca.ca_node = node;
110 		if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
111 			continue;
112 		DPRINTF(CBUSDB_AC, ("cbus_attach() - buf %s\n", buf));
113 		ca.ca_name = buf;
114 		ca.ca_bustag = sc->sc_bustag;
115 		ca.ca_dmatag = sc->sc_dmatag;
116 		prom_getprop(node, "reg", sizeof(*ca.ca_reg),
117 			     &ca.ca_nreg, (void **)&ca.ca_reg);
118 		int rc = cbus_get_channel_endpoint(sc, &ca);
119 		DPRINTF(CBUSDB_AC, ("cbus_attach() - cbus_get_channel_endpoint() %d\n", rc));
120 		if ( rc != 0) {
121 		  continue;
122 		}
123 
124 		config_found(self, &ca, cbus_print);
125 
126 	}
127 }
128 
129 int
130 cbus_print(void *aux, const char *name)
131 {
132 	struct cbus_attach_args *ca = aux;
133 	DPRINTF(CBUSDB_AC, ("cbus_print() name %s\n", name));
134 
135 	if (name)
136 		printf("\"%s\" at %s", ca->ca_name, name);
137 	if (ca->ca_id != -1)
138 	  printf(" chan 0x%llx", (long long unsigned int)ca->ca_id);
139 	return (UNCONF);
140 }
141 
142 int
143 cbus_intr_setstate(bus_space_tag_t t, uint64_t devino, uint64_t state)
144 {
145 	struct cbus_softc *sc = t->cookie;
146 	uint64_t devhandle = sc->sc_devhandle;
147 	int err;
148 
149 	err = hv_vintr_setstate(devhandle, devino, state);
150 	if (err != H_EOK)
151 		return (-1);
152 
153 	return (0);
154 }
155 
156 int
157 cbus_intr_setenabled(bus_space_tag_t t, uint64_t devino, uint64_t enabled)
158 {
159 	struct cbus_softc *sc = t->cookie;
160 	uint64_t devhandle = sc->sc_devhandle;
161 	int err;
162 
163 	err = hv_vintr_setenabled(devhandle, devino, enabled);
164 	if (err != H_EOK)
165 		return (-1);
166 
167 	return (0);
168 }
169 
170 void *
171 cbus_intr_establish(bus_space_tag_t t, int ihandle, int level,
172 	int (*handler)(void *), void *arg, void (*fastvec)(void) /* ignored */)
173 {
174 	struct cbus_softc *sc = t->cookie;
175 	uint64_t devhandle = sc->sc_devhandle;
176 	uint64_t devino = ihandle;
177 	struct intrhand *ih;
178 	int ino;
179 	int err;
180 
181 	ino = INTINO(ihandle);
182 
183 	DPRINTF(CBUSDB_INTR, ("cbus_intr_establish(): ino 0x%x\n", ino));
184 
185 	ih = intrhand_alloc();
186 
187 	ih->ih_ivec = ihandle;
188 	ih->ih_fun = handler;
189 	ih->ih_arg = arg;
190 	ih->ih_pil = level;
191 	ih->ih_number = ino;
192 	ih->ih_bus = t;
193 
194 	err = hv_vintr_setenabled(devhandle, devino, INTR_DISABLED);
195 	if (err != H_EOK) {
196 		printf("hv_vintr_setenabled: %d\n", err);
197 		return (NULL);
198 	}
199 
200 	err = hv_vintr_setcookie(devhandle, devino, (vaddr_t)ih);
201 	if (err != H_EOK) {
202 		printf("hv_vintr_setcookie: %d\n", err);
203 		return (NULL);
204 	}
205 
206 	ih->ih_ack = cbus_intr_ack;
207 
208 	err = hv_vintr_settarget(devhandle, devino, cpus->ci_cpuid);
209 	if (err != H_EOK) {
210 		printf("hv_vintr_settarget: %d\n", err);
211 		return (NULL);
212 	}
213 
214 	/* Clear pending interrupts. */
215 	err = hv_vintr_setstate(devhandle, devino, INTR_IDLE);
216 	if (err != H_EOK) {
217 		printf("hv_vintr_setstate: %d\n", err);
218 		return (NULL);
219 	}
220 
221 	return (ih);
222 }
223 
224 void
225 cbus_intr_ack(struct intrhand *ih)
226 {
227 	DPRINTF(CBUSDB_INTR, ("cbus_intr_ack()\n"));
228 	bus_space_tag_t t = ih->ih_bus;
229 	struct cbus_softc *sc = t->cookie;
230 	uint64_t devhandle = sc->sc_devhandle;
231 	uint64_t devino = ih->ih_number;
232 
233 	hv_vintr_setstate(devhandle, devino, INTR_IDLE);
234 }
235 
236 bus_space_tag_t
237 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent)
238 {
239 	struct sparc_bus_space_tag *bt;
240 
241 	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
242 	if (bt == NULL)
243 		panic("could not allocate cbus bus tag");
244 
245 	bt->cookie = sc;
246 	bt->parent = parent;
247 	bt->sparc_bus_map = parent->sparc_bus_map;
248 	bt->sparc_intr_establish = cbus_intr_establish;
249 
250 	return (bt);
251 }
252 
253 int
254 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca)
255 {
256 	struct md_header *hdr;
257 	struct md_element *elem;
258 	const char *name_blk;
259 	const char *str;
260 	int idx;
261 	int arc;
262 
263 	idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]);
264 	if (idx == -1) {
265 	  DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - mdesc_find_child() failed\n"));
266 	  return (ENOENT);
267 	}
268 	DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx));
269 
270 	hdr = (struct md_header *)mdesc;
271 	elem = (struct md_element *)(mdesc + sizeof(struct md_header));
272 	name_blk = (char *)mdesc + sizeof(struct md_header) + hdr->node_blk_sz;
273 
274 	ca->ca_idx = idx;
275 
276 	ca->ca_id = -1;
277 	ca->ca_tx_ino = -1;
278 	ca->ca_rx_ino = -1;
279 
280 	if (strcmp(ca->ca_name, "disk") != 0 &&
281 	    strcmp(ca->ca_name, "network") != 0) {
282 	  DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - neither disk nor network\n"));
283 	  return (0);
284 	}
285 
286 	for (; elem[idx].tag != 'E'; idx++) {
287 		str = name_blk + elem[idx].name_offset;
288 		if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
289 			continue;
290 
291 		arc = elem[idx].d.val;
292 		str = name_blk + elem[arc].name_offset;
293 		DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - str %s\n", str));
294 		if (strcmp(str, "virtual-device-port") == 0) {
295 			idx = arc;
296 			DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx));
297 			continue;
298 		}
299 
300 		if (strcmp(str, "channel-endpoint") == 0) {
301 			ca->ca_id = mdesc_get_prop_val(arc, "id");
302 			ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino");
303 			ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino");
304 			DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() "
305 					    "- tx-ino %lu rx-ino %lu\n",
306 					    ca->ca_tx_ino, ca->ca_rx_ino));
307 			return (0);
308 		}
309 	}
310 
311 	DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - exit\n"));
312 
313 	return (0);
314 }
315