xref: /freebsd/sys/dev/dialog/da9063/da9063_iic.c (revision 4d846d26)
180466455SJessica Clarke /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
380466455SJessica Clarke  *
480466455SJessica Clarke  * Copyright (c) 2022 Jessica Clarke <jrtc27@FreeBSD.org>
580466455SJessica Clarke  *
680466455SJessica Clarke  * Redistribution and use in source and binary forms, with or without
780466455SJessica Clarke  * modification, are permitted provided that the following conditions
880466455SJessica Clarke  * are met:
980466455SJessica Clarke  * 1. Redistributions of source code must retain the above copyright
1080466455SJessica Clarke  *    notice, this list of conditions and the following disclaimer.
1180466455SJessica Clarke  * 2. Redistributions in binary form must reproduce the above copyright
1280466455SJessica Clarke  *    notice, this list of conditions and the following disclaimer in the
1380466455SJessica Clarke  *    documentation and/or other materials provided with the distribution.
1480466455SJessica Clarke  *
1580466455SJessica Clarke  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1680466455SJessica Clarke  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1780466455SJessica Clarke  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1880466455SJessica Clarke  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1980466455SJessica Clarke  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2080466455SJessica Clarke  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2180466455SJessica Clarke  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2280466455SJessica Clarke  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2380466455SJessica Clarke  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2480466455SJessica Clarke  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2580466455SJessica Clarke  * SUCH DAMAGE.
2680466455SJessica Clarke  */
2780466455SJessica Clarke 
2880466455SJessica Clarke /* Dialog Semiconductor DA9063 PMIC, 2-WIRE */
2980466455SJessica Clarke 
3080466455SJessica Clarke #include <sys/param.h>
3180466455SJessica Clarke #include <sys/kernel.h>
3280466455SJessica Clarke #include <sys/bus.h>
3380466455SJessica Clarke #include <sys/lock.h>
3480466455SJessica Clarke #include <sys/module.h>
3580466455SJessica Clarke #include <sys/mutex.h>
3680466455SJessica Clarke 
3780466455SJessica Clarke #include <dev/dialog/da9063/da9063reg.h>
3880466455SJessica Clarke #include <dev/fdt/simplebus.h>
3980466455SJessica Clarke #include <dev/iicbus/iiconf.h>
4080466455SJessica Clarke #include <dev/ofw/ofw_bus_subr.h>
4180466455SJessica Clarke 
4280466455SJessica Clarke #include "da9063_if.h"
4380466455SJessica Clarke 
4480466455SJessica Clarke #define	DA9063_IIC_PAGE_SHIFT	8
4580466455SJessica Clarke #define	DA9063_IIC_PAGE_SIZE	(1 << DA9063_IIC_PAGE_SHIFT)
4680466455SJessica Clarke #define	DA9063_IIC_PAGE(_a)	((_a) >> DA9063_IIC_PAGE_SHIFT)
4780466455SJessica Clarke #define	DA9063_IIC_PAGE_OFF(_a)	((_a) & (DA9063_IIC_PAGE_SIZE - 1))
4880466455SJessica Clarke #define	DA9063_IIC_ADDR(_p, _o)	(((_p) << DA9063_IIC_PAGE_SHIFT) | (_o))
4980466455SJessica Clarke 
5080466455SJessica Clarke /*
5180466455SJessica Clarke  * For 2-WIRE (I2C) operation pages are 256 registers but PAGE_CON is in units
5280466455SJessica Clarke  * of 128 registers with the LSB ignored so scale the page when writing to it.
5380466455SJessica Clarke  */
5480466455SJessica Clarke #define	DA9063_IIC_PAGE_CON_REG_PAGE_SHIFT	1
5580466455SJessica Clarke 
5680466455SJessica Clarke struct da9063_iic_softc {
5780466455SJessica Clarke 	struct simplebus_softc	simplebus_sc;
5880466455SJessica Clarke 	device_t		dev;
5980466455SJessica Clarke 	struct mtx		mtx;
6080466455SJessica Clarke 	uint8_t			page;
6180466455SJessica Clarke };
6280466455SJessica Clarke 
6380466455SJessica Clarke #define	DA9063_IIC_LOCK(sc)		mtx_lock(&(sc)->mtx)
6480466455SJessica Clarke #define	DA9063_IIC_UNLOCK(sc)		mtx_unlock(&(sc)->mtx)
6580466455SJessica Clarke #define	DA9063_IIC_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->mtx, MA_OWNED);
6680466455SJessica Clarke #define	DA9063_IIC_ASSERT_UNLOCKED(sc)	mtx_assert(&(sc)->mtx, MA_NOTOWNED);
6780466455SJessica Clarke 
6880466455SJessica Clarke static struct ofw_compat_data compat_data[] = {
6980466455SJessica Clarke 	{ "dlg,da9063",	1 },
7080466455SJessica Clarke 	{ NULL,		0 }
7180466455SJessica Clarke };
7280466455SJessica Clarke 
7380466455SJessica Clarke static int
da9063_iic_select_page(struct da9063_iic_softc * sc,uint16_t page)7480466455SJessica Clarke da9063_iic_select_page(struct da9063_iic_softc *sc, uint16_t page)
7580466455SJessica Clarke {
7680466455SJessica Clarke 	uint8_t reg;
7780466455SJessica Clarke 	int error;
7880466455SJessica Clarke 
7980466455SJessica Clarke 	DA9063_IIC_ASSERT_LOCKED(sc);
8080466455SJessica Clarke 
8180466455SJessica Clarke 	if (page == sc->page)
8280466455SJessica Clarke 		return (0);
8380466455SJessica Clarke 
8480466455SJessica Clarke 	error = iicdev_readfrom(sc->dev, DA9063_PAGE_CON, &reg, 1, IIC_WAIT);
8580466455SJessica Clarke 	if (error != 0)
8680466455SJessica Clarke 		return (iic2errno(error));
8780466455SJessica Clarke 
8880466455SJessica Clarke 	reg &= ~(DA9063_PAGE_CON_REG_PAGE_MASK <<
8980466455SJessica Clarke 	    DA9063_PAGE_CON_REG_PAGE_SHIFT);
9080466455SJessica Clarke 	reg |= (page << DA9063_IIC_PAGE_CON_REG_PAGE_SHIFT) <<
9180466455SJessica Clarke 	    DA9063_PAGE_CON_REG_PAGE_SHIFT;
9280466455SJessica Clarke 
9380466455SJessica Clarke 	error = iicdev_writeto(sc->dev, DA9063_PAGE_CON, &reg, 1, IIC_WAIT);
9480466455SJessica Clarke 	if (error != 0)
9580466455SJessica Clarke 		return (iic2errno(error));
9680466455SJessica Clarke 
9780466455SJessica Clarke 	sc->page = page;
9880466455SJessica Clarke 
9980466455SJessica Clarke 	return (0);
10080466455SJessica Clarke }
10180466455SJessica Clarke 
10280466455SJessica Clarke static int
da9063_iic_read(device_t dev,uint16_t addr,uint8_t * val)10380466455SJessica Clarke da9063_iic_read(device_t dev, uint16_t addr, uint8_t *val)
10480466455SJessica Clarke {
10580466455SJessica Clarke 	struct da9063_iic_softc *sc;
10680466455SJessica Clarke 	int error;
10780466455SJessica Clarke 
10880466455SJessica Clarke 	sc = device_get_softc(dev);
10980466455SJessica Clarke 
11080466455SJessica Clarke 	DA9063_IIC_LOCK(sc);
11180466455SJessica Clarke 
11280466455SJessica Clarke 	error = da9063_iic_select_page(sc, DA9063_IIC_PAGE(addr));
11380466455SJessica Clarke 	if (error != 0)
11480466455SJessica Clarke 		goto error;
11580466455SJessica Clarke 
11680466455SJessica Clarke 	error = iicdev_readfrom(dev, DA9063_IIC_PAGE_OFF(addr), val, 1,
11780466455SJessica Clarke 	    IIC_WAIT);
11880466455SJessica Clarke 	if (error != 0)
11980466455SJessica Clarke 		error = iic2errno(error);
12080466455SJessica Clarke 
12180466455SJessica Clarke error:
12280466455SJessica Clarke 	DA9063_IIC_UNLOCK(sc);
12380466455SJessica Clarke 
12480466455SJessica Clarke 	return (error);
12580466455SJessica Clarke }
12680466455SJessica Clarke 
12780466455SJessica Clarke static int
da9063_iic_write(device_t dev,uint16_t addr,uint8_t val)12880466455SJessica Clarke da9063_iic_write(device_t dev, uint16_t addr, uint8_t val)
12980466455SJessica Clarke {
13080466455SJessica Clarke 	struct da9063_iic_softc *sc;
13180466455SJessica Clarke 	int error;
13280466455SJessica Clarke 
13380466455SJessica Clarke 	sc = device_get_softc(dev);
13480466455SJessica Clarke 
13580466455SJessica Clarke 	DA9063_IIC_LOCK(sc);
13680466455SJessica Clarke 
13780466455SJessica Clarke 	error = da9063_iic_select_page(sc, DA9063_IIC_PAGE(addr));
13880466455SJessica Clarke 	if (error != 0)
13980466455SJessica Clarke 		goto error;
14080466455SJessica Clarke 
14180466455SJessica Clarke 	error = iicdev_writeto(dev, DA9063_IIC_PAGE_OFF(addr), &val, 1,
14280466455SJessica Clarke 	    IIC_WAIT);
14380466455SJessica Clarke 	if (error != 0)
14480466455SJessica Clarke 		error = iic2errno(error);
14580466455SJessica Clarke 
14680466455SJessica Clarke error:
14780466455SJessica Clarke 	DA9063_IIC_UNLOCK(sc);
14880466455SJessica Clarke 
14980466455SJessica Clarke 	return (error);
15080466455SJessica Clarke }
15180466455SJessica Clarke 
15280466455SJessica Clarke static int
da9063_iic_modify(device_t dev,uint16_t addr,uint8_t clear_mask,uint8_t set_mask)15380466455SJessica Clarke da9063_iic_modify(device_t dev, uint16_t addr, uint8_t clear_mask,
15480466455SJessica Clarke     uint8_t set_mask)
15580466455SJessica Clarke {
15680466455SJessica Clarke 	struct da9063_iic_softc *sc;
15780466455SJessica Clarke 	uint8_t reg;
15880466455SJessica Clarke 	int error;
15980466455SJessica Clarke 
16080466455SJessica Clarke 	sc = device_get_softc(dev);
16180466455SJessica Clarke 
16280466455SJessica Clarke 	DA9063_IIC_LOCK(sc);
16380466455SJessica Clarke 
16480466455SJessica Clarke 	error = da9063_iic_select_page(sc, DA9063_IIC_PAGE(addr));
16580466455SJessica Clarke 	if (error != 0)
16680466455SJessica Clarke 		goto error;
16780466455SJessica Clarke 
16880466455SJessica Clarke 	error = iicdev_readfrom(dev, DA9063_IIC_PAGE_OFF(addr), &reg, 1,
16980466455SJessica Clarke 	    IIC_WAIT);
17080466455SJessica Clarke 	if (error != 0) {
17180466455SJessica Clarke 		error = iic2errno(error);
17280466455SJessica Clarke 		goto error;
17380466455SJessica Clarke 	}
17480466455SJessica Clarke 
17580466455SJessica Clarke 	reg &= ~clear_mask;
17680466455SJessica Clarke 	reg |= set_mask;
17780466455SJessica Clarke 
17880466455SJessica Clarke 	error = iicdev_writeto(dev, DA9063_IIC_PAGE_OFF(addr), &reg, 1,
17980466455SJessica Clarke 	    IIC_WAIT);
18080466455SJessica Clarke 	if (error != 0)
18180466455SJessica Clarke 		error = iic2errno(error);
18280466455SJessica Clarke 
18380466455SJessica Clarke error:
18480466455SJessica Clarke 	DA9063_IIC_UNLOCK(sc);
18580466455SJessica Clarke 
18680466455SJessica Clarke 	return (error);
18780466455SJessica Clarke }
18880466455SJessica Clarke 
18980466455SJessica Clarke static int
da9063_iic_probe(device_t dev)19080466455SJessica Clarke da9063_iic_probe(device_t dev)
19180466455SJessica Clarke {
19280466455SJessica Clarke 	if (!ofw_bus_status_okay(dev))
19380466455SJessica Clarke 		return (ENXIO);
19480466455SJessica Clarke 
19580466455SJessica Clarke 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
19680466455SJessica Clarke 		return (ENXIO);
19780466455SJessica Clarke 
19880466455SJessica Clarke 	device_set_desc(dev, "Dialog DA9063 PMIC");
19980466455SJessica Clarke 
20080466455SJessica Clarke 	return (BUS_PROBE_DEFAULT);
20180466455SJessica Clarke }
20280466455SJessica Clarke 
20380466455SJessica Clarke static int
da9063_iic_attach(device_t dev)20480466455SJessica Clarke da9063_iic_attach(device_t dev)
20580466455SJessica Clarke {
20680466455SJessica Clarke 	struct da9063_iic_softc *sc;
20780466455SJessica Clarke 	uint8_t reg;
20880466455SJessica Clarke 	int error;
20980466455SJessica Clarke 
21080466455SJessica Clarke 	sc = device_get_softc(dev);
21180466455SJessica Clarke 
21280466455SJessica Clarke 	sc->dev = dev;
21380466455SJessica Clarke 
21480466455SJessica Clarke 	error = iicdev_readfrom(dev, DA9063_PAGE_CON, &reg, 1, IIC_WAIT);
21580466455SJessica Clarke 	if (error != 0)
21680466455SJessica Clarke 		return (iic2errno(error));
21780466455SJessica Clarke 
21880466455SJessica Clarke 	sc->page = ((reg >> DA9063_PAGE_CON_REG_PAGE_SHIFT) &
21980466455SJessica Clarke 	    DA9063_PAGE_CON_REG_PAGE_MASK) >> DA9063_IIC_PAGE_CON_REG_PAGE_SHIFT;
22080466455SJessica Clarke 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
22180466455SJessica Clarke 
22280466455SJessica Clarke 	sc->simplebus_sc.flags |= SB_FLAG_NO_RANGES;
22380466455SJessica Clarke 
22480466455SJessica Clarke 	return (simplebus_attach(dev));
22580466455SJessica Clarke }
22680466455SJessica Clarke 
22780466455SJessica Clarke static int
da9063_iic_detach(device_t dev)22880466455SJessica Clarke da9063_iic_detach(device_t dev)
22980466455SJessica Clarke {
23080466455SJessica Clarke 	struct da9063_iic_softc *sc;
23180466455SJessica Clarke 	int error;
23280466455SJessica Clarke 
23380466455SJessica Clarke 	sc = device_get_softc(dev);
23480466455SJessica Clarke 
23580466455SJessica Clarke 	error = simplebus_detach(dev);
23680466455SJessica Clarke 	if (error != 0)
23780466455SJessica Clarke 		return (error);
23880466455SJessica Clarke 
23980466455SJessica Clarke 	mtx_destroy(&sc->mtx);
24080466455SJessica Clarke 
24180466455SJessica Clarke 	return (0);
24280466455SJessica Clarke }
24380466455SJessica Clarke 
24480466455SJessica Clarke static device_method_t da9063_iic_methods[] = {
24580466455SJessica Clarke 	/* Device interface */
24680466455SJessica Clarke 	DEVMETHOD(device_probe,		da9063_iic_probe),
24780466455SJessica Clarke 	DEVMETHOD(device_attach,	da9063_iic_attach),
24880466455SJessica Clarke 	DEVMETHOD(device_detach,	da9063_iic_detach),
24980466455SJessica Clarke 
25080466455SJessica Clarke 	/* DA9063 interface */
25180466455SJessica Clarke 	DEVMETHOD(da9063_read,		da9063_iic_read),
25280466455SJessica Clarke 	DEVMETHOD(da9063_write,		da9063_iic_write),
25380466455SJessica Clarke 	DEVMETHOD(da9063_modify,	da9063_iic_modify),
25480466455SJessica Clarke 
25580466455SJessica Clarke 	DEVMETHOD_END
25680466455SJessica Clarke };
25780466455SJessica Clarke 
25880466455SJessica Clarke DEFINE_CLASS_1(da9063_pmic, da9063_iic_driver, da9063_iic_methods,
25980466455SJessica Clarke     sizeof(struct da9063_iic_softc), simplebus_driver);
26080466455SJessica Clarke 
26180466455SJessica Clarke DRIVER_MODULE(da9063_iic, iicbus, da9063_iic_driver, NULL, NULL);
262