1 /* $NetBSD: sunxi_rtc.c,v 1.5 2019/09/05 23:42:26 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2014-2017 Jared McNeill <jmcneill@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.5 2019/09/05 23:42:26 thorpej 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 { "allwinner,sun8i-h3-rtc", (uintptr_t)&sun6i_rtc_config }, 134 { "allwinner,sun50i-h5-rtc", (uintptr_t)&sun6i_rtc_config }, 135 { NULL } 136 }; 137 138 struct sunxi_rtc_softc { 139 device_t sc_dev; 140 bus_space_tag_t sc_bst; 141 bus_space_handle_t sc_bsh; 142 struct todr_chip_handle sc_todr; 143 const struct sunxi_rtc_config *sc_conf; 144 }; 145 146 #define RTC_READ(sc, reg) \ 147 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 148 #define RTC_WRITE(sc, reg, val) \ 149 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 150 151 static int sunxi_rtc_match(device_t, cfdata_t, void *); 152 static void sunxi_rtc_attach(device_t, device_t, void *); 153 154 static int sunxi_rtc_gettime(todr_chip_handle_t, struct clock_ymdhms *); 155 static int sunxi_rtc_settime(todr_chip_handle_t, struct clock_ymdhms *); 156 157 CFATTACH_DECL_NEW(sunxi_rtc, sizeof(struct sunxi_rtc_softc), 158 sunxi_rtc_match, sunxi_rtc_attach, NULL, NULL); 159 160 static int 161 sunxi_rtc_match(device_t parent, cfdata_t cf, void *aux) 162 { 163 struct fdt_attach_args * const faa = aux; 164 165 return of_match_compat_data(faa->faa_phandle, compat_data); 166 } 167 168 static void 169 sunxi_rtc_attach(device_t parent, device_t self, void *aux) 170 { 171 struct sunxi_rtc_softc * const sc = device_private(self); 172 struct fdt_attach_args * const faa = aux; 173 const int phandle = faa->faa_phandle; 174 bus_addr_t addr; 175 bus_size_t size; 176 177 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 178 aprint_error(": couldn't get registers\n"); 179 return; 180 } 181 182 sc->sc_dev = self; 183 sc->sc_bst = faa->faa_bst; 184 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 185 aprint_error(": couldn't map registers\n"); 186 return; 187 } 188 sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data; 189 190 aprint_naive("\n"); 191 aprint_normal(": RTC\n"); 192 193 sc->sc_todr.cookie = sc; 194 sc->sc_todr.todr_gettime_ymdhms = sunxi_rtc_gettime; 195 sc->sc_todr.todr_settime_ymdhms = sunxi_rtc_settime; 196 197 fdtbus_todr_attach(self, phandle, &sc->sc_todr); 198 } 199 200 static int 201 sunxi_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt) 202 { 203 struct sunxi_rtc_softc *sc = tch->cookie; 204 const struct sunxi_rtc_config *conf = sc->sc_conf; 205 206 const uint32_t yymmdd = RTC_READ(sc, conf->yy_mm_dd_reg); 207 const uint32_t hhmmss = RTC_READ(sc, conf->hh_mm_ss_reg); 208 209 dt->dt_year = __SHIFTOUT(yymmdd, conf->year) + conf->base_year; 210 dt->dt_mon = __SHIFTOUT(yymmdd, conf->month); 211 dt->dt_day = __SHIFTOUT(yymmdd, conf->day); 212 dt->dt_wday = __SHIFTOUT(hhmmss, conf->wk_no); 213 dt->dt_hour = __SHIFTOUT(hhmmss, conf->hour); 214 dt->dt_min = __SHIFTOUT(hhmmss, conf->minute); 215 dt->dt_sec = __SHIFTOUT(hhmmss, conf->second); 216 217 return 0; 218 } 219 220 static int 221 sunxi_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt) 222 { 223 struct sunxi_rtc_softc *sc = tch->cookie; 224 const struct sunxi_rtc_config *conf = sc->sc_conf; 225 uint32_t yymmdd, hhmmss, maxyear; 226 227 /* 228 * Sanity check the date before writing it back 229 */ 230 if (dt->dt_year < conf->base_year) { 231 aprint_normal_dev(sc->sc_dev, "year pre the epoch: %" PRIu64 232 ", not writing back time\n", dt->dt_year); 233 return EIO; 234 } 235 maxyear = __SHIFTOUT(0xffffffff, conf->year) + conf->base_year; 236 if (dt->dt_year > maxyear) { 237 aprint_normal_dev(sc->sc_dev, "year exceeds available field:" 238 " %" PRIu64 ", not writing back time\n", dt->dt_year); 239 return EIO; 240 } 241 242 yymmdd = __SHIFTIN(dt->dt_year - conf->base_year, conf->year); 243 yymmdd |= __SHIFTIN(dt->dt_mon, conf->month); 244 yymmdd |= __SHIFTIN(dt->dt_day, conf->day); 245 246 hhmmss = __SHIFTIN(dt->dt_wday, conf->wk_no); 247 hhmmss |= __SHIFTIN(dt->dt_hour, conf->hour); 248 hhmmss |= __SHIFTIN(dt->dt_min, conf->minute); 249 hhmmss |= __SHIFTIN(dt->dt_sec, conf->second); 250 251 RTC_WRITE(sc, conf->yy_mm_dd_reg, yymmdd); 252 RTC_WRITE(sc, conf->hh_mm_ss_reg, hhmmss); 253 254 return 0; 255 } 256