1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Justin Hibbits 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/module.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/disk.h> 36 #include <sys/kernel.h> 37 #include <sys/mutex.h> 38 #include <sys/uio.h> 39 40 #include <dev/ofw/openfirm.h> 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include <machine/bus.h> 45 #include <machine/md_var.h> 46 #include <machine/pio.h> 47 #include <machine/resource.h> 48 49 #include "opal.h" 50 51 #include <sys/rman.h> 52 53 #include <vm/vm.h> 54 #include <vm/pmap.h> 55 56 #define NVRAM_BUFSIZE (65536) /* 64k blocks */ 57 58 struct opal_nvram_softc { 59 device_t sc_dev; 60 struct mtx sc_mtx; 61 uint32_t sc_size; 62 uint8_t *sc_buf; 63 vm_paddr_t sc_buf_phys; 64 65 struct cdev *sc_cdev; 66 int sc_isopen; 67 }; 68 69 #define NVRAM_LOCK(sc) mtx_lock(&sc->sc_mtx) 70 #define NVRAM_UNLOCK(sc) mtx_unlock(&sc->sc_mtx) 71 72 /* 73 * Device interface. 74 */ 75 static int opal_nvram_probe(device_t); 76 static int opal_nvram_attach(device_t); 77 static int opal_nvram_detach(device_t); 78 79 /* 80 * Driver methods. 81 */ 82 static device_method_t opal_nvram_methods[] = { 83 /* Device interface */ 84 DEVMETHOD(device_probe, opal_nvram_probe), 85 DEVMETHOD(device_attach, opal_nvram_attach), 86 DEVMETHOD(device_detach, opal_nvram_detach), 87 88 { 0, 0 } 89 }; 90 91 static driver_t opal_nvram_driver = { 92 "opal_nvram", 93 opal_nvram_methods, 94 sizeof(struct opal_nvram_softc) 95 }; 96 97 static devclass_t opal_nvram_devclass; 98 99 DRIVER_MODULE(opal_nvram, opal, opal_nvram_driver, opal_nvram_devclass, 0, 0); 100 101 /* 102 * Cdev methods. 103 */ 104 105 static d_open_t opal_nvram_open; 106 static d_close_t opal_nvram_close; 107 static d_read_t opal_nvram_read; 108 static d_write_t opal_nvram_write; 109 static d_ioctl_t opal_nvram_ioctl; 110 111 static struct cdevsw opal_nvram_cdevsw = { 112 .d_version = D_VERSION, 113 .d_open = opal_nvram_open, 114 .d_close = opal_nvram_close, 115 .d_read = opal_nvram_read, 116 .d_write = opal_nvram_write, 117 .d_ioctl = opal_nvram_ioctl, 118 .d_name = "nvram", 119 }; 120 121 static int 122 opal_nvram_probe(device_t dev) 123 { 124 125 if (!ofw_bus_is_compatible(dev, "ibm,opal-nvram")) 126 return (ENXIO); 127 128 device_set_desc(dev, "OPAL NVRAM"); 129 return (BUS_PROBE_DEFAULT); 130 } 131 132 static int 133 opal_nvram_attach(device_t dev) 134 { 135 struct opal_nvram_softc *sc; 136 phandle_t node; 137 int err; 138 139 node = ofw_bus_get_node(dev); 140 sc = device_get_softc(dev); 141 142 sc->sc_dev = dev; 143 144 err = OF_getencprop(node, "#bytes", &sc->sc_size, 145 sizeof(sc->sc_size)); 146 147 if (err < 0) 148 return (ENXIO); 149 150 sc->sc_buf = contigmalloc(NVRAM_BUFSIZE, M_DEVBUF, M_WAITOK, 151 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 152 if (sc->sc_buf == NULL) { 153 device_printf(dev, "No memory for buffer.\n"); 154 return (ENXIO); 155 } 156 sc->sc_buf_phys = pmap_kextract((vm_offset_t)sc->sc_buf); 157 sc->sc_cdev = make_dev(&opal_nvram_cdevsw, 0, 0, 0, 0600, 158 "nvram"); 159 sc->sc_cdev->si_drv1 = sc; 160 161 mtx_init(&sc->sc_mtx, "opal_nvram", 0, MTX_DEF); 162 163 return (0); 164 } 165 166 static int 167 opal_nvram_detach(device_t dev) 168 { 169 struct opal_nvram_softc *sc; 170 171 sc = device_get_softc(dev); 172 173 if (sc->sc_cdev != NULL) 174 destroy_dev(sc->sc_cdev); 175 if (sc->sc_buf != NULL) 176 contigfree(sc->sc_buf, NVRAM_BUFSIZE, M_DEVBUF); 177 178 mtx_destroy(&sc->sc_mtx); 179 180 return (0); 181 } 182 183 static int 184 opal_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td) 185 { 186 struct opal_nvram_softc *sc = dev->si_drv1; 187 int err; 188 189 err = 0; 190 191 NVRAM_LOCK(sc); 192 if (sc->sc_isopen) 193 err = EBUSY; 194 else 195 sc->sc_isopen = 1; 196 NVRAM_UNLOCK(sc); 197 198 return (err); 199 } 200 201 static int 202 opal_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 203 { 204 struct opal_nvram_softc *sc = dev->si_drv1; 205 206 NVRAM_LOCK(sc); 207 sc->sc_isopen = 0; 208 NVRAM_UNLOCK(sc); 209 210 return (0); 211 } 212 213 static int 214 opal_nvram_read(struct cdev *dev, struct uio *uio, int ioflag) 215 { 216 struct opal_nvram_softc *sc = dev->si_drv1; 217 int rv, amnt; 218 219 rv = 0; 220 221 NVRAM_LOCK(sc); 222 while (uio->uio_resid > 0) { 223 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 224 amnt = MIN(amnt, NVRAM_BUFSIZE); 225 if (amnt == 0) 226 break; 227 228 rv = opal_call(OPAL_READ_NVRAM, sc->sc_buf_phys, 229 amnt, uio->uio_offset); 230 if (rv != OPAL_SUCCESS) { 231 switch (rv) { 232 case OPAL_HARDWARE: 233 rv = EIO; 234 break; 235 case OPAL_PARAMETER: 236 rv = EINVAL; 237 break; 238 } 239 break; 240 } 241 rv = uiomove(sc->sc_buf, amnt, uio); 242 if (rv != 0) 243 break; 244 } 245 NVRAM_UNLOCK(sc); 246 247 return (rv); 248 } 249 250 static int 251 opal_nvram_write(struct cdev *dev, struct uio *uio, int ioflag) 252 { 253 off_t offset; 254 int rv, amnt; 255 struct opal_nvram_softc *sc = dev->si_drv1; 256 257 rv = 0; 258 259 NVRAM_LOCK(sc); 260 while (uio->uio_resid > 0) { 261 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 262 amnt = MIN(amnt, NVRAM_BUFSIZE); 263 if (amnt == 0) { 264 rv = ENOSPC; 265 break; 266 } 267 offset = uio->uio_offset; 268 rv = uiomove(sc->sc_buf, amnt, uio); 269 if (rv != 0) 270 break; 271 rv = opal_call(OPAL_WRITE_NVRAM, sc->sc_buf_phys, amnt, 272 offset); 273 if (rv != OPAL_SUCCESS) { 274 switch (rv) { 275 case OPAL_HARDWARE: 276 rv = EIO; 277 break; 278 case OPAL_PARAMETER: 279 rv = EINVAL; 280 break; 281 } 282 break; 283 } 284 } 285 286 NVRAM_UNLOCK(sc); 287 288 return (rv); 289 } 290 291 static int 292 opal_nvram_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 293 struct thread *td) 294 { 295 struct opal_nvram_softc *sc = dev->si_drv1; 296 297 switch (cmd) { 298 case DIOCGMEDIASIZE: 299 *(off_t *)data = sc->sc_size; 300 return (0); 301 } 302 return (EINVAL); 303 } 304