xref: /freebsd/sys/dev/iicbus/pmic/act8846.c (revision b2f0caf1)
14388c70cSMichal Meloun /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
34388c70cSMichal Meloun  *
44388c70cSMichal Meloun  * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
54388c70cSMichal Meloun  *
64388c70cSMichal Meloun  * Redistribution and use in source and binary forms, with or without
74388c70cSMichal Meloun  * modification, are permitted provided that the following conditions
84388c70cSMichal Meloun  * are met:
94388c70cSMichal Meloun  * 1. Redistributions of source code must retain the above copyright
104388c70cSMichal Meloun  *    notice, this list of conditions and the following disclaimer.
114388c70cSMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
124388c70cSMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
134388c70cSMichal Meloun  *    documentation and/or other materials provided with the distribution.
144388c70cSMichal Meloun  *
154388c70cSMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164388c70cSMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174388c70cSMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184388c70cSMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194388c70cSMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204388c70cSMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214388c70cSMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224388c70cSMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234388c70cSMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244388c70cSMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254388c70cSMichal Meloun  * SUCH DAMAGE.
264388c70cSMichal Meloun  */
274388c70cSMichal Meloun 
284388c70cSMichal Meloun #include <sys/cdefs.h>
294388c70cSMichal Meloun /*
304388c70cSMichal Meloun  * ACT8846 PMIC driver
314388c70cSMichal Meloun  */
324388c70cSMichal Meloun 
334388c70cSMichal Meloun #include <sys/param.h>
344388c70cSMichal Meloun #include <sys/systm.h>
354388c70cSMichal Meloun #include <sys/bus.h>
364388c70cSMichal Meloun #include <sys/kernel.h>
374388c70cSMichal Meloun #include <sys/module.h>
384388c70cSMichal Meloun #include <sys/malloc.h>
394388c70cSMichal Meloun #include <sys/rman.h>
404388c70cSMichal Meloun #include <sys/sx.h>
414388c70cSMichal Meloun 
424388c70cSMichal Meloun #include <machine/bus.h>
434388c70cSMichal Meloun 
44*b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
454388c70cSMichal Meloun #include <dev/fdt/fdt_pinctrl.h>
464388c70cSMichal Meloun #include <dev/iicbus/iiconf.h>
474388c70cSMichal Meloun #include <dev/iicbus/iicbus.h>
484388c70cSMichal Meloun #include <dev/ofw/ofw_bus.h>
494388c70cSMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
504388c70cSMichal Meloun 
514388c70cSMichal Meloun #include <dev/iicbus/pmic/act8846.h>
524388c70cSMichal Meloun 
534388c70cSMichal Meloun #include "regdev_if.h"
544388c70cSMichal Meloun 
554388c70cSMichal Meloun static struct ofw_compat_data compat_data[] = {
564388c70cSMichal Meloun 	{"active-semi,act8846",	1},
574388c70cSMichal Meloun 	{NULL,			0}
584388c70cSMichal Meloun };
594388c70cSMichal Meloun 
604388c70cSMichal Meloun #define	LOCK(_sc)		sx_xlock(&(_sc)->lock)
614388c70cSMichal Meloun #define	UNLOCK(_sc)		sx_xunlock(&(_sc)->lock)
624388c70cSMichal Meloun #define	LOCK_INIT(_sc)		sx_init(&(_sc)->lock, "act8846")
634388c70cSMichal Meloun #define	LOCK_DESTROY(_sc)	sx_destroy(&(_sc)->lock);
644388c70cSMichal Meloun #define	ASSERT_LOCKED(_sc)	sx_assert(&(_sc)->lock, SA_XLOCKED);
654388c70cSMichal Meloun #define	ASSERT_UNLOCKED(_sc)	sx_assert(&(_sc)->lock, SA_UNLOCKED);
664388c70cSMichal Meloun 
674388c70cSMichal Meloun 
684388c70cSMichal Meloun /*
694388c70cSMichal Meloun  * Raw register access function.
704388c70cSMichal Meloun  */
714388c70cSMichal Meloun int
act8846_read(struct act8846_softc * sc,uint8_t reg,uint8_t * val)724388c70cSMichal Meloun act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val)
734388c70cSMichal Meloun {
744388c70cSMichal Meloun 	uint8_t addr;
754388c70cSMichal Meloun 	int rv;
764388c70cSMichal Meloun 	struct iic_msg msgs[2] = {
774388c70cSMichal Meloun 		{0, IIC_M_WR, 1, &addr},
784388c70cSMichal Meloun 		{0, IIC_M_RD, 1, val},
794388c70cSMichal Meloun 	};
804388c70cSMichal Meloun 
814388c70cSMichal Meloun 	msgs[0].slave = sc->bus_addr;
824388c70cSMichal Meloun 	msgs[1].slave = sc->bus_addr;
834388c70cSMichal Meloun 	addr = reg;
844388c70cSMichal Meloun 
854388c70cSMichal Meloun 	rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
864388c70cSMichal Meloun 	if (rv != 0) {
874388c70cSMichal Meloun 		device_printf(sc->dev,
884388c70cSMichal Meloun 		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
894388c70cSMichal Meloun 		return (EIO);
904388c70cSMichal Meloun 	}
914388c70cSMichal Meloun 
924388c70cSMichal Meloun 	return (0);
934388c70cSMichal Meloun }
944388c70cSMichal Meloun 
act8846_read_buf(struct act8846_softc * sc,uint8_t reg,uint8_t * buf,size_t size)954388c70cSMichal Meloun int act8846_read_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf,
964388c70cSMichal Meloun     size_t size)
974388c70cSMichal Meloun {
984388c70cSMichal Meloun 	uint8_t addr;
994388c70cSMichal Meloun 	int rv;
1004388c70cSMichal Meloun 	struct iic_msg msgs[2] = {
1014388c70cSMichal Meloun 		{0, IIC_M_WR, 1, &addr},
1024388c70cSMichal Meloun 		{0, IIC_M_RD, size, buf},
1034388c70cSMichal Meloun 	};
1044388c70cSMichal Meloun 
1054388c70cSMichal Meloun 	msgs[0].slave = sc->bus_addr;
1064388c70cSMichal Meloun 	msgs[1].slave = sc->bus_addr;
1074388c70cSMichal Meloun 	addr = reg;
1084388c70cSMichal Meloun 
1094388c70cSMichal Meloun 	rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
1104388c70cSMichal Meloun 	if (rv != 0) {
1114388c70cSMichal Meloun 		device_printf(sc->dev,
1124388c70cSMichal Meloun 		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
1134388c70cSMichal Meloun 		return (EIO);
1144388c70cSMichal Meloun 	}
1154388c70cSMichal Meloun 
1164388c70cSMichal Meloun 	return (0);
1174388c70cSMichal Meloun }
1184388c70cSMichal Meloun 
1194388c70cSMichal Meloun int
act8846_write(struct act8846_softc * sc,uint8_t reg,uint8_t val)1204388c70cSMichal Meloun act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val)
1214388c70cSMichal Meloun {
1224388c70cSMichal Meloun 	uint8_t data[2];
1234388c70cSMichal Meloun 	int rv;
1244388c70cSMichal Meloun 
1254388c70cSMichal Meloun 	struct iic_msg msgs[1] = {
1264388c70cSMichal Meloun 		{0, IIC_M_WR, 2, data},
1274388c70cSMichal Meloun 	};
1284388c70cSMichal Meloun 
1294388c70cSMichal Meloun 	msgs[0].slave = sc->bus_addr;
1304388c70cSMichal Meloun 	data[0] = reg;
1314388c70cSMichal Meloun 	data[1] = val;
1324388c70cSMichal Meloun 
1334388c70cSMichal Meloun 	rv = iicbus_transfer_excl(sc->dev, msgs, 1, IIC_INTRWAIT);
1344388c70cSMichal Meloun 	if (rv != 0) {
1354388c70cSMichal Meloun 		device_printf(sc->dev,
1364388c70cSMichal Meloun 		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
1374388c70cSMichal Meloun 		return (EIO);
1384388c70cSMichal Meloun 	}
1394388c70cSMichal Meloun 	return (0);
1404388c70cSMichal Meloun }
1414388c70cSMichal Meloun 
act8846_write_buf(struct act8846_softc * sc,uint8_t reg,uint8_t * buf,size_t size)1424388c70cSMichal Meloun int act8846_write_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf,
1434388c70cSMichal Meloun     size_t size)
1444388c70cSMichal Meloun {
1454388c70cSMichal Meloun 	uint8_t data[1];
1464388c70cSMichal Meloun 	int rv;
1474388c70cSMichal Meloun 	struct iic_msg msgs[2] = {
1484388c70cSMichal Meloun 		{0, IIC_M_WR, 1, data},
1494388c70cSMichal Meloun 		{0, IIC_M_WR | IIC_M_NOSTART, size, buf},
1504388c70cSMichal Meloun 	};
1514388c70cSMichal Meloun 
1524388c70cSMichal Meloun 	msgs[0].slave = sc->bus_addr;
1534388c70cSMichal Meloun 	msgs[1].slave = sc->bus_addr;
1544388c70cSMichal Meloun 	data[0] = reg;
1554388c70cSMichal Meloun 
1564388c70cSMichal Meloun 	rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
1574388c70cSMichal Meloun 	if (rv != 0) {
1584388c70cSMichal Meloun 		device_printf(sc->dev,
1594388c70cSMichal Meloun 		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
1604388c70cSMichal Meloun 		return (EIO);
1614388c70cSMichal Meloun 	}
1624388c70cSMichal Meloun 	return (0);
1634388c70cSMichal Meloun }
1644388c70cSMichal Meloun 
1654388c70cSMichal Meloun int
act8846_modify(struct act8846_softc * sc,uint8_t reg,uint8_t clear,uint8_t set)1664388c70cSMichal Meloun act8846_modify(struct act8846_softc *sc, uint8_t reg, uint8_t clear, uint8_t set)
1674388c70cSMichal Meloun {
1684388c70cSMichal Meloun 	uint8_t val;
1694388c70cSMichal Meloun 	int rv;
1704388c70cSMichal Meloun 
1714388c70cSMichal Meloun 	rv = act8846_read(sc, reg, &val);
1724388c70cSMichal Meloun 	if (rv != 0)
1734388c70cSMichal Meloun 		return (rv);
1744388c70cSMichal Meloun 
1754388c70cSMichal Meloun 	val &= ~clear;
1764388c70cSMichal Meloun 	val |= set;
1774388c70cSMichal Meloun 
1784388c70cSMichal Meloun 	rv = act8846_write(sc, reg, val);
1794388c70cSMichal Meloun 	if (rv != 0)
1804388c70cSMichal Meloun 		return (rv);
1814388c70cSMichal Meloun 
1824388c70cSMichal Meloun 	return (0);
1834388c70cSMichal Meloun }
1844388c70cSMichal Meloun 
1854388c70cSMichal Meloun static int
act8846_probe(device_t dev)1864388c70cSMichal Meloun act8846_probe(device_t dev)
1874388c70cSMichal Meloun {
1884388c70cSMichal Meloun 
1894388c70cSMichal Meloun 	if (!ofw_bus_status_okay(dev))
1904388c70cSMichal Meloun 		return (ENXIO);
1914388c70cSMichal Meloun 
1924388c70cSMichal Meloun 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
1934388c70cSMichal Meloun 		return (ENXIO);
1944388c70cSMichal Meloun 
1954388c70cSMichal Meloun 	device_set_desc(dev, "ACT8846 PMIC");
1964388c70cSMichal Meloun 	return (BUS_PROBE_DEFAULT);
1974388c70cSMichal Meloun }
1984388c70cSMichal Meloun 
1994388c70cSMichal Meloun static int
act8846_attach(device_t dev)2004388c70cSMichal Meloun act8846_attach(device_t dev)
2014388c70cSMichal Meloun {
2024388c70cSMichal Meloun 	struct act8846_softc *sc;
2034388c70cSMichal Meloun 	int rv;
2044388c70cSMichal Meloun 	phandle_t node;
2054388c70cSMichal Meloun 
2064388c70cSMichal Meloun 	sc = device_get_softc(dev);
2074388c70cSMichal Meloun 	sc->dev = dev;
2084388c70cSMichal Meloun 	sc->bus_addr = iicbus_get_addr(dev);
2094388c70cSMichal Meloun 	node = ofw_bus_get_node(sc->dev);
2104388c70cSMichal Meloun 	rv = 0;
2114388c70cSMichal Meloun 	LOCK_INIT(sc);
2124388c70cSMichal Meloun 
2134388c70cSMichal Meloun 
2144388c70cSMichal Meloun 	rv = act8846_regulator_attach(sc, node);
2154388c70cSMichal Meloun 	if (rv != 0)
2164388c70cSMichal Meloun 		goto fail;
2174388c70cSMichal Meloun 
2184388c70cSMichal Meloun 	return (bus_generic_attach(dev));
2194388c70cSMichal Meloun 
2204388c70cSMichal Meloun fail:
2214388c70cSMichal Meloun 	LOCK_DESTROY(sc);
2224388c70cSMichal Meloun 	return (rv);
2234388c70cSMichal Meloun }
2244388c70cSMichal Meloun 
2254388c70cSMichal Meloun static int
act8846_detach(device_t dev)2264388c70cSMichal Meloun act8846_detach(device_t dev)
2274388c70cSMichal Meloun {
2284388c70cSMichal Meloun 	struct act8846_softc *sc;
2294388c70cSMichal Meloun 
2304388c70cSMichal Meloun 	sc = device_get_softc(dev);
2314388c70cSMichal Meloun 	LOCK_DESTROY(sc);
2324388c70cSMichal Meloun 
2334388c70cSMichal Meloun 	return (bus_generic_detach(dev));
2344388c70cSMichal Meloun }
2354388c70cSMichal Meloun 
2364388c70cSMichal Meloun static device_method_t act8846_methods[] = {
2374388c70cSMichal Meloun 	/* Device interface */
2384388c70cSMichal Meloun 	DEVMETHOD(device_probe,		act8846_probe),
2394388c70cSMichal Meloun 	DEVMETHOD(device_attach,	act8846_attach),
2404388c70cSMichal Meloun 	DEVMETHOD(device_detach,	act8846_detach),
2414388c70cSMichal Meloun 
2424388c70cSMichal Meloun 	/* Regdev interface */
2434388c70cSMichal Meloun 	DEVMETHOD(regdev_map,		act8846_regulator_map),
2444388c70cSMichal Meloun 
2454388c70cSMichal Meloun 	DEVMETHOD_END
2464388c70cSMichal Meloun };
2474388c70cSMichal Meloun 
2484388c70cSMichal Meloun static DEFINE_CLASS_0(act8846_pmu, act8846_driver, act8846_methods,
2494388c70cSMichal Meloun     sizeof(struct act8846_softc));
2503a866152SJohn Baldwin EARLY_DRIVER_MODULE(act8846_pmic, iicbus, act8846_driver,
2514388c70cSMichal Meloun     NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
2524388c70cSMichal Meloun MODULE_VERSION(act8846_pmic, 1);
2534388c70cSMichal Meloun MODULE_DEPEND(act8846_pmic, iicbus, IICBUS_MINVER, IICBUS_PREFVER,
2544388c70cSMichal Meloun     IICBUS_MAXVER);
2554388c70cSMichal Meloun IICBUS_FDT_PNP_INFO(compat_data);
256