1 /* $NetBSD: cbus.c,v 1.2 2016/07/18 19:32:44 palle Exp $ */ 2 /* $OpenBSD: cbus.c,v 1.15 2015/09/27 11:29:20 kettenis Exp $ */ 3 /* 4 * Copyright (c) 2008 Mark Kettenis 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #include <sys/systm.h> 23 24 #include <machine/autoconf.h> 25 #include <machine/hypervisor.h> 26 #include <machine/openfirm.h> 27 #include <machine/mdesc.h> 28 29 #include <sparc64/dev/cbusvar.h> 30 #include <sparc64/dev/vbusvar.h> 31 32 #include <sparc64/dev/iommureg.h> 33 34 #ifdef DEBUG 35 #define CBUSDB_AC 0x01 36 #define CBUSDB_INTR 0x02 37 int cbus_debug = 0x00; 38 #define DPRINTF(l, s) do { if (cbus_debug & l) printf s; } while (0) 39 #else 40 #define DPRINTF(l, s) 41 #endif 42 43 struct cbus_softc { 44 device_t sc_dv; 45 bus_space_tag_t sc_bustag; 46 bus_dma_tag_t sc_dmatag; 47 48 uint64_t sc_devhandle; 49 50 /* Machine description. */ 51 int sc_idx; 52 }; 53 54 int cbus_match(device_t, cfdata_t, void *); 55 void cbus_attach(device_t, device_t, void *); 56 int cbus_print(void *, const char *); 57 58 CFATTACH_DECL_NEW(cbus, sizeof(struct cbus_softc), 59 cbus_match, cbus_attach, NULL, NULL); 60 61 62 void *cbus_intr_establish(bus_space_tag_t, int, int, 63 int (*)(void *), void *, void (*)(void)); 64 void cbus_intr_ack(struct intrhand *); 65 bus_space_tag_t cbus_alloc_bus_tag(struct cbus_softc *, bus_space_tag_t); 66 67 int cbus_get_channel_endpoint(struct cbus_softc *, 68 struct cbus_attach_args *); 69 70 int 71 cbus_match(device_t parent, cfdata_t match, void *aux) 72 { 73 struct vbus_attach_args *va = aux; 74 75 if (strcmp(va->va_name, "channel-devices") == 0) 76 return (1); 77 78 return (0); 79 } 80 81 void 82 cbus_attach(device_t parent, device_t self, void *aux) 83 { 84 struct cbus_softc *sc = device_private(self); 85 struct vbus_attach_args *va = aux; 86 int node; 87 int reg; 88 89 sc->sc_bustag = cbus_alloc_bus_tag(sc, va->va_bustag); 90 sc->sc_dmatag = va->va_dmatag; 91 92 if (OF_getprop(va->va_node, "reg", ®, sizeof(reg)) != sizeof(reg)) 93 return; 94 sc->sc_devhandle = reg; 95 96 printf("\n"); 97 98 sc->sc_idx = mdesc_find(va->va_name, va->va_reg[0]); 99 if (sc->sc_idx == -1) { 100 DPRINTF(CBUSDB_AC, ("cbus_attach() - no idx\n")); 101 return; 102 } 103 104 for (node = OF_child(va->va_node); node; node = OF_peer(node)) { 105 struct cbus_attach_args ca; 106 char buf[32]; 107 108 bzero(&ca, sizeof(ca)); 109 ca.ca_node = node; 110 if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 111 continue; 112 DPRINTF(CBUSDB_AC, ("cbus_attach() - buf %s\n", buf)); 113 ca.ca_name = buf; 114 ca.ca_bustag = sc->sc_bustag; 115 ca.ca_dmatag = sc->sc_dmatag; 116 prom_getprop(node, "reg", sizeof(*ca.ca_reg), 117 &ca.ca_nreg, (void **)&ca.ca_reg); 118 int rc = cbus_get_channel_endpoint(sc, &ca); 119 DPRINTF(CBUSDB_AC, ("cbus_attach() - cbus_get_channel_endpoint() %d\n", rc)); 120 if ( rc != 0) { 121 continue; 122 } 123 124 config_found(self, &ca, cbus_print); 125 126 } 127 } 128 129 int 130 cbus_print(void *aux, const char *name) 131 { 132 struct cbus_attach_args *ca = aux; 133 DPRINTF(CBUSDB_AC, ("cbus_print() name %s\n", name)); 134 135 if (name) 136 printf("\"%s\" at %s", ca->ca_name, name); 137 if (ca->ca_id != -1) 138 printf(" chan 0x%llx", (long long unsigned int)ca->ca_id); 139 return (UNCONF); 140 } 141 142 int 143 cbus_intr_setstate(bus_space_tag_t t, uint64_t devino, uint64_t state) 144 { 145 struct cbus_softc *sc = t->cookie; 146 uint64_t devhandle = sc->sc_devhandle; 147 int err; 148 149 err = hv_vintr_setstate(devhandle, devino, state); 150 if (err != H_EOK) 151 return (-1); 152 153 return (0); 154 } 155 156 int 157 cbus_intr_setenabled(bus_space_tag_t t, uint64_t devino, uint64_t enabled) 158 { 159 struct cbus_softc *sc = t->cookie; 160 uint64_t devhandle = sc->sc_devhandle; 161 int err; 162 163 err = hv_vintr_setenabled(devhandle, devino, enabled); 164 if (err != H_EOK) 165 return (-1); 166 167 return (0); 168 } 169 170 void * 171 cbus_intr_establish(bus_space_tag_t t, int ihandle, int level, 172 int (*handler)(void *), void *arg, void (*fastvec)(void) /* ignored */) 173 { 174 struct cbus_softc *sc = t->cookie; 175 uint64_t devhandle = sc->sc_devhandle; 176 uint64_t devino = ihandle; 177 struct intrhand *ih; 178 int ino; 179 int err; 180 181 ino = INTINO(ihandle); 182 183 DPRINTF(CBUSDB_INTR, ("cbus_intr_establish(): ino 0x%x\n", ino)); 184 185 ih = intrhand_alloc(); 186 187 ih->ih_ivec = ihandle; 188 ih->ih_fun = handler; 189 ih->ih_arg = arg; 190 ih->ih_pil = level; 191 ih->ih_number = ino; 192 ih->ih_bus = t; 193 194 err = hv_vintr_setenabled(devhandle, devino, INTR_DISABLED); 195 if (err != H_EOK) { 196 printf("hv_vintr_setenabled: %d\n", err); 197 return (NULL); 198 } 199 200 err = hv_vintr_setcookie(devhandle, devino, (vaddr_t)ih); 201 if (err != H_EOK) { 202 printf("hv_vintr_setcookie: %d\n", err); 203 return (NULL); 204 } 205 206 ih->ih_ack = cbus_intr_ack; 207 208 err = hv_vintr_settarget(devhandle, devino, cpus->ci_cpuid); 209 if (err != H_EOK) { 210 printf("hv_vintr_settarget: %d\n", err); 211 return (NULL); 212 } 213 214 /* Clear pending interrupts. */ 215 err = hv_vintr_setstate(devhandle, devino, INTR_IDLE); 216 if (err != H_EOK) { 217 printf("hv_vintr_setstate: %d\n", err); 218 return (NULL); 219 } 220 221 return (ih); 222 } 223 224 void 225 cbus_intr_ack(struct intrhand *ih) 226 { 227 DPRINTF(CBUSDB_INTR, ("cbus_intr_ack()\n")); 228 bus_space_tag_t t = ih->ih_bus; 229 struct cbus_softc *sc = t->cookie; 230 uint64_t devhandle = sc->sc_devhandle; 231 uint64_t devino = ih->ih_number; 232 233 hv_vintr_setstate(devhandle, devino, INTR_IDLE); 234 } 235 236 bus_space_tag_t 237 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent) 238 { 239 struct sparc_bus_space_tag *bt; 240 241 bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO); 242 if (bt == NULL) 243 panic("could not allocate cbus bus tag"); 244 245 bt->cookie = sc; 246 bt->parent = parent; 247 bt->sparc_bus_map = parent->sparc_bus_map; 248 bt->sparc_intr_establish = cbus_intr_establish; 249 250 return (bt); 251 } 252 253 int 254 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 255 { 256 struct md_header *hdr; 257 struct md_element *elem; 258 const char *name_blk; 259 const char *str; 260 int idx; 261 int arc; 262 263 idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]); 264 if (idx == -1) { 265 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - mdesc_find_child() failed\n")); 266 return (ENOENT); 267 } 268 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx)); 269 270 hdr = (struct md_header *)mdesc; 271 elem = (struct md_element *)(mdesc + sizeof(struct md_header)); 272 name_blk = (char *)mdesc + sizeof(struct md_header) + hdr->node_blk_sz; 273 274 ca->ca_idx = idx; 275 276 ca->ca_id = -1; 277 ca->ca_tx_ino = -1; 278 ca->ca_rx_ino = -1; 279 280 if (strcmp(ca->ca_name, "disk") != 0 && 281 strcmp(ca->ca_name, "network") != 0) { 282 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - neither disk nor network\n")); 283 return (0); 284 } 285 286 for (; elem[idx].tag != 'E'; idx++) { 287 str = name_blk + elem[idx].name_offset; 288 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 289 continue; 290 291 arc = elem[idx].d.val; 292 str = name_blk + elem[arc].name_offset; 293 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - str %s\n", str)); 294 if (strcmp(str, "virtual-device-port") == 0) { 295 idx = arc; 296 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx)); 297 continue; 298 } 299 300 if (strcmp(str, "channel-endpoint") == 0) { 301 ca->ca_id = mdesc_get_prop_val(arc, "id"); 302 ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino"); 303 ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino"); 304 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() " 305 "- tx-ino %lu rx-ino %lu\n", 306 ca->ca_tx_ino, ca->ca_rx_ino)); 307 return (0); 308 } 309 } 310 311 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - exit\n")); 312 313 return (0); 314 } 315