1 /* $OpenBSD: sxirtc.c,v 1.9 2024/01/27 11:22:16 kettenis Exp $ */
2 /*
3 * Copyright (c) 2008 Mark Kettenis
4 * Copyright (c) 2013 Artturi Alm
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23
24 #include <dev/clock_subr.h>
25
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28
29 #include <dev/fdt/sunxireg.h>
30
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/fdt.h>
33 #include <dev/ofw/ofw_clock.h>
34
35 #define SXIRTC_LOSC_CTRL 0x00
36 #define SXIRTC_LOSC_CTRL_KEY_FIELD 0x16aa0000
37 #define SXIRTC_LOSC_CTRL_SEL_EXT32K 0x00000001
38 #define SXIRTC_YYMMDD_A10 0x04
39 #define SXIRTC_HHMMSS_A10 0x08
40 #define SXIRTC_YYMMDD_A31 0x10
41 #define SXIRTC_HHMMSS_A31 0x14
42 #define SXIRTC_LOSC_OUT_GATING 0x60
43
44 #define LEAPYEAR(y) \
45 (((y) % 4 == 0 && \
46 (y) % 100 != 0) || \
47 (y) % 400 == 0)
48
49 struct sxirtc_softc {
50 struct device sc_dev;
51 bus_space_tag_t sc_iot;
52 bus_space_handle_t sc_ioh;
53
54 struct clock_device sc_cd;
55
56 bus_size_t sc_yymmdd;
57 bus_size_t sc_hhmmss;
58 uint32_t base_year;
59 uint32_t year_mask;
60 uint32_t leap_shift;
61 int linear_day;
62 };
63
64 int sxirtc_match(struct device *, void *, void *);
65 void sxirtc_attach(struct device *, struct device *, void *);
66
67 const struct cfattach sxirtc_ca = {
68 sizeof(struct sxirtc_softc), sxirtc_match, sxirtc_attach
69 };
70
71 struct cfdriver sxirtc_cd = {
72 NULL, "sxirtc", DV_DULL
73 };
74
75 uint32_t sxirtc_get_frequency(void *, uint32_t *);
76 void sxirtc_enable(void *, uint32_t *, int);
77 int sxirtc_gettime(todr_chip_handle_t, struct timeval *);
78 int sxirtc_settime(todr_chip_handle_t, struct timeval *);
79
80 int
sxirtc_match(struct device * parent,void * match,void * aux)81 sxirtc_match(struct device *parent, void *match, void *aux)
82 {
83 struct fdt_attach_args *faa = aux;
84
85 return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-rtc") ||
86 OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc") ||
87 OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") ||
88 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") ||
89 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc") ||
90 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h616-rtc") ||
91 OF_is_compatible(faa->fa_node, "allwinner,sun50i-r329-rtc"));
92 }
93
94 void
sxirtc_attach(struct device * parent,struct device * self,void * aux)95 sxirtc_attach(struct device *parent, struct device *self, void *aux)
96 {
97 struct sxirtc_softc *sc = (struct sxirtc_softc *)self;
98 struct fdt_attach_args *faa = aux;
99 todr_chip_handle_t handle;
100
101 if (faa->fa_nreg < 1)
102 return;
103
104 handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
105 if (handle == NULL)
106 panic("sxirtc_attach: couldn't allocate todr_handle");
107
108 sc->sc_iot = faa->fa_iot;
109 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
110 faa->fa_reg[0].size, 0, &sc->sc_ioh))
111 panic("sxirtc_attach: bus_space_map failed!");
112
113 if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") ||
114 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") ||
115 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc") ||
116 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h616-rtc") ||
117 OF_is_compatible(faa->fa_node, "allwinner,sun50i-r329-rtc")) {
118 sc->sc_yymmdd = SXIRTC_YYMMDD_A31;
119 sc->sc_hhmmss = SXIRTC_HHMMSS_A31;
120 } else {
121 sc->sc_yymmdd = SXIRTC_YYMMDD_A10;
122 sc->sc_hhmmss = SXIRTC_HHMMSS_A10;
123 }
124
125 if (OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc")) {
126 sc->base_year = 1970;
127 sc->year_mask = 0xff;
128 sc->leap_shift = 24;
129 } else {
130 sc->base_year = 2010;
131 sc->year_mask = 0x3f;
132 sc->leap_shift = 22;
133 }
134
135 /*
136 * Newer SoCs store the number of days since a fixed epoch
137 * instead of YYMMDD. Take this to be the number of days
138 * since the Unix epoch since that is what Linux does.
139 */
140 if (OF_is_compatible(faa->fa_node, "allwinner,sun50i-h616-rtc") ||
141 OF_is_compatible(faa->fa_node, "allwinner,sun50i-r329-rtc")) {
142 sc->base_year = 1970;
143 sc->linear_day = 1;
144 }
145
146 if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") ||
147 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc")) {
148 /* Switch to external oscillator. */
149 SXIWRITE4(sc, SXIRTC_LOSC_CTRL,
150 SXIRTC_LOSC_CTRL_KEY_FIELD | SXIRTC_LOSC_CTRL_SEL_EXT32K);
151
152 sc->sc_cd.cd_node = faa->fa_node;
153 sc->sc_cd.cd_cookie = sc;
154 sc->sc_cd.cd_get_frequency = sxirtc_get_frequency;
155 sc->sc_cd.cd_enable = sxirtc_enable;
156 clock_register(&sc->sc_cd);
157 }
158
159 handle->cookie = self;
160 handle->todr_gettime = sxirtc_gettime;
161 handle->todr_settime = sxirtc_settime;
162 handle->bus_cookie = NULL;
163 handle->todr_setwen = NULL;
164 handle->todr_quality = 0;
165 todr_attach(handle);
166
167 printf("\n");
168 }
169
170 uint32_t
sxirtc_get_frequency(void * cookie,uint32_t * cells)171 sxirtc_get_frequency(void *cookie, uint32_t *cells)
172 {
173 struct sxirtc_softc *sc = cookie;
174 uint32_t idx = cells[0];
175
176 switch (idx) {
177 case 0: /* osc32k */
178 case 1: /* osc32k-out */
179 return clock_get_frequency_idx(sc->sc_cd.cd_node, 0);
180 case 2: /* iosc */
181 return 16000000;
182 }
183
184 printf("%s: 0x%08x\n", __func__, idx);
185 return 0;
186 }
187
188 void
sxirtc_enable(void * cookie,uint32_t * cells,int on)189 sxirtc_enable(void *cookie, uint32_t *cells, int on)
190 {
191 struct sxirtc_softc *sc = cookie;
192 uint32_t idx = cells[0];
193
194 switch (idx) {
195 case 0: /* osc32k */
196 break;
197 case 1: /* osc32k-out */
198 if (on)
199 SXISET4(sc, SXIRTC_LOSC_OUT_GATING, 1);
200 else
201 SXICLR4(sc, SXIRTC_LOSC_OUT_GATING, 1);
202 break;
203 case 2: /* iosc */
204 break;
205 default:
206 printf("%s: 0x%08x\n", __func__, idx);
207 break;
208 }
209 }
210
211 int
sxirtc_gettime(todr_chip_handle_t handle,struct timeval * tv)212 sxirtc_gettime(todr_chip_handle_t handle, struct timeval *tv)
213 {
214 struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie;
215 struct clock_ymdhms dt;
216 uint32_t reg;
217
218 reg = SXIREAD4(sc, sc->sc_yymmdd);
219 if (sc->linear_day) {
220 clock_secs_to_ymdhms(reg * SECDAY, &dt);
221 } else {
222 dt.dt_day = reg & 0x1f;
223 dt.dt_mon = reg >> 8 & 0x0f;
224 dt.dt_year = (reg >> 16 & sc->year_mask) + sc->base_year;
225 }
226
227 reg = SXIREAD4(sc, sc->sc_hhmmss);
228 dt.dt_sec = reg & 0x3f;
229 dt.dt_min = reg >> 8 & 0x3f;
230 dt.dt_hour = reg >> 16 & 0x1f;
231 dt.dt_wday = reg >> 29 & 0x07;
232
233 if (dt.dt_sec > 59 || dt.dt_min > 59 ||
234 dt.dt_hour > 23 || dt.dt_wday > 6 ||
235 dt.dt_day > 31 || dt.dt_day == 0 ||
236 dt.dt_mon > 12 || dt.dt_mon == 0)
237 return 1;
238
239 /*
240 * Reject the first year that can be represented by the clock.
241 * This avoids reporting a bogus time if the RTC isn't battery
242 * powered.
243 */
244 if (dt.dt_year == sc->base_year)
245 return 1;
246
247 tv->tv_sec = clock_ymdhms_to_secs(&dt);
248 tv->tv_usec = 0;
249 return 0;
250 }
251
252 int
sxirtc_settime(todr_chip_handle_t handle,struct timeval * tv)253 sxirtc_settime(todr_chip_handle_t handle, struct timeval *tv)
254 {
255 struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie;
256 struct clock_ymdhms dt;
257
258 clock_secs_to_ymdhms(tv->tv_sec, &dt);
259
260 if (dt.dt_sec > 59 || dt.dt_min > 59 ||
261 dt.dt_hour > 23 || dt.dt_wday > 6 ||
262 dt.dt_day > 31 || dt.dt_day == 0 ||
263 dt.dt_mon > 12 || dt.dt_mon == 0)
264 return 1;
265
266 SXICMS4(sc, sc->sc_hhmmss, 0xe0000000 | 0x1f0000 | 0x3f00 | 0x3f,
267 dt.dt_sec | (dt.dt_min << 8) | (dt.dt_hour << 16) |
268 (dt.dt_wday << 29));
269
270 if (sc->linear_day) {
271 SXICMS4(sc, sc->sc_yymmdd, 0xffff, tv->tv_sec / SECDAY);
272 } else {
273 SXICMS4(sc, sc->sc_yymmdd, 0x00400000 | (sc->year_mask << 16) |
274 0x0f00 | 0x1f, dt.dt_day | (dt.dt_mon << 8) |
275 ((dt.dt_year - sc->base_year) << 16) |
276 (LEAPYEAR(dt.dt_year) << sc->leap_shift));
277 }
278
279 return 0;
280 }
281