1 /* $NetBSD: vbus.c,v 1.12 2026/10/24 21:28:16 palle Exp $ */ 2 /* $OpenBSD: vbus.c,v 1.8 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/kmem.h> 23 #include <sys/systm.h> 24 25 #include <machine/autoconf.h> 26 #include <machine/hypervisor.h> 27 #include <machine/openfirm.h> 28 29 #include <sparc64/dev/vbusvar.h> 30 31 #include <sparc64/dev/iommureg.h> 32 33 #include <dev/clock_subr.h> 34 extern todr_chip_handle_t todr_handle; 35 36 #ifdef DEBUG 37 #define VBUS_INTR 0x01 38 int vbus_debug = 0x00|VBUS_INTR; 39 #define DPRINTF(l, s) do { if (vbus_debug & l) printf s; } while (0) 40 #else 41 #define DPRINTF(l, s) 42 #endif 43 44 struct vbus_softc { 45 device_t sc_dv; 46 bus_space_tag_t sc_bustag; 47 bus_dma_tag_t sc_dmatag; 48 uint64_t sc_devhandle; 49 }; 50 int vbus_cmp_cells(int *, int *, int *, int); 51 int vbus_match(device_t, cfdata_t, void *); 52 void vbus_attach(device_t, device_t, void *); 53 int vbus_print(void *, const char *); 54 55 CFATTACH_DECL_NEW(vbus, sizeof(struct vbus_softc), 56 vbus_match, vbus_attach, NULL, NULL); 57 58 void *vbus_intr_establish(bus_space_tag_t, int, int, 59 int (*)(void *), void *, void (*)(void)); 60 void vbus_intr_ack(struct intrhand *); 61 bus_space_tag_t vbus_alloc_bus_tag(struct vbus_softc *, bus_space_tag_t); 62 63 int 64 vbus_match(device_t parent, cfdata_t match, void *aux) 65 { 66 struct mainbus_attach_args *ma = aux; 67 68 if (strcmp(ma->ma_name, "virtual-devices") == 0) 69 return (1); 70 71 return (0); 72 } 73 74 void 75 vbus_attach(device_t parent, device_t self, void *aux) 76 { 77 struct vbus_softc *sc = device_private(self); 78 struct mainbus_attach_args *ma = aux; 79 int node; 80 81 sc->sc_bustag = vbus_alloc_bus_tag(sc, ma->ma_bustag); 82 sc->sc_dmatag = ma->ma_dmatag; 83 sc->sc_devhandle = (ma->ma_reg[0].ur_paddr >> 32) & 0x0fffffff; 84 printf("\n"); 85 86 devhandle_t selfh = device_handle(self); 87 for (node = OF_child(ma->ma_node); node; node = OF_peer(node)) { 88 struct vbus_attach_args va; 89 char buf[32]; 90 91 bzero(&va, sizeof(va)); 92 va.va_node = node; 93 if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 94 continue; 95 va.va_name = buf; 96 va.va_bustag = sc->sc_bustag; 97 va.va_dmatag = sc->sc_dmatag; 98 prom_getprop(node, "reg", sizeof(*va.va_reg), 99 &va.va_nreg, (void **)&va.va_reg); 100 prom_getprop(node, "interrupts", sizeof(*va.va_intr), 101 &va.va_nintr, (void **)&va.va_intr); 102 config_found(self, &va, vbus_print, 103 CFARGS(.devhandle = prom_node_to_devhandle(selfh, 104 va.va_node))); 105 } 106 107 struct vbus_attach_args va; 108 bzero(&va, sizeof(va)); 109 va.va_name = "rtc"; 110 config_found(self, &va, vbus_print, CFARGS_NONE); 111 112 } 113 114 int 115 vbus_print(void *aux, const char *name) 116 { 117 struct vbus_attach_args *va = aux; 118 119 if (name) 120 printf("\"%s\" at %s", va->va_name, name); 121 return (UNCONF); 122 } 123 124 /* 125 * Compare a sequence of cells with a mask, return 1 if they match and 126 * 0 if they don't. 127 */ 128 int 129 vbus_cmp_cells(int *cell1, int *cell2, int *mask, int ncells) 130 { 131 int i; 132 133 for (i = 0; i < ncells; i++) { 134 if (((cell1[i] ^ cell2[i]) & mask[i]) != 0) 135 return (0); 136 } 137 return (1); 138 } 139 140 int 141 vbus_intr_map(int node, int ino, uint64_t *sysino) 142 { 143 int *imap = NULL, nimap; 144 int *reg = NULL, nreg; 145 int *imap_mask; 146 int parent; 147 int address_cells, interrupt_cells; 148 uint64_t devhandle; 149 uint64_t devino; 150 int len; 151 int err; 152 153 DPRINTF(VBUS_INTR, ("vbus_intr_map(): ino 0x%x\n", ino)); 154 155 parent = OF_parent(node); 156 157 address_cells = prom_getpropint(parent, "#address-cells", 2); 158 interrupt_cells = prom_getpropint(parent, "#interrupt-cells", 1); 159 KASSERT(interrupt_cells == 1); 160 161 len = OF_getproplen(parent, "interrupt-map-mask"); 162 if (len < (address_cells + interrupt_cells) * sizeof(int)) 163 return (-1); 164 imap_mask = malloc(len, M_DEVBUF, M_WAITOK); 165 if (OF_getprop(parent, "interrupt-map-mask", imap_mask, len) != len) 166 goto out; 167 168 if (prom_getprop(parent, "interrupt-map", sizeof(int), &nimap, (void **)&imap)) 169 panic("vbus: can't get interrupt-map"); 170 171 if (prom_getprop(node, "reg", sizeof(*reg), &nreg, (void **)®)) 172 panic("vbus: can't get reg"); 173 if (nreg < address_cells) 174 goto out; 175 176 while (nimap >= address_cells + interrupt_cells + 2) { 177 if (vbus_cmp_cells(imap, reg, imap_mask, address_cells) && 178 vbus_cmp_cells(&imap[address_cells], &ino, 179 &imap_mask[address_cells], interrupt_cells)) { 180 node = imap[address_cells + interrupt_cells]; 181 devino = imap[address_cells + interrupt_cells + 1]; 182 183 free(reg, M_DEVBUF); 184 reg = NULL; 185 186 if (prom_getprop(node, "reg", sizeof(*reg), &nreg, (void **)®)) 187 panic("vbus: can't get reg"); 188 189 devhandle = reg[0] & 0x0fffffff; 190 191 err = sun4v_intr_devino_to_sysino(devhandle, devino, sysino); 192 if (err != H_EOK) 193 goto out; 194 195 KASSERT(*sysino == INTVEC(*sysino)); 196 free(imap_mask, M_DEVBUF); 197 return (0); 198 } 199 imap += address_cells + interrupt_cells + 2; 200 nimap -= address_cells + interrupt_cells + 2; 201 } 202 203 out: 204 free(imap_mask, M_DEVBUF); 205 return (-1); 206 } 207 208 void * 209 vbus_intr_establish(bus_space_tag_t t, int ihandle, int level, 210 int (*handler)(void *), void *arg, void (*fastvec)(void) /* ignored */) 211 { 212 struct vbus_softc *sc = t->cookie; 213 uint64_t devhandle = sc->sc_devhandle; 214 uint64_t sysino = INTVEC(ihandle); 215 struct intrhand *ih; 216 int ino; 217 int err; 218 219 DPRINTF(VBUS_INTR, ("vbus_intr_establish()\n")); 220 221 ino = INTINO(ihandle); 222 223 ih = intrhand_alloc(); 224 225 ih->ih_ivec = ihandle; 226 ih->ih_fun = handler; 227 ih->ih_arg = arg; 228 ih->ih_pil = level; 229 ih->ih_number = ino; 230 ih->ih_pending = 0; 231 232 intr_establish(ih->ih_pil, level != IPL_VM, ih); 233 ih->ih_ack = vbus_intr_ack; 234 235 err = sun4v_intr_setcookie(devhandle, sysino, (vaddr_t)ih); 236 if (err != H_EOK) 237 panic("%s: sun4v_intr_setcookie(%#lx, %#lx, %#lx) failed - err = %d\n", 238 __FUNCTION__, 239 (long unsigned int)devhandle, (long unsigned int)sysino, 240 (long unsigned int)ih, err); 241 242 err = sun4v_intr_settarget(devhandle, sysino, cpus->ci_cpuid); 243 if (err != H_EOK) 244 panic("%s: sun4v_intr_settarget(%#lx, %#lx, %d) failed - err = %d\n", 245 __FUNCTION__, 246 (long unsigned int)devhandle, (long unsigned int)sysino, 247 cpus->ci_cpuid, err); 248 249 /* Clear pending interrupts. */ 250 err = sun4v_intr_setstate(devhandle, sysino, INTR_IDLE); 251 if (err != H_EOK) { 252 panic("%s: sun4v_intr_setstate(%lx, %lu, INTR_IDLE) failed - err = %d\n", 253 __FUNCTION__, 254 (long unsigned int)devhandle, (long unsigned int)sysino, err); 255 } 256 257 err = sun4v_intr_setenabled(devhandle, sysino, INTR_ENABLED); 258 if (err != H_EOK) { 259 panic("%s: sun4v_intr_setenabled(%lx, %lu) failed - err = %d\n", 260 __FUNCTION__, 261 (long unsigned int)devhandle, (long unsigned int)sysino, err); 262 } 263 264 return (ih); 265 } 266 267 void 268 vbus_intr_ack(struct intrhand *ih) 269 { 270 /* Drivers explicitly ack interrupts. */ 271 } 272 273 bus_space_tag_t 274 vbus_alloc_bus_tag(struct vbus_softc *sc, bus_space_tag_t parent) 275 { 276 struct sparc_bus_space_tag *bt; 277 278 bt = kmem_zalloc(sizeof(*bt), KM_SLEEP); 279 bt->cookie = sc; 280 bt->parent = parent; 281 bt->sparc_bus_map = parent->sparc_bus_map; 282 bt->sparc_intr_establish = vbus_intr_establish; 283 284 return (bt); 285 } 286