1cf824886SRobert Watson /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4f00e79c8SRobert Watson * Copyright (c) 2012-2013, 2016 Robert N. M. Watson 5cf824886SRobert Watson * All rights reserved. 6cf824886SRobert Watson * 7cf824886SRobert Watson * This software was developed by SRI International and the University of 8cf824886SRobert Watson * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 9cf824886SRobert Watson * ("CTSRD"), as part of the DARPA CRASH research programme. 10cf824886SRobert Watson * 11cf824886SRobert Watson * Redistribution and use in source and binary forms, with or without 12cf824886SRobert Watson * modification, are permitted provided that the following conditions 13cf824886SRobert Watson * are met: 14cf824886SRobert Watson * 1. Redistributions of source code must retain the above copyright 15cf824886SRobert Watson * notice, this list of conditions and the following disclaimer. 16cf824886SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 17cf824886SRobert Watson * notice, this list of conditions and the following disclaimer in the 18cf824886SRobert Watson * documentation and/or other materials provided with the distribution. 19cf824886SRobert Watson * 20cf824886SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21cf824886SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22cf824886SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23cf824886SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24cf824886SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25cf824886SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26cf824886SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27cf824886SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28cf824886SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29cf824886SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30cf824886SRobert Watson * SUCH DAMAGE. 31cf824886SRobert Watson */ 32cf824886SRobert Watson 33cf824886SRobert Watson #include <sys/cdefs.h> 34cf824886SRobert Watson __FBSDID("$FreeBSD$"); 35cf824886SRobert Watson 36cf824886SRobert Watson #include <sys/param.h> 37f00e79c8SRobert Watson #include <sys/bio.h> 38cf824886SRobert Watson #include <sys/bus.h> 39cf824886SRobert Watson #include <sys/condvar.h> 40cf824886SRobert Watson #include <sys/conf.h> 41cf824886SRobert Watson #include <sys/kernel.h> 42cf824886SRobert Watson #include <sys/lock.h> 43cf824886SRobert Watson #include <sys/malloc.h> 44cf824886SRobert Watson #include <sys/module.h> 45cf824886SRobert Watson #include <sys/mutex.h> 46cf824886SRobert Watson #include <sys/rman.h> 47cf824886SRobert Watson #include <sys/stat.h> 48cf824886SRobert Watson #include <sys/systm.h> 49cf824886SRobert Watson #include <sys/uio.h> 50cf824886SRobert Watson 51f00e79c8SRobert Watson #include <geom/geom_disk.h> 52f00e79c8SRobert Watson 53cf824886SRobert Watson #include <machine/bus.h> 54cf824886SRobert Watson #include <machine/resource.h> 55cf824886SRobert Watson 56cf824886SRobert Watson #include <vm/vm.h> 57cf824886SRobert Watson 58cf824886SRobert Watson #include <dev/altera/avgen/altera_avgen.h> 59cf824886SRobert Watson 60cf824886SRobert Watson /* 61cf824886SRobert Watson * Generic device driver for allowing read(), write(), and mmap() on 62cf824886SRobert Watson * memory-mapped, Avalon-attached devices. There is no actual dependence on 63cf824886SRobert Watson * Avalon, so conceivably this should just be soc_dev or similar, since many 64cf824886SRobert Watson * system-on-chip bus environments would work fine with the same code. 65cf824886SRobert Watson */ 66cf824886SRobert Watson 67cf824886SRobert Watson static d_mmap_t altera_avgen_mmap; 68cf824886SRobert Watson static d_read_t altera_avgen_read; 69cf824886SRobert Watson static d_write_t altera_avgen_write; 70cf824886SRobert Watson 71f00e79c8SRobert Watson #define ALTERA_AVGEN_DEVNAME "altera_avgen" 72f00e79c8SRobert Watson #define ALTERA_AVGEN_DEVNAME_FMT (ALTERA_AVGEN_DEVNAME "%d") 73f00e79c8SRobert Watson 74cf824886SRobert Watson static struct cdevsw avg_cdevsw = { 75cf824886SRobert Watson .d_version = D_VERSION, 76cf824886SRobert Watson .d_mmap = altera_avgen_mmap, 77cf824886SRobert Watson .d_read = altera_avgen_read, 78cf824886SRobert Watson .d_write = altera_avgen_write, 79f00e79c8SRobert Watson .d_name = ALTERA_AVGEN_DEVNAME, 80cf824886SRobert Watson }; 81cf824886SRobert Watson 82f00e79c8SRobert Watson #define ALTERA_AVGEN_SECTORSIZE 512 /* Not configurable at this time. */ 83f00e79c8SRobert Watson 84cf824886SRobert Watson static int 85cf824886SRobert Watson altera_avgen_read(struct cdev *dev, struct uio *uio, int flag) 86cf824886SRobert Watson { 87cf824886SRobert Watson struct altera_avgen_softc *sc; 88cf824886SRobert Watson u_long offset, size; 89cf824886SRobert Watson #ifdef NOTYET 90cf824886SRobert Watson uint64_t v8; 91cf824886SRobert Watson #endif 92cf824886SRobert Watson uint32_t v4; 93cf824886SRobert Watson uint16_t v2; 94cf824886SRobert Watson uint8_t v1; 95cf824886SRobert Watson u_int width; 96cf824886SRobert Watson int error; 97cf824886SRobert Watson 98cf824886SRobert Watson sc = dev->si_drv1; 99cf824886SRobert Watson if ((sc->avg_flags & ALTERA_AVALON_FLAG_READ) == 0) 100cf824886SRobert Watson return (EACCES); 101cf824886SRobert Watson width = sc->avg_width; 102cf824886SRobert Watson if (uio->uio_offset < 0 || uio->uio_offset % width != 0 || 103cf824886SRobert Watson uio->uio_resid % width != 0) 104cf824886SRobert Watson return (ENODEV); 105cf824886SRobert Watson size = rman_get_size(sc->avg_res); 106cf824886SRobert Watson if ((uio->uio_offset + uio->uio_resid < 0) || 107cf824886SRobert Watson (uio->uio_offset + uio->uio_resid > size)) 108cf824886SRobert Watson return (ENODEV); 109cf824886SRobert Watson while (uio->uio_resid > 0) { 110cf824886SRobert Watson offset = uio->uio_offset; 111cf824886SRobert Watson if (offset + width > size) 112cf824886SRobert Watson return (ENODEV); 113cf824886SRobert Watson switch (width) { 114cf824886SRobert Watson case 1: 115cf824886SRobert Watson v1 = bus_read_1(sc->avg_res, offset); 116cf824886SRobert Watson error = uiomove(&v1, sizeof(v1), uio); 117cf824886SRobert Watson break; 118cf824886SRobert Watson 119cf824886SRobert Watson case 2: 120cf824886SRobert Watson v2 = bus_read_2(sc->avg_res, offset); 121cf824886SRobert Watson error = uiomove(&v2, sizeof(v2), uio); 122cf824886SRobert Watson break; 123cf824886SRobert Watson 124cf824886SRobert Watson case 4: 125cf824886SRobert Watson v4 = bus_read_4(sc->avg_res, offset); 126cf824886SRobert Watson error = uiomove(&v4, sizeof(v4), uio); 127cf824886SRobert Watson break; 128cf824886SRobert Watson 129cf824886SRobert Watson #ifdef NOTYET 130cf824886SRobert Watson case 8: 131cf824886SRobert Watson v8 = bus_read_8(sc->avg_res, offset); 132cf824886SRobert Watson error = uiomove(&v8, sizeof(v8), uio); 133cf824886SRobert Watson break; 134cf824886SRobert Watson 135cf824886SRobert Watson #endif 136cf824886SRobert Watson 137cf824886SRobert Watson default: 138cf824886SRobert Watson panic("%s: unexpected widthment %u", __func__, width); 139cf824886SRobert Watson } 140cf824886SRobert Watson if (error) 141cf824886SRobert Watson return (error); 142cf824886SRobert Watson } 143cf824886SRobert Watson return (0); 144cf824886SRobert Watson } 145cf824886SRobert Watson 146cf824886SRobert Watson static int 147cf824886SRobert Watson altera_avgen_write(struct cdev *dev, struct uio *uio, int flag) 148cf824886SRobert Watson { 149cf824886SRobert Watson struct altera_avgen_softc *sc; 150cf824886SRobert Watson u_long offset, size; 151cf824886SRobert Watson #ifdef NOTYET 152cf824886SRobert Watson uint64_t v8; 153cf824886SRobert Watson #endif 154cf824886SRobert Watson uint32_t v4; 155cf824886SRobert Watson uint16_t v2; 156cf824886SRobert Watson uint8_t v1; 157cf824886SRobert Watson u_int width; 158cf824886SRobert Watson int error; 159cf824886SRobert Watson 160cf824886SRobert Watson sc = dev->si_drv1; 161cf824886SRobert Watson if ((sc->avg_flags & ALTERA_AVALON_FLAG_WRITE) == 0) 162cf824886SRobert Watson return (EACCES); 163cf824886SRobert Watson width = sc->avg_width; 164cf824886SRobert Watson if (uio->uio_offset < 0 || uio->uio_offset % width != 0 || 165cf824886SRobert Watson uio->uio_resid % width != 0) 166cf824886SRobert Watson return (ENODEV); 167cf824886SRobert Watson size = rman_get_size(sc->avg_res); 168cf824886SRobert Watson while (uio->uio_resid > 0) { 169cf824886SRobert Watson offset = uio->uio_offset; 170cf824886SRobert Watson if (offset + width > size) 171cf824886SRobert Watson return (ENODEV); 172cf824886SRobert Watson switch (width) { 173cf824886SRobert Watson case 1: 174cf824886SRobert Watson error = uiomove(&v1, sizeof(v1), uio); 175cf824886SRobert Watson if (error) 176cf824886SRobert Watson return (error); 177cf824886SRobert Watson bus_write_1(sc->avg_res, offset, v1); 178cf824886SRobert Watson break; 179cf824886SRobert Watson 180cf824886SRobert Watson case 2: 181cf824886SRobert Watson error = uiomove(&v2, sizeof(v2), uio); 182cf824886SRobert Watson if (error) 183cf824886SRobert Watson return (error); 184cf824886SRobert Watson bus_write_2(sc->avg_res, offset, v2); 185cf824886SRobert Watson break; 186cf824886SRobert Watson 187cf824886SRobert Watson case 4: 188cf824886SRobert Watson error = uiomove(&v4, sizeof(v4), uio); 189cf824886SRobert Watson if (error) 190cf824886SRobert Watson return (error); 191cf824886SRobert Watson bus_write_4(sc->avg_res, offset, v4); 192cf824886SRobert Watson break; 193cf824886SRobert Watson 194cf824886SRobert Watson #ifdef NOTYET 195cf824886SRobert Watson case 8: 196cf824886SRobert Watson error = uiomove(&v8, sizeof(v8), uio); 197cf824886SRobert Watson if (error) 198cf824886SRobert Watson return (error); 199cf824886SRobert Watson bus_write_8(sc->avg_res, offset, v8); 200cf824886SRobert Watson break; 201cf824886SRobert Watson #endif 202cf824886SRobert Watson 203cf824886SRobert Watson default: 204cf824886SRobert Watson panic("%s: unexpected width %u", __func__, width); 205cf824886SRobert Watson } 206cf824886SRobert Watson } 207cf824886SRobert Watson return (0); 208cf824886SRobert Watson } 209cf824886SRobert Watson 210cf824886SRobert Watson static int 211cf824886SRobert Watson altera_avgen_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 212cf824886SRobert Watson int nprot, vm_memattr_t *memattr) 213cf824886SRobert Watson { 214cf824886SRobert Watson struct altera_avgen_softc *sc; 215cf824886SRobert Watson 216cf824886SRobert Watson sc = dev->si_drv1; 217cf824886SRobert Watson if (nprot & VM_PROT_READ) { 218cf824886SRobert Watson if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_READ) == 0) 219cf824886SRobert Watson return (EACCES); 220cf824886SRobert Watson } 221cf824886SRobert Watson if (nprot & VM_PROT_WRITE) { 222cf824886SRobert Watson if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_WRITE) == 0) 223cf824886SRobert Watson return (EACCES); 224cf824886SRobert Watson } 225cf824886SRobert Watson if (nprot & VM_PROT_EXECUTE) { 226cf824886SRobert Watson if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_EXEC) == 0) 227cf824886SRobert Watson return (EACCES); 228cf824886SRobert Watson } 229cf824886SRobert Watson if (trunc_page(offset) == offset && 230b8c20c02SKonstantin Belousov offset + PAGE_SIZE > offset && 231cf824886SRobert Watson rman_get_size(sc->avg_res) >= offset + PAGE_SIZE) { 232cf824886SRobert Watson *paddr = rman_get_start(sc->avg_res) + offset; 233cf824886SRobert Watson *memattr = VM_MEMATTR_UNCACHEABLE; 234cf824886SRobert Watson } else 235cf824886SRobert Watson return (ENODEV); 236cf824886SRobert Watson return (0); 237cf824886SRobert Watson } 238cf824886SRobert Watson 239f00e79c8SRobert Watson /* 240f00e79c8SRobert Watson * NB: We serialise block reads and writes in case the OS is generating 241f00e79c8SRobert Watson * concurrent I/O against the same block, in which case we want one I/O (or 242f00e79c8SRobert Watson * another) to win. This is not sufficient to provide atomicity for the 243f00e79c8SRobert Watson * sector in the presence of a fail stop -- however, we're just writing this 244f00e79c8SRobert Watson * to non-persistent DRAM .. right? 245f00e79c8SRobert Watson */ 246f00e79c8SRobert Watson static void 247f00e79c8SRobert Watson altera_avgen_disk_strategy(struct bio *bp) 248f00e79c8SRobert Watson { 249f00e79c8SRobert Watson struct altera_avgen_softc *sc; 250f00e79c8SRobert Watson void *data; 251f00e79c8SRobert Watson long bcount; 252f00e79c8SRobert Watson daddr_t pblkno; 253d176b803SScott Long int error; 254f00e79c8SRobert Watson 255f00e79c8SRobert Watson sc = bp->bio_disk->d_drv1; 256f00e79c8SRobert Watson data = bp->bio_data; 257f00e79c8SRobert Watson bcount = bp->bio_bcount; 258f00e79c8SRobert Watson pblkno = bp->bio_pblkno; 259d176b803SScott Long error = 0; 260f00e79c8SRobert Watson 261f00e79c8SRobert Watson /* 262f00e79c8SRobert Watson * Serialize block reads / writes. 263f00e79c8SRobert Watson */ 264f00e79c8SRobert Watson mtx_lock(&sc->avg_disk_mtx); 265f00e79c8SRobert Watson switch (bp->bio_cmd) { 266f00e79c8SRobert Watson case BIO_READ: 267f00e79c8SRobert Watson if (!(sc->avg_flags & ALTERA_AVALON_FLAG_GEOM_READ)) { 268d176b803SScott Long error = EROFS; 269f00e79c8SRobert Watson break; 270f00e79c8SRobert Watson } 271f00e79c8SRobert Watson switch (sc->avg_width) { 272f00e79c8SRobert Watson case 1: 273f00e79c8SRobert Watson bus_read_region_1(sc->avg_res, 274f00e79c8SRobert Watson bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE, 275f00e79c8SRobert Watson (uint8_t *)data, bcount); 276f00e79c8SRobert Watson break; 277f00e79c8SRobert Watson 278f00e79c8SRobert Watson case 2: 279f00e79c8SRobert Watson bus_read_region_2(sc->avg_res, 280f00e79c8SRobert Watson bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE, 281f00e79c8SRobert Watson (uint16_t *)data, bcount / 2); 282f00e79c8SRobert Watson break; 283f00e79c8SRobert Watson 284f00e79c8SRobert Watson case 4: 285f00e79c8SRobert Watson bus_read_region_4(sc->avg_res, 286f00e79c8SRobert Watson bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE, 287f00e79c8SRobert Watson (uint32_t *)data, bcount / 4); 288f00e79c8SRobert Watson break; 289f00e79c8SRobert Watson 290f00e79c8SRobert Watson default: 291f00e79c8SRobert Watson panic("%s: unexpected width %u", __func__, 292f00e79c8SRobert Watson sc->avg_width); 293f00e79c8SRobert Watson } 294f00e79c8SRobert Watson break; 295f00e79c8SRobert Watson 296f00e79c8SRobert Watson case BIO_WRITE: 297f00e79c8SRobert Watson if (!(sc->avg_flags & ALTERA_AVALON_FLAG_GEOM_WRITE)) { 298f00e79c8SRobert Watson biofinish(bp, NULL, EROFS); 299f00e79c8SRobert Watson break; 300f00e79c8SRobert Watson } 301f00e79c8SRobert Watson switch (sc->avg_width) { 302f00e79c8SRobert Watson case 1: 303f00e79c8SRobert Watson bus_write_region_1(sc->avg_res, 304f00e79c8SRobert Watson bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE, 305f00e79c8SRobert Watson (uint8_t *)data, bcount); 306f00e79c8SRobert Watson break; 307f00e79c8SRobert Watson 308f00e79c8SRobert Watson case 2: 309f00e79c8SRobert Watson bus_write_region_2(sc->avg_res, 310f00e79c8SRobert Watson bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE, 311f00e79c8SRobert Watson (uint16_t *)data, bcount / 2); 312f00e79c8SRobert Watson break; 313f00e79c8SRobert Watson 314f00e79c8SRobert Watson case 4: 315f00e79c8SRobert Watson bus_write_region_4(sc->avg_res, 316f00e79c8SRobert Watson bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE, 317f00e79c8SRobert Watson (uint32_t *)data, bcount / 4); 318f00e79c8SRobert Watson break; 319f00e79c8SRobert Watson 320f00e79c8SRobert Watson default: 321f00e79c8SRobert Watson panic("%s: unexpected width %u", __func__, 322f00e79c8SRobert Watson sc->avg_width); 323f00e79c8SRobert Watson } 324f00e79c8SRobert Watson break; 325f00e79c8SRobert Watson 326f00e79c8SRobert Watson default: 327d176b803SScott Long error = EOPNOTSUPP; 328d176b803SScott Long break; 329f00e79c8SRobert Watson } 330f00e79c8SRobert Watson mtx_unlock(&sc->avg_disk_mtx); 331d176b803SScott Long biofinish(bp, NULL, error); 332f00e79c8SRobert Watson } 333cf824886SRobert Watson 334cf824886SRobert Watson static int 335cf824886SRobert Watson altera_avgen_process_options(struct altera_avgen_softc *sc, 336f00e79c8SRobert Watson const char *str_fileio, const char *str_geomio, const char *str_mmapio, 337f00e79c8SRobert Watson const char *str_devname, int devunit) 338cf824886SRobert Watson { 339cf824886SRobert Watson const char *cp; 340cf824886SRobert Watson device_t dev = sc->avg_dev; 341cf824886SRobert Watson 342cf824886SRobert Watson /* 343cf824886SRobert Watson * Check for valid combinations of options. 344cf824886SRobert Watson */ 345f00e79c8SRobert Watson if (str_fileio == NULL && str_geomio == NULL && str_mmapio == NULL) { 346cf824886SRobert Watson device_printf(dev, 347f00e79c8SRobert Watson "at least one of %s, %s, or %s must be specified\n", 348f00e79c8SRobert Watson ALTERA_AVALON_STR_FILEIO, ALTERA_AVALON_STR_GEOMIO, 349f00e79c8SRobert Watson ALTERA_AVALON_STR_MMAPIO); 350cf824886SRobert Watson return (ENXIO); 351cf824886SRobert Watson } 352f00e79c8SRobert Watson 353f00e79c8SRobert Watson /* 354f00e79c8SRobert Watson * Validity check: a device can either be a GEOM device (in which case 355f00e79c8SRobert Watson * we use GEOM to register the device node), or a special device -- 356f00e79c8SRobert Watson * but not both as that causes a collision in /dev. 357f00e79c8SRobert Watson */ 358f00e79c8SRobert Watson if (str_geomio != NULL && (str_fileio != NULL || str_mmapio != NULL)) { 359f00e79c8SRobert Watson device_printf(dev, 360f00e79c8SRobert Watson "at most one of %s and (%s or %s) may be specified\n", 361f00e79c8SRobert Watson ALTERA_AVALON_STR_GEOMIO, ALTERA_AVALON_STR_FILEIO, 362f00e79c8SRobert Watson ALTERA_AVALON_STR_MMAPIO); 363f00e79c8SRobert Watson return (ENXIO); 364f00e79c8SRobert Watson } 365f00e79c8SRobert Watson 366f00e79c8SRobert Watson /* 367f00e79c8SRobert Watson * Ensure that a unit is specified if a name is also specified. 368f00e79c8SRobert Watson */ 369cf824886SRobert Watson if (str_devname == NULL && devunit != -1) { 370cf824886SRobert Watson device_printf(dev, "%s requires %s be specified\n", 371cf824886SRobert Watson ALTERA_AVALON_STR_DEVUNIT, ALTERA_AVALON_STR_DEVNAME); 372cf824886SRobert Watson return (ENXIO); 373cf824886SRobert Watson } 374cf824886SRobert Watson 375cf824886SRobert Watson /* 376cf824886SRobert Watson * Extract, digest, and save values. 377cf824886SRobert Watson */ 378cf824886SRobert Watson switch (sc->avg_width) { 379cf824886SRobert Watson case 1: 380cf824886SRobert Watson case 2: 381cf824886SRobert Watson case 4: 382cf824886SRobert Watson #ifdef NOTYET 383cf824886SRobert Watson case 8: 384cf824886SRobert Watson #endif 385cf824886SRobert Watson break; 386cf824886SRobert Watson 387cf824886SRobert Watson default: 388cf824886SRobert Watson device_printf(dev, "%s unsupported value %u\n", 389cf824886SRobert Watson ALTERA_AVALON_STR_WIDTH, sc->avg_width); 390cf824886SRobert Watson return (ENXIO); 391cf824886SRobert Watson } 392cf824886SRobert Watson sc->avg_flags = 0; 393cf824886SRobert Watson if (str_fileio != NULL) { 394cf824886SRobert Watson for (cp = str_fileio; *cp != '\0'; cp++) { 395cf824886SRobert Watson switch (*cp) { 396cf824886SRobert Watson case ALTERA_AVALON_CHAR_READ: 397cf824886SRobert Watson sc->avg_flags |= ALTERA_AVALON_FLAG_READ; 398cf824886SRobert Watson break; 399cf824886SRobert Watson 400cf824886SRobert Watson case ALTERA_AVALON_CHAR_WRITE: 401cf824886SRobert Watson sc->avg_flags |= ALTERA_AVALON_FLAG_WRITE; 402cf824886SRobert Watson break; 403cf824886SRobert Watson 404cf824886SRobert Watson default: 405cf824886SRobert Watson device_printf(dev, 406cf824886SRobert Watson "invalid %s character %c\n", 407cf824886SRobert Watson ALTERA_AVALON_STR_FILEIO, *cp); 408cf824886SRobert Watson return (ENXIO); 409cf824886SRobert Watson } 410cf824886SRobert Watson } 411cf824886SRobert Watson } 412f00e79c8SRobert Watson if (str_geomio != NULL) { 413f00e79c8SRobert Watson for (cp = str_geomio; *cp != '\0'; cp++){ 414f00e79c8SRobert Watson switch (*cp) { 415f00e79c8SRobert Watson case ALTERA_AVALON_CHAR_READ: 416f00e79c8SRobert Watson sc->avg_flags |= ALTERA_AVALON_FLAG_GEOM_READ; 417f00e79c8SRobert Watson break; 418f00e79c8SRobert Watson 419f00e79c8SRobert Watson case ALTERA_AVALON_CHAR_WRITE: 420f00e79c8SRobert Watson sc->avg_flags |= ALTERA_AVALON_FLAG_GEOM_WRITE; 421f00e79c8SRobert Watson break; 422f00e79c8SRobert Watson 423f00e79c8SRobert Watson default: 424f00e79c8SRobert Watson device_printf(dev, 425f00e79c8SRobert Watson "invalid %s character %c\n", 426f00e79c8SRobert Watson ALTERA_AVALON_STR_GEOMIO, *cp); 427f00e79c8SRobert Watson return (ENXIO); 428f00e79c8SRobert Watson } 429f00e79c8SRobert Watson } 430f00e79c8SRobert Watson } 431cf824886SRobert Watson if (str_mmapio != NULL) { 432cf824886SRobert Watson for (cp = str_mmapio; *cp != '\0'; cp++) { 433cf824886SRobert Watson switch (*cp) { 434cf824886SRobert Watson case ALTERA_AVALON_CHAR_READ: 435cf824886SRobert Watson sc->avg_flags |= ALTERA_AVALON_FLAG_MMAP_READ; 436cf824886SRobert Watson break; 437cf824886SRobert Watson 438cf824886SRobert Watson case ALTERA_AVALON_CHAR_WRITE: 439cf824886SRobert Watson sc->avg_flags |= 440cf824886SRobert Watson ALTERA_AVALON_FLAG_MMAP_WRITE; 441cf824886SRobert Watson break; 442cf824886SRobert Watson 443cf824886SRobert Watson case ALTERA_AVALON_CHAR_EXEC: 444cf824886SRobert Watson sc->avg_flags |= ALTERA_AVALON_FLAG_MMAP_EXEC; 445cf824886SRobert Watson break; 446cf824886SRobert Watson 447cf824886SRobert Watson default: 448cf824886SRobert Watson device_printf(dev, 449cf824886SRobert Watson "invalid %s character %c\n", 450cf824886SRobert Watson ALTERA_AVALON_STR_MMAPIO, *cp); 451cf824886SRobert Watson return (ENXIO); 452cf824886SRobert Watson } 453cf824886SRobert Watson } 454cf824886SRobert Watson } 455cf824886SRobert Watson return (0); 456cf824886SRobert Watson } 457cf824886SRobert Watson 458b364a525SRobert Watson int 459b364a525SRobert Watson altera_avgen_attach(struct altera_avgen_softc *sc, const char *str_fileio, 460f00e79c8SRobert Watson const char *str_geomio, const char *str_mmapio, const char *str_devname, 461f00e79c8SRobert Watson int devunit) 462cf824886SRobert Watson { 463b364a525SRobert Watson device_t dev = sc->avg_dev; 464b364a525SRobert Watson int error; 465cf824886SRobert Watson 466f00e79c8SRobert Watson error = altera_avgen_process_options(sc, str_fileio, str_geomio, 467f00e79c8SRobert Watson str_mmapio, str_devname, devunit); 468cf824886SRobert Watson if (error) 469cf824886SRobert Watson return (error); 470cf824886SRobert Watson 471cf824886SRobert Watson if (rman_get_size(sc->avg_res) >= PAGE_SIZE || str_mmapio != NULL) { 472cf824886SRobert Watson if (rman_get_size(sc->avg_res) % PAGE_SIZE != 0) { 473cf824886SRobert Watson device_printf(dev, 474cf824886SRobert Watson "memory region not even multiple of page size\n"); 475b364a525SRobert Watson return (ENXIO); 476cf824886SRobert Watson } 477cf824886SRobert Watson if (rman_get_start(sc->avg_res) % PAGE_SIZE != 0) { 478cf824886SRobert Watson device_printf(dev, "memory region not page-aligned\n"); 479b364a525SRobert Watson return (ENXIO); 480cf824886SRobert Watson } 481cf824886SRobert Watson } 482cf824886SRobert Watson 483f00e79c8SRobert Watson /* 484f00e79c8SRobert Watson * If a GEOM permission is requested, then create the device via GEOM. 485f00e79c8SRobert Watson * Otherwise, create a special device. We checked during options 486f00e79c8SRobert Watson * processing that both weren't requested a once. 487f00e79c8SRobert Watson */ 488f00e79c8SRobert Watson if (str_devname != NULL) { 489f00e79c8SRobert Watson sc->avg_name = strdup(str_devname, M_TEMP); 490f00e79c8SRobert Watson devunit = sc->avg_unit; 491f00e79c8SRobert Watson } else 492f00e79c8SRobert Watson sc->avg_name = strdup(ALTERA_AVGEN_DEVNAME, M_TEMP); 493f00e79c8SRobert Watson if (sc->avg_flags & (ALTERA_AVALON_FLAG_GEOM_READ | 494f00e79c8SRobert Watson ALTERA_AVALON_FLAG_GEOM_WRITE)) { 495f00e79c8SRobert Watson mtx_init(&sc->avg_disk_mtx, "altera_avgen_disk", NULL, 496f00e79c8SRobert Watson MTX_DEF); 497f00e79c8SRobert Watson sc->avg_disk = disk_alloc(); 498f00e79c8SRobert Watson sc->avg_disk->d_drv1 = sc; 499f00e79c8SRobert Watson sc->avg_disk->d_strategy = altera_avgen_disk_strategy; 500f00e79c8SRobert Watson if (devunit == -1) 501f00e79c8SRobert Watson devunit = 0; 502f00e79c8SRobert Watson sc->avg_disk->d_name = sc->avg_name; 503f00e79c8SRobert Watson sc->avg_disk->d_unit = devunit; 504f00e79c8SRobert Watson 505f00e79c8SRobert Watson /* 506f00e79c8SRobert Watson * NB: As avg_res is a multiple of PAGE_SIZE, it is also a 507f00e79c8SRobert Watson * multiple of ALTERA_AVGEN_SECTORSIZE. 508f00e79c8SRobert Watson */ 509f00e79c8SRobert Watson sc->avg_disk->d_sectorsize = ALTERA_AVGEN_SECTORSIZE; 510f00e79c8SRobert Watson sc->avg_disk->d_mediasize = rman_get_size(sc->avg_res); 511f00e79c8SRobert Watson sc->avg_disk->d_maxsize = ALTERA_AVGEN_SECTORSIZE; 512f00e79c8SRobert Watson disk_create(sc->avg_disk, DISK_VERSION); 513f00e79c8SRobert Watson } else { 514cf824886SRobert Watson /* Device node allocation. */ 515cf824886SRobert Watson if (str_devname == NULL) { 516f00e79c8SRobert Watson str_devname = ALTERA_AVGEN_DEVNAME_FMT; 517cf824886SRobert Watson devunit = sc->avg_unit; 518cf824886SRobert Watson } 519cf824886SRobert Watson if (devunit != -1) 520f00e79c8SRobert Watson sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, 521f00e79c8SRobert Watson UID_ROOT, GID_WHEEL, S_IRUSR | S_IWUSR, "%s%d", 522f00e79c8SRobert Watson str_devname, devunit); 523cf824886SRobert Watson else 524f00e79c8SRobert Watson sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, 525f00e79c8SRobert Watson UID_ROOT, GID_WHEEL, S_IRUSR | S_IWUSR, 526f00e79c8SRobert Watson "%s", str_devname); 527cf824886SRobert Watson if (sc->avg_cdev == NULL) { 528f00e79c8SRobert Watson device_printf(sc->avg_dev, "%s: make_dev failed\n", 529f00e79c8SRobert Watson __func__); 530b364a525SRobert Watson return (ENXIO); 531cf824886SRobert Watson } 532f00e79c8SRobert Watson 533cf824886SRobert Watson /* XXXRW: Slight race between make_dev(9) and here. */ 534cf824886SRobert Watson sc->avg_cdev->si_drv1 = sc; 535f00e79c8SRobert Watson } 536cf824886SRobert Watson return (0); 537cf824886SRobert Watson } 538cf824886SRobert Watson 539b364a525SRobert Watson void 540b364a525SRobert Watson altera_avgen_detach(struct altera_avgen_softc *sc) 541cf824886SRobert Watson { 542cf824886SRobert Watson 543f00e79c8SRobert Watson KASSERT((sc->avg_disk != NULL) || (sc->avg_cdev != NULL), 544f00e79c8SRobert Watson ("%s: neither GEOM nor special device", __func__)); 545f00e79c8SRobert Watson 546f00e79c8SRobert Watson if (sc->avg_disk != NULL) { 547f00e79c8SRobert Watson disk_gone(sc->avg_disk); 548f00e79c8SRobert Watson disk_destroy(sc->avg_disk); 549f00e79c8SRobert Watson free(sc->avg_name, M_TEMP); 550f00e79c8SRobert Watson mtx_destroy(&sc->avg_disk_mtx); 551f00e79c8SRobert Watson } else { 552cf824886SRobert Watson destroy_dev(sc->avg_cdev); 553cf824886SRobert Watson } 554f00e79c8SRobert Watson } 555