xref: /openbsd/sys/arch/sparc64/dev/cbus.c (revision 274d7c50)
1 /*	$OpenBSD: cbus.c,v 1.16 2016/12/20 13:40:50 jsg 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 	evcount_attach(&ih->ih_count, ih->ih_name, NULL);
185 
186 	ih->ih_ack = cbus_intr_ack;
187 	ih->ih_cpu = cpus;
188 
189 	err = hv_vintr_settarget(devhandle, devino, ih->ih_cpu->ci_upaid);
190 	if (err != H_EOK) {
191 		printf("hv_vintr_settarget: %d\n", err);
192 		return (NULL);
193 	}
194 
195 	/* Clear pending interrupts. */
196 	err = hv_vintr_setstate(devhandle, devino, INTR_IDLE);
197 	if (err != H_EOK) {
198 		printf("hv_vintr_setstate: %d\n", err);
199 		return (NULL);
200 	}
201 
202 	return (ih);
203 }
204 
205 void
206 cbus_intr_ack(struct intrhand *ih)
207 {
208 	bus_space_tag_t t = ih->ih_bus;
209 	struct cbus_softc *sc = t->cookie;
210 	uint64_t devhandle = sc->sc_devhandle;
211 	uint64_t devino = ih->ih_number;
212 
213 	hv_vintr_setstate(devhandle, devino, INTR_IDLE);
214 }
215 
216 bus_space_tag_t
217 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent)
218 {
219 	struct sparc_bus_space_tag *bt;
220 
221 	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
222 	if (bt == NULL)
223 		panic("could not allocate cbus bus tag");
224 
225 	strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name));
226 	bt->cookie = sc;
227 	bt->parent = parent;
228 	bt->asi = parent->asi;
229 	bt->sasi = parent->sasi;
230 	bt->sparc_bus_map = parent->sparc_bus_map;
231 	bt->sparc_intr_establish = cbus_intr_establish;
232 
233 	return (bt);
234 }
235 
236 int
237 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca)
238 {
239 	struct md_header *hdr;
240 	struct md_element *elem;
241 	const char *name_blk;
242 	const char *str;
243 	int idx;
244 	int arc;
245 
246 	idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]);
247 	if (idx == -1)
248 		return (ENOENT);
249 
250 	hdr = (struct md_header *)mdesc;
251 	elem = (struct md_element *)(mdesc + sizeof(struct md_header));
252 	name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz;
253 
254 	ca->ca_idx = idx;
255 
256 	ca->ca_id = -1;
257 	ca->ca_tx_ino = -1;
258 	ca->ca_rx_ino = -1;
259 
260 	if (strcmp(ca->ca_name, "disk") != 0 &&
261 	    strcmp(ca->ca_name, "network") != 0)
262 		return (0);
263 
264 	for (; elem[idx].tag != 'E'; idx++) {
265 		str = name_blk + elem[idx].name_offset;
266 		if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0)
267 			continue;
268 
269 		arc = elem[idx].d.val;
270 		str = name_blk + elem[arc].name_offset;
271 		if (strcmp(str, "virtual-device-port") == 0) {
272 			idx = arc;
273 			continue;
274 		}
275 
276 		if (strcmp(str, "channel-endpoint") == 0) {
277 			ca->ca_id = mdesc_get_prop_val(arc, "id");
278 			ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino");
279 			ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino");
280 			return (0);
281 		}
282 	}
283 
284 	return (0);
285 }
286