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