1 /* $OpenBSD: dapmic.c,v 1.4 2022/10/12 13:39:50 kettenis Exp $ */
2 /*
3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/task.h>
23 #include <sys/proc.h>
24 #include <sys/signalvar.h>
25
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_regulator.h>
28 #include <dev/ofw/fdt.h>
29
30 #include <dev/i2c/i2cvar.h>
31
32 #include <dev/clock_subr.h>
33
34 #include <machine/fdt.h>
35
36 extern void (*cpuresetfn)(void);
37 extern void (*powerdownfn)(void);
38
39 /* Registers */
40 #define FAULT_LOG 0x05
41 #define EVENT_A 0x06
42 #define EVENT_A_EVENTS_D (1 << 7)
43 #define EVENT_A_EVENTS_C (1 << 6)
44 #define EVENT_A_EVENTS_B (1 << 5)
45 #define EVENT_A_E_nONKEY (1 << 0)
46 #define EVENT_B 0x07
47 #define EVENT_C 0x08
48 #define EVENT_D 0x09
49 #define IRQ_MASK_A 0x0a
50 #define IRQ_MASK_A_M_RESERVED ((1 << 7) | (1 << 6) | (1 << 5))
51 #define IRQ_MASK_A_M_SEQ_RDY (1 << 4)
52 #define IRQ_MASK_A_M_ADC_RDY (1 << 3)
53 #define IRQ_MASK_A_M_TICK (1 << 2)
54 #define IRQ_MASK_A_M_ALARM (1 << 1)
55 #define IRQ_MASK_A_M_nONKEY (1 << 0)
56 #define IRQ_MASK_B 0x0b
57 #define IRQ_MASK_C 0x0c
58 #define IRQ_MASK_D 0x0d
59 #define CONTROL_F 0x13
60 #define CONTROL_F_WAKE_UP (1 << 2)
61 #define CONTROL_F_SHUTDOWN (1 << 1)
62 #define COUNT_S 0x40
63 #define COUNT_S_COUNT_SEC 0x3f
64 #define COUNT_MI 0x41
65 #define COUNT_MI_COUNT_MIN 0x3f
66 #define COUNT_H 0x42
67 #define COUNT_H_COUNT_HOUR 0x1f
68 #define COUNT_D 0x43
69 #define COUNT_D_COUNT_DAY 0x1f
70 #define COUNT_MO 0x44
71 #define COUNT_MO_COUNT_MONTH 0x0f
72 #define COUNT_Y 0x45
73 #define COUNT_Y_MONITOR (1 << 6)
74 #define COUNT_Y_COUNT_YEAR 0x3f
75 #define ALARM_MO 0x4a
76 #define ALARM_MO_TICK_WAKE (1 << 5)
77 #define ALARM_MO_TICK_TYPE (1 << 4)
78 #define ALARM_Y 0x4b
79 #define ALARM_Y_TICK_ON (1 << 7)
80
81 #ifdef DAPMIC_DEBUG
82 # define DPRINTF(args) do { printf args; } while (0)
83 #else
84 # define DPRINTF(args) do {} while (0)
85 #endif
86
87 struct dapmic_softc {
88 struct device sc_dev;
89 i2c_tag_t sc_tag;
90 i2c_addr_t sc_addr;
91
92 int (*sc_ih)(void *);
93 struct task sc_task;
94
95 struct todr_chip_handle sc_todr;
96 };
97
98 int dapmic_match(struct device *, void *, void *);
99 void dapmic_attach(struct device *, struct device *, void *);
100
101 const struct cfattach dapmic_ca = {
102 sizeof(struct dapmic_softc), dapmic_match, dapmic_attach
103 };
104
105 struct cfdriver dapmic_cd = {
106 NULL, "dapmic", DV_DULL
107 };
108
109 uint8_t dapmic_reg_read(struct dapmic_softc *, int);
110 void dapmic_reg_write(struct dapmic_softc *, int, uint8_t);
111 int dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *);
112 int dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *);
113 int dapmic_gettime(struct todr_chip_handle *, struct timeval *);
114 int dapmic_settime(struct todr_chip_handle *, struct timeval *);
115 void dapmic_reset_irq_mask(struct dapmic_softc *);
116 void dapmic_reset(void);
117 void dapmic_powerdown(void);
118 int dapmic_intr(void *);
119 void dapmic_shutdown_task(void *);
120
121 int
dapmic_match(struct device * parent,void * match,void * aux)122 dapmic_match(struct device *parent, void *match, void *aux)
123 {
124 struct i2c_attach_args *ia = aux;
125
126 return (strcmp(ia->ia_name, "dlg,da9063") == 0);
127 }
128
129 void
dapmic_attach(struct device * parent,struct device * self,void * aux)130 dapmic_attach(struct device *parent, struct device *self, void *aux)
131 {
132 struct dapmic_softc *sc = (struct dapmic_softc *)self;
133 struct i2c_attach_args *ia = aux;
134 int node = *(int *)ia->ia_cookie;
135
136 sc->sc_tag = ia->ia_tag;
137 sc->sc_addr = ia->ia_addr;
138
139 sc->sc_todr.cookie = sc;
140 sc->sc_todr.todr_gettime = dapmic_gettime;
141 sc->sc_todr.todr_settime = dapmic_settime;
142 sc->sc_todr.todr_quality = 0;
143 todr_attach(&sc->sc_todr);
144
145 if (cpuresetfn == NULL)
146 cpuresetfn = dapmic_reset;
147 if (powerdownfn == NULL)
148 powerdownfn = dapmic_powerdown;
149
150 task_set(&sc->sc_task, dapmic_shutdown_task, sc);
151
152 /* Mask away events we don't care about */
153 dapmic_reg_write(sc, IRQ_MASK_A,
154 0xff & ~(IRQ_MASK_A_M_RESERVED | IRQ_MASK_A_M_nONKEY));
155 dapmic_reg_write(sc, IRQ_MASK_B, 0xff);
156 dapmic_reg_write(sc, IRQ_MASK_C, 0xff);
157 dapmic_reg_write(sc, IRQ_MASK_D, 0xff);
158
159 /* Clear past faults and events. */
160 dapmic_reg_write(sc, FAULT_LOG, dapmic_reg_read(sc, FAULT_LOG));
161 dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A));
162 dapmic_reg_write(sc, EVENT_B, dapmic_reg_read(sc, EVENT_B));
163 dapmic_reg_write(sc, EVENT_C, dapmic_reg_read(sc, EVENT_C));
164 dapmic_reg_write(sc, EVENT_D, dapmic_reg_read(sc, EVENT_D));
165
166 if (node != 0) {
167 sc->sc_ih = fdt_intr_establish_idx(node, 0, IPL_CLOCK,
168 dapmic_intr, sc, sc->sc_dev.dv_xname);
169 if (sc->sc_ih == NULL)
170 printf(", can't establish interrupt");
171 }
172
173 printf("\n");
174 }
175
176 uint8_t
dapmic_reg_read(struct dapmic_softc * sc,int reg)177 dapmic_reg_read(struct dapmic_softc *sc, int reg)
178 {
179 uint8_t cmd = reg;
180 uint8_t val;
181 int error;
182
183 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
184 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
185 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
186 iic_release_bus(sc->sc_tag, I2C_F_POLL);
187
188 if (error) {
189 printf("%s: can't read register 0x%02x\n",
190 sc->sc_dev.dv_xname, reg);
191 val = 0xff;
192 }
193
194 return val;
195 }
196
197 void
dapmic_reg_write(struct dapmic_softc * sc,int reg,uint8_t val)198 dapmic_reg_write(struct dapmic_softc *sc, int reg, uint8_t val)
199 {
200 uint8_t cmd = reg;
201 int error;
202
203 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
204 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
205 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
206 iic_release_bus(sc->sc_tag, I2C_F_POLL);
207
208 if (error) {
209 printf("%s: can't write register 0x%02x\n",
210 sc->sc_dev.dv_xname, reg);
211 }
212 }
213
214 int
dapmic_clock_read(struct dapmic_softc * sc,struct clock_ymdhms * dt)215 dapmic_clock_read(struct dapmic_softc *sc, struct clock_ymdhms *dt)
216 {
217 uint8_t regs[6];
218 uint8_t cmd = COUNT_S;
219 int error;
220
221 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
222 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
223 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL);
224 iic_release_bus(sc->sc_tag, I2C_F_POLL);
225
226 if (error)
227 return error;
228
229 dt->dt_sec = (regs[0] & COUNT_S_COUNT_SEC);
230 dt->dt_min = (regs[1] & COUNT_MI_COUNT_MIN);
231 dt->dt_hour = (regs[2] & COUNT_H_COUNT_HOUR);
232 dt->dt_day = (regs[3] & COUNT_D_COUNT_DAY);
233 dt->dt_mon = (regs[4] & COUNT_MO_COUNT_MONTH);
234 dt->dt_year = (regs[5] & COUNT_Y_COUNT_YEAR) + 2000;
235
236 /* Consider the time to be invalid if the MONITOR bit isn't set. */
237 if ((regs[5] & COUNT_Y_MONITOR) == 0)
238 return EINVAL;
239
240 return 0;
241 }
242
243 int
dapmic_clock_write(struct dapmic_softc * sc,struct clock_ymdhms * dt)244 dapmic_clock_write(struct dapmic_softc *sc, struct clock_ymdhms *dt)
245 {
246 uint8_t regs[6];
247 uint8_t cmd = COUNT_S;
248 int error;
249
250 regs[0] = dt->dt_sec;
251 regs[1] = dt->dt_min;
252 regs[2] = dt->dt_hour;
253 regs[3] = dt->dt_day;
254 regs[4] = dt->dt_mon;
255 regs[5] = (dt->dt_year - 2000) | COUNT_Y_MONITOR;
256
257 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
258 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
259 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL);
260 iic_release_bus(sc->sc_tag, I2C_F_POLL);
261
262 if (error)
263 return error;
264
265 return 0;
266 }
267
268 int
dapmic_gettime(struct todr_chip_handle * handle,struct timeval * tv)269 dapmic_gettime(struct todr_chip_handle *handle, struct timeval *tv)
270 {
271 struct dapmic_softc *sc = handle->cookie;
272 struct clock_ymdhms dt;
273 int error;
274
275 error = dapmic_clock_read(sc, &dt);
276 if (error)
277 return error;
278
279 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
280 dt.dt_day > 31 || dt.dt_day == 0 ||
281 dt.dt_mon > 12 || dt.dt_mon == 0 ||
282 dt.dt_year < POSIX_BASE_YEAR)
283 return EINVAL;
284
285 tv->tv_sec = clock_ymdhms_to_secs(&dt);
286 tv->tv_usec = 0;
287 return 0;
288 }
289
290 int
dapmic_settime(struct todr_chip_handle * handle,struct timeval * tv)291 dapmic_settime(struct todr_chip_handle *handle, struct timeval *tv)
292 {
293 struct dapmic_softc *sc = handle->cookie;
294 struct clock_ymdhms dt;
295
296 clock_secs_to_ymdhms(tv->tv_sec, &dt);
297
298 return dapmic_clock_write(sc, &dt);
299 }
300
301 void
dapmic_reset_irq_mask(struct dapmic_softc * sc)302 dapmic_reset_irq_mask(struct dapmic_softc *sc)
303 {
304 dapmic_reg_write(sc, IRQ_MASK_A, 0);
305 dapmic_reg_write(sc, IRQ_MASK_B, 0);
306 dapmic_reg_write(sc, IRQ_MASK_C, 0);
307 dapmic_reg_write(sc, IRQ_MASK_D, 0);
308 }
309
310 void
dapmic_reset(void)311 dapmic_reset(void)
312 {
313 struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
314 uint8_t reg;
315
316 /* Re-enable irqs and the associated wake-up events. */
317 dapmic_reset_irq_mask(sc);
318
319 /* Enable tick alarm wakeup with a one second interval. */
320 reg = dapmic_reg_read(sc, ALARM_MO);
321 reg &= ~ALARM_MO_TICK_TYPE;
322 reg |= ALARM_MO_TICK_WAKE;
323 dapmic_reg_write(sc, ALARM_MO, reg);
324
325 /* Enable tick function. */
326 reg = dapmic_reg_read(sc, ALARM_Y);
327 reg |= ALARM_Y_TICK_ON;
328 dapmic_reg_write(sc, ALARM_Y, reg);
329
330 /* Clear events such that we wake up again. */
331 dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A));
332 dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
333 }
334
335 void
dapmic_powerdown(void)336 dapmic_powerdown(void)
337 {
338 struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
339 uint8_t reg;
340
341 /* Re-enable irqs and the associated wake-up events. */
342 dapmic_reset_irq_mask(sc);
343
344 /* Disable tick function such that it doesn't wake us up. */
345 reg = dapmic_reg_read(sc, ALARM_Y);
346 reg &= ~ALARM_Y_TICK_ON;
347 dapmic_reg_write(sc, ALARM_Y, reg);
348
349 dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
350 }
351
352 void
dapmic_shutdown_task(void * arg)353 dapmic_shutdown_task(void *arg)
354 {
355 extern int allowpowerdown;
356
357 if (allowpowerdown == 1) {
358 allowpowerdown = 0;
359 prsignal(initprocess, SIGUSR2);
360 }
361 }
362
363 int
dapmic_intr(void * arg)364 dapmic_intr(void *arg)
365 {
366 struct dapmic_softc *sc = arg;
367 uint8_t event_a, event_b, event_c, event_d, fault;
368
369 event_b = event_c = event_d = 0;
370
371 event_a = dapmic_reg_read(sc, EVENT_A);
372 DPRINTF(("%s: %s: event_a %#02.2hhx", sc->sc_dev.dv_xname, __func__,
373 event_a));
374
375 /* Acknowledge all events. */
376 if (event_a & EVENT_A_EVENTS_B) {
377 event_b = dapmic_reg_read(sc, EVENT_B);
378 DPRINTF((", event_b %#02.2hhx", event_b));
379 if (event_b != 0)
380 dapmic_reg_write(sc, EVENT_B, event_b);
381 }
382 if (event_a & EVENT_A_EVENTS_C) {
383 event_c = dapmic_reg_read(sc, EVENT_C);
384 DPRINTF((", event_c %#02.2hhx", event_c));
385 if (event_c != 0)
386 dapmic_reg_write(sc, EVENT_C, event_c);
387 }
388 if (event_a & EVENT_A_EVENTS_D) {
389 event_d = dapmic_reg_read(sc, EVENT_D);
390 DPRINTF((", event_d %#02.2hhx", event_d));
391 if (event_d != 0)
392 dapmic_reg_write(sc, EVENT_D, event_d);
393 }
394 event_a &= ~(EVENT_A_EVENTS_B|EVENT_A_EVENTS_C|EVENT_A_EVENTS_D);
395 if (event_a != 0)
396 dapmic_reg_write(sc, EVENT_A, event_a);
397
398 DPRINTF(("\n"));
399
400 fault = dapmic_reg_read(sc, FAULT_LOG);
401 if (fault != 0) {
402 static int warned;
403 if (!warned) {
404 warned = 1;
405 printf("%s: FAULT_LOG %#02.2hhx\n", sc->sc_dev.dv_xname,
406 fault);
407 }
408 /*
409 * Don't blindly acknowledge the fault log bits, else we may
410 * prevent legit behavior like a forced poweroff with a long
411 * power button press.
412 */
413 }
414
415 if (event_a & EVENT_A_E_nONKEY)
416 task_add(systq, &sc->sc_task);
417
418 if (event_a | event_b | event_c | event_d)
419 return 1;
420
421 return 0;
422 }
423