1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29/*
30 * ACT8846 PMIC driver
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36#include <sys/kernel.h>
37#include <sys/module.h>
38#include <sys/malloc.h>
39#include <sys/rman.h>
40#include <sys/sx.h>
41
42#include <machine/bus.h>
43
44#include <dev/regulator/regulator.h>
45#include <dev/fdt/fdt_pinctrl.h>
46#include <dev/iicbus/iiconf.h>
47#include <dev/iicbus/iicbus.h>
48#include <dev/ofw/ofw_bus.h>
49#include <dev/ofw/ofw_bus_subr.h>
50
51#include <dev/iicbus/pmic/act8846.h>
52
53#include "regdev_if.h"
54
55static struct ofw_compat_data compat_data[] = {
56	{"active-semi,act8846",	1},
57	{NULL,			0}
58};
59
60#define	LOCK(_sc)		sx_xlock(&(_sc)->lock)
61#define	UNLOCK(_sc)		sx_xunlock(&(_sc)->lock)
62#define	LOCK_INIT(_sc)		sx_init(&(_sc)->lock, "act8846")
63#define	LOCK_DESTROY(_sc)	sx_destroy(&(_sc)->lock);
64#define	ASSERT_LOCKED(_sc)	sx_assert(&(_sc)->lock, SA_XLOCKED);
65#define	ASSERT_UNLOCKED(_sc)	sx_assert(&(_sc)->lock, SA_UNLOCKED);
66
67
68/*
69 * Raw register access function.
70 */
71int
72act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val)
73{
74	uint8_t addr;
75	int rv;
76	struct iic_msg msgs[2] = {
77		{0, IIC_M_WR, 1, &addr},
78		{0, IIC_M_RD, 1, val},
79	};
80
81	msgs[0].slave = sc->bus_addr;
82	msgs[1].slave = sc->bus_addr;
83	addr = reg;
84
85	rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
86	if (rv != 0) {
87		device_printf(sc->dev,
88		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
89		return (EIO);
90	}
91
92	return (0);
93}
94
95int act8846_read_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf,
96    size_t size)
97{
98	uint8_t addr;
99	int rv;
100	struct iic_msg msgs[2] = {
101		{0, IIC_M_WR, 1, &addr},
102		{0, IIC_M_RD, size, buf},
103	};
104
105	msgs[0].slave = sc->bus_addr;
106	msgs[1].slave = sc->bus_addr;
107	addr = reg;
108
109	rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
110	if (rv != 0) {
111		device_printf(sc->dev,
112		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
113		return (EIO);
114	}
115
116	return (0);
117}
118
119int
120act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val)
121{
122	uint8_t data[2];
123	int rv;
124
125	struct iic_msg msgs[1] = {
126		{0, IIC_M_WR, 2, data},
127	};
128
129	msgs[0].slave = sc->bus_addr;
130	data[0] = reg;
131	data[1] = val;
132
133	rv = iicbus_transfer_excl(sc->dev, msgs, 1, IIC_INTRWAIT);
134	if (rv != 0) {
135		device_printf(sc->dev,
136		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
137		return (EIO);
138	}
139	return (0);
140}
141
142int act8846_write_buf(struct act8846_softc *sc, uint8_t reg, uint8_t *buf,
143    size_t size)
144{
145	uint8_t data[1];
146	int rv;
147	struct iic_msg msgs[2] = {
148		{0, IIC_M_WR, 1, data},
149		{0, IIC_M_WR | IIC_M_NOSTART, size, buf},
150	};
151
152	msgs[0].slave = sc->bus_addr;
153	msgs[1].slave = sc->bus_addr;
154	data[0] = reg;
155
156	rv = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
157	if (rv != 0) {
158		device_printf(sc->dev,
159		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
160		return (EIO);
161	}
162	return (0);
163}
164
165int
166act8846_modify(struct act8846_softc *sc, uint8_t reg, uint8_t clear, uint8_t set)
167{
168	uint8_t val;
169	int rv;
170
171	rv = act8846_read(sc, reg, &val);
172	if (rv != 0)
173		return (rv);
174
175	val &= ~clear;
176	val |= set;
177
178	rv = act8846_write(sc, reg, val);
179	if (rv != 0)
180		return (rv);
181
182	return (0);
183}
184
185static int
186act8846_probe(device_t dev)
187{
188
189	if (!ofw_bus_status_okay(dev))
190		return (ENXIO);
191
192	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
193		return (ENXIO);
194
195	device_set_desc(dev, "ACT8846 PMIC");
196	return (BUS_PROBE_DEFAULT);
197}
198
199static int
200act8846_attach(device_t dev)
201{
202	struct act8846_softc *sc;
203	int rv;
204	phandle_t node;
205
206	sc = device_get_softc(dev);
207	sc->dev = dev;
208	sc->bus_addr = iicbus_get_addr(dev);
209	node = ofw_bus_get_node(sc->dev);
210	rv = 0;
211	LOCK_INIT(sc);
212
213
214	rv = act8846_regulator_attach(sc, node);
215	if (rv != 0)
216		goto fail;
217
218	return (bus_generic_attach(dev));
219
220fail:
221	LOCK_DESTROY(sc);
222	return (rv);
223}
224
225static int
226act8846_detach(device_t dev)
227{
228	struct act8846_softc *sc;
229
230	sc = device_get_softc(dev);
231	LOCK_DESTROY(sc);
232
233	return (bus_generic_detach(dev));
234}
235
236static device_method_t act8846_methods[] = {
237	/* Device interface */
238	DEVMETHOD(device_probe,		act8846_probe),
239	DEVMETHOD(device_attach,	act8846_attach),
240	DEVMETHOD(device_detach,	act8846_detach),
241
242	/* Regdev interface */
243	DEVMETHOD(regdev_map,		act8846_regulator_map),
244
245	DEVMETHOD_END
246};
247
248static DEFINE_CLASS_0(act8846_pmu, act8846_driver, act8846_methods,
249    sizeof(struct act8846_softc));
250EARLY_DRIVER_MODULE(act8846_pmic, iicbus, act8846_driver,
251    NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
252MODULE_VERSION(act8846_pmic, 1);
253MODULE_DEPEND(act8846_pmic, iicbus, IICBUS_MINVER, IICBUS_PREFVER,
254    IICBUS_MAXVER);
255IICBUS_FDT_PNP_INFO(compat_data);
256