dapmic.c revision 1.4
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
36extern void (*cpuresetfn)(void);
37extern 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
87struct 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
98int	dapmic_match(struct device *, void *, void *);
99void	dapmic_attach(struct device *, struct device *, void *);
100
101const struct cfattach dapmic_ca = {
102	sizeof(struct dapmic_softc), dapmic_match, dapmic_attach
103};
104
105struct cfdriver dapmic_cd = {
106	NULL, "dapmic", DV_DULL
107};
108
109uint8_t	dapmic_reg_read(struct dapmic_softc *, int);
110void	dapmic_reg_write(struct dapmic_softc *, int, uint8_t);
111int	dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *);
112int	dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *);
113int	dapmic_gettime(struct todr_chip_handle *, struct timeval *);
114int	dapmic_settime(struct todr_chip_handle *, struct timeval *);
115void	dapmic_reset_irq_mask(struct dapmic_softc *);
116void	dapmic_reset(void);
117void	dapmic_powerdown(void);
118int	dapmic_intr(void *);
119void	dapmic_shutdown_task(void *);
120
121int
122dapmic_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
129void
130dapmic_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
176uint8_t
177dapmic_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
197void
198dapmic_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
214int
215dapmic_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
243int
244dapmic_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
268int
269dapmic_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
290int
291dapmic_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
301void
302dapmic_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
310void
311dapmic_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
335void
336dapmic_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
352void
353dapmic_shutdown_task(void *arg)
354{
355	extern int allowpowerdown;
356
357	if (allowpowerdown == 1) {
358		allowpowerdown = 0;
359		prsignal(initprocess, SIGUSR2);
360	}
361}
362
363int
364dapmic_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