1 /* $NetBSD: axp809.c,v 1.3 2019/07/27 16:02:27 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2014 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 AXP_DEBUG
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: axp809.c,v 1.3 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/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
axp809_match(device_t parent,cfdata_t match,void * aux)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
axp809_attach(device_t parent,device_t self,void * aux)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
axp809_read(struct axp809_softc * sc,uint8_t reg,uint8_t * val)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, 0);
151 }
152
153 static int
axp809_write(struct axp809_softc * sc,uint8_t reg,uint8_t val)154 axp809_write(struct axp809_softc *sc, uint8_t reg, uint8_t val)
155 {
156 return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0);
157 }
158
159 static void
axp809_print(struct axp809_ctrl * c)160 axp809_print(struct axp809_ctrl *c)
161 {
162 struct axp809_softc *sc = device_private(c->c_dev);
163 u_int voltage;
164 bool enabled;
165
166 device_printf(sc->sc_dev, "%s:", c->c_name);
167 if (c->c_voltage_reg) {
168 if (axp809_get_voltage(c, &voltage)) {
169 printf(" [??? V]");
170 } else {
171 printf(" [%d.%03dV]", voltage / 1000,
172 voltage % 1000);
173 }
174 }
175 if (c->c_enable_reg) {
176 if (axp809_is_enabled(c, &enabled)) {
177 printf(" [unknown state]");
178 } else {
179 printf(" [%s]", enabled ? "ON" : "OFF");
180 }
181 }
182 printf("\n");
183 }
184
185 struct axp809_ctrl *
axp809_lookup(device_t dev,const char * name)186 axp809_lookup(device_t dev, const char *name)
187 {
188 struct axp809_softc *sc = device_private(dev);
189 struct axp809_ctrl *c;
190 u_int n;
191
192 for (n = 0; n < sc->sc_nctrl; n++) {
193 c = &sc->sc_ctrl[n];
194 if (strcmp(c->c_name, name) == 0) {
195 return c;
196 }
197 }
198
199 return NULL;
200 }
201
202 int
axp809_set_voltage(struct axp809_ctrl * c,u_int min,u_int max)203 axp809_set_voltage(struct axp809_ctrl *c, u_int min, u_int max)
204 {
205 struct axp809_softc *sc = device_private(c->c_dev);
206 u_int vol, reg_val;
207 int nstep, error;
208 uint8_t val;
209
210 if (!c->c_voltage_mask)
211 return EINVAL;
212
213 if (min < c->c_min || min > c->c_max)
214 return EINVAL;
215
216 reg_val = 0;
217 nstep = 1;
218 vol = c->c_min;
219
220 for (nstep = 0; nstep < c->c_step1cnt && vol < min; nstep++) {
221 ++reg_val;
222 vol += c->c_step1;
223 }
224 for (nstep = 0; nstep < c->c_step2cnt && vol < min; nstep++) {
225 ++reg_val;
226 vol += c->c_step2;
227 }
228
229 if (vol > max)
230 return EINVAL;
231
232 iic_acquire_bus(sc->sc_i2c, 0);
233 if ((error = axp809_read(sc, c->c_voltage_reg, &val)) != 0)
234 goto done;
235 val &= ~c->c_voltage_mask;
236 val |= __SHIFTIN(reg_val, c->c_voltage_mask);
237 error = axp809_write(sc, c->c_voltage_reg, val);
238 done:
239 iic_release_bus(sc->sc_i2c, 0);
240 #ifdef AXP_DEBUG
241 if (error == 0)
242 axp809_print(c);
243 #endif
244
245 return error;
246 }
247
248 int
axp809_get_voltage(struct axp809_ctrl * c,u_int * pvol)249 axp809_get_voltage(struct axp809_ctrl *c, u_int *pvol)
250 {
251 struct axp809_softc *sc = device_private(c->c_dev);
252 int reg_val, error;
253 uint8_t val;
254
255 if (!c->c_voltage_mask)
256 return EINVAL;
257
258 iic_acquire_bus(sc->sc_i2c, 0);
259 error = axp809_read(sc, c->c_voltage_reg, &val);
260 iic_release_bus(sc->sc_i2c, 0);
261 if (error)
262 return error;
263
264 reg_val = __SHIFTOUT(val, c->c_voltage_mask);
265 if (reg_val < c->c_step1cnt) {
266 *pvol = c->c_min + reg_val * c->c_step1;
267 } else {
268 *pvol = c->c_min + (c->c_step1cnt * c->c_step1) +
269 ((reg_val - c->c_step1cnt) * c->c_step2);
270 }
271
272 return 0;
273 }
274
275 int
axp809_is_enabled(struct axp809_ctrl * c,bool * penabled)276 axp809_is_enabled(struct axp809_ctrl *c, bool *penabled)
277 {
278 struct axp809_softc *sc = device_private(c->c_dev);
279 uint8_t val;
280 int error;
281
282 if (!c->c_enable_mask)
283 return EINVAL;
284
285 iic_acquire_bus(sc->sc_i2c, 0);
286 error = axp809_read(sc, c->c_enable_reg, &val);
287 iic_release_bus(sc->sc_i2c, 0);
288 if (error)
289 return error;
290
291 *penabled = !!(val & c->c_enable_mask);
292 return 0;
293 }
294
295 int
axp809_enable(struct axp809_ctrl * c)296 axp809_enable(struct axp809_ctrl *c)
297 {
298 struct axp809_softc *sc = device_private(c->c_dev);
299 uint8_t val;
300 int error;
301
302 if (!c->c_enable_mask)
303 return EINVAL;
304
305 iic_acquire_bus(sc->sc_i2c, 0);
306 if ((error = axp809_read(sc, c->c_enable_reg, &val)) != 0)
307 goto done;
308 val |= c->c_enable_mask;
309 error = axp809_write(sc, c->c_enable_reg, val);
310 done:
311 iic_release_bus(sc->sc_i2c, 0);
312 #ifdef AXP_DEBUG
313 if (error == 0)
314 axp809_print(c);
315 #endif
316
317 return error;
318 }
319
320 int
axp809_disable(struct axp809_ctrl * c)321 axp809_disable(struct axp809_ctrl *c)
322 {
323 struct axp809_softc *sc = device_private(c->c_dev);
324 uint8_t val;
325 int error;
326
327 if (!c->c_enable_mask)
328 return EINVAL;
329
330 iic_acquire_bus(sc->sc_i2c, 0);
331 if ((error = axp809_read(sc, c->c_enable_reg, &val)) != 0)
332 goto done;
333 val &= ~c->c_enable_mask;
334 error = axp809_write(sc, c->c_enable_reg, val);
335 done:
336 iic_release_bus(sc->sc_i2c, 0);
337 #ifdef AXP_DEBUG
338 if (error == 0)
339 axp809_print(c);
340 #endif
341
342 return error;
343 }
344