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