1 /* $NetBSD: qemufwcfg.c,v 1.3 2024/04/06 13:42:18 skrll 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.3 2024/04/06 13:42:18 skrll 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 #elif defined(__riscv) 56 #define FWCFG_SEL_REG 0x08 57 #define FWCFG_SEL_SWAP htobe16 58 #define FWCFG_DATA_REG 0x00 59 #define FWCFG_DMA_ADDR 0x10 60 #else 61 #error driver does not support this architecture 62 #endif 63 64 static dev_type_open(fwcfg_open); 65 static dev_type_close(fwcfg_close); 66 static dev_type_read(fwcfg_read); 67 static dev_type_ioctl(fwcfg_ioctl); 68 69 #define FWCFGUNIT(d) minor(d) 70 71 static void 72 fwcfg_select(struct fwcfg_softc *sc, uint16_t index) 73 { 74 bus_space_write_2(sc->sc_bst, sc->sc_bsh, FWCFG_SEL_REG, FWCFG_SEL_SWAP(index)); 75 } 76 77 static int 78 fwcfg_open(dev_t dev, int flag, int mode, lwp_t *l) 79 { 80 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 81 int error; 82 83 if (sc == NULL) 84 return ENXIO; 85 86 mutex_enter(&sc->sc_lock); 87 if (!sc->sc_open) { 88 error = 0; 89 sc->sc_open = true; 90 } else 91 error = EBUSY; 92 mutex_exit(&sc->sc_lock); 93 94 return error; 95 } 96 97 static int 98 fwcfg_close(dev_t dev, int flag, int mode, lwp_t *l) 99 { 100 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 101 int error; 102 103 if (sc == NULL) 104 return ENXIO; 105 106 mutex_enter(&sc->sc_lock); 107 if (sc->sc_open) { 108 error = 0; 109 sc->sc_open = false; 110 } else 111 error = EINVAL; 112 mutex_exit(&sc->sc_lock); 113 114 return error; 115 } 116 117 static int 118 fwcfg_read(dev_t dev, struct uio *uio, int flags) 119 { 120 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 121 uint8_t buf[64]; 122 size_t count; 123 int error = 0; 124 125 if (sc == NULL) 126 return ENXIO; 127 128 while (uio->uio_resid > 0) { 129 count = uimin(sizeof(buf), uio->uio_resid); 130 bus_space_read_multi_1(sc->sc_bst, sc->sc_bsh, FWCFG_DATA_REG, buf, count); 131 error = uiomove(buf, count, uio); 132 if (error != 0) 133 break; 134 } 135 136 return error; 137 } 138 139 static int 140 fwcfg_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 141 { 142 struct fwcfg_softc * const sc = device_lookup_private(&qemufwcfg_cd, FWCFGUNIT(dev)); 143 uint16_t index; 144 145 if (sc == NULL) 146 return ENXIO; 147 148 switch (cmd) { 149 case FWCFGIO_SET_INDEX: 150 index = *(uint16_t *)data; 151 fwcfg_select(sc, index); 152 return 0; 153 default: 154 return ENOTTY; 155 } 156 } 157 158 const struct cdevsw qemufwcfg_cdevsw = { 159 .d_open = fwcfg_open, 160 .d_close = fwcfg_close, 161 .d_read = fwcfg_read, 162 .d_write = nowrite, 163 .d_ioctl = fwcfg_ioctl, 164 .d_stop = nostop, 165 .d_tty = notty, 166 .d_poll = nopoll, 167 .d_mmap = nommap, 168 .d_kqfilter = nokqfilter, 169 .d_discard = nodiscard, 170 .d_flag = D_OTHER | D_MPSAFE 171 }; 172 173 void 174 fwcfg_attach(struct fwcfg_softc *sc) 175 { 176 char sig[4]; 177 178 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 179 180 /* Read signature */ 181 fwcfg_select(sc, FW_CFG_SIGNATURE); 182 bus_space_read_multi_1(sc->sc_bst, sc->sc_bsh, FWCFG_DATA_REG, sig, sizeof(sig)); 183 aprint_verbose_dev(sc->sc_dev, "<%c%c%c%c>\n", sig[0], sig[1], sig[2], sig[3]); 184 } 185