1/* $OpenBSD: acrtc.c,v 1.6 2022/10/17 19:09:46 kettenis Exp $ */ 2/* 3 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/device.h> 21#include <sys/malloc.h> 22 23#include <dev/fdt/rsbvar.h> 24 25#include <dev/ofw/openfirm.h> 26#include <dev/ofw/ofw_clock.h> 27#include <dev/ofw/fdt.h> 28 29#include <dev/clock_subr.h> 30 31#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) 32 33#define CK32K_OUT_CTRL1 0xc1 34#define CK32K_OUT_CTRL_PRE_DIV_MASK (0x7 << 5) 35#define CK32K_OUT_CTRL_PRE_DIV_32K (0x7 << 5) 36#define CK32K_OUT_CTRL_MUX_SEL_MASK (1 << 4) 37#define CK32K_OUT_CTRL_MUX_SEL_32K (0 << 4) 38#define CK32K_OUT_CTRL_POST_DIV_MASK (0x7 << 1) 39#define CK32K_OUT_CTRL_POST_DIV_32K (0x0 << 1) 40#define CK32K_OUT_CTRL_ENA (1 << 0) 41#define RTC_CTRL 0xc7 42#define RTC_CTRL_12H_24H_MODE (1 << 0) 43#define RTC_SEC 0xc8 44#define RTC_SEC_MASK (0x7f << 0) 45#define RTC_MIN 0xc9 46#define RTC_MIN_MASK (0x7f << 0) 47#define RTC_HOU 0xca 48#define RTC_HOU_MASK (0x3f << 0) 49#define RTC_WEE 0xcb 50#define RTC_WEE_MASK (0x07 << 0) 51#define RTC_DAY 0xcc 52#define RTC_DAY_MASK (0x3f << 0) 53#define RTC_MON 0xcd 54#define RTC_MON_MASK (0x1f << 0) 55#define RTC_YEA 0xce 56#define RTC_YEA_LEAP_YEAR (1 << 15) 57#define RTC_YEA_MASK (0xff << 0) 58#define RTC_UPD_TRIG 0xcf 59#define RTC_UPD_TRIG_UPDATE (1 << 15) 60 61struct acrtc_softc { 62 struct device sc_dev; 63 void *sc_cookie; 64 uint16_t sc_rta; 65 66 struct todr_chip_handle sc_todr; 67 struct clock_device sc_cd; 68}; 69 70int acrtc_match(struct device *, void *, void *); 71void acrtc_attach(struct device *, struct device *, void *); 72 73const struct cfattach acrtc_ca = { 74 sizeof(struct acrtc_softc), acrtc_match, acrtc_attach 75}; 76 77struct cfdriver acrtc_cd = { 78 NULL, "acrtc", DV_DULL 79}; 80 81int acrtc_clock_read(struct acrtc_softc *, struct clock_ymdhms *); 82int acrtc_clock_write(struct acrtc_softc *, struct clock_ymdhms *); 83int acrtc_gettime(struct todr_chip_handle *, struct timeval *); 84int acrtc_settime(struct todr_chip_handle *, struct timeval *); 85 86void acrtc_ck32k_enable(void *, uint32_t *, int); 87 88int 89acrtc_match(struct device *parent, void *match, void *aux) 90{ 91 struct rsb_attach_args *ra = aux; 92 93 if (strcmp(ra->ra_name, "x-powers,ac100") == 0) 94 return 1; 95 return 0; 96} 97 98void 99acrtc_attach(struct device *parent, struct device *self, void *aux) 100{ 101 struct acrtc_softc *sc = (struct acrtc_softc *)self; 102 struct rsb_attach_args *ra = aux; 103 int node; 104 105 sc->sc_cookie = ra->ra_cookie; 106 sc->sc_rta = ra->ra_rta; 107 108 printf("\n"); 109 110 sc->sc_todr.cookie = sc; 111 sc->sc_todr.todr_gettime = acrtc_gettime; 112 sc->sc_todr.todr_settime = acrtc_settime; 113 sc->sc_todr.todr_quality = 1000; 114 todr_attach(&sc->sc_todr); 115 116 node = OF_getnodebyname(ra->ra_node, "rtc"); 117 if (node == 0) 118 return; 119 120 sc->sc_cd.cd_node = node; 121 sc->sc_cd.cd_cookie = sc; 122 sc->sc_cd.cd_enable = acrtc_ck32k_enable; 123 clock_register(&sc->sc_cd); 124} 125 126static inline uint16_t 127acrtc_read_reg(struct acrtc_softc *sc, uint8_t reg) 128{ 129 return rsb_read_2(sc->sc_cookie, sc->sc_rta, reg); 130} 131 132static inline void 133acrtc_write_reg(struct acrtc_softc *sc, uint8_t reg, uint16_t value) 134{ 135 rsb_write_2(sc->sc_cookie, sc->sc_rta, reg, value); 136} 137 138int 139acrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 140{ 141 struct acrtc_softc *sc = handle->cookie; 142 struct clock_ymdhms dt; 143 int error; 144 145 error = acrtc_clock_read(sc, &dt); 146 if (error) 147 return error; 148 149 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || 150 dt.dt_day > 31 || dt.dt_day == 0 || 151 dt.dt_mon > 12 || dt.dt_mon == 0 || 152 dt.dt_year < POSIX_BASE_YEAR) 153 return EINVAL; 154 155 tv->tv_sec = clock_ymdhms_to_secs(&dt); 156 tv->tv_usec = 0; 157 return 0; 158} 159 160int 161acrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 162{ 163 struct acrtc_softc *sc = handle->cookie; 164 struct clock_ymdhms dt; 165 166 clock_secs_to_ymdhms(tv->tv_sec, &dt); 167 168 return acrtc_clock_write(sc, &dt); 169} 170 171int 172acrtc_clock_read(struct acrtc_softc *sc, struct clock_ymdhms *dt) 173{ 174 uint16_t ctrl; 175 176 dt->dt_sec = FROMBCD(acrtc_read_reg(sc, RTC_SEC) & RTC_SEC_MASK); 177 dt->dt_min = FROMBCD(acrtc_read_reg(sc, RTC_MIN) & RTC_MIN_MASK); 178 dt->dt_hour = FROMBCD(acrtc_read_reg(sc, RTC_HOU) & RTC_HOU_MASK); 179 dt->dt_day = FROMBCD(acrtc_read_reg(sc, RTC_DAY) & RTC_DAY_MASK); 180 dt->dt_mon = FROMBCD(acrtc_read_reg(sc, RTC_MON) & RTC_MON_MASK); 181 dt->dt_year = FROMBCD(acrtc_read_reg(sc, RTC_YEA) & RTC_YEA_MASK); 182 dt->dt_year += 2000; 183 184#ifdef DEBUG 185 printf("%02d/%02d/%04d %02d:%02d:%0d\n", dt->dt_day, dt->dt_mon, 186 dt->dt_year, dt->dt_hour, dt->dt_min, dt->dt_sec); 187#endif 188 189 /* Consider the time to be invalid if the clock is in 12H mode. */ 190 ctrl = acrtc_read_reg(sc, RTC_CTRL); 191 if ((ctrl & RTC_CTRL_12H_24H_MODE) == 0) 192 return EINVAL; 193 194 return 0; 195} 196 197int 198acrtc_clock_write(struct acrtc_softc *sc, struct clock_ymdhms *dt) 199{ 200 uint16_t leap = isleap(dt->dt_year) ? RTC_YEA_LEAP_YEAR : 0; 201 202 acrtc_write_reg(sc, RTC_SEC, TOBCD(dt->dt_sec)); 203 acrtc_write_reg(sc, RTC_MIN, TOBCD(dt->dt_min)); 204 acrtc_write_reg(sc, RTC_HOU, TOBCD(dt->dt_hour)); 205 acrtc_write_reg(sc, RTC_WEE, TOBCD(dt->dt_wday)); 206 acrtc_write_reg(sc, RTC_DAY, TOBCD(dt->dt_day)); 207 acrtc_write_reg(sc, RTC_MON, TOBCD(dt->dt_mon)); 208 acrtc_write_reg(sc, RTC_YEA, TOBCD(dt->dt_year - 2000) | leap); 209 acrtc_write_reg(sc, RTC_UPD_TRIG, RTC_UPD_TRIG_UPDATE); 210 211 /* Switch to 24H mode to indicate the time is now valid. */ 212 acrtc_write_reg(sc, RTC_CTRL, RTC_CTRL_12H_24H_MODE); 213 214 return 0; 215} 216 217void 218acrtc_ck32k_enable(void *cookie, uint32_t *cells, int on) 219{ 220 struct acrtc_softc *sc = cookie; 221 uint32_t idx = cells[0]; 222 uint16_t reg; 223 224 reg = acrtc_read_reg(sc, CK32K_OUT_CTRL1 + idx); 225 reg &= ~CK32K_OUT_CTRL_PRE_DIV_MASK; 226 reg &= ~CK32K_OUT_CTRL_MUX_SEL_MASK; 227 reg &= ~CK32K_OUT_CTRL_POST_DIV_MASK; 228 reg |= CK32K_OUT_CTRL_PRE_DIV_32K; 229 reg |= CK32K_OUT_CTRL_MUX_SEL_32K; 230 reg |= CK32K_OUT_CTRL_POST_DIV_32K; 231 if (on) 232 reg |= CK32K_OUT_CTRL_ENA; 233 else 234 reg &= ~CK32K_OUT_CTRL_ENA; 235 acrtc_write_reg(sc, CK32K_OUT_CTRL1 + idx, reg); 236} 237