1 /* $OpenBSD: cbus.c,v 1.4 2009/04/04 11:35:03 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/openfirm.h> 26 27 #include <sparc64/dev/cbusvar.h> 28 #include <sparc64/dev/vbusvar.h> 29 30 struct md_header { 31 uint32_t transport_version; 32 uint32_t node_blk_sz; 33 uint32_t name_blk_sz; 34 uint32_t data_blk_sz; 35 }; 36 37 struct md_element { 38 uint8_t tag; 39 uint8_t name_len; 40 uint16_t _reserved_field; 41 uint32_t name_offset; 42 union { 43 struct { 44 uint32_t data_len; 45 uint32_t data_offset; 46 } y; 47 uint64_t val; 48 } d; 49 }; 50 51 struct cbus_softc { 52 struct device sc_dv; 53 bus_space_tag_t sc_bustag; 54 bus_dma_tag_t sc_dmatag; 55 56 /* Machine description. */ 57 caddr_t sc_md; 58 int sc_idx; 59 }; 60 61 int cbus_match(struct device *, void *, void *); 62 void cbus_attach(struct device *, struct device *, void *); 63 int cbus_print(void *, const char *); 64 65 struct cfattach cbus_ca = { 66 sizeof(struct cbus_softc), cbus_match, cbus_attach 67 }; 68 69 struct cfdriver cbus_cd = { 70 NULL, "cbus", DV_DULL 71 }; 72 73 void *cbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int, 74 int (*)(void *), void *, const char *); 75 void cbus_intr_ack(struct intrhand *); 76 bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t); 77 78 caddr_t cbus_get_mach_desc(struct cbus_softc *); 79 int cbus_get_channel_endpoint(struct cbus_softc *, 80 struct cbus_attach_args *); 81 82 uint64_t sun4v_mdesc_get_prop_val(caddr_t, int, const char *); 83 const char *sun4v_mdesc_get_prop_string(caddr_t, int, const char *); 84 int sun4v_mdesc_find(caddr_t, const char *, uint64_t); 85 int sun4v_mdesc_find_child(caddr_t, int, const char *, uint64_t); 86 87 int 88 cbus_match(struct device *parent, void *match, void *aux) 89 { 90 struct vbus_attach_args *va = aux; 91 92 if (strcmp(va->va_name, "channel-devices") == 0) 93 return (1); 94 95 return (0); 96 } 97 98 void 99 cbus_attach(struct device *parent, struct device *self, void *aux) 100 { 101 struct cbus_softc *sc = (struct cbus_softc *)self; 102 struct vbus_attach_args *va = aux; 103 int node; 104 105 sc->sc_bustag = cbus_alloc_bus_tag(sc, va->va_bustag); 106 sc->sc_dmatag = va->va_dmatag; 107 108 sc->sc_md = cbus_get_mach_desc(sc); 109 if (sc->sc_md == NULL) { 110 printf(": can't read machine description\n"); 111 return; 112 } 113 114 printf("\n"); 115 116 sc->sc_idx = sun4v_mdesc_find(sc->sc_md, va->va_name, va->va_reg[0]); 117 if (sc->sc_idx == -1) 118 return; 119 120 for (node = OF_child(va->va_node); node; node = OF_peer(node)) { 121 struct cbus_attach_args ca; 122 char buf[32]; 123 124 bzero(&ca, sizeof(ca)); 125 ca.ca_node = node; 126 if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 127 continue; 128 ca.ca_name = buf; 129 ca.ca_bustag = sc->sc_bustag; 130 ca.ca_dmatag = sc->sc_dmatag; 131 getprop(node, "reg", sizeof(*ca.ca_reg), 132 &ca.ca_nreg, (void **)&ca.ca_reg); 133 if (cbus_get_channel_endpoint(sc, &ca) != 0) 134 continue; 135 136 config_found(self, &ca, cbus_print); 137 } 138 } 139 140 int 141 cbus_print(void *aux, const char *name) 142 { 143 struct cbus_attach_args *ca = aux; 144 145 if (name) 146 printf("\"%s\" at %s", ca->ca_name, name); 147 if (ca->ca_id != -1) 148 printf(" chan 0x%llx", ca->ca_id); 149 return (UNCONF); 150 } 151 152 int 153 cbus_intr_map(int node, int ino, uint64_t *sysino) 154 { 155 int parent; 156 int reg; 157 int err; 158 159 parent = OF_parent(node); 160 if (OF_getprop(parent, "reg", ®, sizeof(reg)) != sizeof(reg)) 161 return (-1); 162 163 *sysino = INTIGN(reg) | INTINO(ino); 164 err = hv_vintr_setcookie(reg, ino, *sysino); 165 if (err != H_EOK) 166 return (-1); 167 168 return (0); 169 } 170 171 void * 172 cbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle, 173 int level, int flags, int (*handler)(void *), void *arg, const char *what) 174 { 175 uint64_t devhandle = INTIGN(ihandle); 176 uint64_t devino = INTINO(ihandle); 177 struct intrhand *ih; 178 int err; 179 180 ih = bus_intr_allocate(t0, handler, arg, ihandle, level, 181 NULL, NULL, what); 182 if (ih == NULL) 183 return (NULL); 184 185 intr_establish(ih->ih_pil, ih); 186 ih->ih_ack = cbus_intr_ack; 187 188 err = hv_vintr_settarget(devhandle, devino, cpus->ci_upaid); 189 if (err != H_EOK) { 190 printf("hv_vintr_settarget: %d\n", err); 191 return (NULL); 192 } 193 194 /* Clear pending interrupts. */ 195 err = hv_vintr_setstate(devhandle, devino, INTR_IDLE); 196 if (err != H_EOK) { 197 printf("hv_vintr_setstate: %d\n", err); 198 return (NULL); 199 } 200 201 err = hv_vintr_setenabled(devhandle, devino, INTR_ENABLED); 202 if (err != H_EOK) { 203 printf("hv_vintr_setenabled: %d\n", err); 204 return (NULL); 205 } 206 207 return (ih); 208 } 209 210 void 211 cbus_intr_ack(struct intrhand *ih) 212 { 213 uint64_t devhandle = INTIGN(ih->ih_number); 214 uint64_t devino = INTINO(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 snprintf(bt->name, sizeof(bt->name), "%s", sc->sc_dv.dv_xname); 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 caddr_t 240 cbus_get_mach_desc(struct cbus_softc *sc) 241 { 242 bus_dmamap_t map; 243 bus_dma_segment_t seg; 244 psize_t len; 245 bus_size_t size; 246 caddr_t va; 247 int nsegs, err = 0; 248 249 len = 0; 250 hv_mach_desc((paddr_t)NULL, &len); 251 KASSERT(len != 0); 252 253 again: 254 size = roundup(len, PAGE_SIZE); 255 256 if (bus_dmamap_create(sc->sc_dmatag, size, 1, size, 0, 257 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &map) != 0) 258 return (NULL); 259 260 if (bus_dmamem_alloc(sc->sc_dmatag, size, PAGE_SIZE, 0, &seg, 261 1, &nsegs, BUS_DMA_NOWAIT) != 0) 262 goto destroy; 263 264 if (bus_dmamem_map(sc->sc_dmatag, &seg, 1, size, 265 &va, BUS_DMA_NOWAIT) != 0) 266 goto free; 267 268 if (bus_dmamap_load(sc->sc_dmatag, map, va, size, 269 NULL, BUS_DMA_NOWAIT) != 0) 270 goto unmap; 271 272 len = size; 273 err = hv_mach_desc(map->dm_segs[0].ds_addr, &len); 274 if (err != H_EOK) 275 goto unload; 276 277 return (va); 278 279 unload: 280 bus_dmamap_unload(sc->sc_dmatag, map); 281 unmap: 282 bus_dmamem_unmap(sc->sc_dmatag, va, size); 283 free: 284 bus_dmamem_free(sc->sc_dmatag, &seg, 1); 285 destroy: 286 bus_dmamap_destroy(sc->sc_dmatag, map); 287 288 /* 289 * If the machine description was updated while we were trying 290 * to fetch it, the allocated buffer may have been to small. 291 * Try again in that case. 292 */ 293 if (err == H_EINVAL && len > size) 294 goto again; 295 296 return (NULL); 297 } 298 299 int 300 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 301 { 302 struct md_header *hdr; 303 struct md_element *elem; 304 const char *name_blk; 305 const char *str; 306 int idx; 307 int arc; 308 309 idx = sun4v_mdesc_find_child(sc->sc_md, sc->sc_idx, 310 ca->ca_name, ca->ca_reg[0]); 311 if (idx == -1) 312 return (ENOENT); 313 314 hdr = (struct md_header *)sc->sc_md; 315 elem = (struct md_element *)(sc->sc_md + sizeof(struct md_header)); 316 name_blk = sc->sc_md + sizeof(struct md_header) + hdr->node_blk_sz; 317 318 for (; elem[idx].tag != 'E'; idx++) { 319 str = name_blk + elem[idx].name_offset; 320 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 321 continue; 322 323 arc = elem[idx].d.val; 324 str = name_blk + elem[arc].name_offset; 325 if (strcmp(str, "virtual-device-port") == 0) { 326 idx = arc; 327 continue; 328 } 329 330 if (strcmp(str, "channel-endpoint") == 0) { 331 ca->ca_id = 332 sun4v_mdesc_get_prop_val(sc->sc_md, arc, "id"); 333 ca->ca_tx_ino = 334 sun4v_mdesc_get_prop_val(sc->sc_md, arc, "tx-ino"); 335 ca->ca_rx_ino = 336 sun4v_mdesc_get_prop_val(sc->sc_md, arc, "rx-ino"); 337 return (0); 338 } 339 } 340 341 ca->ca_id = -1; 342 ca->ca_tx_ino = -1; 343 ca->ca_rx_ino = -1; 344 return (0); 345 } 346 347 uint64_t 348 sun4v_mdesc_get_prop_val(caddr_t md, int idx, const char *name) 349 { 350 struct md_header *hdr; 351 struct md_element *elem; 352 const char *name_blk; 353 const char *str; 354 355 hdr = (struct md_header *)md; 356 elem = (struct md_element *)(md + sizeof(struct md_header)); 357 name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz; 358 359 while (elem[idx].tag != 'E') { 360 str = name_blk + elem[idx].name_offset; 361 if (elem[idx].tag == 'v' && strcmp(str, name) == 0) 362 return (elem[idx].d.val); 363 idx++; 364 } 365 366 return (-1); 367 } 368 369 const char * 370 sun4v_mdesc_get_prop_string(caddr_t md, int idx, const char *name) 371 { 372 struct md_header *hdr; 373 struct md_element *elem; 374 const char *name_blk; 375 const char *data_blk; 376 const char *str; 377 378 hdr = (struct md_header *)md; 379 elem = (struct md_element *)(md + sizeof(struct md_header)); 380 name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz; 381 data_blk = name_blk + hdr->name_blk_sz; 382 383 while (elem[idx].tag != 'E') { 384 str = name_blk + elem[idx].name_offset; 385 if (elem[idx].tag == 's' && strcmp(str, name) == 0) 386 return (data_blk + elem[idx].d.y.data_offset); 387 idx++; 388 } 389 390 return (NULL); 391 } 392 393 int 394 sun4v_mdesc_find(caddr_t md, const char *name, uint64_t cfg_handle) 395 { 396 struct md_header *hdr; 397 struct md_element *elem; 398 const char *str; 399 uint64_t val; 400 int idx; 401 402 hdr = (struct md_header *)md; 403 elem = (struct md_element *)(md + sizeof(struct md_header)); 404 405 for (idx = 0; elem[idx].tag == 'N'; idx = elem[idx].d.val) { 406 str = sun4v_mdesc_get_prop_string(md, idx, "name"); 407 val = sun4v_mdesc_get_prop_val(md, idx, "cfg-handle"); 408 if (str && strcmp(str, name) == 0 && val == cfg_handle) 409 return (idx); 410 } 411 412 return (-1); 413 } 414 415 int 416 sun4v_mdesc_find_child(caddr_t md, int idx, const char *name, 417 uint64_t cfg_handle) 418 { 419 struct md_header *hdr; 420 struct md_element *elem; 421 const char *name_blk; 422 const char *str; 423 uint64_t val; 424 int arc; 425 426 hdr = (struct md_header *)md; 427 elem = (struct md_element *)(md + sizeof(struct md_header)); 428 name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz; 429 430 for (; elem[idx].tag != 'E'; idx++) { 431 str = name_blk + elem[idx].name_offset; 432 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 433 continue; 434 435 arc = elem[idx].d.val; 436 str = sun4v_mdesc_get_prop_string(md, arc, "name"); 437 val = sun4v_mdesc_get_prop_val(md, arc, "cfg-handle"); 438 if (str && strcmp(str, name) == 0 && val == cfg_handle) 439 return (arc); 440 } 441 442 return (-1); 443 } 444