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 64static dev_type_open(fwcfg_open); 65static dev_type_close(fwcfg_close); 66static dev_type_read(fwcfg_read); 67static dev_type_ioctl(fwcfg_ioctl); 68 69#define FWCFGUNIT(d) minor(d) 70 71static void 72fwcfg_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 77static int 78fwcfg_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 97static int 98fwcfg_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 117static int 118fwcfg_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 139static int 140fwcfg_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 158const 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 173void 174fwcfg_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