cbus.c revision 1.10
1/* $OpenBSD: cbus.c,v 1.10 2012/11/23 21:55:43 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#define CBUS_HANDLE(x) ((x) & ~0xff) 32#define CBUS_INO(x) ((x) & 0xff) 33 34struct cbus_softc { 35 struct device sc_dv; 36 bus_space_tag_t sc_bustag; 37 bus_dma_tag_t sc_dmatag; 38 39 /* Machine description. */ 40 int sc_idx; 41}; 42 43int cbus_match(struct device *, void *, void *); 44void cbus_attach(struct device *, struct device *, void *); 45int cbus_print(void *, const char *); 46 47struct cfattach cbus_ca = { 48 sizeof(struct cbus_softc), cbus_match, cbus_attach 49}; 50 51struct cfdriver cbus_cd = { 52 NULL, "cbus", DV_DULL 53}; 54 55void *cbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int, 56 int (*)(void *), void *, const char *); 57void cbus_intr_ack(struct intrhand *); 58bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t); 59 60int cbus_get_channel_endpoint(struct cbus_softc *, 61 struct cbus_attach_args *); 62 63int 64cbus_match(struct device *parent, void *match, void *aux) 65{ 66 struct vbus_attach_args *va = aux; 67 68 if (strcmp(va->va_name, "channel-devices") == 0) 69 return (1); 70 71 return (0); 72} 73 74void 75cbus_attach(struct device *parent, struct device *self, void *aux) 76{ 77 struct cbus_softc *sc = (struct cbus_softc *)self; 78 struct vbus_attach_args *va = aux; 79 int node; 80 81 sc->sc_bustag = cbus_alloc_bus_tag(sc, va->va_bustag); 82 sc->sc_dmatag = va->va_dmatag; 83 84 printf("\n"); 85 86 sc->sc_idx = mdesc_find(va->va_name, va->va_reg[0]); 87 if (sc->sc_idx == -1) 88 return; 89 90 for (node = OF_child(va->va_node); node; node = OF_peer(node)) { 91 struct cbus_attach_args ca; 92 char buf[32]; 93 94 bzero(&ca, sizeof(ca)); 95 ca.ca_node = node; 96 if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 97 continue; 98 ca.ca_name = buf; 99 ca.ca_bustag = sc->sc_bustag; 100 ca.ca_dmatag = sc->sc_dmatag; 101 getprop(node, "reg", sizeof(*ca.ca_reg), 102 &ca.ca_nreg, (void **)&ca.ca_reg); 103 if (cbus_get_channel_endpoint(sc, &ca) != 0) 104 continue; 105 106 config_found(self, &ca, cbus_print); 107 } 108} 109 110int 111cbus_print(void *aux, const char *name) 112{ 113 struct cbus_attach_args *ca = aux; 114 115 if (name) 116 printf("\"%s\" at %s", ca->ca_name, name); 117 if (ca->ca_id != -1) 118 printf(" chan 0x%llx", ca->ca_id); 119 return (UNCONF); 120} 121 122int 123cbus_intr_map(int node, int ino, uint64_t *sysino) 124{ 125 int parent; 126 int reg; 127 int err; 128 129 parent = OF_parent(node); 130 if (OF_getprop(parent, "reg", ®, sizeof(reg)) != sizeof(reg)) 131 return (-1); 132 133 *sysino = CBUS_HANDLE(reg) | CBUS_INO(ino); 134 err = hv_vintr_setcookie(reg, ino, *sysino); 135 if (err != H_EOK) 136 return (-1); 137 138 return (0); 139} 140 141int 142cbus_intr_setstate(uint64_t sysino, uint64_t state) 143{ 144 uint64_t devhandle = CBUS_HANDLE(sysino); 145 uint64_t devino = CBUS_INO(sysino); 146 int err; 147 148 err = hv_vintr_setstate(devhandle, devino, state); 149 if (err != H_EOK) 150 return (-1); 151 152 return (0); 153} 154 155int 156cbus_intr_setenabled(uint64_t sysino, uint64_t enabled) 157{ 158 uint64_t devhandle = CBUS_HANDLE(sysino); 159 uint64_t devino = CBUS_INO(sysino); 160 int err; 161 162 err = hv_vintr_setenabled(devhandle, devino, enabled); 163 if (err != H_EOK) 164 return (-1); 165 166 return (0); 167} 168 169void * 170cbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle, 171 int level, int flags, int (*handler)(void *), void *arg, const char *what) 172{ 173 uint64_t devhandle = CBUS_HANDLE(ihandle); 174 uint64_t devino = CBUS_INO(ihandle); 175 struct intrhand *ih; 176 int err; 177 178 ih = bus_intr_allocate(t0, handler, arg, ihandle, level, 179 NULL, NULL, what); 180 if (ih == NULL) 181 return (NULL); 182 183 err = hv_vintr_setenabled(devhandle, devino, INTR_DISABLED); 184 if (err != H_EOK) { 185 printf("hv_vintr_setenabled: %d\n", err); 186 return (NULL); 187 } 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 return (ih); 206} 207 208void 209cbus_intr_ack(struct intrhand *ih) 210{ 211 uint64_t devhandle = CBUS_HANDLE(ih->ih_number); 212 uint64_t devino = CBUS_INO(ih->ih_number); 213 214 hv_vintr_setstate(devhandle, devino, INTR_IDLE); 215} 216 217bus_space_tag_t 218cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent) 219{ 220 struct sparc_bus_space_tag *bt; 221 222 bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO); 223 if (bt == NULL) 224 panic("could not allocate cbus bus tag"); 225 226 strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name)); 227 bt->cookie = sc; 228 bt->parent = parent; 229 bt->asi = parent->asi; 230 bt->sasi = parent->sasi; 231 bt->sparc_bus_map = parent->sparc_bus_map; 232 bt->sparc_intr_establish = cbus_intr_establish; 233 234 return (bt); 235} 236 237int 238cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 239{ 240 struct md_header *hdr; 241 struct md_element *elem; 242 const char *name_blk; 243 const char *str; 244 int idx; 245 int arc; 246 247 idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]); 248 if (idx == -1) 249 return (ENOENT); 250 251 hdr = (struct md_header *)mdesc; 252 elem = (struct md_element *)(mdesc + sizeof(struct md_header)); 253 name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz; 254 255 ca->ca_idx = idx; 256 257 ca->ca_id = -1; 258 ca->ca_tx_ino = -1; 259 ca->ca_rx_ino = -1; 260 261 if (strcmp(ca->ca_name, "disk") != 0 && 262 strcmp(ca->ca_name, "network") != 0) 263 return (0); 264 265 for (; elem[idx].tag != 'E'; idx++) { 266 str = name_blk + elem[idx].name_offset; 267 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 268 continue; 269 270 arc = elem[idx].d.val; 271 str = name_blk + elem[arc].name_offset; 272 if (strcmp(str, "virtual-device-port") == 0) { 273 idx = arc; 274 continue; 275 } 276 277 if (strcmp(str, "channel-endpoint") == 0) { 278 ca->ca_id = mdesc_get_prop_val(arc, "id"); 279 ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino"); 280 ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino"); 281 return (0); 282 } 283 } 284 285 return (0); 286} 287