sxirtc.c revision 1.3
1/* $OpenBSD: sxirtc.c,v 1.3 2019/01/21 11:24:05 jsg 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 34#define SXIRTC_YYMMDD 0x04 35#define SXIRTC_HHMMSS 0x08 36#define SXIRTC_YYMMDD_A31 0x10 37#define SXIRTC_HHMMSS_A31 0x14 38 39#define LEAPYEAR(y) \ 40 (((y) % 4 == 0 && \ 41 (y) % 100 != 0) || \ 42 (y) % 400 == 0) 43 44 45extern todr_chip_handle_t todr_handle; 46 47struct sxirtc_softc { 48 struct device sc_dev; 49 bus_space_tag_t sc_iot; 50 bus_space_handle_t sc_ioh; 51 bus_size_t sc_yymmdd; 52 bus_size_t sc_hhmmss; 53 uint32_t base_year; 54 uint32_t year_mask; 55 uint32_t leap_shift; 56}; 57 58int sxirtc_match(struct device *, void *, void *); 59void sxirtc_attach(struct device *, struct device *, void *); 60 61struct cfattach sxirtc_ca = { 62 sizeof(struct sxirtc_softc), sxirtc_match, sxirtc_attach 63}; 64 65struct cfdriver sxirtc_cd = { 66 NULL, "sxirtc", DV_DULL 67}; 68 69int sxirtc_gettime(todr_chip_handle_t, struct timeval *); 70int sxirtc_settime(todr_chip_handle_t, struct timeval *); 71 72int 73sxirtc_match(struct device *parent, void *match, void *aux) 74{ 75 struct fdt_attach_args *faa = aux; 76 77 return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-rtc") || 78 OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc") || 79 OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") || 80 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") || 81 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc")); 82} 83 84void 85sxirtc_attach(struct device *parent, struct device *self, void *aux) 86{ 87 struct sxirtc_softc *sc = (struct sxirtc_softc *)self; 88 struct fdt_attach_args *faa = aux; 89 todr_chip_handle_t handle; 90 91 if (faa->fa_nreg < 1) 92 return; 93 94 handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT); 95 if (handle == NULL) 96 panic("sxirtc_attach: couldn't allocate todr_handle"); 97 98 sc->sc_iot = faa->fa_iot; 99 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 100 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 101 panic("sxirtc_attach: bus_space_map failed!"); 102 103 if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-rtc") || 104 OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-rtc") || 105 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-rtc")) { 106 sc->sc_yymmdd = SXIRTC_YYMMDD_A31; 107 sc->sc_hhmmss = SXIRTC_HHMMSS_A31; 108 } else { 109 sc->sc_yymmdd = SXIRTC_YYMMDD; 110 sc->sc_hhmmss = SXIRTC_HHMMSS; 111 } 112 113 if (OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-rtc")) { 114 sc->base_year = 1970; 115 sc->year_mask = 0xff; 116 sc->leap_shift = 24; 117 } else { 118 sc->base_year = 2010; 119 sc->year_mask = 0x3f; 120 sc->leap_shift = 22; 121 } 122 123 handle->cookie = self; 124 handle->todr_gettime = sxirtc_gettime; 125 handle->todr_settime = sxirtc_settime; 126 handle->todr_getcal = NULL; 127 handle->todr_setcal = NULL; 128 handle->bus_cookie = NULL; 129 handle->todr_setwen = NULL; 130 todr_handle = handle; 131 132 printf("\n"); 133} 134 135int 136sxirtc_gettime(todr_chip_handle_t handle, struct timeval *tv) 137{ 138 struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie; 139 struct clock_ymdhms dt; 140 uint32_t reg; 141 142 reg = SXIREAD4(sc, sc->sc_hhmmss); 143 dt.dt_sec = reg & 0x3f; 144 dt.dt_min = reg >> 8 & 0x3f; 145 dt.dt_hour = reg >> 16 & 0x1f; 146 dt.dt_wday = reg >> 29 & 0x07; 147 148 reg = SXIREAD4(sc, sc->sc_yymmdd); 149 dt.dt_day = reg & 0x1f; 150 dt.dt_mon = reg >> 8 & 0x0f; 151 dt.dt_year = (reg >> 16 & sc->year_mask) + sc->base_year; 152 153 if (dt.dt_sec > 59 || dt.dt_min > 59 || 154 dt.dt_hour > 23 || dt.dt_wday > 6 || 155 dt.dt_day > 31 || dt.dt_day == 0 || 156 dt.dt_mon > 12 || dt.dt_mon == 0) 157 return 1; 158 159 /* 160 * Reject the first year that can be represented by the clock. 161 * This avoids reporting a bogus time if the RTC isn't battery 162 * powered. 163 */ 164 if (dt.dt_year == sc->base_year) 165 return 1; 166 167 tv->tv_sec = clock_ymdhms_to_secs(&dt); 168 tv->tv_usec = 0; 169 return 0; 170} 171 172int 173sxirtc_settime(todr_chip_handle_t handle, struct timeval *tv) 174{ 175 struct sxirtc_softc *sc = (struct sxirtc_softc *)handle->cookie; 176 struct clock_ymdhms dt; 177 178 clock_secs_to_ymdhms(tv->tv_sec, &dt); 179 180 if (dt.dt_sec > 59 || dt.dt_min > 59 || 181 dt.dt_hour > 23 || dt.dt_wday > 6 || 182 dt.dt_day > 31 || dt.dt_day == 0 || 183 dt.dt_mon > 12 || dt.dt_mon == 0) 184 return 1; 185 186 SXICMS4(sc, sc->sc_hhmmss, 0xe0000000 | 0x1f0000 | 0x3f00 | 0x3f, 187 dt.dt_sec | (dt.dt_min << 8) | (dt.dt_hour << 16) | 188 (dt.dt_wday << 29)); 189 190 SXICMS4(sc, sc->sc_yymmdd, 0x00400000 | (sc->year_mask << 16) | 191 0x0f00 | 0x1f, dt.dt_day | (dt.dt_mon << 8) | 192 ((dt.dt_year - sc->base_year) << 16) | 193 (LEAPYEAR(dt.dt_year) << sc->leap_shift)); 194 195 return 0; 196} 197