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", ®, 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