vbus.c revision 1.10
1/* $OpenBSD: vbus.c,v 1.10 2017/12/22 15:52:36 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> 30extern todr_chip_handle_t todr_handle; 31 32struct 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 40int vbus_cmp_cells(int *, int *, int *, int); 41int vbus_match(struct device *, void *, void *); 42void vbus_attach(struct device *, struct device *, void *); 43int vbus_print(void *, const char *); 44 45struct cfattach vbus_ca = { 46 sizeof(struct vbus_softc), vbus_match, vbus_attach 47}; 48 49struct cfdriver vbus_cd = { 50 NULL, "vbus", DV_DULL 51}; 52 53void *vbus_intr_establish(bus_space_tag_t, bus_space_tag_t, int, int, int, 54 int (*)(void *), void *, const char *); 55void vbus_intr_ack(struct intrhand *); 56bus_space_tag_t vbus_alloc_bus_tag(struct vbus_softc *, bus_space_tag_t); 57 58int 59vbus_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 69void 70vbus_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 108int 109vbus_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 */ 122int 123vbus_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 134int 135vbus_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 193void * 194vbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle, 195 int level, int flags, int (*handler)(void *), void *arg, const char *what) 196{ 197 struct vbus_softc *sc = t->cookie; 198 uint64_t devhandle = sc->sc_devhandle; 199 uint64_t sysino = INTVEC(ihandle); 200 struct intrhand *ih; 201 int err; 202 203 ih = bus_intr_allocate(t0, handler, arg, ihandle, level, 204 NULL, NULL, what); 205 if (ih == NULL) 206 return (NULL); 207 208 if (flags & BUS_INTR_ESTABLISH_MPSAFE) 209 ih->ih_mpsafe = 1; 210 211 err = sun4v_intr_setcookie(devhandle, sysino, (vaddr_t)ih); 212 if (err != H_EOK) 213 return (NULL); 214 215 intr_establish(ih->ih_pil, ih); 216 ih->ih_ack = vbus_intr_ack; 217 218 err = sun4v_intr_settarget(devhandle, sysino, ih->ih_cpu->ci_upaid); 219 if (err != H_EOK) 220 return (NULL); 221 222 /* Clear pending interrupts. */ 223 err = sun4v_intr_setstate(devhandle, sysino, INTR_IDLE); 224 if (err != H_EOK) 225 return (NULL); 226 227 err = sun4v_intr_setenabled(devhandle, sysino, INTR_ENABLED); 228 if (err != H_EOK) 229 return (NULL); 230 231 return (ih); 232} 233 234void 235vbus_intr_ack(struct intrhand *ih) 236{ 237 bus_space_tag_t t = ih->ih_bus; 238 struct vbus_softc *sc = t->cookie; 239 uint64_t devhandle = sc->sc_devhandle; 240 uint64_t sysino = INTVEC(ih->ih_number); 241 242 sun4v_intr_setstate(devhandle, sysino, INTR_IDLE); 243} 244 245bus_space_tag_t 246vbus_alloc_bus_tag(struct vbus_softc *sc, bus_space_tag_t parent) 247{ 248 struct sparc_bus_space_tag *bt; 249 250 bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO); 251 if (bt == NULL) 252 panic("could not allocate vbus bus tag"); 253 254 strlcpy(bt->name, sc->sc_dv.dv_xname, sizeof(bt->name)); 255 bt->cookie = sc; 256 bt->parent = parent; 257 bt->asi = parent->asi; 258 bt->sasi = parent->sasi; 259 bt->sparc_bus_map = parent->sparc_bus_map; 260 bt->sparc_intr_establish = vbus_intr_establish; 261 262 return (bt); 263} 264