xref: /openbsd-src/sys/dev/fdt/acrtc.c (revision 4b70baf6e17fc8b27fc1f7fa7929335753fa94c3)
1 /*	$OpenBSD: acrtc.c,v 1.3 2019/04/20 22:40:13 deraadt Exp $	*/
2 /*
3  * Copyright (c) 2017 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/fdt/rsbvar.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/ofw_clock.h>
27 #include <dev/ofw/fdt.h>
28 
29 #include <dev/clock_subr.h>
30 
31 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
32 
33 extern todr_chip_handle_t todr_handle;
34 
35 #define CK32K_OUT_CTRL1			0xc1
36 #define  CK32K_OUT_CTRL_PRE_DIV_MASK	(0x7 << 5)
37 #define  CK32K_OUT_CTRL_PRE_DIV_32K	(0x7 << 5)
38 #define  CK32K_OUT_CTRL_MUX_SEL_MASK	(1 << 4)
39 #define  CK32K_OUT_CTRL_MUX_SEL_32K	(0 << 4)
40 #define  CK32K_OUT_CTRL_POST_DIV_MASK	(0x7 << 1)
41 #define  CK32K_OUT_CTRL_POST_DIV_32K	(0x0 << 1)
42 #define  CK32K_OUT_CTRL_ENA		(1 << 0)
43 #define RTC_CTRL			0xc7
44 #define  RTC_CTRL_12H_24H_MODE		(1 << 0)
45 #define RTC_SEC				0xc8
46 #define RTC_MIN				0xc9
47 #define RTC_HOU				0xca
48 #define RTC_WEE				0xcb
49 #define RTC_DAY				0xcc
50 #define RTC_MON				0xcd
51 #define RTC_YEA				0xce
52 #define  RTC_YEA_LEAP_YEAR		(1 << 15)
53 #define RTC_UPD_TRIG			0xcf
54 #define  RTC_UPD_TRIG_UPDATE		(1 << 15)
55 
56 struct acrtc_softc {
57 	struct device		sc_dev;
58 	void			*sc_cookie;
59 	uint16_t 		sc_rta;
60 
61 	struct todr_chip_handle	sc_todr;
62 	struct clock_device	sc_cd;
63 };
64 
65 int	acrtc_match(struct device *, void *, void *);
66 void	acrtc_attach(struct device *, struct device *, void *);
67 
68 struct cfattach acrtc_ca = {
69 	sizeof(struct acrtc_softc), acrtc_match, acrtc_attach
70 };
71 
72 struct cfdriver acrtc_cd = {
73 	NULL, "acrtc", DV_DULL
74 };
75 
76 int	acrtc_clock_read(struct acrtc_softc *, struct clock_ymdhms *);
77 int	acrtc_clock_write(struct acrtc_softc *, struct clock_ymdhms *);
78 int	acrtc_gettime(struct todr_chip_handle *, struct timeval *);
79 int	acrtc_settime(struct todr_chip_handle *, struct timeval *);
80 
81 void	acrtc_ck32k_enable(void *, uint32_t *, int);
82 
83 int
84 acrtc_match(struct device *parent, void *match, void *aux)
85 {
86 	struct rsb_attach_args *ra = aux;
87 
88 	if (strcmp(ra->ra_name, "x-powers,ac100") == 0)
89 		return 1;
90 	return 0;
91 }
92 
93 void
94 acrtc_attach(struct device *parent, struct device *self, void *aux)
95 {
96 	struct acrtc_softc *sc = (struct acrtc_softc *)self;
97 	struct rsb_attach_args *ra = aux;
98 	int node;
99 
100 	sc->sc_cookie = ra->ra_cookie;
101 	sc->sc_rta = ra->ra_rta;
102 
103 	printf("\n");
104 
105 	sc->sc_todr.cookie = sc;
106 	sc->sc_todr.todr_gettime = acrtc_gettime;
107 	sc->sc_todr.todr_settime = acrtc_settime;
108 	todr_handle = &sc->sc_todr;
109 
110 	node = OF_getnodebyname(ra->ra_node, "rtc");
111 	if (node == 0)
112 		return;
113 
114 	sc->sc_cd.cd_node = node;
115 	sc->sc_cd.cd_cookie = sc;
116 	sc->sc_cd.cd_enable = acrtc_ck32k_enable;
117 	clock_register(&sc->sc_cd);
118 }
119 
120 static inline uint16_t
121 acrtc_read_reg(struct acrtc_softc *sc, uint8_t reg)
122 {
123 	return rsb_read_2(sc->sc_cookie, sc->sc_rta, reg);
124 }
125 
126 static inline void
127 acrtc_write_reg(struct acrtc_softc *sc, uint8_t reg, uint16_t value)
128 {
129 	rsb_write_2(sc->sc_cookie, sc->sc_rta, reg, value);
130 }
131 
132 int
133 acrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
134 {
135 	struct acrtc_softc *sc = handle->cookie;
136 	struct clock_ymdhms dt;
137 	int error;
138 
139 	error = acrtc_clock_read(sc, &dt);
140 	if (error)
141 		return error;
142 
143 	if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
144 	    dt.dt_day > 31 || dt.dt_day == 0 ||
145 	    dt.dt_mon > 12 || dt.dt_mon == 0 ||
146 	    dt.dt_year < POSIX_BASE_YEAR)
147 		return EINVAL;
148 
149 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
150 	tv->tv_usec = 0;
151 	return 0;
152 }
153 
154 int
155 acrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
156 {
157 	struct acrtc_softc *sc = handle->cookie;
158 	struct clock_ymdhms dt;
159 
160 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
161 
162 	return acrtc_clock_write(sc, &dt);
163 }
164 
165 int
166 acrtc_clock_read(struct acrtc_softc *sc, struct clock_ymdhms *dt)
167 {
168 	uint16_t ctrl;
169 
170 	dt->dt_sec = FROMBCD(acrtc_read_reg(sc, RTC_SEC));
171 	dt->dt_min = FROMBCD(acrtc_read_reg(sc, RTC_MIN));
172 	dt->dt_hour = FROMBCD(acrtc_read_reg(sc, RTC_HOU));
173 	dt->dt_day = FROMBCD(acrtc_read_reg(sc, RTC_DAY));
174 	dt->dt_mon = FROMBCD(acrtc_read_reg(sc, RTC_MON));
175 	dt->dt_year = FROMBCD(acrtc_read_reg(sc, RTC_YEA)) + 2000;
176 
177 #ifdef DEBUG
178 	printf("%02d/%02d/%04d %02d:%02d:%0d\n", dt->dt_day, dt->dt_mon,
179 	    dt->dt_year, dt->dt_hour, dt->dt_min, dt->dt_sec);
180 #endif
181 
182 	/* Consider the time to be invalid if the clock is in 12H mode. */
183 	ctrl = acrtc_read_reg(sc, RTC_CTRL);
184 	if ((ctrl & RTC_CTRL_12H_24H_MODE) == 0)
185 		return EINVAL;
186 
187 	return 0;
188 }
189 
190 int
191 acrtc_clock_write(struct acrtc_softc *sc, struct clock_ymdhms *dt)
192 {
193 	uint16_t leap = isleap(dt->dt_year) ? RTC_YEA_LEAP_YEAR : 0;
194 
195 	acrtc_write_reg(sc, RTC_SEC, TOBCD(dt->dt_sec));
196 	acrtc_write_reg(sc, RTC_MIN, TOBCD(dt->dt_min));
197 	acrtc_write_reg(sc, RTC_HOU, TOBCD(dt->dt_hour));
198 	acrtc_write_reg(sc, RTC_WEE, TOBCD(dt->dt_wday));
199 	acrtc_write_reg(sc, RTC_DAY, TOBCD(dt->dt_day));
200 	acrtc_write_reg(sc, RTC_MON, TOBCD(dt->dt_mon));
201 	acrtc_write_reg(sc, RTC_YEA, TOBCD(dt->dt_year - 2000) | leap);
202 	acrtc_write_reg(sc, RTC_UPD_TRIG, RTC_UPD_TRIG_UPDATE);
203 
204 	/* Switch to 24H mode to indicate the time is now valid. */
205 	acrtc_write_reg(sc, RTC_CTRL, RTC_CTRL_12H_24H_MODE);
206 
207 	return 0;
208 }
209 
210 void
211 acrtc_ck32k_enable(void *cookie, uint32_t *cells, int on)
212 {
213 	struct acrtc_softc *sc = cookie;
214 	uint32_t idx = cells[0];
215 	uint16_t reg;
216 
217 	reg = acrtc_read_reg(sc, CK32K_OUT_CTRL1 + idx);
218 	reg &= ~CK32K_OUT_CTRL_PRE_DIV_MASK;
219 	reg &= ~CK32K_OUT_CTRL_MUX_SEL_MASK;
220 	reg &= ~CK32K_OUT_CTRL_POST_DIV_MASK;
221 	reg |= CK32K_OUT_CTRL_PRE_DIV_32K;
222 	reg |= CK32K_OUT_CTRL_MUX_SEL_32K;
223 	reg |= CK32K_OUT_CTRL_POST_DIV_32K;
224 	if (on)
225 		reg |= CK32K_OUT_CTRL_ENA;
226 	else
227 		reg &= ~CK32K_OUT_CTRL_ENA;
228 	acrtc_write_reg(sc, CK32K_OUT_CTRL1 + idx, reg);
229 }
230