1 /* $NetBSD: cbus.c,v 1.8 2022/01/22 11:49:17 thorpej 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/kmem.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 devhandle_t selfh = device_handle(self); 105 for (node = OF_child(va->va_node); node; node = OF_peer(node)) { 106 struct cbus_attach_args ca; 107 char buf[32]; 108 109 bzero(&ca, sizeof(ca)); 110 ca.ca_node = node; 111 if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 112 continue; 113 DPRINTF(CBUSDB_AC, ("cbus_attach() - buf %s\n", buf)); 114 ca.ca_name = buf; 115 ca.ca_bustag = sc->sc_bustag; 116 ca.ca_dmatag = sc->sc_dmatag; 117 prom_getprop(node, "reg", sizeof(*ca.ca_reg), 118 &ca.ca_nreg, (void **)&ca.ca_reg); 119 int rc = cbus_get_channel_endpoint(sc, &ca); 120 DPRINTF(CBUSDB_AC, ("cbus_attach() - cbus_get_channel_endpoint() %d\n", rc)); 121 if ( rc != 0) { 122 continue; 123 } 124 125 config_found(self, &ca, cbus_print, 126 CFARGS(.devhandle = devhandle_from_of(selfh, ca.ca_node))); 127 } 128 } 129 130 int 131 cbus_print(void *aux, const char *name) 132 { 133 struct cbus_attach_args *ca = aux; 134 DPRINTF(CBUSDB_AC, ("cbus_print() name %s\n", name)); 135 136 if (name) 137 printf("\"%s\" at %s", ca->ca_name, name); 138 if (ca->ca_id != -1) 139 printf(" chan 0x%llx", (long long unsigned int)ca->ca_id); 140 return (UNCONF); 141 } 142 143 int 144 cbus_intr_setstate(bus_space_tag_t t, uint64_t devino, uint64_t state) 145 { 146 struct cbus_softc *sc = t->cookie; 147 uint64_t devhandle = sc->sc_devhandle; 148 int err; 149 150 err = hv_vintr_setstate(devhandle, devino, state); 151 if (err != H_EOK) 152 return (-1); 153 154 return (0); 155 } 156 157 int 158 cbus_intr_setenabled(bus_space_tag_t t, uint64_t devino, uint64_t enabled) 159 { 160 struct cbus_softc *sc = t->cookie; 161 uint64_t devhandle = sc->sc_devhandle; 162 int err; 163 164 err = hv_vintr_setenabled(devhandle, devino, enabled); 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, int ihandle, int level, 173 int (*handler)(void *), void *arg, void (*fastvec)(void) /* ignored */) 174 { 175 struct cbus_softc *sc = t->cookie; 176 uint64_t devhandle = sc->sc_devhandle; 177 uint64_t devino = ihandle; 178 struct intrhand *ih; 179 int ino; 180 int err; 181 182 ino = INTINO(ihandle); 183 184 DPRINTF(CBUSDB_INTR, ("cbus_intr_establish(): ino 0x%x\n", ino)); 185 186 ih = intrhand_alloc(); 187 188 ih->ih_ivec = ihandle; 189 ih->ih_fun = handler; 190 ih->ih_arg = arg; 191 ih->ih_pil = level; 192 ih->ih_number = ino; 193 ih->ih_bus = t; 194 195 err = hv_vintr_setenabled(devhandle, devino, INTR_DISABLED); 196 if (err != H_EOK) { 197 printf("hv_vintr_setenabled: %d\n", err); 198 return (NULL); 199 } 200 201 err = hv_vintr_setcookie(devhandle, devino, (vaddr_t)ih); 202 if (err != H_EOK) { 203 printf("hv_vintr_setcookie: %d\n", err); 204 return (NULL); 205 } 206 207 ih->ih_ack = cbus_intr_ack; 208 209 err = hv_vintr_settarget(devhandle, devino, cpus->ci_cpuid); 210 if (err != H_EOK) { 211 printf("hv_vintr_settarget: %d\n", err); 212 return (NULL); 213 } 214 215 /* Clear pending interrupts. */ 216 err = hv_vintr_setstate(devhandle, devino, INTR_IDLE); 217 if (err != H_EOK) { 218 printf("hv_vintr_setstate: %d\n", err); 219 return (NULL); 220 } 221 222 return (ih); 223 } 224 225 void 226 cbus_intr_ack(struct intrhand *ih) 227 { 228 DPRINTF(CBUSDB_INTR, ("cbus_intr_ack()\n")); 229 bus_space_tag_t t = ih->ih_bus; 230 struct cbus_softc *sc = t->cookie; 231 uint64_t devhandle = sc->sc_devhandle; 232 uint64_t devino = ih->ih_number; 233 234 hv_vintr_setstate(devhandle, devino, INTR_IDLE); 235 } 236 237 bus_space_tag_t 238 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent) 239 { 240 struct sparc_bus_space_tag *bt; 241 242 bt = kmem_zalloc(sizeof(*bt), KM_SLEEP); 243 bt->cookie = sc; 244 bt->parent = parent; 245 bt->sparc_bus_map = parent->sparc_bus_map; 246 bt->sparc_intr_establish = cbus_intr_establish; 247 248 return (bt); 249 } 250 251 int 252 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 253 { 254 struct md_header *hdr; 255 struct md_element *elem; 256 const char *name_blk; 257 const char *str; 258 int idx; 259 int arc; 260 261 idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]); 262 if (idx == -1) { 263 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - mdesc_find_child() failed\n")); 264 return (ENOENT); 265 } 266 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx)); 267 268 hdr = (struct md_header *)mdesc; 269 elem = (struct md_element *)(mdesc + sizeof(struct md_header)); 270 name_blk = (char *)mdesc + sizeof(struct md_header) + hdr->node_blk_sz; 271 272 ca->ca_idx = idx; 273 274 ca->ca_id = -1; 275 ca->ca_tx_ino = -1; 276 ca->ca_rx_ino = -1; 277 278 if (strcmp(ca->ca_name, "disk") != 0 && 279 strcmp(ca->ca_name, "network") != 0) { 280 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - neither disk nor network\n")); 281 return (0); 282 } 283 284 for (; elem[idx].tag != 'E'; idx++) { 285 str = name_blk + elem[idx].name_offset; 286 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 287 continue; 288 289 arc = elem[idx].d.val; 290 str = name_blk + elem[arc].name_offset; 291 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - str %s\n", str)); 292 if (strcmp(str, "virtual-device-port") == 0) { 293 idx = arc; 294 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx)); 295 continue; 296 } 297 298 if (strcmp(str, "channel-endpoint") == 0) { 299 ca->ca_id = mdesc_get_prop_val(arc, "id"); 300 ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino"); 301 ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino"); 302 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() " 303 "- tx-ino %lu rx-ino %lu\n", 304 ca->ca_tx_ino, ca->ca_rx_ino)); 305 return (0); 306 } 307 } 308 309 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - exit\n")); 310 311 return (0); 312 } 313