xref: /netbsd-src/sys/dev/i2c/act8846.c (revision 03dcb730d46d34d85c9f496c1f5a3a6a43f2b7b3)
1 /* $NetBSD: act8846.c,v 1.3 2015/01/02 21:55:31 jmcneill 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.3 2015/01/02 21:55:31 jmcneill 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
149 act8846_match(device_t parent, cfdata_t match, void *aux)
150 {
151 	return 1;
152 }
153 
154 static void
155 act8846_attach(device_t parent, device_t self, void *aux)
156 {
157 	struct act8846_softc *sc = device_private(self);
158 	struct i2c_attach_args *ia = aux;
159 	u_int n;
160 
161 	sc->sc_dev = self;
162 	sc->sc_i2c = ia->ia_tag;
163 	sc->sc_addr = ia->ia_addr;
164 
165 	aprint_naive("\n");
166 	aprint_normal("\n");
167 
168 	sc->sc_nctrl = __arraycount(act8846_ctrls);
169 	sc->sc_ctrl = kmem_alloc(sizeof(act8846_ctrls), KM_SLEEP);
170 	memcpy(sc->sc_ctrl, act8846_ctrls, sizeof(act8846_ctrls));
171 	for (n = 0; n < sc->sc_nctrl; n++) {
172 		sc->sc_ctrl[n].c_dev = self;
173 	}
174 
175 	for (n = 0; n < sc->sc_nctrl; n++) {
176 		act8846_print(&sc->sc_ctrl[n]);
177 	}
178 }
179 
180 static int
181 act8846_read(struct act8846_softc *sc, uint8_t reg, uint8_t *val)
182 {
183 	return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val,
184 	    cold ? I2C_F_POLL : 0);
185 }
186 
187 static int
188 act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val)
189 {
190 	return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val,
191 	    cold ? I2C_F_POLL : 0);
192 }
193 
194 static void
195 act8846_print(struct act8846_ctrl *c)
196 {
197 	struct act8846_softc *sc = device_private(c->c_dev);
198 	u_int voltage;
199 	bool enabled;
200 
201 	device_printf(sc->sc_dev, "%s:", c->c_name);
202 	if (act8846_get_voltage(c, &voltage)) {
203 		printf(" [??? V]");
204 	} else {
205 		printf(" [%d.%03dV]", voltage / 1000,
206 		    voltage % 1000);
207 	}
208 	if (act8846_is_enabled(c, &enabled)) {
209 		printf(" [unknown state]");
210 	} else {
211 		printf(" [%s]", enabled ? "ON" : "OFF");
212 	}
213 	printf("\n");
214 }
215 
216 struct act8846_ctrl *
217 act8846_lookup(device_t dev, const char *name)
218 {
219 	struct act8846_softc *sc = device_private(dev);
220 	struct act8846_ctrl *c;
221 	u_int n;
222 
223 	for (n = 0; n < sc->sc_nctrl; n++) {
224 		c = &sc->sc_ctrl[n];
225 		if (strcmp(c->c_name, name) == 0) {
226 			return c;
227 		}
228 	}
229 
230 	return NULL;
231 }
232 
233 int
234 act8846_set_voltage(struct act8846_ctrl *c, u_int min, u_int max)
235 {
236 	struct act8846_softc *sc = device_private(c->c_dev);
237 	uint8_t val;
238 	int error, n;
239 
240 	if (min < c->c_min || min > c->c_max || (min % 25) != 0)
241 		return EINVAL;
242 
243 	for (n = 0; n < __arraycount(act8846_vset); n++) {
244 		if (min >= act8846_vset[n] && max <= act8846_vset[n]) {
245 			break;
246 		}
247 	}
248 	if (n == __arraycount(act8846_vset))
249 		return EINVAL;
250 
251 	val = __SHIFTIN(n, ACT_VSET_VSET);
252 
253 	iic_acquire_bus(sc->sc_i2c, 0);
254 	error = act8846_write(sc, c->c_base + ACT_VSET0_OFFSET, val);
255 	iic_release_bus(sc->sc_i2c, 0);
256 #ifdef ACT_DEBUG
257 	if (error == 0)
258 		act8846_print(c);
259 #endif
260 	return error;
261 }
262 
263 int
264 act8846_get_voltage(struct act8846_ctrl *c, u_int *pvol)
265 {
266 	struct act8846_softc *sc = device_private(c->c_dev);
267 	uint8_t val;
268 	int error;
269 
270 	iic_acquire_bus(sc->sc_i2c, 0);
271 	error = act8846_read(sc, c->c_base + ACT_VSET0_OFFSET, &val);
272 	iic_release_bus(sc->sc_i2c, 0);
273 	if (error)
274 		return error;
275 
276 	*pvol = act8846_vset[__SHIFTOUT(val, ACT_VSET_VSET)];
277 
278 	return 0;
279 }
280 
281 int
282 act8846_is_enabled(struct act8846_ctrl *c, bool *penabled)
283 {
284 	struct act8846_softc *sc = device_private(c->c_dev);
285 	uint8_t val, regoff, regmask;
286 	int error;
287 
288 	if (c->c_type == ACT_CTRL_DCDC) {
289 		regoff = ACT_DCDC_CTRL_OFFSET;
290 		regmask = ACT_DCDC_CTRL_ON;
291 	} else {
292 		regoff = ACT_LDO_CTRL_OFFSET;
293 		regmask = ACT_LDO_CTRL_ON;
294 	}
295 
296 	iic_acquire_bus(sc->sc_i2c, 0);
297 	error = act8846_read(sc, c->c_base + regoff, &val);
298 	iic_release_bus(sc->sc_i2c, 0);
299 	if (error)
300 		return error;
301 
302 	*penabled = !!(val & regmask);
303 	return 0;
304 }
305 
306 int
307 act8846_enable(struct act8846_ctrl *c)
308 {
309 	struct act8846_softc *sc = device_private(c->c_dev);
310 	uint8_t val, regoff, regmask;
311 	int error;
312 
313 	if (c->c_type == ACT_CTRL_DCDC) {
314 		regoff = ACT_DCDC_CTRL_OFFSET;
315 		regmask = ACT_DCDC_CTRL_ON;
316 	} else {
317 		regoff = ACT_LDO_CTRL_OFFSET;
318 		regmask = ACT_LDO_CTRL_ON;
319 	}
320 
321 	iic_acquire_bus(sc->sc_i2c, 0);
322 	if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0)
323 		goto done;
324 	val |= regmask;
325 	error = act8846_write(sc, c->c_base + regoff, val);
326 done:
327 	iic_release_bus(sc->sc_i2c, 0);
328 #ifdef ACT_DEBUG
329 	if (error == 0)
330 		act8846_print(c);
331 #endif
332 
333 	return error;
334 }
335 
336 int
337 act8846_disable(struct act8846_ctrl *c)
338 {
339 	struct act8846_softc *sc = device_private(c->c_dev);
340 	uint8_t val, regoff, regmask;
341 	int error;
342 
343 	if (c->c_type == ACT_CTRL_DCDC) {
344 		regoff = ACT_DCDC_CTRL_OFFSET;
345 		regmask = ACT_DCDC_CTRL_ON;
346 	} else {
347 		regoff = ACT_LDO_CTRL_OFFSET;
348 		regmask = ACT_LDO_CTRL_ON;
349 	}
350 
351 	iic_acquire_bus(sc->sc_i2c, 0);
352 	if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0)
353 		goto done;
354 	val &= ~regmask;
355 	error = act8846_write(sc, c->c_base + regoff, val);
356 done:
357 	iic_release_bus(sc->sc_i2c, 0);
358 #ifdef ACT_DEBUG
359 	if (error == 0)
360 		act8846_print(c);
361 #endif
362 
363 	return error;
364 }
365