axp809.c revision 1.2 1 /* $NetBSD: axp809.c,v 1.2 2018/06/16 21:22:13 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2014 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 AXP_DEBUG
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: axp809.c,v 1.2 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/axp809.h>
44
45 #define AXP_GPIO1_CTRL_REG 0x92
46 #define AXP_GPIO1LDO_CTRL_REG 0x93
47
48 struct axp809_ctrl {
49 device_t c_dev;
50
51 const char * c_name;
52 u_int c_min;
53 u_int c_max;
54 u_int c_step1;
55 u_int c_step1cnt;
56 u_int c_step2;
57 u_int c_step2cnt;
58
59 uint8_t c_enable_reg;
60 uint8_t c_enable_mask;
61
62 uint8_t c_voltage_reg;
63 uint8_t c_voltage_mask;
64 };
65
66 #define AXP_CTRL(name, min, max, step, ereg, emask, vreg, vmask) \
67 { .c_name = (name), .c_min = (min), .c_max = (max), \
68 .c_step1 = (step), .c_step1cnt = (((max) - (min)) / (step)) + 1, \
69 .c_step2 = 0, .c_step2cnt = 0, \
70 .c_enable_reg = AXP_##ereg##_REG, .c_enable_mask = (emask), \
71 .c_voltage_reg = AXP_##vreg##_REG, .c_voltage_mask = (vmask) }
72
73 #define AXP_CTRL2(name, min, max, step1, step1cnt, step2, step2cnt, ereg, emask, vreg, vmask) \
74 { .c_name = (name), .c_min = (min), .c_max = (max), \
75 .c_step1 = (step1), .c_step1cnt = (step1cnt), \
76 .c_step2 = (step2), .c_step2cnt = (step2cnt), \
77 .c_enable_reg = AXP_##ereg##_REG, .c_enable_mask = (emask), \
78 .c_voltage_reg = AXP_##vreg##_REG, .c_voltage_mask = (vmask) }
79
80 static const struct axp809_ctrl axp809_ctrls[] = {
81 AXP_CTRL("GPIO1", 700, 3300, 100,
82 GPIO1_CTRL, __BIT(0), GPIO1LDO_CTRL, __BITS(4,0)),
83 };
84
85 struct axp809_softc {
86 device_t sc_dev;
87 i2c_tag_t sc_i2c;
88 i2c_addr_t sc_addr;
89
90 u_int sc_nctrl;
91 struct axp809_ctrl *sc_ctrl;
92 };
93
94 static int axp809_match(device_t, cfdata_t, void *);
95 static void axp809_attach(device_t, device_t, void *);
96
97 static int axp809_read(struct axp809_softc *, uint8_t, uint8_t *);
98 static int axp809_write(struct axp809_softc *, uint8_t, uint8_t);
99
100 static void axp809_print(struct axp809_ctrl *c);
101
102 CFATTACH_DECL_NEW(axp809pm, sizeof(struct axp809_softc),
103 axp809_match, axp809_attach, NULL, NULL);
104
105 static int
106 axp809_match(device_t parent, cfdata_t match, void *aux)
107 {
108 struct i2c_attach_args *ia = aux;
109 int match_result;
110
111 if (iic_use_direct_match(ia, match, NULL, &match_result))
112 return match_result;
113
114 /* This device is direct-config only. */
115
116 return 0;
117 }
118
119 static void
120 axp809_attach(device_t parent, device_t self, void *aux)
121 {
122 struct axp809_softc *sc = device_private(self);
123 struct i2c_attach_args *ia = aux;
124 u_int n;
125
126 sc->sc_dev = self;
127 sc->sc_i2c = ia->ia_tag;
128 sc->sc_addr = ia->ia_addr;
129
130 aprint_naive("\n");
131 aprint_normal("\n");
132
133 sc->sc_nctrl = __arraycount(axp809_ctrls);
134 sc->sc_ctrl = kmem_alloc(sizeof(axp809_ctrls), KM_SLEEP);
135 memcpy(sc->sc_ctrl, axp809_ctrls, sizeof(axp809_ctrls));
136 for (n = 0; n < sc->sc_nctrl; n++) {
137 sc->sc_ctrl[n].c_dev = self;
138 }
139
140 #ifdef AXP_DEBUG
141 for (n = 0; n < sc->sc_nctrl; n++) {
142 axp809_print(&sc->sc_ctrl[n]);
143 }
144 #endif
145 }
146
147 static int
148 axp809_read(struct axp809_softc *sc, uint8_t reg, uint8_t *val)
149 {
150 return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val,
151 cold ? I2C_F_POLL : 0);
152 }
153
154 static int
155 axp809_write(struct axp809_softc *sc, uint8_t reg, uint8_t val)
156 {
157 return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val,
158 cold ? I2C_F_POLL : 0);
159 }
160
161 static void
162 axp809_print(struct axp809_ctrl *c)
163 {
164 struct axp809_softc *sc = device_private(c->c_dev);
165 u_int voltage;
166 bool enabled;
167
168 device_printf(sc->sc_dev, "%s:", c->c_name);
169 if (c->c_voltage_reg) {
170 if (axp809_get_voltage(c, &voltage)) {
171 printf(" [??? V]");
172 } else {
173 printf(" [%d.%03dV]", voltage / 1000,
174 voltage % 1000);
175 }
176 }
177 if (c->c_enable_reg) {
178 if (axp809_is_enabled(c, &enabled)) {
179 printf(" [unknown state]");
180 } else {
181 printf(" [%s]", enabled ? "ON" : "OFF");
182 }
183 }
184 printf("\n");
185 }
186
187 struct axp809_ctrl *
188 axp809_lookup(device_t dev, const char *name)
189 {
190 struct axp809_softc *sc = device_private(dev);
191 struct axp809_ctrl *c;
192 u_int n;
193
194 for (n = 0; n < sc->sc_nctrl; n++) {
195 c = &sc->sc_ctrl[n];
196 if (strcmp(c->c_name, name) == 0) {
197 return c;
198 }
199 }
200
201 return NULL;
202 }
203
204 int
205 axp809_set_voltage(struct axp809_ctrl *c, u_int min, u_int max)
206 {
207 struct axp809_softc *sc = device_private(c->c_dev);
208 u_int vol, reg_val;
209 int nstep, error;
210 uint8_t val;
211
212 if (!c->c_voltage_mask)
213 return EINVAL;
214
215 if (min < c->c_min || min > c->c_max)
216 return EINVAL;
217
218 reg_val = 0;
219 nstep = 1;
220 vol = c->c_min;
221
222 for (nstep = 0; nstep < c->c_step1cnt && vol < min; nstep++) {
223 ++reg_val;
224 vol += c->c_step1;
225 }
226 for (nstep = 0; nstep < c->c_step2cnt && vol < min; nstep++) {
227 ++reg_val;
228 vol += c->c_step2;
229 }
230
231 if (vol > max)
232 return EINVAL;
233
234 iic_acquire_bus(sc->sc_i2c, 0);
235 if ((error = axp809_read(sc, c->c_voltage_reg, &val)) != 0)
236 goto done;
237 val &= ~c->c_voltage_mask;
238 val |= __SHIFTIN(reg_val, c->c_voltage_mask);
239 error = axp809_write(sc, c->c_voltage_reg, val);
240 done:
241 iic_release_bus(sc->sc_i2c, 0);
242 #ifdef AXP_DEBUG
243 if (error == 0)
244 axp809_print(c);
245 #endif
246
247 return error;
248 }
249
250 int
251 axp809_get_voltage(struct axp809_ctrl *c, u_int *pvol)
252 {
253 struct axp809_softc *sc = device_private(c->c_dev);
254 int reg_val, error;
255 uint8_t val;
256
257 if (!c->c_voltage_mask)
258 return EINVAL;
259
260 iic_acquire_bus(sc->sc_i2c, 0);
261 error = axp809_read(sc, c->c_voltage_reg, &val);
262 iic_release_bus(sc->sc_i2c, 0);
263 if (error)
264 return error;
265
266 reg_val = __SHIFTOUT(val, c->c_voltage_mask);
267 if (reg_val < c->c_step1cnt) {
268 *pvol = c->c_min + reg_val * c->c_step1;
269 } else {
270 *pvol = c->c_min + (c->c_step1cnt * c->c_step1) +
271 ((reg_val - c->c_step1cnt) * c->c_step2);
272 }
273
274 return 0;
275 }
276
277 int
278 axp809_is_enabled(struct axp809_ctrl *c, bool *penabled)
279 {
280 struct axp809_softc *sc = device_private(c->c_dev);
281 uint8_t val;
282 int error;
283
284 if (!c->c_enable_mask)
285 return EINVAL;
286
287 iic_acquire_bus(sc->sc_i2c, 0);
288 error = axp809_read(sc, c->c_enable_reg, &val);
289 iic_release_bus(sc->sc_i2c, 0);
290 if (error)
291 return error;
292
293 *penabled = !!(val & c->c_enable_mask);
294 return 0;
295 }
296
297 int
298 axp809_enable(struct axp809_ctrl *c)
299 {
300 struct axp809_softc *sc = device_private(c->c_dev);
301 uint8_t val;
302 int error;
303
304 if (!c->c_enable_mask)
305 return EINVAL;
306
307 iic_acquire_bus(sc->sc_i2c, 0);
308 if ((error = axp809_read(sc, c->c_enable_reg, &val)) != 0)
309 goto done;
310 val |= c->c_enable_mask;
311 error = axp809_write(sc, c->c_enable_reg, val);
312 done:
313 iic_release_bus(sc->sc_i2c, 0);
314 #ifdef AXP_DEBUG
315 if (error == 0)
316 axp809_print(c);
317 #endif
318
319 return error;
320 }
321
322 int
323 axp809_disable(struct axp809_ctrl *c)
324 {
325 struct axp809_softc *sc = device_private(c->c_dev);
326 uint8_t val;
327 int error;
328
329 if (!c->c_enable_mask)
330 return EINVAL;
331
332 iic_acquire_bus(sc->sc_i2c, 0);
333 if ((error = axp809_read(sc, c->c_enable_reg, &val)) != 0)
334 goto done;
335 val &= ~c->c_enable_mask;
336 error = axp809_write(sc, c->c_enable_reg, val);
337 done:
338 iic_release_bus(sc->sc_i2c, 0);
339 #ifdef AXP_DEBUG
340 if (error == 0)
341 axp809_print(c);
342 #endif
343
344 return error;
345 }
346