act8846.c revision 1.5 1 /* $NetBSD: act8846.c,v 1.5 2018/06/16 21:22:13 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) 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.5 2018/06/16 21:22:13 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
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
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
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,
189 cold ? I2C_F_POLL : 0);
190 }
191
192 static int
193 act8846_write(struct act8846_softc *sc, uint8_t reg, uint8_t val)
194 {
195 return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val,
196 cold ? I2C_F_POLL : 0);
197 }
198
199 static void
200 act8846_print(struct act8846_ctrl *c)
201 {
202 struct act8846_softc *sc = device_private(c->c_dev);
203 u_int voltage;
204 bool enabled;
205
206 device_printf(sc->sc_dev, "%s:", c->c_name);
207 if (act8846_get_voltage(c, &voltage)) {
208 printf(" [??? V]");
209 } else {
210 printf(" [%d.%03dV]", voltage / 1000,
211 voltage % 1000);
212 }
213 if (act8846_is_enabled(c, &enabled)) {
214 printf(" [unknown state]");
215 } else {
216 printf(" [%s]", enabled ? "ON" : "OFF");
217 }
218 printf("\n");
219 }
220
221 struct act8846_ctrl *
222 act8846_lookup(device_t dev, const char *name)
223 {
224 struct act8846_softc *sc = device_private(dev);
225 struct act8846_ctrl *c;
226 u_int n;
227
228 for (n = 0; n < sc->sc_nctrl; n++) {
229 c = &sc->sc_ctrl[n];
230 if (strcmp(c->c_name, name) == 0) {
231 return c;
232 }
233 }
234
235 return NULL;
236 }
237
238 int
239 act8846_set_voltage(struct act8846_ctrl *c, u_int min, u_int max)
240 {
241 struct act8846_softc *sc = device_private(c->c_dev);
242 uint8_t val;
243 int error, n;
244
245 if (min < c->c_min || min > c->c_max || (min % 25) != 0)
246 return EINVAL;
247
248 for (n = 0; n < __arraycount(act8846_vset); n++) {
249 if (min >= act8846_vset[n] && max <= act8846_vset[n]) {
250 break;
251 }
252 }
253 if (n == __arraycount(act8846_vset))
254 return EINVAL;
255
256 val = __SHIFTIN(n, ACT_VSET_VSET);
257
258 iic_acquire_bus(sc->sc_i2c, 0);
259 error = act8846_write(sc, c->c_base + ACT_VSET0_OFFSET, val);
260 iic_release_bus(sc->sc_i2c, 0);
261 #ifdef ACT_DEBUG
262 if (error == 0)
263 act8846_print(c);
264 #endif
265 return error;
266 }
267
268 int
269 act8846_get_voltage(struct act8846_ctrl *c, u_int *pvol)
270 {
271 struct act8846_softc *sc = device_private(c->c_dev);
272 uint8_t val;
273 int error;
274
275 iic_acquire_bus(sc->sc_i2c, 0);
276 error = act8846_read(sc, c->c_base + ACT_VSET0_OFFSET, &val);
277 iic_release_bus(sc->sc_i2c, 0);
278 if (error)
279 return error;
280
281 *pvol = act8846_vset[__SHIFTOUT(val, ACT_VSET_VSET)];
282
283 return 0;
284 }
285
286 int
287 act8846_is_enabled(struct act8846_ctrl *c, bool *penabled)
288 {
289 struct act8846_softc *sc = device_private(c->c_dev);
290 uint8_t val, regoff, regmask;
291 int error;
292
293 if (c->c_type == ACT_CTRL_DCDC) {
294 regoff = ACT_DCDC_CTRL_OFFSET;
295 regmask = ACT_DCDC_CTRL_ON;
296 } else {
297 regoff = ACT_LDO_CTRL_OFFSET;
298 regmask = ACT_LDO_CTRL_ON;
299 }
300
301 iic_acquire_bus(sc->sc_i2c, 0);
302 error = act8846_read(sc, c->c_base + regoff, &val);
303 iic_release_bus(sc->sc_i2c, 0);
304 if (error)
305 return error;
306
307 *penabled = !!(val & regmask);
308 return 0;
309 }
310
311 int
312 act8846_enable(struct act8846_ctrl *c)
313 {
314 struct act8846_softc *sc = device_private(c->c_dev);
315 uint8_t val, regoff, regmask;
316 int error;
317
318 if (c->c_type == ACT_CTRL_DCDC) {
319 regoff = ACT_DCDC_CTRL_OFFSET;
320 regmask = ACT_DCDC_CTRL_ON;
321 } else {
322 regoff = ACT_LDO_CTRL_OFFSET;
323 regmask = ACT_LDO_CTRL_ON;
324 }
325
326 iic_acquire_bus(sc->sc_i2c, 0);
327 if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0)
328 goto done;
329 val |= regmask;
330 error = act8846_write(sc, c->c_base + regoff, val);
331 done:
332 iic_release_bus(sc->sc_i2c, 0);
333 #ifdef ACT_DEBUG
334 if (error == 0)
335 act8846_print(c);
336 #endif
337
338 return error;
339 }
340
341 int
342 act8846_disable(struct act8846_ctrl *c)
343 {
344 struct act8846_softc *sc = device_private(c->c_dev);
345 uint8_t val, regoff, regmask;
346 int error;
347
348 if (c->c_type == ACT_CTRL_DCDC) {
349 regoff = ACT_DCDC_CTRL_OFFSET;
350 regmask = ACT_DCDC_CTRL_ON;
351 } else {
352 regoff = ACT_LDO_CTRL_OFFSET;
353 regmask = ACT_LDO_CTRL_ON;
354 }
355
356 iic_acquire_bus(sc->sc_i2c, 0);
357 if ((error = act8846_read(sc, c->c_base + regoff, &val)) != 0)
358 goto done;
359 val &= ~regmask;
360 error = act8846_write(sc, c->c_base + regoff, val);
361 done:
362 iic_release_bus(sc->sc_i2c, 0);
363 #ifdef ACT_DEBUG
364 if (error == 0)
365 act8846_print(c);
366 #endif
367
368 return error;
369 }
370