1 /* $NetBSD: vme.c,v 1.31 2024/04/24 02:27:33 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1999 5 * Matthias Drochner. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: vme.c,v 1.31 2024/04/24 02:27:33 thorpej Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/device.h> 37 #include <sys/malloc.h> 38 #include <sys/vmem.h> 39 #include <sys/bus.h> 40 41 #include <dev/vme/vmereg.h> 42 #include <dev/vme/vmevar.h> 43 44 static void vme_extractlocators(int*, struct vme_attach_args *); 45 static int vmeprint(struct vme_attach_args *, char *); 46 static int vmesubmatch1(device_t, cfdata_t, const int *, void *); 47 static int vmesubmatch(device_t, cfdata_t, const int *, void *); 48 int vmematch(device_t, cfdata_t, void *); 49 void vmeattach(device_t, device_t, void *); 50 static vmem_t *vme_select_map(struct vmebus_softc*, vme_am_t); 51 52 #define VME_SLAVE_DUMMYDRV "vme_slv" 53 54 #define VME_NUMCFRANGES 3 /* cf. "files.vme" */ 55 56 CFATTACH_DECL_NEW(vme, sizeof(struct vmebus_softc), 57 vmematch, vmeattach, NULL, NULL); 58 59 const struct cfattach vme_slv_ca = { 60 0 /* never used */ 61 }; 62 63 static void 64 vme_extractlocators(int *loc, struct vme_attach_args *aa) 65 { 66 int i = 0; 67 68 /* XXX can't use constants in locators.h this way */ 69 70 while (i < VME_NUMCFRANGES && i < VME_MAXCFRANGES && 71 loc[i] != -1) { 72 aa->r[i].offset = (vme_addr_t)loc[i]; 73 aa->r[i].size = (vme_size_t)loc[3 + i]; 74 aa->r[i].am = (vme_am_t)loc[6 + i]; 75 i++; 76 } 77 aa->numcfranges = i; 78 aa->ilevel = loc[9]; 79 aa->ivector = loc[10]; 80 } 81 82 static int 83 vmeprint(struct vme_attach_args *v, char *dummy) 84 { 85 int i; 86 87 for (i = 0; i < v->numcfranges; i++) { 88 aprint_normal(" addr %x", v->r[i].offset); 89 if (v->r[i].size != -1) 90 aprint_normal("-%x", v->r[i].offset + v->r[i].size - 1); 91 if (v->r[i].am != -1) 92 aprint_normal(" am %02x", v->r[i].am); 93 } 94 if (v->ilevel != -1) { 95 aprint_normal(" irq %d", v->ilevel); 96 if (v->ivector != -1) 97 aprint_normal(" vector %x", v->ivector); 98 } 99 return (UNCONF); 100 } 101 102 /* 103 * This looks for a (dummy) vme device "VME_SLAVE_DUMMYDRV". 104 * A callback provided by the bus's parent is called for every such 105 * entry in the config database. 106 * This is a special hack allowing to communicate the address settings 107 * of the VME master's slave side to its driver via the normal 108 * configuration mechanism. 109 * Needed in following cases: 110 * -DMA windows are hardware settable but not readable by software 111 * (driver gets offsets for DMA address calculations this way) 112 * -DMA windows are software settable, but not persistent 113 * (hardware is set up from config file entry) 114 * -other adapter VME slave ranges which should be kept track of 115 * for address space accounting 116 * In any case, the adapter driver must get the data before VME 117 * devices are attached. 118 */ 119 static int 120 vmesubmatch1(device_t bus, cfdata_t dev, const int *ldesc, void *aux) 121 { 122 struct vmebus_softc *sc = device_private(bus); 123 struct vme_attach_args v; 124 125 if (strcmp(dev->cf_name, VME_SLAVE_DUMMYDRV)) 126 return (0); 127 128 vme_extractlocators(dev->cf_loc, &v); 129 130 v.va_vct = sc->sc_vct; /* for space allocation */ 131 132 (*sc->slaveconfig)(device_parent(bus), &v); 133 return (0); 134 } 135 136 static int 137 vmesubmatch(device_t bus, cfdata_t dev, const int *ldesc, void *aux) 138 { 139 struct vmebus_softc *sc = device_private(bus); 140 struct vme_attach_args v; 141 142 if (!strcmp(dev->cf_name, VME_SLAVE_DUMMYDRV)) 143 return (0); 144 145 vme_extractlocators(dev->cf_loc, &v); 146 147 v.va_vct = sc->sc_vct; 148 v.va_bdt = sc->sc_bdt; 149 150 if (config_probe(bus, dev, &v)) { 151 config_attach(bus, dev, &v, (cfprint_t)vmeprint, CFARGS_NONE); 152 return (1); 153 } 154 return (0); 155 } 156 157 int 158 vmematch(device_t parent, cfdata_t match, void *aux) 159 { 160 return (1); 161 } 162 163 void 164 vmeattach(device_t parent, device_t self, void *aux) 165 { 166 struct vmebus_softc *sc = device_private(self); 167 168 struct vmebus_attach_args *aa = aux; 169 170 sc->sc_vct = aa->va_vct; 171 sc->sc_bdt = aa->va_bdt; 172 173 /* the "bus" are we ourselves */ 174 sc->sc_vct->bus = sc; 175 176 sc->slaveconfig = aa->va_slaveconfig; 177 178 printf("\n"); 179 180 /* 181 * set up address space accounting - assume incomplete decoding 182 */ 183 sc->vme32_arena = vmem_create("vme32", 184 0, /* base */ 185 /* XXX loses last byte */ 0xffffffff, /* size */ 186 1, /* quantum */ 187 NULL, /* allocfn */ 188 NULL, /* releasefn */ 189 NULL, /* source */ 190 0, /* qcache_max */ 191 VM_SLEEP, 192 IPL_NONE); 193 if (!sc->vme32_arena) { 194 device_printf(self, "error creating A32 map\n"); 195 return; 196 } 197 198 sc->vme24_arena = vmem_create("vme24", 199 0, /* base */ 200 0x01000000, /* size */ 201 1, /* quantum */ 202 NULL, /* allocfn */ 203 NULL, /* releasefn */ 204 NULL, /* source */ 205 0, /* qcache_max */ 206 VM_SLEEP, 207 IPL_NONE); 208 if (!sc->vme24_arena) { 209 device_printf(self, "error creating A24 map\n"); 210 return; 211 } 212 213 sc->vme16_arena = vmem_create("vme16", 214 0, /* base */ 215 0x00010000, /* size */ 216 1, /* quantum */ 217 NULL, /* allocfn */ 218 NULL, /* releasefn */ 219 NULL, /* source */ 220 0, /* qcache_max */ 221 VM_SLEEP, 222 IPL_NONE); 223 if (!sc->vme16_arena) { 224 device_printf(self, "error creating A16 map\n"); 225 return; 226 } 227 228 if (sc->slaveconfig) { 229 /* first get info about the bus master's slave side, 230 if present */ 231 config_search(self, NULL, 232 CFARGS(.search = vmesubmatch1)); 233 } 234 config_search(self, NULL, 235 CFARGS(.search = vmesubmatch)); 236 237 #if 0 /* XXX VMEDEBUG */ 238 if (sc->vme32_arena) 239 vmem_print(sc->vme32_arena); 240 if (sc->vme24_arena) 241 vmem_print(sc->vme24_arena); 242 if (sc->vme16_arena) 243 vmem_print(sc->vme16_arena); 244 #endif 245 } 246 247 #ifdef notyet 248 int 249 vmedetach(device_t dev) 250 { 251 struct vmebus_softc *sc = device_private(dev); 252 253 if (sc->slaveconfig) { 254 /* allow bus master to free its bus resources */ 255 (*sc->slaveconfig)(device_parent(dev), 0); 256 } 257 258 /* extent maps should be empty now */ 259 260 if (sc->vme32_arena) { 261 #ifdef VMEDEBUG 262 vmem_print(sc->vme32_arena); 263 #endif 264 vmem_destroy(sc->vme32_arena); 265 } 266 if (sc->vme24_arena) { 267 #ifdef VMEDEBUG 268 vmem_print(sc->vme24_arena); 269 #endif 270 vmem_destroy(sc->vme24_arena); 271 } 272 if (sc->vme16_arena) { 273 #ifdef VMEDEBUG 274 vmem_print(sc->vme16_arena); 275 #endif 276 vmem_destroy(sc->vme16_arena); 277 } 278 279 return (0); 280 } 281 #endif 282 283 static vmem_t * 284 vme_select_map(struct vmebus_softc *sc, vme_am_t ams) 285 { 286 if ((ams & VME_AM_ADRSIZEMASK) == VME_AM_A32) 287 return (sc->vme32_arena); 288 else if ((ams & VME_AM_ADRSIZEMASK) == VME_AM_A24) 289 return (sc->vme24_arena); 290 else if ((ams & VME_AM_ADRSIZEMASK) == VME_AM_A16) 291 return (sc->vme16_arena); 292 else 293 return (0); 294 } 295 296 int 297 _vme_space_alloc(struct vmebus_softc *sc, vme_addr_t addr, vme_size_t len, vme_am_t ams) 298 { 299 vmem_t *vm; 300 301 vm = vme_select_map(sc, ams); 302 if (!vm) 303 return (EINVAL); 304 305 return vmem_xalloc_addr(vm, addr, len, VM_NOSLEEP); 306 } 307 308 void 309 _vme_space_free(struct vmebus_softc *sc, vme_addr_t addr, vme_size_t len, vme_am_t ams) 310 { 311 vmem_t *vm; 312 313 vm = vme_select_map(sc, ams); 314 if (!vm) { 315 panic("vme_space_free: invalid am %x", ams); 316 return; 317 } 318 319 vmem_xfree(vm, addr, len); 320 } 321 322 int 323 _vme_space_get(struct vmebus_softc *sc, vme_size_t len, vme_am_t ams, u_long align, vme_addr_t *addr) 324 { 325 vmem_t *vm; 326 vmem_addr_t help; 327 int res; 328 329 vm = vme_select_map(sc, ams); 330 if (!vm) 331 return (EINVAL); 332 333 res = vmem_xalloc(vm, len, 334 align, /* align */ 335 0, /* phase */ 336 0, /* nocross */ 337 VMEM_ADDR_MIN, /* minaddr */ 338 VMEM_ADDR_MAX, /* maxaddr */ 339 VM_BESTFIT | VM_NOSLEEP, 340 &help); 341 if (!res) 342 *addr = help; 343 return (res); 344 } 345