sxirtc.c revision 1.3
1/*	$OpenBSD: sxirtc.c,v 1.3 2019/01/21 11:24:05 jsg 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
34#define	SXIRTC_YYMMDD		0x04
35#define	SXIRTC_HHMMSS		0x08
36#define	SXIRTC_YYMMDD_A31	0x10
37#define	SXIRTC_HHMMSS_A31	0x14
38
39#define LEAPYEAR(y)        \
40    (((y) % 4 == 0 &&    \
41    (y) % 100 != 0) ||    \
42    (y) % 400 == 0)
43
44
45extern todr_chip_handle_t todr_handle;
46
47struct sxirtc_softc {
48	struct device		sc_dev;
49	bus_space_tag_t		sc_iot;
50	bus_space_handle_t	sc_ioh;
51	bus_size_t		sc_yymmdd;
52	bus_size_t		sc_hhmmss;
53	uint32_t		base_year;
54	uint32_t		year_mask;
55	uint32_t		leap_shift;
56};
57
58int	sxirtc_match(struct device *, void *, void *);
59void	sxirtc_attach(struct device *, struct device *, void *);
60
61struct cfattach sxirtc_ca = {
62	sizeof(struct sxirtc_softc), sxirtc_match, sxirtc_attach
63};
64
65struct cfdriver sxirtc_cd = {
66	NULL, "sxirtc", DV_DULL
67};
68
69int	sxirtc_gettime(todr_chip_handle_t, struct timeval *);
70int	sxirtc_settime(todr_chip_handle_t, struct timeval *);
71
72int
73sxirtc_match(struct device *parent, void *match, void *aux)
74{
75	struct fdt_attach_args *faa = aux;
76
77	return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-rtc") ||
78	    OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc") ||
79	    OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") ||
80	    OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") ||
81	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc"));
82}
83
84void
85sxirtc_attach(struct device *parent, struct device *self, void *aux)
86{
87	struct sxirtc_softc *sc = (struct sxirtc_softc *)self;
88	struct fdt_attach_args *faa = aux;
89	todr_chip_handle_t handle;
90
91	if (faa->fa_nreg < 1)
92		return;
93
94	handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
95	if (handle == NULL)
96		panic("sxirtc_attach: couldn't allocate todr_handle");
97
98	sc->sc_iot = faa->fa_iot;
99	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
100	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
101		panic("sxirtc_attach: bus_space_map failed!");
102
103	if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") ||
104	    OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") ||
105	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc")) {
106		sc->sc_yymmdd = SXIRTC_YYMMDD_A31;
107		sc->sc_hhmmss = SXIRTC_HHMMSS_A31;
108	} else {
109		sc->sc_yymmdd = SXIRTC_YYMMDD;
110		sc->sc_hhmmss = SXIRTC_HHMMSS;
111	}
112
113	if (OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc")) {
114		sc->base_year = 1970;
115		sc->year_mask = 0xff;
116		sc->leap_shift = 24;
117	} else {
118		sc->base_year = 2010;
119		sc->year_mask = 0x3f;
120		sc->leap_shift = 22;
121	}
122
123	handle->cookie = self;
124	handle->todr_gettime = sxirtc_gettime;
125	handle->todr_settime = sxirtc_settime;
126	handle->todr_getcal = NULL;
127	handle->todr_setcal = NULL;
128	handle->bus_cookie = NULL;
129	handle->todr_setwen = NULL;
130	todr_handle = handle;
131
132	printf("\n");
133}
134
135int
136sxirtc_gettime(todr_chip_handle_t handle, struct timeval *tv)
137{
138	struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie;
139	struct clock_ymdhms dt;
140	uint32_t reg;
141
142	reg = SXIREAD4(sc, sc->sc_hhmmss);
143	dt.dt_sec = reg & 0x3f;
144	dt.dt_min = reg >> 8 & 0x3f;
145	dt.dt_hour = reg >> 16 & 0x1f;
146	dt.dt_wday = reg >> 29 & 0x07;
147
148	reg = SXIREAD4(sc, sc->sc_yymmdd);
149	dt.dt_day = reg & 0x1f;
150	dt.dt_mon = reg >> 8 & 0x0f;
151	dt.dt_year = (reg >> 16 & sc->year_mask) + sc->base_year;
152
153	if (dt.dt_sec > 59 || dt.dt_min > 59 ||
154	    dt.dt_hour > 23 || dt.dt_wday > 6 ||
155	    dt.dt_day > 31 || dt.dt_day == 0 ||
156	    dt.dt_mon > 12 || dt.dt_mon == 0)
157		return 1;
158
159	/*
160	 * Reject the first year that can be represented by the clock.
161	 * This avoids reporting a bogus time if the RTC isn't battery
162	 * powered.
163	 */
164	if (dt.dt_year == sc->base_year)
165		return 1;
166
167	tv->tv_sec = clock_ymdhms_to_secs(&dt);
168	tv->tv_usec = 0;
169	return 0;
170}
171
172int
173sxirtc_settime(todr_chip_handle_t handle, struct timeval *tv)
174{
175	struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie;
176	struct clock_ymdhms dt;
177
178	clock_secs_to_ymdhms(tv->tv_sec, &dt);
179
180	if (dt.dt_sec > 59 || dt.dt_min > 59 ||
181	    dt.dt_hour > 23 || dt.dt_wday > 6 ||
182	    dt.dt_day > 31 || dt.dt_day == 0 ||
183	    dt.dt_mon > 12 || dt.dt_mon == 0)
184		return 1;
185
186	SXICMS4(sc, sc->sc_hhmmss, 0xe0000000 | 0x1f0000 | 0x3f00 | 0x3f,
187	    dt.dt_sec | (dt.dt_min << 8) | (dt.dt_hour << 16) |
188	    (dt.dt_wday << 29));
189
190	SXICMS4(sc, sc->sc_yymmdd, 0x00400000 | (sc->year_mask << 16) |
191	    0x0f00 | 0x1f, dt.dt_day | (dt.dt_mon << 8) |
192	    ((dt.dt_year - sc->base_year) << 16) |
193	    (LEAPYEAR(dt.dt_year) << sc->leap_shift));
194
195	return 0;
196}
197