1 /* $NetBSD: cbus.c,v 1.10 2026/10/19 18:59:57 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/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 = sun4v_intr_setenabled(devhandle, devino, INTR_DISABLED); 196 if (err != H_EOK) 197 panic("%s: sun4v_intr_setenabled(%lx, %lu) failed - err = %d\n", 198 __FUNCTION__, 199 (long unsigned int)devhandle, (long unsigned int)devino, err); 200 201 err = sun4v_intr_setcookie(devhandle, devino, (vaddr_t)ih); 202 if (err != H_EOK) 203 panic("%s: sun4v_intr_setcookie(%#lx, %#lx, %#lx) failed - err = %d\n", 204 __FUNCTION__, 205 (long unsigned int)devhandle,(long unsigned int)devino, 206 (long unsigned int)ih, err); 207 208 ih->ih_ack = cbus_intr_ack; 209 210 err = sun4v_intr_settarget(devhandle, devino, cpus->ci_cpuid); 211 if (err != H_EOK) 212 panic("%s: sun4v_intr_settarget(%#lx, %#lx, %d) failed - err = %d\n", 213 __FUNCTION__, 214 (long unsigned int)devhandle, (long unsigned int)devino, 215 cpus->ci_cpuid, err); 216 217 /* Clear pending interrupts. */ 218 err = sun4v_intr_setstate(devhandle, devino, INTR_IDLE); 219 if (err != H_EOK) 220 panic("%s: sun4v_intr_setenabled(%lx, %lu) failed - err = %d\n", 221 __FUNCTION__, 222 (long unsigned int)devhandle, (long unsigned int)devino, err); 223 224 return (ih); 225 } 226 227 void 228 cbus_intr_ack(struct intrhand *ih) 229 { 230 DPRINTF(CBUSDB_INTR, ("cbus_intr_ack()\n")); 231 bus_space_tag_t t = ih->ih_bus; 232 struct cbus_softc *sc = t->cookie; 233 uint64_t devhandle = sc->sc_devhandle; 234 uint64_t devino = ih->ih_number; 235 236 int err; 237 err = sun4v_intr_setstate(devhandle, devino, INTR_IDLE); 238 if (err != H_EOK) 239 panic("%s(%#lx, %lu, INTR_IDLE) failed - err = %d\n", 240 __func__, (long unsigned int)devhandle, (long unsigned int)devino, err); 241 } 242 243 bus_space_tag_t 244 cbus_alloc_bus_tag(struct cbus_softc *sc, bus_space_tag_t parent) 245 { 246 struct sparc_bus_space_tag *bt; 247 248 bt = kmem_zalloc(sizeof(*bt), KM_SLEEP); 249 bt->cookie = sc; 250 bt->parent = parent; 251 bt->sparc_bus_map = parent->sparc_bus_map; 252 bt->sparc_intr_establish = cbus_intr_establish; 253 254 return (bt); 255 } 256 257 int 258 cbus_get_channel_endpoint(struct cbus_softc *sc, struct cbus_attach_args *ca) 259 { 260 struct md_header *hdr; 261 struct md_element *elem; 262 const char *name_blk; 263 const char *str; 264 int idx; 265 int arc; 266 267 idx = mdesc_find_child(sc->sc_idx, ca->ca_name, ca->ca_reg[0]); 268 if (idx == -1) { 269 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - mdesc_find_child() failed\n")); 270 return (ENOENT); 271 } 272 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx)); 273 274 hdr = (struct md_header *)mdesc; 275 elem = (struct md_element *)(mdesc + sizeof(struct md_header)); 276 name_blk = (char *)mdesc + sizeof(struct md_header) + hdr->node_blk_sz; 277 278 ca->ca_idx = idx; 279 280 ca->ca_id = -1; 281 ca->ca_tx_ino = -1; 282 ca->ca_rx_ino = -1; 283 284 if (strcmp(ca->ca_name, "disk") != 0 && 285 strcmp(ca->ca_name, "network") != 0) { 286 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - neither disk nor network\n")); 287 return (0); 288 } 289 290 for (; elem[idx].tag != 'E'; idx++) { 291 str = name_blk + elem[idx].name_offset; 292 if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) 293 continue; 294 295 arc = elem[idx].d.val; 296 str = name_blk + elem[arc].name_offset; 297 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - str %s\n", str)); 298 if (strcmp(str, "virtual-device-port") == 0) { 299 idx = arc; 300 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - idx %d\n", idx)); 301 continue; 302 } 303 304 if (strcmp(str, "channel-endpoint") == 0) { 305 ca->ca_id = mdesc_get_prop_val(arc, "id"); 306 ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino"); 307 ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino"); 308 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() " 309 "- tx-ino %lu rx-ino %lu\n", 310 ca->ca_tx_ino, ca->ca_rx_ino)); 311 return (0); 312 } 313 } 314 315 DPRINTF(CBUSDB_AC, ("cbus_get_channel_endpoint() - exit\n")); 316 317 return (0); 318 } 319