1/* $OpenBSD: sxirtc.c,v 1.9 2024/01/27 11:22:16 kettenis Exp $ */ 2/* 3 * Copyright (c) 2008 Mark Kettenis 4 * Copyright (c) 2013 Artturi Alm 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/param.h> 20#include <sys/device.h> 21#include <sys/malloc.h> 22#include <sys/systm.h> 23 24#include <dev/clock_subr.h> 25 26#include <machine/bus.h> 27#include <machine/fdt.h> 28 29#include <dev/fdt/sunxireg.h> 30 31#include <dev/ofw/openfirm.h> 32#include <dev/ofw/fdt.h> 33#include <dev/ofw/ofw_clock.h> 34 35#define SXIRTC_LOSC_CTRL 0x00 36#define SXIRTC_LOSC_CTRL_KEY_FIELD 0x16aa0000 37#define SXIRTC_LOSC_CTRL_SEL_EXT32K 0x00000001 38#define SXIRTC_YYMMDD_A10 0x04 39#define SXIRTC_HHMMSS_A10 0x08 40#define SXIRTC_YYMMDD_A31 0x10 41#define SXIRTC_HHMMSS_A31 0x14 42#define SXIRTC_LOSC_OUT_GATING 0x60 43 44#define LEAPYEAR(y) \ 45 (((y) % 4 == 0 && \ 46 (y) % 100 != 0) || \ 47 (y) % 400 == 0) 48 49struct sxirtc_softc { 50 struct device sc_dev; 51 bus_space_tag_t sc_iot; 52 bus_space_handle_t sc_ioh; 53 54 struct clock_device sc_cd; 55 56 bus_size_t sc_yymmdd; 57 bus_size_t sc_hhmmss; 58 uint32_t base_year; 59 uint32_t year_mask; 60 uint32_t leap_shift; 61 int linear_day; 62}; 63 64int sxirtc_match(struct device *, void *, void *); 65void sxirtc_attach(struct device *, struct device *, void *); 66 67const struct cfattach sxirtc_ca = { 68 sizeof(struct sxirtc_softc), sxirtc_match, sxirtc_attach 69}; 70 71struct cfdriver sxirtc_cd = { 72 NULL, "sxirtc", DV_DULL 73}; 74 75uint32_t sxirtc_get_frequency(void *, uint32_t *); 76void sxirtc_enable(void *, uint32_t *, int); 77int sxirtc_gettime(todr_chip_handle_t, struct timeval *); 78int sxirtc_settime(todr_chip_handle_t, struct timeval *); 79 80int 81sxirtc_match(struct device *parent, void *match, void *aux) 82{ 83 struct fdt_attach_args *faa = aux; 84 85 return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-rtc") || 86 OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc") || 87 OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") || 88 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") || 89 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc") || 90 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h616-rtc") || 91 OF_is_compatible(faa->fa_node, "allwinner,sun50i-r329-rtc")); 92} 93 94void 95sxirtc_attach(struct device *parent, struct device *self, void *aux) 96{ 97 struct sxirtc_softc *sc = (struct sxirtc_softc *)self; 98 struct fdt_attach_args *faa = aux; 99 todr_chip_handle_t handle; 100 101 if (faa->fa_nreg < 1) 102 return; 103 104 handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT); 105 if (handle == NULL) 106 panic("sxirtc_attach: couldn't allocate todr_handle"); 107 108 sc->sc_iot = faa->fa_iot; 109 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 110 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 111 panic("sxirtc_attach: bus_space_map failed!"); 112 113 if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") || 114 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") || 115 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc") || 116 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h616-rtc") || 117 OF_is_compatible(faa->fa_node, "allwinner,sun50i-r329-rtc")) { 118 sc->sc_yymmdd = SXIRTC_YYMMDD_A31; 119 sc->sc_hhmmss = SXIRTC_HHMMSS_A31; 120 } else { 121 sc->sc_yymmdd = SXIRTC_YYMMDD_A10; 122 sc->sc_hhmmss = SXIRTC_HHMMSS_A10; 123 } 124 125 if (OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc")) { 126 sc->base_year = 1970; 127 sc->year_mask = 0xff; 128 sc->leap_shift = 24; 129 } else { 130 sc->base_year = 2010; 131 sc->year_mask = 0x3f; 132 sc->leap_shift = 22; 133 } 134 135 /* 136 * Newer SoCs store the number of days since a fixed epoch 137 * instead of YYMMDD. Take this to be the number of days 138 * since the Unix epoch since that is what Linux does. 139 */ 140 if (OF_is_compatible(faa->fa_node, "allwinner,sun50i-h616-rtc") || 141 OF_is_compatible(faa->fa_node, "allwinner,sun50i-r329-rtc")) { 142 sc->base_year = 1970; 143 sc->linear_day = 1; 144 } 145 146 if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") || 147 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc")) { 148 /* Switch to external oscillator. */ 149 SXIWRITE4(sc, SXIRTC_LOSC_CTRL, 150 SXIRTC_LOSC_CTRL_KEY_FIELD | SXIRTC_LOSC_CTRL_SEL_EXT32K); 151 152 sc->sc_cd.cd_node = faa->fa_node; 153 sc->sc_cd.cd_cookie = sc; 154 sc->sc_cd.cd_get_frequency = sxirtc_get_frequency; 155 sc->sc_cd.cd_enable = sxirtc_enable; 156 clock_register(&sc->sc_cd); 157 } 158 159 handle->cookie = self; 160 handle->todr_gettime = sxirtc_gettime; 161 handle->todr_settime = sxirtc_settime; 162 handle->bus_cookie = NULL; 163 handle->todr_setwen = NULL; 164 handle->todr_quality = 0; 165 todr_attach(handle); 166 167 printf("\n"); 168} 169 170uint32_t 171sxirtc_get_frequency(void *cookie, uint32_t *cells) 172{ 173 struct sxirtc_softc *sc = cookie; 174 uint32_t idx = cells[0]; 175 176 switch (idx) { 177 case 0: /* osc32k */ 178 case 1: /* osc32k-out */ 179 return clock_get_frequency_idx(sc->sc_cd.cd_node, 0); 180 case 2: /* iosc */ 181 return 16000000; 182 } 183 184 printf("%s: 0x%08x\n", __func__, idx); 185 return 0; 186} 187 188void 189sxirtc_enable(void *cookie, uint32_t *cells, int on) 190{ 191 struct sxirtc_softc *sc = cookie; 192 uint32_t idx = cells[0]; 193 194 switch (idx) { 195 case 0: /* osc32k */ 196 break; 197 case 1: /* osc32k-out */ 198 if (on) 199 SXISET4(sc, SXIRTC_LOSC_OUT_GATING, 1); 200 else 201 SXICLR4(sc, SXIRTC_LOSC_OUT_GATING, 1); 202 break; 203 case 2: /* iosc */ 204 break; 205 default: 206 printf("%s: 0x%08x\n", __func__, idx); 207 break; 208 } 209} 210 211int 212sxirtc_gettime(todr_chip_handle_t handle, struct timeval *tv) 213{ 214 struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie; 215 struct clock_ymdhms dt; 216 uint32_t reg; 217 218 reg = SXIREAD4(sc, sc->sc_yymmdd); 219 if (sc->linear_day) { 220 clock_secs_to_ymdhms(reg * SECDAY, &dt); 221 } else { 222 dt.dt_day = reg & 0x1f; 223 dt.dt_mon = reg >> 8 & 0x0f; 224 dt.dt_year = (reg >> 16 & sc->year_mask) + sc->base_year; 225 } 226 227 reg = SXIREAD4(sc, sc->sc_hhmmss); 228 dt.dt_sec = reg & 0x3f; 229 dt.dt_min = reg >> 8 & 0x3f; 230 dt.dt_hour = reg >> 16 & 0x1f; 231 dt.dt_wday = reg >> 29 & 0x07; 232 233 if (dt.dt_sec > 59 || dt.dt_min > 59 || 234 dt.dt_hour > 23 || dt.dt_wday > 6 || 235 dt.dt_day > 31 || dt.dt_day == 0 || 236 dt.dt_mon > 12 || dt.dt_mon == 0) 237 return 1; 238 239 /* 240 * Reject the first year that can be represented by the clock. 241 * This avoids reporting a bogus time if the RTC isn't battery 242 * powered. 243 */ 244 if (dt.dt_year == sc->base_year) 245 return 1; 246 247 tv->tv_sec = clock_ymdhms_to_secs(&dt); 248 tv->tv_usec = 0; 249 return 0; 250} 251 252int 253sxirtc_settime(todr_chip_handle_t handle, struct timeval *tv) 254{ 255 struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie; 256 struct clock_ymdhms dt; 257 258 clock_secs_to_ymdhms(tv->tv_sec, &dt); 259 260 if (dt.dt_sec > 59 || dt.dt_min > 59 || 261 dt.dt_hour > 23 || dt.dt_wday > 6 || 262 dt.dt_day > 31 || dt.dt_day == 0 || 263 dt.dt_mon > 12 || dt.dt_mon == 0) 264 return 1; 265 266 SXICMS4(sc, sc->sc_hhmmss, 0xe0000000 | 0x1f0000 | 0x3f00 | 0x3f, 267 dt.dt_sec | (dt.dt_min << 8) | (dt.dt_hour << 16) | 268 (dt.dt_wday << 29)); 269 270 if (sc->linear_day) { 271 SXICMS4(sc, sc->sc_yymmdd, 0xffff, tv->tv_sec / SECDAY); 272 } else { 273 SXICMS4(sc, sc->sc_yymmdd, 0x00400000 | (sc->year_mask << 16) | 274 0x0f00 | 0x1f, dt.dt_day | (dt.dt_mon << 8) | 275 ((dt.dt_year - sc->base_year) << 16) | 276 (LEAPYEAR(dt.dt_year) << sc->leap_shift)); 277 } 278 279 return 0; 280} 281