1 /* $NetBSD: qemufwcfg.c,v 1.2 2018/09/03 16:29:31 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: qemufwcfg.c,v 1.2 2018/09/03 16:29:31 riastradh Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/systm.h> 35 #include <sys/conf.h> 36 #include <sys/mutex.h> 37 #include <sys/bus.h> 38 39 #include <dev/ic/qemufwcfgvar.h> 40 #include <dev/ic/qemufwcfgio.h> 41 42 #include "ioconf.h" 43 44 /* Register locations are MD */ 45 #if defined(__i386__) || defined(__x86_64__) 46 #define FWCFG_SEL_REG 0x00 47 #define FWCFG_SEL_SWAP htole16 48 #define FWCFG_DATA_REG 0x01 49 #define FWCFG_DMA_ADDR 0x04 50 #elif defined(__arm__) || defined(__aarch64__) 51 #define FWCFG_SEL_REG 0x08 52 #define FWCFG_SEL_SWAP htobe16 53 #define FWCFG_DATA_REG 0x00 54 #define FWCFG_DMA_ADDR 0x10 55 #else 56 #error driver does not support this architecture 57 #endif 58 59 static dev_type_open(fwcfg_open); 60 static dev_type_close(fwcfg_close); 61 static dev_type_read(fwcfg_read); 62 static dev_type_ioctl(fwcfg_ioctl); 63 64 #define FWCFGUNIT(d) minor(d) 65 66 static void 67 fwcfg_select(struct fwcfg_softc *sc, uint16_t index) 68 { 69 bus_space_write_2(sc->sc_bst, sc->sc_bsh, FWCFG_SEL_REG, FWCFG_SEL_SWAP(index)); 70 } 71 72 static int 73 fwcfg_open(dev_t dev, int flag, int mode, lwp_t *l) 74 { 75 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 76 int error; 77 78 if (sc == NULL) 79 return ENXIO; 80 81 mutex_enter(&sc->sc_lock); 82 if (!sc->sc_open) { 83 error = 0; 84 sc->sc_open = true; 85 } else 86 error = EBUSY; 87 mutex_exit(&sc->sc_lock); 88 89 return error; 90 } 91 92 static int 93 fwcfg_close(dev_t dev, int flag, int mode, lwp_t *l) 94 { 95 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 96 int error; 97 98 if (sc == NULL) 99 return ENXIO; 100 101 mutex_enter(&sc->sc_lock); 102 if (sc->sc_open) { 103 error = 0; 104 sc->sc_open = false; 105 } else 106 error = EINVAL; 107 mutex_exit(&sc->sc_lock); 108 109 return error; 110 } 111 112 static int 113 fwcfg_read(dev_t dev, struct uio *uio, int flags) 114 { 115 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 116 uint8_t buf[64]; 117 size_t count; 118 int error = 0; 119 120 if (sc == NULL) 121 return ENXIO; 122 123 while (uio->uio_resid > 0) { 124 count = uimin(sizeof(buf), uio->uio_resid); 125 bus_space_read_multi_1(sc->sc_bst, sc->sc_bsh, FWCFG_DATA_REG, buf, count); 126 error = uiomove(buf, count, uio); 127 if (error != 0) 128 break; 129 } 130 131 return error; 132 } 133 134 static int 135 fwcfg_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 136 { 137 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 138 uint16_t index; 139 140 if (sc == NULL) 141 return ENXIO; 142 143 switch (cmd) { 144 case FWCFGIO_SET_INDEX: 145 index = *(uint16_t *)data; 146 fwcfg_select(sc, index); 147 return 0; 148 default: 149 return ENOTTY; 150 } 151 } 152 153 const struct cdevsw qemufwcfg_cdevsw = { 154 .d_open = fwcfg_open, 155 .d_close = fwcfg_close, 156 .d_read = fwcfg_read, 157 .d_write = nowrite, 158 .d_ioctl = fwcfg_ioctl, 159 .d_stop = nostop, 160 .d_tty = notty, 161 .d_poll = nopoll, 162 .d_mmap = nommap, 163 .d_kqfilter = nokqfilter, 164 .d_discard = nodiscard, 165 .d_flag = D_OTHER | D_MPSAFE 166 }; 167 168 void 169 fwcfg_attach(struct fwcfg_softc *sc) 170 { 171 char sig[4]; 172 173 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 174 175 /* Read signature */ 176 fwcfg_select(sc, FW_CFG_SIGNATURE); 177 bus_space_read_multi_1(sc->sc_bst, sc->sc_bsh, FWCFG_DATA_REG, sig, sizeof(sig)); 178 aprint_verbose_dev(sc->sc_dev, "<%c%c%c%c>\n", sig[0], sig[1], sig[2], sig[3]); 179 } 180