11c56203bSJustin Hibbits /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31c56203bSJustin Hibbits * 41c56203bSJustin Hibbits * Copyright (c) 2019 Justin Hibbits 51c56203bSJustin Hibbits * 61c56203bSJustin Hibbits * Redistribution and use in source and binary forms, with or without 71c56203bSJustin Hibbits * modification, are permitted provided that the following conditions 81c56203bSJustin Hibbits * are met: 91c56203bSJustin Hibbits * 1. Redistributions of source code must retain the above copyright 101c56203bSJustin Hibbits * notice, this list of conditions and the following disclaimer. 111c56203bSJustin Hibbits * 2. Redistributions in binary form must reproduce the above copyright 121c56203bSJustin Hibbits * notice, this list of conditions and the following disclaimer in the 131c56203bSJustin Hibbits * documentation and/or other materials provided with the distribution. 141c56203bSJustin Hibbits * 151c56203bSJustin Hibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 161c56203bSJustin Hibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 171c56203bSJustin Hibbits * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 181c56203bSJustin Hibbits * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 191c56203bSJustin Hibbits * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 201c56203bSJustin Hibbits * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 211c56203bSJustin Hibbits * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 221c56203bSJustin Hibbits * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 231c56203bSJustin Hibbits * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 241c56203bSJustin Hibbits * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 251c56203bSJustin Hibbits * POSSIBILITY OF SUCH DAMAGE. 261c56203bSJustin Hibbits */ 271c56203bSJustin Hibbits 281c56203bSJustin Hibbits #include <sys/param.h> 291c56203bSJustin Hibbits #include <sys/systm.h> 301c56203bSJustin Hibbits #include <sys/module.h> 311c56203bSJustin Hibbits #include <sys/bus.h> 321c56203bSJustin Hibbits #include <sys/conf.h> 331c56203bSJustin Hibbits #include <sys/disk.h> 341c56203bSJustin Hibbits #include <sys/kernel.h> 3503b6e7a6SJustin Hibbits #include <sys/mutex.h> 361c56203bSJustin Hibbits #include <sys/uio.h> 371c56203bSJustin Hibbits 381c56203bSJustin Hibbits #include <dev/ofw/openfirm.h> 391c56203bSJustin Hibbits #include <dev/ofw/ofw_bus.h> 401c56203bSJustin Hibbits #include <dev/ofw/ofw_bus_subr.h> 411c56203bSJustin Hibbits 421c56203bSJustin Hibbits #include <machine/bus.h> 431c56203bSJustin Hibbits #include <machine/md_var.h> 441c56203bSJustin Hibbits #include <machine/pio.h> 451c56203bSJustin Hibbits #include <machine/resource.h> 461c56203bSJustin Hibbits 471c56203bSJustin Hibbits #include "opal.h" 481c56203bSJustin Hibbits 491c56203bSJustin Hibbits #include <sys/rman.h> 501c56203bSJustin Hibbits 511c56203bSJustin Hibbits #include <vm/vm.h> 521c56203bSJustin Hibbits #include <vm/pmap.h> 531c56203bSJustin Hibbits 541c56203bSJustin Hibbits #define NVRAM_BUFSIZE (65536) /* 64k blocks */ 551c56203bSJustin Hibbits 561c56203bSJustin Hibbits struct opal_nvram_softc { 571c56203bSJustin Hibbits device_t sc_dev; 5803b6e7a6SJustin Hibbits struct mtx sc_mtx; 591c56203bSJustin Hibbits uint32_t sc_size; 601c56203bSJustin Hibbits uint8_t *sc_buf; 611c56203bSJustin Hibbits vm_paddr_t sc_buf_phys; 621c56203bSJustin Hibbits 631c56203bSJustin Hibbits struct cdev *sc_cdev; 641c56203bSJustin Hibbits int sc_isopen; 651c56203bSJustin Hibbits }; 661c56203bSJustin Hibbits 6703b6e7a6SJustin Hibbits #define NVRAM_LOCK(sc) mtx_lock(&sc->sc_mtx) 6803b6e7a6SJustin Hibbits #define NVRAM_UNLOCK(sc) mtx_unlock(&sc->sc_mtx) 6903b6e7a6SJustin Hibbits 701c56203bSJustin Hibbits /* 711c56203bSJustin Hibbits * Device interface. 721c56203bSJustin Hibbits */ 731c56203bSJustin Hibbits static int opal_nvram_probe(device_t); 741c56203bSJustin Hibbits static int opal_nvram_attach(device_t); 751c56203bSJustin Hibbits static int opal_nvram_detach(device_t); 761c56203bSJustin Hibbits 771c56203bSJustin Hibbits /* 781c56203bSJustin Hibbits * Driver methods. 791c56203bSJustin Hibbits */ 801c56203bSJustin Hibbits static device_method_t opal_nvram_methods[] = { 811c56203bSJustin Hibbits /* Device interface */ 821c56203bSJustin Hibbits DEVMETHOD(device_probe, opal_nvram_probe), 831c56203bSJustin Hibbits DEVMETHOD(device_attach, opal_nvram_attach), 841c56203bSJustin Hibbits DEVMETHOD(device_detach, opal_nvram_detach), 851c56203bSJustin Hibbits { 0, 0 } 861c56203bSJustin Hibbits }; 871c56203bSJustin Hibbits 881c56203bSJustin Hibbits static driver_t opal_nvram_driver = { 891c56203bSJustin Hibbits "opal_nvram", 901c56203bSJustin Hibbits opal_nvram_methods, 911c56203bSJustin Hibbits sizeof(struct opal_nvram_softc) 921c56203bSJustin Hibbits }; 931c56203bSJustin Hibbits 945edf159fSJohn Baldwin DRIVER_MODULE(opal_nvram, opal, opal_nvram_driver, 0, 0); 951c56203bSJustin Hibbits 961c56203bSJustin Hibbits /* 971c56203bSJustin Hibbits * Cdev methods. 981c56203bSJustin Hibbits */ 991c56203bSJustin Hibbits 1001c56203bSJustin Hibbits static d_open_t opal_nvram_open; 1011c56203bSJustin Hibbits static d_close_t opal_nvram_close; 1021c56203bSJustin Hibbits static d_read_t opal_nvram_read; 1031c56203bSJustin Hibbits static d_write_t opal_nvram_write; 1041c56203bSJustin Hibbits static d_ioctl_t opal_nvram_ioctl; 1051c56203bSJustin Hibbits 1061c56203bSJustin Hibbits static struct cdevsw opal_nvram_cdevsw = { 1071c56203bSJustin Hibbits .d_version = D_VERSION, 1081c56203bSJustin Hibbits .d_open = opal_nvram_open, 1091c56203bSJustin Hibbits .d_close = opal_nvram_close, 1101c56203bSJustin Hibbits .d_read = opal_nvram_read, 1111c56203bSJustin Hibbits .d_write = opal_nvram_write, 1121c56203bSJustin Hibbits .d_ioctl = opal_nvram_ioctl, 1131c56203bSJustin Hibbits .d_name = "nvram", 1141c56203bSJustin Hibbits }; 1151c56203bSJustin Hibbits 1161c56203bSJustin Hibbits static int 1171c56203bSJustin Hibbits opal_nvram_probe(device_t dev) 1181c56203bSJustin Hibbits { 1191c56203bSJustin Hibbits 1201c56203bSJustin Hibbits if (!ofw_bus_is_compatible(dev, "ibm,opal-nvram")) 1211c56203bSJustin Hibbits return (ENXIO); 1221c56203bSJustin Hibbits 1231c56203bSJustin Hibbits device_set_desc(dev, "OPAL NVRAM"); 1241c56203bSJustin Hibbits return (BUS_PROBE_DEFAULT); 1251c56203bSJustin Hibbits } 1261c56203bSJustin Hibbits 1271c56203bSJustin Hibbits static int 1281c56203bSJustin Hibbits opal_nvram_attach(device_t dev) 1291c56203bSJustin Hibbits { 1301c56203bSJustin Hibbits struct opal_nvram_softc *sc; 1311c56203bSJustin Hibbits phandle_t node; 1321c56203bSJustin Hibbits int err; 1331c56203bSJustin Hibbits 1341c56203bSJustin Hibbits node = ofw_bus_get_node(dev); 1351c56203bSJustin Hibbits sc = device_get_softc(dev); 1361c56203bSJustin Hibbits 1371c56203bSJustin Hibbits sc->sc_dev = dev; 1381c56203bSJustin Hibbits 1391c56203bSJustin Hibbits err = OF_getencprop(node, "#bytes", &sc->sc_size, 1401c56203bSJustin Hibbits sizeof(sc->sc_size)); 1411c56203bSJustin Hibbits 1421c56203bSJustin Hibbits if (err < 0) 1431c56203bSJustin Hibbits return (ENXIO); 1441c56203bSJustin Hibbits 1451c56203bSJustin Hibbits sc->sc_buf = contigmalloc(NVRAM_BUFSIZE, M_DEVBUF, M_WAITOK, 1461c56203bSJustin Hibbits 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 1471c56203bSJustin Hibbits if (sc->sc_buf == NULL) { 1481c56203bSJustin Hibbits device_printf(dev, "No memory for buffer.\n"); 1491c56203bSJustin Hibbits return (ENXIO); 1501c56203bSJustin Hibbits } 1511c56203bSJustin Hibbits sc->sc_buf_phys = pmap_kextract((vm_offset_t)sc->sc_buf); 1521c56203bSJustin Hibbits sc->sc_cdev = make_dev(&opal_nvram_cdevsw, 0, 0, 0, 0600, 1531c56203bSJustin Hibbits "nvram"); 1541c56203bSJustin Hibbits sc->sc_cdev->si_drv1 = sc; 1551c56203bSJustin Hibbits 15603b6e7a6SJustin Hibbits mtx_init(&sc->sc_mtx, "opal_nvram", 0, MTX_DEF); 15703b6e7a6SJustin Hibbits 1581c56203bSJustin Hibbits return (0); 1591c56203bSJustin Hibbits } 1601c56203bSJustin Hibbits 1611c56203bSJustin Hibbits static int 1621c56203bSJustin Hibbits opal_nvram_detach(device_t dev) 1631c56203bSJustin Hibbits { 1641c56203bSJustin Hibbits struct opal_nvram_softc *sc; 1651c56203bSJustin Hibbits 1661c56203bSJustin Hibbits sc = device_get_softc(dev); 1671c56203bSJustin Hibbits 1681c56203bSJustin Hibbits if (sc->sc_cdev != NULL) 1691c56203bSJustin Hibbits destroy_dev(sc->sc_cdev); 170*d1bdc282SBjoern A. Zeeb free(sc->sc_buf, M_DEVBUF); 1711c56203bSJustin Hibbits 17203b6e7a6SJustin Hibbits mtx_destroy(&sc->sc_mtx); 17303b6e7a6SJustin Hibbits 1741c56203bSJustin Hibbits return (0); 1751c56203bSJustin Hibbits } 1761c56203bSJustin Hibbits 1771c56203bSJustin Hibbits static int 1781c56203bSJustin Hibbits opal_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td) 1791c56203bSJustin Hibbits { 1801c56203bSJustin Hibbits struct opal_nvram_softc *sc = dev->si_drv1; 18103b6e7a6SJustin Hibbits int err; 1821c56203bSJustin Hibbits 18303b6e7a6SJustin Hibbits err = 0; 18403b6e7a6SJustin Hibbits 18503b6e7a6SJustin Hibbits NVRAM_LOCK(sc); 1861c56203bSJustin Hibbits if (sc->sc_isopen) 18703b6e7a6SJustin Hibbits err = EBUSY; 18803b6e7a6SJustin Hibbits else 1891c56203bSJustin Hibbits sc->sc_isopen = 1; 19003b6e7a6SJustin Hibbits NVRAM_UNLOCK(sc); 19103b6e7a6SJustin Hibbits 19203b6e7a6SJustin Hibbits return (err); 1931c56203bSJustin Hibbits } 1941c56203bSJustin Hibbits 1951c56203bSJustin Hibbits static int 1961c56203bSJustin Hibbits opal_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 1971c56203bSJustin Hibbits { 1981c56203bSJustin Hibbits struct opal_nvram_softc *sc = dev->si_drv1; 1991c56203bSJustin Hibbits 20003b6e7a6SJustin Hibbits NVRAM_LOCK(sc); 2011c56203bSJustin Hibbits sc->sc_isopen = 0; 20203b6e7a6SJustin Hibbits NVRAM_UNLOCK(sc); 20303b6e7a6SJustin Hibbits 2041c56203bSJustin Hibbits return (0); 2051c56203bSJustin Hibbits } 2061c56203bSJustin Hibbits 2071c56203bSJustin Hibbits static int 2081c56203bSJustin Hibbits opal_nvram_read(struct cdev *dev, struct uio *uio, int ioflag) 2091c56203bSJustin Hibbits { 2101c56203bSJustin Hibbits struct opal_nvram_softc *sc = dev->si_drv1; 2111c56203bSJustin Hibbits int rv, amnt; 2121c56203bSJustin Hibbits 2131c56203bSJustin Hibbits rv = 0; 21403b6e7a6SJustin Hibbits 21503b6e7a6SJustin Hibbits NVRAM_LOCK(sc); 2161c56203bSJustin Hibbits while (uio->uio_resid > 0) { 2171c56203bSJustin Hibbits amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 2181c56203bSJustin Hibbits amnt = MIN(amnt, NVRAM_BUFSIZE); 2191c56203bSJustin Hibbits if (amnt == 0) 2201c56203bSJustin Hibbits break; 2211c56203bSJustin Hibbits 2221c56203bSJustin Hibbits rv = opal_call(OPAL_READ_NVRAM, sc->sc_buf_phys, 2231c56203bSJustin Hibbits amnt, uio->uio_offset); 2241c56203bSJustin Hibbits if (rv != OPAL_SUCCESS) { 2251c56203bSJustin Hibbits switch (rv) { 2261c56203bSJustin Hibbits case OPAL_HARDWARE: 2271c56203bSJustin Hibbits rv = EIO; 2281c56203bSJustin Hibbits break; 2291c56203bSJustin Hibbits case OPAL_PARAMETER: 2301c56203bSJustin Hibbits rv = EINVAL; 2311c56203bSJustin Hibbits break; 2321c56203bSJustin Hibbits } 2331c56203bSJustin Hibbits break; 2341c56203bSJustin Hibbits } 2351c56203bSJustin Hibbits rv = uiomove(sc->sc_buf, amnt, uio); 2361c56203bSJustin Hibbits if (rv != 0) 2371c56203bSJustin Hibbits break; 2381c56203bSJustin Hibbits } 23903b6e7a6SJustin Hibbits NVRAM_UNLOCK(sc); 24003b6e7a6SJustin Hibbits 2411c56203bSJustin Hibbits return (rv); 2421c56203bSJustin Hibbits } 2431c56203bSJustin Hibbits 2441c56203bSJustin Hibbits static int 2451c56203bSJustin Hibbits opal_nvram_write(struct cdev *dev, struct uio *uio, int ioflag) 2461c56203bSJustin Hibbits { 2471c56203bSJustin Hibbits off_t offset; 2481c56203bSJustin Hibbits int rv, amnt; 2491c56203bSJustin Hibbits struct opal_nvram_softc *sc = dev->si_drv1; 2501c56203bSJustin Hibbits 2511c56203bSJustin Hibbits rv = 0; 25203b6e7a6SJustin Hibbits 25303b6e7a6SJustin Hibbits NVRAM_LOCK(sc); 2541c56203bSJustin Hibbits while (uio->uio_resid > 0) { 2551c56203bSJustin Hibbits amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 2561c56203bSJustin Hibbits amnt = MIN(amnt, NVRAM_BUFSIZE); 2571c56203bSJustin Hibbits if (amnt == 0) { 2581c56203bSJustin Hibbits rv = ENOSPC; 2591c56203bSJustin Hibbits break; 2601c56203bSJustin Hibbits } 2611c56203bSJustin Hibbits offset = uio->uio_offset; 2621c56203bSJustin Hibbits rv = uiomove(sc->sc_buf, amnt, uio); 2631c56203bSJustin Hibbits if (rv != 0) 2641c56203bSJustin Hibbits break; 2651c56203bSJustin Hibbits rv = opal_call(OPAL_WRITE_NVRAM, sc->sc_buf_phys, amnt, 2661c56203bSJustin Hibbits offset); 2671c56203bSJustin Hibbits if (rv != OPAL_SUCCESS) { 2681c56203bSJustin Hibbits switch (rv) { 2691c56203bSJustin Hibbits case OPAL_HARDWARE: 2701c56203bSJustin Hibbits rv = EIO; 2711c56203bSJustin Hibbits break; 2721c56203bSJustin Hibbits case OPAL_PARAMETER: 2731c56203bSJustin Hibbits rv = EINVAL; 2741c56203bSJustin Hibbits break; 2751c56203bSJustin Hibbits } 2761c56203bSJustin Hibbits break; 2771c56203bSJustin Hibbits } 2781c56203bSJustin Hibbits } 27903b6e7a6SJustin Hibbits 28003b6e7a6SJustin Hibbits NVRAM_UNLOCK(sc); 28103b6e7a6SJustin Hibbits 2821c56203bSJustin Hibbits return (rv); 2831c56203bSJustin Hibbits } 2841c56203bSJustin Hibbits 2851c56203bSJustin Hibbits static int 2861c56203bSJustin Hibbits opal_nvram_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 2871c56203bSJustin Hibbits struct thread *td) 2881c56203bSJustin Hibbits { 2891c56203bSJustin Hibbits struct opal_nvram_softc *sc = dev->si_drv1; 2901c56203bSJustin Hibbits 2911c56203bSJustin Hibbits switch (cmd) { 2921c56203bSJustin Hibbits case DIOCGMEDIASIZE: 2931c56203bSJustin Hibbits *(off_t *)data = sc->sc_size; 2941c56203bSJustin Hibbits return (0); 2951c56203bSJustin Hibbits } 2961c56203bSJustin Hibbits return (EINVAL); 2971c56203bSJustin Hibbits } 298