1239674Srwatson/*- 2245376Srwatson * Copyright (c) 2012-2013 Robert N. M. Watson 3239674Srwatson * All rights reserved. 4239674Srwatson * 5239674Srwatson * This software was developed by SRI International and the University of 6239674Srwatson * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7239674Srwatson * ("CTSRD"), as part of the DARPA CRASH research programme. 8239674Srwatson * 9239674Srwatson * Redistribution and use in source and binary forms, with or without 10239674Srwatson * modification, are permitted provided that the following conditions 11239674Srwatson * are met: 12239674Srwatson * 1. Redistributions of source code must retain the above copyright 13239674Srwatson * notice, this list of conditions and the following disclaimer. 14239674Srwatson * 2. Redistributions in binary form must reproduce the above copyright 15239674Srwatson * notice, this list of conditions and the following disclaimer in the 16239674Srwatson * documentation and/or other materials provided with the distribution. 17239674Srwatson * 18239674Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19239674Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20239674Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21239674Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22239674Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23239674Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24239674Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25239674Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26239674Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27239674Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28239674Srwatson * SUCH DAMAGE. 29239674Srwatson */ 30239674Srwatson 31239674Srwatson#include <sys/cdefs.h> 32239674Srwatson__FBSDID("$FreeBSD$"); 33239674Srwatson 34239674Srwatson#include <sys/param.h> 35239674Srwatson#include <sys/bus.h> 36239674Srwatson#include <sys/condvar.h> 37239674Srwatson#include <sys/conf.h> 38239674Srwatson#include <sys/kernel.h> 39239674Srwatson#include <sys/lock.h> 40239674Srwatson#include <sys/malloc.h> 41239674Srwatson#include <sys/module.h> 42239674Srwatson#include <sys/mutex.h> 43239674Srwatson#include <sys/rman.h> 44239674Srwatson#include <sys/stat.h> 45239674Srwatson#include <sys/systm.h> 46239674Srwatson#include <sys/uio.h> 47239674Srwatson 48239674Srwatson#include <machine/bus.h> 49239674Srwatson#include <machine/resource.h> 50239674Srwatson#include <machine/vm.h> 51239674Srwatson 52239674Srwatson#include <vm/vm.h> 53239674Srwatson 54239674Srwatson#include <dev/altera/avgen/altera_avgen.h> 55239674Srwatson 56239674Srwatson/* 57239674Srwatson * Generic device driver for allowing read(), write(), and mmap() on 58239674Srwatson * memory-mapped, Avalon-attached devices. There is no actual dependence on 59239674Srwatson * Avalon, so conceivably this should just be soc_dev or similar, since many 60239674Srwatson * system-on-chip bus environments would work fine with the same code. 61239674Srwatson */ 62239674Srwatson 63245380Srwatsondevclass_t altera_avgen_devclass; 64245380Srwatson 65239674Srwatsonstatic d_mmap_t altera_avgen_mmap; 66239674Srwatsonstatic d_read_t altera_avgen_read; 67239674Srwatsonstatic d_write_t altera_avgen_write; 68239674Srwatson 69239674Srwatsonstatic struct cdevsw avg_cdevsw = { 70239674Srwatson .d_version = D_VERSION, 71239674Srwatson .d_mmap = altera_avgen_mmap, 72239674Srwatson .d_read = altera_avgen_read, 73239674Srwatson .d_write = altera_avgen_write, 74239674Srwatson .d_name = "altera_avgen", 75239674Srwatson}; 76239674Srwatson 77239674Srwatsonstatic int 78239674Srwatsonaltera_avgen_read(struct cdev *dev, struct uio *uio, int flag) 79239674Srwatson{ 80239674Srwatson struct altera_avgen_softc *sc; 81239674Srwatson u_long offset, size; 82239674Srwatson#ifdef NOTYET 83239674Srwatson uint64_t v8; 84239674Srwatson#endif 85239674Srwatson uint32_t v4; 86239674Srwatson uint16_t v2; 87239674Srwatson uint8_t v1; 88239674Srwatson u_int width; 89239674Srwatson int error; 90239674Srwatson 91239674Srwatson sc = dev->si_drv1; 92239674Srwatson if ((sc->avg_flags & ALTERA_AVALON_FLAG_READ) == 0) 93239674Srwatson return (EACCES); 94239674Srwatson width = sc->avg_width; 95239674Srwatson if (uio->uio_offset < 0 || uio->uio_offset % width != 0 || 96239674Srwatson uio->uio_resid % width != 0) 97239674Srwatson return (ENODEV); 98239674Srwatson size = rman_get_size(sc->avg_res); 99239674Srwatson if ((uio->uio_offset + uio->uio_resid < 0) || 100239674Srwatson (uio->uio_offset + uio->uio_resid > size)) 101239674Srwatson return (ENODEV); 102239674Srwatson while (uio->uio_resid > 0) { 103239674Srwatson offset = uio->uio_offset; 104239674Srwatson if (offset + width > size) 105239674Srwatson return (ENODEV); 106239674Srwatson switch (width) { 107239674Srwatson case 1: 108239674Srwatson v1 = bus_read_1(sc->avg_res, offset); 109239674Srwatson error = uiomove(&v1, sizeof(v1), uio); 110239674Srwatson break; 111239674Srwatson 112239674Srwatson case 2: 113239674Srwatson v2 = bus_read_2(sc->avg_res, offset); 114239674Srwatson error = uiomove(&v2, sizeof(v2), uio); 115239674Srwatson break; 116239674Srwatson 117239674Srwatson case 4: 118239674Srwatson v4 = bus_read_4(sc->avg_res, offset); 119239674Srwatson error = uiomove(&v4, sizeof(v4), uio); 120239674Srwatson break; 121239674Srwatson 122239674Srwatson#ifdef NOTYET 123239674Srwatson case 8: 124239674Srwatson v8 = bus_read_8(sc->avg_res, offset); 125239674Srwatson error = uiomove(&v8, sizeof(v8), uio); 126239674Srwatson break; 127239674Srwatson 128239674Srwatson#endif 129239674Srwatson 130239674Srwatson default: 131239674Srwatson panic("%s: unexpected widthment %u", __func__, width); 132239674Srwatson } 133239674Srwatson if (error) 134239674Srwatson return (error); 135239674Srwatson } 136239674Srwatson return (0); 137239674Srwatson} 138239674Srwatson 139239674Srwatsonstatic int 140239674Srwatsonaltera_avgen_write(struct cdev *dev, struct uio *uio, int flag) 141239674Srwatson{ 142239674Srwatson struct altera_avgen_softc *sc; 143239674Srwatson u_long offset, size; 144239674Srwatson#ifdef NOTYET 145239674Srwatson uint64_t v8; 146239674Srwatson#endif 147239674Srwatson uint32_t v4; 148239674Srwatson uint16_t v2; 149239674Srwatson uint8_t v1; 150239674Srwatson u_int width; 151239674Srwatson int error; 152239674Srwatson 153239674Srwatson sc = dev->si_drv1; 154239674Srwatson if ((sc->avg_flags & ALTERA_AVALON_FLAG_WRITE) == 0) 155239674Srwatson return (EACCES); 156239674Srwatson width = sc->avg_width; 157239674Srwatson if (uio->uio_offset < 0 || uio->uio_offset % width != 0 || 158239674Srwatson uio->uio_resid % width != 0) 159239674Srwatson return (ENODEV); 160239674Srwatson size = rman_get_size(sc->avg_res); 161239674Srwatson while (uio->uio_resid > 0) { 162239674Srwatson offset = uio->uio_offset; 163239674Srwatson if (offset + width > size) 164239674Srwatson return (ENODEV); 165239674Srwatson switch (width) { 166239674Srwatson case 1: 167239674Srwatson error = uiomove(&v1, sizeof(v1), uio); 168239674Srwatson if (error) 169239674Srwatson return (error); 170239674Srwatson bus_write_1(sc->avg_res, offset, v1); 171239674Srwatson break; 172239674Srwatson 173239674Srwatson case 2: 174239674Srwatson error = uiomove(&v2, sizeof(v2), uio); 175239674Srwatson if (error) 176239674Srwatson return (error); 177239674Srwatson bus_write_2(sc->avg_res, offset, v2); 178239674Srwatson break; 179239674Srwatson 180239674Srwatson case 4: 181239674Srwatson error = uiomove(&v4, sizeof(v4), uio); 182239674Srwatson if (error) 183239674Srwatson return (error); 184239674Srwatson bus_write_4(sc->avg_res, offset, v4); 185239674Srwatson break; 186239674Srwatson 187239674Srwatson#ifdef NOTYET 188239674Srwatson case 8: 189239674Srwatson error = uiomove(&v8, sizeof(v8), uio); 190239674Srwatson if (error) 191239674Srwatson return (error); 192239674Srwatson bus_write_8(sc->avg_res, offset, v8); 193239674Srwatson break; 194239674Srwatson#endif 195239674Srwatson 196239674Srwatson default: 197239674Srwatson panic("%s: unexpected width %u", __func__, width); 198239674Srwatson } 199239674Srwatson } 200239674Srwatson return (0); 201239674Srwatson} 202239674Srwatson 203239674Srwatsonstatic int 204239674Srwatsonaltera_avgen_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 205239674Srwatson int nprot, vm_memattr_t *memattr) 206239674Srwatson{ 207239674Srwatson struct altera_avgen_softc *sc; 208239674Srwatson 209239674Srwatson sc = dev->si_drv1; 210239674Srwatson if (nprot & VM_PROT_READ) { 211239674Srwatson if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_READ) == 0) 212239674Srwatson return (EACCES); 213239674Srwatson } 214239674Srwatson if (nprot & VM_PROT_WRITE) { 215239674Srwatson if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_WRITE) == 0) 216239674Srwatson return (EACCES); 217239674Srwatson } 218239674Srwatson if (nprot & VM_PROT_EXECUTE) { 219239674Srwatson if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_EXEC) == 0) 220239674Srwatson return (EACCES); 221239674Srwatson } 222239674Srwatson if (trunc_page(offset) == offset && 223239674Srwatson rman_get_size(sc->avg_res) >= offset + PAGE_SIZE) { 224239674Srwatson *paddr = rman_get_start(sc->avg_res) + offset; 225239674Srwatson *memattr = VM_MEMATTR_UNCACHEABLE; 226239674Srwatson } else 227239674Srwatson return (ENODEV); 228239674Srwatson return (0); 229239674Srwatson} 230239674Srwatson 231239674Srwatson 232239674Srwatsonstatic int 233239674Srwatsonaltera_avgen_process_options(struct altera_avgen_softc *sc, 234239674Srwatson const char *str_fileio, const char *str_mmapio, const char *str_devname, 235239674Srwatson int devunit) 236239674Srwatson{ 237239674Srwatson const char *cp; 238239674Srwatson device_t dev = sc->avg_dev; 239239674Srwatson 240239674Srwatson /* 241239674Srwatson * Check for valid combinations of options. 242239674Srwatson */ 243239674Srwatson if (str_fileio == NULL && str_mmapio == NULL) { 244239674Srwatson device_printf(dev, 245239674Srwatson "at least one of %s or %s must be specified\n", 246239674Srwatson ALTERA_AVALON_STR_FILEIO, ALTERA_AVALON_STR_MMAPIO); 247239674Srwatson return (ENXIO); 248239674Srwatson } 249239674Srwatson if (str_devname == NULL && devunit != -1) { 250239674Srwatson device_printf(dev, "%s requires %s be specified\n", 251239674Srwatson ALTERA_AVALON_STR_DEVUNIT, ALTERA_AVALON_STR_DEVNAME); 252239674Srwatson return (ENXIO); 253239674Srwatson } 254239674Srwatson 255239674Srwatson /* 256239674Srwatson * Extract, digest, and save values. 257239674Srwatson */ 258239674Srwatson switch (sc->avg_width) { 259239674Srwatson case 1: 260239674Srwatson case 2: 261239674Srwatson case 4: 262239674Srwatson#ifdef NOTYET 263239674Srwatson case 8: 264239674Srwatson#endif 265239674Srwatson break; 266239674Srwatson 267239674Srwatson default: 268239674Srwatson device_printf(dev, "%s unsupported value %u\n", 269239674Srwatson ALTERA_AVALON_STR_WIDTH, sc->avg_width); 270239674Srwatson return (ENXIO); 271239674Srwatson } 272239674Srwatson sc->avg_flags = 0; 273239674Srwatson if (str_fileio != NULL) { 274239674Srwatson for (cp = str_fileio; *cp != '\0'; cp++) { 275239674Srwatson switch (*cp) { 276239674Srwatson case ALTERA_AVALON_CHAR_READ: 277239674Srwatson sc->avg_flags |= ALTERA_AVALON_FLAG_READ; 278239674Srwatson break; 279239674Srwatson 280239674Srwatson case ALTERA_AVALON_CHAR_WRITE: 281239674Srwatson sc->avg_flags |= ALTERA_AVALON_FLAG_WRITE; 282239674Srwatson break; 283239674Srwatson 284239674Srwatson default: 285239674Srwatson device_printf(dev, 286239674Srwatson "invalid %s character %c\n", 287239674Srwatson ALTERA_AVALON_STR_FILEIO, *cp); 288239674Srwatson return (ENXIO); 289239674Srwatson } 290239674Srwatson } 291239674Srwatson } 292239674Srwatson if (str_mmapio != NULL) { 293239674Srwatson for (cp = str_mmapio; *cp != '\0'; cp++) { 294239674Srwatson switch (*cp) { 295239674Srwatson case ALTERA_AVALON_CHAR_READ: 296239674Srwatson sc->avg_flags |= ALTERA_AVALON_FLAG_MMAP_READ; 297239674Srwatson break; 298239674Srwatson 299239674Srwatson case ALTERA_AVALON_CHAR_WRITE: 300239674Srwatson sc->avg_flags |= 301239674Srwatson ALTERA_AVALON_FLAG_MMAP_WRITE; 302239674Srwatson break; 303239674Srwatson 304239674Srwatson case ALTERA_AVALON_CHAR_EXEC: 305239674Srwatson sc->avg_flags |= ALTERA_AVALON_FLAG_MMAP_EXEC; 306239674Srwatson break; 307239674Srwatson 308239674Srwatson default: 309239674Srwatson device_printf(dev, 310239674Srwatson "invalid %s character %c\n", 311239674Srwatson ALTERA_AVALON_STR_MMAPIO, *cp); 312239674Srwatson return (ENXIO); 313239674Srwatson } 314239674Srwatson } 315239674Srwatson } 316239674Srwatson return (0); 317239674Srwatson} 318239674Srwatson 319245376Srwatsonint 320245376Srwatsonaltera_avgen_attach(struct altera_avgen_softc *sc, const char *str_fileio, 321245376Srwatson const char *str_mmapio, const char *str_devname, int devunit) 322239674Srwatson{ 323245376Srwatson device_t dev = sc->avg_dev; 324245376Srwatson int error; 325239674Srwatson 326239674Srwatson error = altera_avgen_process_options(sc, str_fileio, str_mmapio, 327239674Srwatson str_devname, devunit); 328239674Srwatson if (error) 329239674Srwatson return (error); 330239674Srwatson 331239674Srwatson if (rman_get_size(sc->avg_res) >= PAGE_SIZE || str_mmapio != NULL) { 332239674Srwatson if (rman_get_size(sc->avg_res) % PAGE_SIZE != 0) { 333239674Srwatson device_printf(dev, 334239674Srwatson "memory region not even multiple of page size\n"); 335245376Srwatson return (ENXIO); 336239674Srwatson } 337239674Srwatson if (rman_get_start(sc->avg_res) % PAGE_SIZE != 0) { 338239674Srwatson device_printf(dev, "memory region not page-aligned\n"); 339245376Srwatson return (ENXIO); 340239674Srwatson } 341239674Srwatson } 342239674Srwatson 343239674Srwatson /* Device node allocation. */ 344239674Srwatson if (str_devname == NULL) { 345239674Srwatson str_devname = "altera_avgen%d"; 346239674Srwatson devunit = sc->avg_unit; 347239674Srwatson } 348239674Srwatson if (devunit != -1) 349239674Srwatson sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, UID_ROOT, 350239674Srwatson GID_WHEEL, S_IRUSR | S_IWUSR, str_devname, devunit); 351239674Srwatson else 352239674Srwatson sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, UID_ROOT, 353239674Srwatson GID_WHEEL, S_IRUSR | S_IWUSR, str_devname); 354239674Srwatson if (sc->avg_cdev == NULL) { 355239674Srwatson device_printf(sc->avg_dev, "%s: make_dev failed\n", __func__); 356245376Srwatson return (ENXIO); 357239674Srwatson } 358239674Srwatson /* XXXRW: Slight race between make_dev(9) and here. */ 359239674Srwatson sc->avg_cdev->si_drv1 = sc; 360239674Srwatson return (0); 361239674Srwatson} 362239674Srwatson 363245376Srwatsonvoid 364245376Srwatsonaltera_avgen_detach(struct altera_avgen_softc *sc) 365239674Srwatson{ 366239674Srwatson 367239674Srwatson destroy_dev(sc->avg_cdev); 368239674Srwatson} 369