xref: /freebsd/sys/powerpc/powernv/opal_nvram.c (revision d1bdc2821fcd416ab9b238580386eb605a6128d0)
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