1 /* $OpenBSD: cbus.c,v 1.11 2013/07/16 19:45:37 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 34 struct 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 43 int cbus_match(struct device *, void *, void *); 44 void cbus_attach(struct device *, struct device *, void *); 45 int cbus_print(void *, const char *); 46 47 struct cfattach cbus_ca = { 48 sizeof(struct cbus_softc), cbus_match, cbus_attach 49 }; 50 51 struct cfdriver cbus_cd = { 52 NULL, "cbus", DV_DULL 53 }; 54 55 void *cbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int, 56 int (*)(void *), void *, const char *); 57 void cbus_intr_ack(struct intrhand *); 58 bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t); 59 60 int cbus_get_channel_endpoint(struct cbus_softc *, 61 struct cbus_attach_args *); 62 63 int 64 cbus_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 74 void 75 cbus_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 110 int 111 cbus_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 122 int 123 cbus_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 141 int 142 cbus_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 155 int 156 cbus_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 169 void * 170 cbus_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 if (flags & BUS_INTR_ESTABLISH_MPSAFE) 190 ih->ih_mpsafe = 1; 191 192 intr_establish(ih->ih_pil, ih); 193 ih->ih_ack = cbus_intr_ack; 194 195 err = hv_vintr_settarget(devhandle, devino, cpus->ci_upaid); 196 if (err != H_EOK) { 197 printf("hv_vintr_settarget: %d\n", err); 198 return (NULL); 199 } 200 201 /* Clear pending interrupts. */ 202 err = hv_vintr_setstate(devhandle, devino, INTR_IDLE); 203 if (err != H_EOK) { 204 printf("hv_vintr_setstate: %d\n", err); 205 return (NULL); 206 } 207 208 return (ih); 209 } 210 211 void 212 cbus_intr_ack(struct intrhand *ih) 213 { 214 uint64_t devhandle = CBUS_HANDLE(ih->ih_number); 215 uint64_t devino = CBUS_INO(ih->ih_number); 216 217 hv_vintr_setstate(devhandle, devino, INTR_IDLE); 218 } 219 220 bus_space_tag_t 221 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent) 222 { 223 struct sparc_bus_space_tag *bt; 224 225 bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO); 226 if (bt == NULL) 227 panic("could not allocate cbus bus tag"); 228 229 strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name)); 230 bt->cookie = sc; 231 bt->parent = parent; 232 bt->asi = parent->asi; 233 bt->sasi = parent->sasi; 234 bt->sparc_bus_map = parent->sparc_bus_map; 235 bt->sparc_intr_establish = cbus_intr_establish; 236 237 return (bt); 238 } 239 240 int 241 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 242 { 243 struct md_header *hdr; 244 struct md_element *elem; 245 const char *name_blk; 246 const char *str; 247 int idx; 248 int arc; 249 250 idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]); 251 if (idx == -1) 252 return (ENOENT); 253 254 hdr = (struct md_header *)mdesc; 255 elem = (struct md_element *)(mdesc + sizeof(struct md_header)); 256 name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz; 257 258 ca->ca_idx = idx; 259 260 ca->ca_id = -1; 261 ca->ca_tx_ino = -1; 262 ca->ca_rx_ino = -1; 263 264 if (strcmp(ca->ca_name, "disk") != 0 && 265 strcmp(ca->ca_name, "network") != 0) 266 return (0); 267 268 for (; elem[idx].tag != 'E'; idx++) { 269 str = name_blk + elem[idx].name_offset; 270 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 271 continue; 272 273 arc = elem[idx].d.val; 274 str = name_blk + elem[arc].name_offset; 275 if (strcmp(str, "virtual-device-port") == 0) { 276 idx = arc; 277 continue; 278 } 279 280 if (strcmp(str, "channel-endpoint") == 0) { 281 ca->ca_id = mdesc_get_prop_val(arc, "id"); 282 ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino"); 283 ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino"); 284 return (0); 285 } 286 } 287 288 return (0); 289 } 290