1 /* $OpenBSD: cbus.c,v 1.17 2021/10/24 17:05:03 mpi 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 const 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
cbus_match(struct device * parent,void * match,void * aux)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
cbus_attach(struct device * parent,struct device * self,void * aux)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", ®, 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
cbus_print(void * aux,const char * name)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
cbus_intr_setstate(bus_space_tag_t t,uint64_t devino,uint64_t state)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
cbus_intr_setenabled(bus_space_tag_t t,uint64_t devino,uint64_t enabled)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 *
cbus_intr_establish(bus_space_tag_t t,bus_space_tag_t t0,int ihandle,int level,int flags,int (* handler)(void *),void * arg,const char * what)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
cbus_intr_ack(struct intrhand * ih)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
cbus_alloc_bus_tag(struct cbus_softc * sc,bus_space_tag_t parent)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
cbus_get_channel_endpoint(struct cbus_softc * sc,struct cbus_attach_args * ca)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