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