cbus.c revision 1.2
1/* $OpenBSD: cbus.c,v 1.2 2009/01/01 23:24:59 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 30struct 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 37struct 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 51struct 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 61int cbus_match(struct device *, void *, void *); 62void cbus_attach(struct device *, struct device *, void *); 63int cbus_print(void *, const char *); 64 65struct cfattach cbus_ca = { 66 sizeof(struct cbus_softc), cbus_match, cbus_attach 67}; 68 69struct cfdriver cbus_cd = { 70 NULL, "cbus", DV_DULL 71}; 72 73void *cbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int, 74 int (*)(void *), void *, const char *); 75void cbus_intr_ack(struct intrhand *); 76bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t); 77 78caddr_t cbus_get_mach_desc(struct cbus_softc *); 79int cbus_get_channel_endpoint(struct cbus_softc *, 80 struct cbus_attach_args *); 81 82uint64_t sun4v_mdesc_get_prop_val(caddr_t, int, const char *); 83const char *sun4v_mdesc_get_prop_string(caddr_t, int, const char *); 84int sun4v_mdesc_find(caddr_t, const char *, uint64_t); 85int sun4v_mdesc_find_child(caddr_t, int, const char *, uint64_t); 86 87int 88cbus_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 98void 99cbus_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 140int 141cbus_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(" id 0x%llx", ca->ca_id); 149 if (ca->ca_tx_ino != -1) 150 printf(" tx-ino 0x%llx", ca->ca_tx_ino); 151 if (ca->ca_rx_ino != -1) 152 printf(" rx-ino 0x%llx", ca->ca_rx_ino); 153 return (UNCONF); 154} 155 156int 157cbus_intr_map(int node, int ino, uint64_t *sysino) 158{ 159 int parent; 160 int reg; 161 int err; 162 163 parent = OF_parent(node); 164 if (OF_getprop(parent, "reg", ®, sizeof(reg)) != sizeof(reg)) 165 return (-1); 166 167 *sysino = INTIGN(reg) | INTINO(ino); 168 err = hv_vintr_setcookie(reg, ino, *sysino); 169 if (err != H_EOK) 170 return (-1); 171 172 return (0); 173} 174 175void * 176cbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle, 177 int level, int flags, int (*handler)(void *), void *arg, const char *what) 178{ 179 uint64_t devhandle = INTIGN(ihandle); 180 uint64_t devino = INTINO(ihandle); 181 struct intrhand *ih; 182 int err; 183 184 ih = bus_intr_allocate(t0, handler, arg, ihandle, level, 185 NULL, NULL, what); 186 if (ih == NULL) 187 return (NULL); 188 189 intr_establish(ih->ih_pil, ih); 190 ih->ih_ack = cbus_intr_ack; 191 192 err = hv_vintr_settarget(devhandle, devino, cpus->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 err = hv_vintr_setenabled(devhandle, devino, INTR_ENABLED); 206 if (err != H_EOK) { 207 printf("hv_vintr_setenabled: %d\n", err); 208 return (NULL); 209 } 210 211 return (ih); 212} 213 214void 215cbus_intr_ack(struct intrhand *ih) 216{ 217 uint64_t devhandle = INTIGN(ih->ih_number); 218 uint64_t devino = INTINO(ih->ih_number); 219 220 hv_vintr_setstate(devhandle, devino, INTR_IDLE); 221} 222 223bus_space_tag_t 224cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent) 225{ 226 struct sparc_bus_space_tag *bt; 227 228 bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO); 229 if (bt == NULL) 230 panic("could not allocate cbus bus tag"); 231 232 snprintf(bt->name, sizeof(bt->name), "%s", sc->sc_dv.dv_xname); 233 bt->cookie = sc; 234 bt->parent = parent; 235 bt->asi = parent->asi; 236 bt->sasi = parent->sasi; 237 bt->sparc_bus_map = parent->sparc_bus_map; 238 bt->sparc_intr_establish = cbus_intr_establish; 239 240 return (bt); 241} 242 243caddr_t 244cbus_get_mach_desc(struct cbus_softc *sc) 245{ 246 bus_dmamap_t map; 247 bus_dma_segment_t seg; 248 psize_t len; 249 bus_size_t size; 250 caddr_t va; 251 int nsegs, err = 0; 252 253 len = 0; 254 hv_mach_desc((paddr_t)NULL, &len); 255 KASSERT(len != 0); 256 257again: 258 size = roundup(len, PAGE_SIZE); 259 260 if (bus_dmamap_create(sc->sc_dmatag, size, 1, size, 0, 261 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &map) != 0) 262 return (NULL); 263 264 if (bus_dmamem_alloc(sc->sc_dmatag, size, PAGE_SIZE, 0, &seg, 265 1, &nsegs, BUS_DMA_NOWAIT) != 0) 266 goto destroy; 267 268 if (bus_dmamem_map(sc->sc_dmatag, &seg, 1, size, 269 &va, BUS_DMA_NOWAIT) != 0) 270 goto free; 271 272 if (bus_dmamap_load(sc->sc_dmatag, map, va, size, 273 NULL, BUS_DMA_NOWAIT) != 0) 274 goto unmap; 275 276 len = size; 277 err = hv_mach_desc(map->dm_segs[0].ds_addr, &len); 278 if (err != H_EOK) 279 goto unload; 280 281 return (va); 282 283unload: 284 printf("unload\n"); 285 bus_dmamap_unload(sc->sc_dmatag, map); 286unmap: 287 printf("unmap\n"); 288 bus_dmamem_unmap(sc->sc_dmatag, va, size); 289free: 290 printf("free\n"); 291 bus_dmamem_free(sc->sc_dmatag, &seg, 1); 292destroy: 293 printf("destroy\n"); 294 bus_dmamap_destroy(sc->sc_dmatag, map); 295 296 /* 297 * If the machine description was updated while we were trying 298 * to fetch it, the allocated buffer may have been to small. 299 * Try again in that case. 300 */ 301 if (err == H_EINVAL && len > size) 302 goto again; 303 304 return (NULL); 305} 306 307int 308cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 309{ 310 struct md_header *hdr; 311 struct md_element *elem; 312 const char *name_blk; 313 const char *str; 314 int idx; 315 int arc; 316 317 idx = sun4v_mdesc_find_child(sc->sc_md, sc->sc_idx, 318 ca->ca_name, ca->ca_reg[0]); 319 if (idx == -1) 320 return (ENOENT); 321 322 hdr = (struct md_header *)sc->sc_md; 323 elem = (struct md_element *)(sc->sc_md + sizeof(struct md_header)); 324 name_blk = sc->sc_md + sizeof(struct md_header) + hdr->node_blk_sz; 325 326 for (; elem[idx].tag != 'E'; idx++) { 327 str = name_blk + elem[idx].name_offset; 328 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 329 continue; 330 331 arc = elem[idx].d.val; 332 str = name_blk + elem[arc].name_offset; 333 if (strcmp(str, "virtual-device-port") == 0) { 334 idx = arc; 335 continue; 336 } 337 338 if (strcmp(str, "channel-endpoint") == 0) { 339 ca->ca_id = 340 sun4v_mdesc_get_prop_val(sc->sc_md, arc, "id"); 341 ca->ca_tx_ino = 342 sun4v_mdesc_get_prop_val(sc->sc_md, arc, "tx-ino"); 343 ca->ca_rx_ino = 344 sun4v_mdesc_get_prop_val(sc->sc_md, arc, "rx-ino"); 345 return (0); 346 } 347 } 348 349 ca->ca_id = -1; 350 ca->ca_tx_ino = -1; 351 ca->ca_rx_ino = -1; 352 return (0); 353} 354 355uint64_t 356sun4v_mdesc_get_prop_val(caddr_t md, int idx, const char *name) 357{ 358 struct md_header *hdr; 359 struct md_element *elem; 360 const char *name_blk; 361 const char *str; 362 363 hdr = (struct md_header *)md; 364 elem = (struct md_element *)(md + sizeof(struct md_header)); 365 name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz; 366 367 while (elem[idx].tag != 'E') { 368 str = name_blk + elem[idx].name_offset; 369 if (elem[idx].tag == 'v' && strcmp(str, name) == 0) 370 return (elem[idx].d.val); 371 idx++; 372 } 373 374 return (-1); 375} 376 377const char * 378sun4v_mdesc_get_prop_string(caddr_t md, int idx, const char *name) 379{ 380 struct md_header *hdr; 381 struct md_element *elem; 382 const char *name_blk; 383 const char *data_blk; 384 const char *str; 385 386 hdr = (struct md_header *)md; 387 elem = (struct md_element *)(md + sizeof(struct md_header)); 388 name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz; 389 data_blk = name_blk + hdr->name_blk_sz; 390 391 while (elem[idx].tag != 'E') { 392 str = name_blk + elem[idx].name_offset; 393 if (elem[idx].tag == 's' && strcmp(str, name) == 0) 394 return (data_blk + elem[idx].d.y.data_offset); 395 idx++; 396 } 397 398 return (NULL); 399} 400 401int 402sun4v_mdesc_find(caddr_t md, const char *name, uint64_t cfg_handle) 403{ 404 struct md_header *hdr; 405 struct md_element *elem; 406 const char *str; 407 uint64_t val; 408 int idx; 409 410 hdr = (struct md_header *)md; 411 elem = (struct md_element *)(md + sizeof(struct md_header)); 412 413 for (idx = 0; elem[idx].tag == 'N'; idx = elem[idx].d.val) { 414 str = sun4v_mdesc_get_prop_string(md, idx, "name"); 415 val = sun4v_mdesc_get_prop_val(md, idx, "cfg-handle"); 416 if (str && strcmp(str, name) == 0 && val == cfg_handle) 417 return (idx); 418 } 419 420 return (-1); 421} 422 423int 424sun4v_mdesc_find_child(caddr_t md, int idx, const char *name, 425 uint64_t cfg_handle) 426{ 427 struct md_header *hdr; 428 struct md_element *elem; 429 const char *name_blk; 430 const char *str; 431 uint64_t val; 432 int arc; 433 434 hdr = (struct md_header *)md; 435 elem = (struct md_element *)(md + sizeof(struct md_header)); 436 name_blk = md + sizeof(struct md_header) + hdr->node_blk_sz; 437 438 for (; elem[idx].tag != 'E'; idx++) { 439 str = name_blk + elem[idx].name_offset; 440 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 441 continue; 442 443 arc = elem[idx].d.val; 444 str = sun4v_mdesc_get_prop_string(md, arc, "name"); 445 val = sun4v_mdesc_get_prop_val(md, arc, "cfg-handle"); 446 if (str && strcmp(str, name) == 0 && val == cfg_handle) 447 return (arc); 448 } 449 450 return (-1); 451} 452