xref: /openbsd/sys/dev/fdt/dapmic.c (revision 5a38ef86)
1 /*	$OpenBSD: dapmic.c,v 1.1 2021/06/16 12:37:24 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 
23 #include <dev/ofw/openfirm.h>
24 #include <dev/ofw/ofw_regulator.h>
25 #include <dev/ofw/fdt.h>
26 
27 #include <dev/i2c/i2cvar.h>
28 
29 #include <dev/clock_subr.h>
30 
31 extern void (*cpuresetfn)(void);
32 extern void (*powerdownfn)(void);
33 
34 /* Registers */
35 #define EVENT_A			0x06
36 #define CONTROL_F		0x13
37 #define  CONTROL_F_WAKE_UP		(1 << 2)
38 #define  CONTROL_F_SHUTDOWN		(1 << 1)
39 #define COUNT_S			0x40
40 #define  COUNT_S_COUNT_SEC		0x3f
41 #define COUNT_MI		0x41
42 #define  COUNT_MI_COUNT_MIN		0x3f
43 #define COUNT_H			0x42
44 #define  COUNT_H_COUNT_HOUR		0x1f
45 #define COUNT_D			0x43
46 #define  COUNT_D_COUNT_DAY		0x1f
47 #define COUNT_MO		0x44
48 #define  COUNT_MO_COUNT_MONTH		0x0f
49 #define COUNT_Y			0x45
50 #define  COUNT_Y_MONITOR		(1 << 6)
51 #define  COUNT_Y_COUNT_YEAR		0x3f
52 #define ALARM_MO		0x4a
53 #define  ALARM_MO_TICK_WAKE		(1 << 5)
54 #define  ALARM_MO_TICK_TYPE		(1 << 4)
55 #define ALARM_Y			0x4b
56 #define  ALARM_Y_TICK_ON		(1 << 7)
57 
58 struct dapmic_softc {
59 	struct device sc_dev;
60 	i2c_tag_t sc_tag;
61 	i2c_addr_t sc_addr;
62 
63 	struct todr_chip_handle sc_todr;
64 };
65 
66 int	dapmic_match(struct device *, void *, void *);
67 void	dapmic_attach(struct device *, struct device *, void *);
68 
69 struct cfattach dapmic_ca = {
70 	sizeof(struct dapmic_softc), dapmic_match, dapmic_attach
71 };
72 
73 struct cfdriver dapmic_cd = {
74 	NULL, "dapmic", DV_DULL
75 };
76 
77 uint8_t	dapmic_reg_read(struct dapmic_softc *, int);
78 void	dapmic_reg_write(struct dapmic_softc *, int, uint8_t);
79 int	dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *);
80 int	dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *);
81 int	dapmic_gettime(struct todr_chip_handle *, struct timeval *);
82 int	dapmic_settime(struct todr_chip_handle *, struct timeval *);
83 void	dapmic_reset(void);
84 void	dapmic_powerdown(void);
85 
86 int
87 dapmic_match(struct device *parent, void *match, void *aux)
88 {
89 	struct i2c_attach_args *ia = aux;
90 
91 	return (strcmp(ia->ia_name, "dlg,da9063") == 0);
92 }
93 
94 void
95 dapmic_attach(struct device *parent, struct device *self, void *aux)
96 {
97 	struct dapmic_softc *sc = (struct dapmic_softc *)self;
98 	struct i2c_attach_args *ia = aux;
99 
100 	sc->sc_tag = ia->ia_tag;
101 	sc->sc_addr = ia->ia_addr;
102 
103 	sc->sc_todr.cookie = sc;
104 	sc->sc_todr.todr_gettime = dapmic_gettime;
105 	sc->sc_todr.todr_settime = dapmic_settime;
106 	todr_attach(&sc->sc_todr);
107 
108 	printf("\n");
109 
110 	if (cpuresetfn == NULL)
111 		cpuresetfn = dapmic_reset;
112 	if (powerdownfn == NULL)
113 		powerdownfn = dapmic_powerdown;
114 }
115 
116 uint8_t
117 dapmic_reg_read(struct dapmic_softc *sc, int reg)
118 {
119 	uint8_t cmd = reg;
120 	uint8_t val;
121 	int error;
122 
123 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
124 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
125 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
126 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
127 
128 	if (error) {
129 		printf("%s: can't read register 0x%02x\n",
130 		    sc->sc_dev.dv_xname, reg);
131 		val = 0xff;
132 	}
133 
134 	return val;
135 }
136 
137 void
138 dapmic_reg_write(struct dapmic_softc *sc, int reg, uint8_t val)
139 {
140 	uint8_t cmd = reg;
141 	int error;
142 
143 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
144 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
145 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
146 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
147 
148 	if (error) {
149 		printf("%s: can't write register 0x%02x\n",
150 		    sc->sc_dev.dv_xname, reg);
151 	}
152 }
153 
154 int
155 dapmic_clock_read(struct dapmic_softc *sc, struct clock_ymdhms *dt)
156 {
157 	uint8_t regs[6];
158 	uint8_t cmd = COUNT_S;
159 	int error;
160 
161 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
162 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
163 	    &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL);
164 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
165 
166 	if (error)
167 		return error;
168 
169 	dt->dt_sec = (regs[0] & COUNT_S_COUNT_SEC);
170 	dt->dt_min = (regs[1] & COUNT_MI_COUNT_MIN);
171 	dt->dt_hour = (regs[2] & COUNT_H_COUNT_HOUR);
172 	dt->dt_day = (regs[3] & COUNT_D_COUNT_DAY);
173 	dt->dt_mon = (regs[4] & COUNT_MO_COUNT_MONTH);
174 	dt->dt_year = (regs[5] & COUNT_Y_COUNT_YEAR) + 2000;
175 
176 	/* Consider the time to be invalid if the MONITOR bit isn't set. */
177 	if ((regs[5] & COUNT_Y_MONITOR) == 0)
178 		return EINVAL;
179 
180 	return 0;
181 }
182 
183 int
184 dapmic_clock_write(struct dapmic_softc *sc, struct clock_ymdhms *dt)
185 {
186 	uint8_t regs[6];
187 	uint8_t cmd = COUNT_S;
188 	int error;
189 
190 	regs[0] = dt->dt_sec;
191 	regs[1] = dt->dt_min;
192 	regs[2] = dt->dt_hour;
193 	regs[3] = dt->dt_day;
194 	regs[4] = dt->dt_mon;
195 	regs[5] = (dt->dt_year - 2000) | COUNT_Y_MONITOR;
196 
197 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
198 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
199 	    &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL);
200 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
201 
202 	if (error)
203 		return error;
204 
205 	return 0;
206 }
207 
208 int
209 dapmic_gettime(struct todr_chip_handle *handle, struct timeval *tv)
210 {
211 	struct dapmic_softc *sc = handle->cookie;
212 	struct clock_ymdhms dt;
213 	int error;
214 
215 	error = dapmic_clock_read(sc, &dt);
216 	if (error)
217 		return error;
218 
219 	if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
220 	    dt.dt_day > 31 || dt.dt_day == 0 ||
221 	    dt.dt_mon > 12 || dt.dt_mon == 0 ||
222 	    dt.dt_year < POSIX_BASE_YEAR)
223 		return EINVAL;
224 
225 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
226 	tv->tv_usec = 0;
227 	return 0;
228 }
229 
230 int
231 dapmic_settime(struct todr_chip_handle *handle, struct timeval *tv)
232 {
233 	struct dapmic_softc *sc = handle->cookie;
234 	struct clock_ymdhms dt;
235 
236 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
237 
238 	return dapmic_clock_write(sc, &dt);
239 }
240 
241 void
242 dapmic_reset(void)
243 {
244 	struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
245 	uint8_t reg;
246 
247 	/* Enable tick alarm wakeup with a one second interval. */
248 	reg = dapmic_reg_read(sc, ALARM_MO);
249 	reg &= ~ALARM_MO_TICK_TYPE;
250 	reg |= ALARM_MO_TICK_WAKE;
251 	dapmic_reg_write(sc, ALARM_MO, reg);
252 
253 	/* Enable tick function. */
254 	reg = dapmic_reg_read(sc, ALARM_Y);
255 	reg |= ALARM_Y_TICK_ON;
256 	dapmic_reg_write(sc, ALARM_Y, reg);
257 
258 	/* Clear events such that we wake up again. */
259 	dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A));
260 	dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
261 }
262 
263 void
264 dapmic_powerdown(void)
265 {
266 	struct dapmic_softc *sc = dapmic_cd.cd_devs[0];
267 	uint8_t reg;
268 
269 	/* Disable tick function such that it doesn't wake us up. */
270 	reg = dapmic_reg_read(sc, ALARM_Y);
271 	reg &= ~ALARM_Y_TICK_ON;
272 	dapmic_reg_write(sc, ALARM_Y, reg);
273 
274 	dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN);
275 }
276