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