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