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