1/* $NetBSD: act8846.c,v 1.6 2019/07/27 16:02:27 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29//#define ACT_DEBUG
30
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: act8846.c,v 1.6 2019/07/27 16:02:27 thorpej Exp $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/device.h>
38#include <sys/conf.h>
39#include <sys/bus.h>
40#include <sys/kmem.h>
41
42#include <dev/i2c/i2cvar.h>
43#include <dev/i2c/act8846.h>
44
45#define ACT_BATTVOL_STATUS_REG		0x00
46#define ACT_THERMAL_CTRL_REG		0x01
47#define ACT_DCDC1_BASE_REG		0x10
48#define ACT_DCDC2_BASE_REG		0x20
49#define ACT_DCDC3_BASE_REG		0x30
50#define ACT_DCDC4_BASE_REG		0x40
51#define ACT_LDO1_BASE_REG		0x50
52#define ACT_LDO2_BASE_REG		0x58
53#define ACT_LDO3_BASE_REG		0x60
54#define ACT_LDO4_BASE_REG		0x68
55#define ACT_LDO5_BASE_REG		0x70
56#define ACT_LDO6_BASE_REG		0x80
57#define ACT_LDO7_BASE_REG		0x90
58#define ACT_LDO8_BASE_REG		0xa0
59#define ACT_LDO9_BASE_REG		0xb0
60
61#define ACT_VSET0_OFFSET		0
62#define ACT_VSET1_OFFSET		1
63#define ACT_DCDC_CTRL_OFFSET		2
64#define ACT_LDO_CTRL_OFFSET		1
65
66#define ACT_VSET_VSET			__BITS(5,0)
67
68#define ACT_DCDC_CTRL_ON		__BIT(7)
69
70#define ACT_LDO_CTRL_ON			__BIT(7)
71
72enum act8846_ctrl_type {
73	ACT_CTRL_DCDC,
74	ACT_CTRL_LDO,
75};
76
77#define ACT_VOLTAGE_MIN			600
78#define ACT_VOLTAGE_MAX			3900
79
80struct act8846_ctrl {
81	device_t	c_dev;
82
83	const char *	c_name;
84	u_int		c_min;
85	u_int		c_max;
86	uint8_t		c_base;
87	enum act8846_ctrl_type c_type;
88};
89
90#define ACT_CTRL(name, base, type)				\
91	{ .c_name = (name),					\
92	  .c_min = ACT_VOLTAGE_MIN, .c_max = ACT_VOLTAGE_MAX,	\
93	  .c_base = ACT_ ## base ## _BASE_REG, .c_type = (type) }
94
95#define ACT_DCDC(name, base)	ACT_CTRL(name, base, ACT_CTRL_DCDC)
96#define ACT_LDO(name, base)	ACT_CTRL(name, base, ACT_CTRL_LDO)
97
98static const struct act8846_ctrl act8846_ctrls[] = {
99	ACT_DCDC("DCDC1", DCDC1),	/* VCC_DDR */
100	ACT_DCDC("DCDC2", DCDC2),	/* VDD_LOG */
101	ACT_DCDC("DCDC3", DCDC3),	/* VDD_ARM */
102	ACT_DCDC("DCDC4", DCDC4),	/* VCC_IO */
103	ACT_LDO("LDO1", LDO1),		/* VDD_10 */
104	ACT_LDO("LDO2", LDO2),		/* VCC_25 */
105	ACT_LDO("LDO3", LDO3),		/* VCC18_CIF */
106	ACT_LDO("LDO4", LDO4),		/* VCCA_33 */
107	ACT_LDO("LDO5", LDO5),		/* VCC_TOUCH */
108	ACT_LDO("LDO6", LDO6),		/* VCC33 */
109	ACT_LDO("LDO7", LDO7),		/* VCC18_IO */
110	ACT_LDO("LDO8", LDO8),		/* VCC28_CIF */
111#if 0
112	ACT_LDO("LDO9", LDO9),		/* VDD_RTC (Always-ON) */
113#endif
114};
115
116/* From datasheet, Table 5: REGx/VSET[] Output Voltage Setting */
117static const u_int act8846_vset[] = {
118	600, 625, 650, 675, 700, 725, 750, 775,
119	800, 825, 850, 875, 900, 925, 950, 975,
120	1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175,
121	1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550,
122	1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950,
123	2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350,
124	2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100,
125	3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900
126};
127
128struct act8846_softc {
129	device_t	sc_dev;
130	i2c_tag_t	sc_i2c;
131	i2c_addr_t	sc_addr;
132
133	u_int		sc_nctrl;
134	struct act8846_ctrl *sc_ctrl;
135};
136
137static int	act8846_match(device_t, cfdata_t, void *);
138static void	act8846_attach(device_t, device_t, void *);
139
140static int	act8846_read(struct act8846_softc *, uint8_t, uint8_t *);
141static int	act8846_write(struct act8846_softc *, uint8_t, uint8_t);
142
143static void	act8846_print(struct act8846_ctrl *c);
144
145CFATTACH_DECL_NEW(act8846pm, sizeof(struct act8846_softc),
146    act8846_match, act8846_attach, NULL, NULL);
147
148static int
149act8846_match(device_t parent, cfdata_t match, void *aux)
150{
151	struct i2c_attach_args *ia = aux;
152
153	if (ia->ia_addr == 0x5a)
154		return I2C_MATCH_ADDRESS_ONLY;
155
156	return 0;
157}
158
159static void
160act8846_attach(device_t parent, device_t self, void *aux)
161{
162	struct act8846_softc *sc = device_private(self);
163	struct i2c_attach_args *ia = aux;
164	u_int n;
165
166	sc->sc_dev = self;
167	sc->sc_i2c = ia->ia_tag;
168	sc->sc_addr = ia->ia_addr;
169
170	aprint_naive("\n");
171	aprint_normal("\n");
172
173	sc->sc_nctrl = __arraycount(act8846_ctrls);
174	sc->sc_ctrl = kmem_alloc(sizeof(act8846_ctrls), KM_SLEEP);
175	memcpy(sc->sc_ctrl, act8846_ctrls, sizeof(act8846_ctrls));
176	for (n = 0; n < sc->sc_nctrl; n++) {
177		sc->sc_ctrl[n].c_dev = self;
178	}
179
180	for (n = 0; n < sc->sc_nctrl; n++) {
181		act8846_print(&sc->sc_ctrl[n]);
182	}
183}
184
185static int
186act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val)
187{
188	return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0);
189}
190
191static int
192act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val)
193{
194	return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0);
195}
196
197static void
198act8846_print(struct act8846_ctrl *c)
199{
200	struct act8846_softc *sc = device_private(c->c_dev);
201	u_int voltage;
202	bool enabled;
203
204	device_printf(sc->sc_dev, "%s:", c->c_name);
205	if (act8846_get_voltage(c, &voltage)) {
206		printf(" [??? V]");
207	} else {
208		printf(" [%d.%03dV]", voltage / 1000,
209		    voltage % 1000);
210	}
211	if (act8846_is_enabled(c, &enabled)) {
212		printf(" [unknown state]");
213	} else {
214		printf(" [%s]", enabled ? "ON" : "OFF");
215	}
216	printf("\n");
217}
218
219struct act8846_ctrl *
220act8846_lookup(device_t dev, const char *name)
221{
222	struct act8846_softc *sc = device_private(dev);
223	struct act8846_ctrl *c;
224	u_int n;
225
226	for (n = 0; n < sc->sc_nctrl; n++) {
227		c = &sc->sc_ctrl[n];
228		if (strcmp(c->c_name, name) == 0) {
229			return c;
230		}
231	}
232
233	return NULL;
234}
235
236int
237act8846_set_voltage(struct act8846_ctrl *c, u_int min, u_int max)
238{
239	struct act8846_softc *sc = device_private(c->c_dev);
240	uint8_t val;
241	int error, n;
242
243	if (min < c->c_min || min > c->c_max || (min % 25) != 0)
244		return EINVAL;
245
246	for (n = 0; n < __arraycount(act8846_vset); n++) {
247		if (min >= act8846_vset[n] && max <= act8846_vset[n]) {
248			break;
249		}
250	}
251	if (n == __arraycount(act8846_vset))
252		return EINVAL;
253
254	val = __SHIFTIN(n, ACT_VSET_VSET);
255
256	iic_acquire_bus(sc->sc_i2c, 0);
257	error = act8846_write(sc, c->c_base + ACT_VSET0_OFFSET, val);
258	iic_release_bus(sc->sc_i2c, 0);
259#ifdef ACT_DEBUG
260	if (error == 0)
261		act8846_print(c);
262#endif
263	return error;
264}
265
266int
267act8846_get_voltage(struct act8846_ctrl *c, u_int *pvol)
268{
269	struct act8846_softc *sc = device_private(c->c_dev);
270	uint8_t val;
271	int error;
272
273	iic_acquire_bus(sc->sc_i2c, 0);
274	error = act8846_read(sc, c->c_base + ACT_VSET0_OFFSET, &val);
275	iic_release_bus(sc->sc_i2c, 0);
276	if (error)
277		return error;
278
279	*pvol = act8846_vset[__SHIFTOUT(val, ACT_VSET_VSET)];
280
281	return 0;
282}
283
284int
285act8846_is_enabled(struct act8846_ctrl *c, bool *penabled)
286{
287	struct act8846_softc *sc = device_private(c->c_dev);
288	uint8_t val, regoff, regmask;
289	int error;
290
291	if (c->c_type == ACT_CTRL_DCDC) {
292		regoff = ACT_DCDC_CTRL_OFFSET;
293		regmask = ACT_DCDC_CTRL_ON;
294	} else {
295		regoff = ACT_LDO_CTRL_OFFSET;
296		regmask = ACT_LDO_CTRL_ON;
297	}
298
299	iic_acquire_bus(sc->sc_i2c, 0);
300	error = act8846_read(sc, c->c_base + regoff, &val);
301	iic_release_bus(sc->sc_i2c, 0);
302	if (error)
303		return error;
304
305	*penabled = !!(val & regmask);
306	return 0;
307}
308
309int
310act8846_enable(struct act8846_ctrl *c)
311{
312	struct act8846_softc *sc = device_private(c->c_dev);
313	uint8_t val, regoff, regmask;
314	int error;
315
316	if (c->c_type == ACT_CTRL_DCDC) {
317		regoff = ACT_DCDC_CTRL_OFFSET;
318		regmask = ACT_DCDC_CTRL_ON;
319	} else {
320		regoff = ACT_LDO_CTRL_OFFSET;
321		regmask = ACT_LDO_CTRL_ON;
322	}
323
324	iic_acquire_bus(sc->sc_i2c, 0);
325	if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0)
326		goto done;
327	val |= regmask;
328	error = act8846_write(sc, c->c_base + regoff, val);
329done:
330	iic_release_bus(sc->sc_i2c, 0);
331#ifdef ACT_DEBUG
332	if (error == 0)
333		act8846_print(c);
334#endif
335
336	return error;
337}
338
339int
340act8846_disable(struct act8846_ctrl *c)
341{
342	struct act8846_softc *sc = device_private(c->c_dev);
343	uint8_t val, regoff, regmask;
344	int error;
345
346	if (c->c_type == ACT_CTRL_DCDC) {
347		regoff = ACT_DCDC_CTRL_OFFSET;
348		regmask = ACT_DCDC_CTRL_ON;
349	} else {
350		regoff = ACT_LDO_CTRL_OFFSET;
351		regmask = ACT_LDO_CTRL_ON;
352	}
353
354	iic_acquire_bus(sc->sc_i2c, 0);
355	if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0)
356		goto done;
357	val &= ~regmask;
358	error = act8846_write(sc, c->c_base + regoff, val);
359done:
360	iic_release_bus(sc->sc_i2c, 0);
361#ifdef ACT_DEBUG
362	if (error == 0)
363		act8846_print(c);
364#endif
365
366	return error;
367}
368