1*62d244edSkettenis /* $OpenBSD: acrtc.c,v 1.6 2022/10/17 19:09:46 kettenis Exp $ */
2d863b242Skettenis /*
3d863b242Skettenis * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4d863b242Skettenis *
5d863b242Skettenis * Permission to use, copy, modify, and distribute this software for any
6d863b242Skettenis * purpose with or without fee is hereby granted, provided that the above
7d863b242Skettenis * copyright notice and this permission notice appear in all copies.
8d863b242Skettenis *
9d863b242Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d863b242Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d863b242Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d863b242Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d863b242Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d863b242Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d863b242Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d863b242Skettenis */
17d863b242Skettenis
18d863b242Skettenis #include <sys/param.h>
19d863b242Skettenis #include <sys/systm.h>
20d863b242Skettenis #include <sys/device.h>
21d863b242Skettenis #include <sys/malloc.h>
22d863b242Skettenis
23d863b242Skettenis #include <dev/fdt/rsbvar.h>
24d863b242Skettenis
25d863b242Skettenis #include <dev/ofw/openfirm.h>
26cfe273a3Skettenis #include <dev/ofw/ofw_clock.h>
27d863b242Skettenis #include <dev/ofw/fdt.h>
28d863b242Skettenis
29d863b242Skettenis #include <dev/clock_subr.h>
30d863b242Skettenis
31d863b242Skettenis #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
32d863b242Skettenis
33cfe273a3Skettenis #define CK32K_OUT_CTRL1 0xc1
34cfe273a3Skettenis #define CK32K_OUT_CTRL_PRE_DIV_MASK (0x7 << 5)
35cfe273a3Skettenis #define CK32K_OUT_CTRL_PRE_DIV_32K (0x7 << 5)
36cfe273a3Skettenis #define CK32K_OUT_CTRL_MUX_SEL_MASK (1 << 4)
37cfe273a3Skettenis #define CK32K_OUT_CTRL_MUX_SEL_32K (0 << 4)
38cfe273a3Skettenis #define CK32K_OUT_CTRL_POST_DIV_MASK (0x7 << 1)
39cfe273a3Skettenis #define CK32K_OUT_CTRL_POST_DIV_32K (0x0 << 1)
40cfe273a3Skettenis #define CK32K_OUT_CTRL_ENA (1 << 0)
41d863b242Skettenis #define RTC_CTRL 0xc7
42d863b242Skettenis #define RTC_CTRL_12H_24H_MODE (1 << 0)
43d863b242Skettenis #define RTC_SEC 0xc8
44aed0989aSkettenis #define RTC_SEC_MASK (0x7f << 0)
45d863b242Skettenis #define RTC_MIN 0xc9
46aed0989aSkettenis #define RTC_MIN_MASK (0x7f << 0)
47d863b242Skettenis #define RTC_HOU 0xca
48aed0989aSkettenis #define RTC_HOU_MASK (0x3f << 0)
49d863b242Skettenis #define RTC_WEE 0xcb
50aed0989aSkettenis #define RTC_WEE_MASK (0x07 << 0)
51d863b242Skettenis #define RTC_DAY 0xcc
52aed0989aSkettenis #define RTC_DAY_MASK (0x3f << 0)
53d863b242Skettenis #define RTC_MON 0xcd
54aed0989aSkettenis #define RTC_MON_MASK (0x1f << 0)
55d863b242Skettenis #define RTC_YEA 0xce
56d863b242Skettenis #define RTC_YEA_LEAP_YEAR (1 << 15)
57aed0989aSkettenis #define RTC_YEA_MASK (0xff << 0)
58d863b242Skettenis #define RTC_UPD_TRIG 0xcf
59d863b242Skettenis #define RTC_UPD_TRIG_UPDATE (1 << 15)
60d863b242Skettenis
61d863b242Skettenis struct acrtc_softc {
62d863b242Skettenis struct device sc_dev;
63d863b242Skettenis void *sc_cookie;
64d863b242Skettenis uint16_t sc_rta;
65d863b242Skettenis
66d863b242Skettenis struct todr_chip_handle sc_todr;
67cfe273a3Skettenis struct clock_device sc_cd;
68d863b242Skettenis };
69d863b242Skettenis
70d863b242Skettenis int acrtc_match(struct device *, void *, void *);
71d863b242Skettenis void acrtc_attach(struct device *, struct device *, void *);
72d863b242Skettenis
739fdf0c62Smpi const struct cfattach acrtc_ca = {
74d863b242Skettenis sizeof(struct acrtc_softc), acrtc_match, acrtc_attach
75d863b242Skettenis };
76d863b242Skettenis
77d863b242Skettenis struct cfdriver acrtc_cd = {
78d863b242Skettenis NULL, "acrtc", DV_DULL
79d863b242Skettenis };
80d863b242Skettenis
81d863b242Skettenis int acrtc_clock_read(struct acrtc_softc *, struct clock_ymdhms *);
82d863b242Skettenis int acrtc_clock_write(struct acrtc_softc *, struct clock_ymdhms *);
83d863b242Skettenis int acrtc_gettime(struct todr_chip_handle *, struct timeval *);
84d863b242Skettenis int acrtc_settime(struct todr_chip_handle *, struct timeval *);
85d863b242Skettenis
86cfe273a3Skettenis void acrtc_ck32k_enable(void *, uint32_t *, int);
87cfe273a3Skettenis
88d863b242Skettenis int
acrtc_match(struct device * parent,void * match,void * aux)89d863b242Skettenis acrtc_match(struct device *parent, void *match, void *aux)
90d863b242Skettenis {
91d863b242Skettenis struct rsb_attach_args *ra = aux;
92d863b242Skettenis
93d863b242Skettenis if (strcmp(ra->ra_name, "x-powers,ac100") == 0)
94d863b242Skettenis return 1;
95d863b242Skettenis return 0;
96d863b242Skettenis }
97d863b242Skettenis
98d863b242Skettenis void
acrtc_attach(struct device * parent,struct device * self,void * aux)99d863b242Skettenis acrtc_attach(struct device *parent, struct device *self, void *aux)
100d863b242Skettenis {
101d863b242Skettenis struct acrtc_softc *sc = (struct acrtc_softc *)self;
102d863b242Skettenis struct rsb_attach_args *ra = aux;
103cfe273a3Skettenis int node;
104d863b242Skettenis
105d863b242Skettenis sc->sc_cookie = ra->ra_cookie;
106d863b242Skettenis sc->sc_rta = ra->ra_rta;
107d863b242Skettenis
108d863b242Skettenis printf("\n");
109d863b242Skettenis
110d863b242Skettenis sc->sc_todr.cookie = sc;
111d863b242Skettenis sc->sc_todr.todr_gettime = acrtc_gettime;
112d863b242Skettenis sc->sc_todr.todr_settime = acrtc_settime;
113*62d244edSkettenis sc->sc_todr.todr_quality = 1000;
114*62d244edSkettenis todr_attach(&sc->sc_todr);
115cfe273a3Skettenis
116cfe273a3Skettenis node = OF_getnodebyname(ra->ra_node, "rtc");
117cfe273a3Skettenis if (node == 0)
118cfe273a3Skettenis return;
119cfe273a3Skettenis
120cfe273a3Skettenis sc->sc_cd.cd_node = node;
121cfe273a3Skettenis sc->sc_cd.cd_cookie = sc;
122cfe273a3Skettenis sc->sc_cd.cd_enable = acrtc_ck32k_enable;
123cfe273a3Skettenis clock_register(&sc->sc_cd);
124d863b242Skettenis }
125d863b242Skettenis
1262dbf7a2aSderaadt static inline uint16_t
acrtc_read_reg(struct acrtc_softc * sc,uint8_t reg)127d863b242Skettenis acrtc_read_reg(struct acrtc_softc *sc, uint8_t reg)
128d863b242Skettenis {
129d863b242Skettenis return rsb_read_2(sc->sc_cookie, sc->sc_rta, reg);
130d863b242Skettenis }
131d863b242Skettenis
1322dbf7a2aSderaadt static inline void
acrtc_write_reg(struct acrtc_softc * sc,uint8_t reg,uint16_t value)133d863b242Skettenis acrtc_write_reg(struct acrtc_softc *sc, uint8_t reg, uint16_t value)
134d863b242Skettenis {
135d863b242Skettenis rsb_write_2(sc->sc_cookie, sc->sc_rta, reg, value);
136d863b242Skettenis }
137d863b242Skettenis
138d863b242Skettenis int
acrtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)139d863b242Skettenis acrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
140d863b242Skettenis {
141d863b242Skettenis struct acrtc_softc *sc = handle->cookie;
142d863b242Skettenis struct clock_ymdhms dt;
143d863b242Skettenis int error;
144d863b242Skettenis
145d863b242Skettenis error = acrtc_clock_read(sc, &dt);
146d863b242Skettenis if (error)
147d863b242Skettenis return error;
148d863b242Skettenis
149d863b242Skettenis if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
150d863b242Skettenis dt.dt_day > 31 || dt.dt_day == 0 ||
151d863b242Skettenis dt.dt_mon > 12 || dt.dt_mon == 0 ||
152d863b242Skettenis dt.dt_year < POSIX_BASE_YEAR)
153d863b242Skettenis return EINVAL;
154d863b242Skettenis
155d863b242Skettenis tv->tv_sec = clock_ymdhms_to_secs(&dt);
156d863b242Skettenis tv->tv_usec = 0;
157d863b242Skettenis return 0;
158d863b242Skettenis }
159d863b242Skettenis
160d863b242Skettenis int
acrtc_settime(struct todr_chip_handle * handle,struct timeval * tv)161d863b242Skettenis acrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
162d863b242Skettenis {
163d863b242Skettenis struct acrtc_softc *sc = handle->cookie;
164d863b242Skettenis struct clock_ymdhms dt;
165d863b242Skettenis
166d863b242Skettenis clock_secs_to_ymdhms(tv->tv_sec, &dt);
167d863b242Skettenis
168d863b242Skettenis return acrtc_clock_write(sc, &dt);
169d863b242Skettenis }
170d863b242Skettenis
171d863b242Skettenis int
acrtc_clock_read(struct acrtc_softc * sc,struct clock_ymdhms * dt)172d863b242Skettenis acrtc_clock_read(struct acrtc_softc *sc, struct clock_ymdhms *dt)
173d863b242Skettenis {
174d863b242Skettenis uint16_t ctrl;
175d863b242Skettenis
176aed0989aSkettenis dt->dt_sec = FROMBCD(acrtc_read_reg(sc, RTC_SEC) & RTC_SEC_MASK);
177aed0989aSkettenis dt->dt_min = FROMBCD(acrtc_read_reg(sc, RTC_MIN) & RTC_MIN_MASK);
178aed0989aSkettenis dt->dt_hour = FROMBCD(acrtc_read_reg(sc, RTC_HOU) & RTC_HOU_MASK);
179aed0989aSkettenis dt->dt_day = FROMBCD(acrtc_read_reg(sc, RTC_DAY) & RTC_DAY_MASK);
180aed0989aSkettenis dt->dt_mon = FROMBCD(acrtc_read_reg(sc, RTC_MON) & RTC_MON_MASK);
181aed0989aSkettenis dt->dt_year = FROMBCD(acrtc_read_reg(sc, RTC_YEA) & RTC_YEA_MASK);
182aed0989aSkettenis dt->dt_year += 2000;
183d863b242Skettenis
184d863b242Skettenis #ifdef DEBUG
185d863b242Skettenis printf("%02d/%02d/%04d %02d:%02d:%0d\n", dt->dt_day, dt->dt_mon,
186d863b242Skettenis dt->dt_year, dt->dt_hour, dt->dt_min, dt->dt_sec);
187d863b242Skettenis #endif
188d863b242Skettenis
189d863b242Skettenis /* Consider the time to be invalid if the clock is in 12H mode. */
190d863b242Skettenis ctrl = acrtc_read_reg(sc, RTC_CTRL);
191d863b242Skettenis if ((ctrl & RTC_CTRL_12H_24H_MODE) == 0)
192d863b242Skettenis return EINVAL;
193d863b242Skettenis
194d863b242Skettenis return 0;
195d863b242Skettenis }
196d863b242Skettenis
197d863b242Skettenis int
acrtc_clock_write(struct acrtc_softc * sc,struct clock_ymdhms * dt)198d863b242Skettenis acrtc_clock_write(struct acrtc_softc *sc, struct clock_ymdhms *dt)
199d863b242Skettenis {
200d863b242Skettenis uint16_t leap = isleap(dt->dt_year) ? RTC_YEA_LEAP_YEAR : 0;
201d863b242Skettenis
202d863b242Skettenis acrtc_write_reg(sc, RTC_SEC, TOBCD(dt->dt_sec));
203d863b242Skettenis acrtc_write_reg(sc, RTC_MIN, TOBCD(dt->dt_min));
204d863b242Skettenis acrtc_write_reg(sc, RTC_HOU, TOBCD(dt->dt_hour));
205d863b242Skettenis acrtc_write_reg(sc, RTC_WEE, TOBCD(dt->dt_wday));
206d863b242Skettenis acrtc_write_reg(sc, RTC_DAY, TOBCD(dt->dt_day));
207d863b242Skettenis acrtc_write_reg(sc, RTC_MON, TOBCD(dt->dt_mon));
208d863b242Skettenis acrtc_write_reg(sc, RTC_YEA, TOBCD(dt->dt_year - 2000) | leap);
209d863b242Skettenis acrtc_write_reg(sc, RTC_UPD_TRIG, RTC_UPD_TRIG_UPDATE);
210d863b242Skettenis
211d863b242Skettenis /* Switch to 24H mode to indicate the time is now valid. */
212d863b242Skettenis acrtc_write_reg(sc, RTC_CTRL, RTC_CTRL_12H_24H_MODE);
213d863b242Skettenis
214d863b242Skettenis return 0;
215d863b242Skettenis }
216cfe273a3Skettenis
217cfe273a3Skettenis void
acrtc_ck32k_enable(void * cookie,uint32_t * cells,int on)218cfe273a3Skettenis acrtc_ck32k_enable(void *cookie, uint32_t *cells, int on)
219cfe273a3Skettenis {
220cfe273a3Skettenis struct acrtc_softc *sc = cookie;
221cfe273a3Skettenis uint32_t idx = cells[0];
222cfe273a3Skettenis uint16_t reg;
223cfe273a3Skettenis
224cfe273a3Skettenis reg = acrtc_read_reg(sc, CK32K_OUT_CTRL1 + idx);
225cfe273a3Skettenis reg &= ~CK32K_OUT_CTRL_PRE_DIV_MASK;
226cfe273a3Skettenis reg &= ~CK32K_OUT_CTRL_MUX_SEL_MASK;
227cfe273a3Skettenis reg &= ~CK32K_OUT_CTRL_POST_DIV_MASK;
228cfe273a3Skettenis reg |= CK32K_OUT_CTRL_PRE_DIV_32K;
229cfe273a3Skettenis reg |= CK32K_OUT_CTRL_MUX_SEL_32K;
230cfe273a3Skettenis reg |= CK32K_OUT_CTRL_POST_DIV_32K;
231cfe273a3Skettenis if (on)
232cfe273a3Skettenis reg |= CK32K_OUT_CTRL_ENA;
233cfe273a3Skettenis else
234cfe273a3Skettenis reg &= ~CK32K_OUT_CTRL_ENA;
235cfe273a3Skettenis acrtc_write_reg(sc, CK32K_OUT_CTRL1 + idx, reg);
236cfe273a3Skettenis }
237