xref: /netbsd-src/sys/dev/i2c/act8846.c (revision 4d2890e121d913234847fd56b03b93e02f722549)
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 
72 enum 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 
80 struct 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 
98 static 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 */
117 static 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 
128 struct 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 
137 static int	act8846_match(device_t, cfdata_t, void *);
138 static void	act8846_attach(device_t, device_t, void *);
139 
140 static int	act8846_read(struct act8846_softc *, uint8_t, uint8_t *);
141 static int	act8846_write(struct act8846_softc *, uint8_t, uint8_t);
142 
143 static void	act8846_print(struct act8846_ctrl *c);
144 
145 CFATTACH_DECL_NEW(act8846pm, sizeof(struct act8846_softc),
146     act8846_match, act8846_attach, NULL, NULL);
147 
148 static int
act8846_match(device_t parent,cfdata_t match,void * aux)149 act8846_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 
159 static void
act8846_attach(device_t parent,device_t self,void * aux)160 act8846_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 
185 static int
act8846_read(struct act8846_softc * sc,uint8_t reg,uint8_t * val)186 act8846_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 
191 static int
act8846_write(struct act8846_softc * sc,uint8_t reg,uint8_t val)192 act8846_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 
197 static void
act8846_print(struct act8846_ctrl * c)198 act8846_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 
219 struct act8846_ctrl *
act8846_lookup(device_t dev,const char * name)220 act8846_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 
236 int
act8846_set_voltage(struct act8846_ctrl * c,u_int min,u_int max)237 act8846_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 
266 int
act8846_get_voltage(struct act8846_ctrl * c,u_int * pvol)267 act8846_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 
284 int
act8846_is_enabled(struct act8846_ctrl * c,bool * penabled)285 act8846_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 
309 int
act8846_enable(struct act8846_ctrl * c)310 act8846_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);
329 done:
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 
339 int
act8846_disable(struct act8846_ctrl * c)340 act8846_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);
359 done:
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