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