sunxi_rtc.c revision 1.3 1 /* $NetBSD: sunxi_rtc.c,v 1.3 2017/10/08 14:03:46 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2014-2017 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sunxi_rtc.c,v 1.3 2017/10/08 14:03:46 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/intr.h>
36 #include <sys/systm.h>
37 #include <sys/mutex.h>
38
39 #include <dev/clock_subr.h>
40
41 #include <dev/fdt/fdtvar.h>
42
43 #define SUN4I_RTC_YY_MM_DD_REG 0x04
44 #define SUN4I_RTC_LEAP __BIT(22)
45 #define SUN4I_RTC_YEAR __BITS(21,16)
46 #define SUN4I_RTC_MONTH __BITS(11,8)
47 #define SUN4I_RTC_DAY __BITS(4,0)
48 #define SUN4I_RTC_HH_MM_SS_REG 0x08
49 #define SUN4I_RTC_WK_NO __BITS(31,29)
50 #define SUN4I_RTC_HOUR __BITS(20,16)
51 #define SUN4I_RTC_MINUTE __BITS(13,8)
52 #define SUN4I_RTC_SECOND __BITS(5,0)
53 #define SUN4I_RTC_BASE_YEAR 2010
54
55 #define SUN7I_RTC_YY_MM_DD_REG 0x04
56 #define SUN7I_RTC_LEAP __BIT(24)
57 #define SUN7I_RTC_YEAR __BITS(23,16)
58 #define SUN7I_RTC_MONTH __BITS(11,8)
59 #define SUN7I_RTC_DAY __BITS(4,0)
60 #define SUN7I_RTC_HH_MM_SS_REG 0x08
61 #define SUN7I_RTC_WK_NO __BITS(31,29)
62 #define SUN7I_RTC_HOUR __BITS(20,16)
63 #define SUN7I_RTC_MINUTE __BITS(13,8)
64 #define SUN7I_RTC_SECOND __BITS(5,0)
65 #define SUN7I_RTC_BASE_YEAR 1970
66
67 #define SUN6I_RTC_YY_MM_DD_REG 0x10
68 #define SUN6I_RTC_LEAP __BIT(22)
69 #define SUN6I_RTC_YEAR __BITS(21,16)
70 #define SUN6I_RTC_MONTH __BITS(11,8)
71 #define SUN6I_RTC_DAY __BITS(4,0)
72 #define SUN6I_RTC_HH_MM_SS_REG 0x14
73 #define SUN6I_RTC_WK_NO __BITS(31,29)
74 #define SUN6I_RTC_HOUR __BITS(20,16)
75 #define SUN6I_RTC_MINUTE __BITS(13,8)
76 #define SUN6I_RTC_SECOND __BITS(5,0)
77 #define SUN6I_RTC_BASE_YEAR 2000
78
79 struct sunxi_rtc_config {
80 bus_size_t yy_mm_dd_reg;
81 uint32_t leap, year, month, day;
82 bus_size_t hh_mm_ss_reg;
83 uint32_t wk_no, hour, minute, second;
84 u_int base_year;
85 };
86
87 static const struct sunxi_rtc_config sun4i_rtc_config = {
88 .yy_mm_dd_reg = SUN4I_RTC_YY_MM_DD_REG,
89 .leap = SUN4I_RTC_LEAP,
90 .year = SUN4I_RTC_YEAR,
91 .month = SUN4I_RTC_MONTH,
92 .day = SUN4I_RTC_DAY,
93 .hh_mm_ss_reg = SUN4I_RTC_HH_MM_SS_REG,
94 .wk_no = SUN4I_RTC_WK_NO,
95 .hour = SUN4I_RTC_HOUR,
96 .minute = SUN4I_RTC_MINUTE,
97 .second = SUN4I_RTC_SECOND,
98 .base_year = SUN4I_RTC_BASE_YEAR,
99 };
100
101 static const struct sunxi_rtc_config sun6i_rtc_config = {
102 .yy_mm_dd_reg = SUN6I_RTC_YY_MM_DD_REG,
103 .leap = SUN6I_RTC_LEAP,
104 .year = SUN6I_RTC_YEAR,
105 .month = SUN6I_RTC_MONTH,
106 .day = SUN6I_RTC_DAY,
107 .hh_mm_ss_reg = SUN6I_RTC_HH_MM_SS_REG,
108 .wk_no = SUN6I_RTC_WK_NO,
109 .hour = SUN6I_RTC_HOUR,
110 .minute = SUN6I_RTC_MINUTE,
111 .second = SUN6I_RTC_SECOND,
112 .base_year = SUN6I_RTC_BASE_YEAR,
113 };
114
115 static const struct sunxi_rtc_config sun7i_rtc_config = {
116 .yy_mm_dd_reg = SUN7I_RTC_YY_MM_DD_REG,
117 .leap = SUN7I_RTC_LEAP,
118 .year = SUN7I_RTC_YEAR,
119 .month = SUN7I_RTC_MONTH,
120 .day = SUN7I_RTC_DAY,
121 .hh_mm_ss_reg = SUN7I_RTC_HH_MM_SS_REG,
122 .wk_no = SUN7I_RTC_WK_NO,
123 .hour = SUN7I_RTC_HOUR,
124 .minute = SUN7I_RTC_MINUTE,
125 .second = SUN7I_RTC_SECOND,
126 .base_year = SUN7I_RTC_BASE_YEAR,
127 };
128
129 static const struct of_compat_data compat_data[] = {
130 { "allwinner,sun4i-a10-rtc", (uintptr_t)&sun4i_rtc_config },
131 { "allwinner,sun6i-a31-rtc", (uintptr_t)&sun6i_rtc_config },
132 { "allwinner,sun7i-a20-rtc", (uintptr_t)&sun7i_rtc_config },
133 { NULL }
134 };
135
136 struct sunxi_rtc_softc {
137 device_t sc_dev;
138 bus_space_tag_t sc_bst;
139 bus_space_handle_t sc_bsh;
140 struct todr_chip_handle sc_todr;
141 const struct sunxi_rtc_config *sc_conf;
142 };
143
144 #define RTC_READ(sc, reg) \
145 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
146 #define RTC_WRITE(sc, reg, val) \
147 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
148
149 static int sunxi_rtc_match(device_t, cfdata_t, void *);
150 static void sunxi_rtc_attach(device_t, device_t, void *);
151
152 static int sunxi_rtc_gettime(todr_chip_handle_t, struct clock_ymdhms *);
153 static int sunxi_rtc_settime(todr_chip_handle_t, struct clock_ymdhms *);
154
155 CFATTACH_DECL_NEW(sunxi_rtc, sizeof(struct sunxi_rtc_softc),
156 sunxi_rtc_match, sunxi_rtc_attach, NULL, NULL);
157
158 static int
159 sunxi_rtc_match(device_t parent, cfdata_t cf, void *aux)
160 {
161 struct fdt_attach_args * const faa = aux;
162
163 return of_match_compat_data(faa->faa_phandle, compat_data);
164 }
165
166 static void
167 sunxi_rtc_attach(device_t parent, device_t self, void *aux)
168 {
169 struct sunxi_rtc_softc * const sc = device_private(self);
170 struct fdt_attach_args * const faa = aux;
171 const int phandle = faa->faa_phandle;
172 bus_addr_t addr;
173 bus_size_t size;
174
175 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
176 aprint_error(": couldn't get registers\n");
177 return;
178 }
179
180 sc->sc_dev = self;
181 sc->sc_bst = faa->faa_bst;
182 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
183 aprint_error(": couldn't map registers\n");
184 return;
185 }
186 sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data;
187
188 aprint_naive("\n");
189 aprint_normal(": RTC\n");
190
191 sc->sc_todr.cookie = sc;
192 sc->sc_todr.todr_gettime_ymdhms = sunxi_rtc_gettime;
193 sc->sc_todr.todr_settime_ymdhms = sunxi_rtc_settime;
194
195 fdtbus_todr_attach(self, phandle, &sc->sc_todr);
196 }
197
198 static int
199 sunxi_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
200 {
201 struct sunxi_rtc_softc *sc = tch->cookie;
202 const struct sunxi_rtc_config *conf = sc->sc_conf;
203
204 const uint32_t yymmdd = RTC_READ(sc, conf->yy_mm_dd_reg);
205 const uint32_t hhmmss = RTC_READ(sc, conf->hh_mm_ss_reg);
206
207 dt->dt_year = __SHIFTOUT(yymmdd, conf->year) + conf->base_year;
208 dt->dt_mon = __SHIFTOUT(yymmdd, conf->month);
209 dt->dt_day = __SHIFTOUT(yymmdd, conf->day);
210 dt->dt_wday = __SHIFTOUT(hhmmss, conf->wk_no);
211 dt->dt_hour = __SHIFTOUT(hhmmss, conf->hour);
212 dt->dt_min = __SHIFTOUT(hhmmss, conf->minute);
213 dt->dt_sec = __SHIFTOUT(hhmmss, conf->second);
214
215 return 0;
216 }
217
218 static int
219 sunxi_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
220 {
221 struct sunxi_rtc_softc *sc = tch->cookie;
222 const struct sunxi_rtc_config *conf = sc->sc_conf;
223 uint32_t yymmdd, hhmmss, maxyear;
224
225 /*
226 * Sanity check the date before writing it back
227 */
228 if (dt->dt_year < conf->base_year) {
229 aprint_normal_dev(sc->sc_dev, "year pre the epoch: %llu, "
230 "not writing back time\n", dt->dt_year);
231 return EIO;
232 }
233 maxyear = __SHIFTOUT(0xffffffff, conf->year) + conf->base_year;
234 if (dt->dt_year > maxyear) {
235 aprint_normal_dev(sc->sc_dev, "year exceeds available field:"
236 " %llu, not writing back time\n", dt->dt_year);
237 return EIO;
238 }
239
240 yymmdd = __SHIFTIN(dt->dt_year - conf->base_year, conf->year);
241 yymmdd |= __SHIFTIN(dt->dt_mon, conf->month);
242 yymmdd |= __SHIFTIN(dt->dt_day, conf->day);
243
244 hhmmss = __SHIFTIN(dt->dt_wday, conf->wk_no);
245 hhmmss |= __SHIFTIN(dt->dt_hour, conf->hour);
246 hhmmss |= __SHIFTIN(dt->dt_min, conf->minute);
247 hhmmss |= __SHIFTIN(dt->dt_sec, conf->second);
248
249 RTC_WRITE(sc, conf->yy_mm_dd_reg, yymmdd);
250 RTC_WRITE(sc, conf->hh_mm_ss_reg, hhmmss);
251
252 return 0;
253 }
254