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