1 /* $OpenBSD: acrtc.c,v 1.3 2019/04/20 22:40:13 deraadt 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 extern todr_chip_handle_t todr_handle; 34 35 #define CK32K_OUT_CTRL1 0xc1 36 #define CK32K_OUT_CTRL_PRE_DIV_MASK (0x7 << 5) 37 #define CK32K_OUT_CTRL_PRE_DIV_32K (0x7 << 5) 38 #define CK32K_OUT_CTRL_MUX_SEL_MASK (1 << 4) 39 #define CK32K_OUT_CTRL_MUX_SEL_32K (0 << 4) 40 #define CK32K_OUT_CTRL_POST_DIV_MASK (0x7 << 1) 41 #define CK32K_OUT_CTRL_POST_DIV_32K (0x0 << 1) 42 #define CK32K_OUT_CTRL_ENA (1 << 0) 43 #define RTC_CTRL 0xc7 44 #define RTC_CTRL_12H_24H_MODE (1 << 0) 45 #define RTC_SEC 0xc8 46 #define RTC_MIN 0xc9 47 #define RTC_HOU 0xca 48 #define RTC_WEE 0xcb 49 #define RTC_DAY 0xcc 50 #define RTC_MON 0xcd 51 #define RTC_YEA 0xce 52 #define RTC_YEA_LEAP_YEAR (1 << 15) 53 #define RTC_UPD_TRIG 0xcf 54 #define RTC_UPD_TRIG_UPDATE (1 << 15) 55 56 struct acrtc_softc { 57 struct device sc_dev; 58 void *sc_cookie; 59 uint16_t sc_rta; 60 61 struct todr_chip_handle sc_todr; 62 struct clock_device sc_cd; 63 }; 64 65 int acrtc_match(struct device *, void *, void *); 66 void acrtc_attach(struct device *, struct device *, void *); 67 68 struct cfattach acrtc_ca = { 69 sizeof(struct acrtc_softc), acrtc_match, acrtc_attach 70 }; 71 72 struct cfdriver acrtc_cd = { 73 NULL, "acrtc", DV_DULL 74 }; 75 76 int acrtc_clock_read(struct acrtc_softc *, struct clock_ymdhms *); 77 int acrtc_clock_write(struct acrtc_softc *, struct clock_ymdhms *); 78 int acrtc_gettime(struct todr_chip_handle *, struct timeval *); 79 int acrtc_settime(struct todr_chip_handle *, struct timeval *); 80 81 void acrtc_ck32k_enable(void *, uint32_t *, int); 82 83 int 84 acrtc_match(struct device *parent, void *match, void *aux) 85 { 86 struct rsb_attach_args *ra = aux; 87 88 if (strcmp(ra->ra_name, "x-powers,ac100") == 0) 89 return 1; 90 return 0; 91 } 92 93 void 94 acrtc_attach(struct device *parent, struct device *self, void *aux) 95 { 96 struct acrtc_softc *sc = (struct acrtc_softc *)self; 97 struct rsb_attach_args *ra = aux; 98 int node; 99 100 sc->sc_cookie = ra->ra_cookie; 101 sc->sc_rta = ra->ra_rta; 102 103 printf("\n"); 104 105 sc->sc_todr.cookie = sc; 106 sc->sc_todr.todr_gettime = acrtc_gettime; 107 sc->sc_todr.todr_settime = acrtc_settime; 108 todr_handle = &sc->sc_todr; 109 110 node = OF_getnodebyname(ra->ra_node, "rtc"); 111 if (node == 0) 112 return; 113 114 sc->sc_cd.cd_node = node; 115 sc->sc_cd.cd_cookie = sc; 116 sc->sc_cd.cd_enable = acrtc_ck32k_enable; 117 clock_register(&sc->sc_cd); 118 } 119 120 static inline uint16_t 121 acrtc_read_reg(struct acrtc_softc *sc, uint8_t reg) 122 { 123 return rsb_read_2(sc->sc_cookie, sc->sc_rta, reg); 124 } 125 126 static inline void 127 acrtc_write_reg(struct acrtc_softc *sc, uint8_t reg, uint16_t value) 128 { 129 rsb_write_2(sc->sc_cookie, sc->sc_rta, reg, value); 130 } 131 132 int 133 acrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 134 { 135 struct acrtc_softc *sc = handle->cookie; 136 struct clock_ymdhms dt; 137 int error; 138 139 error = acrtc_clock_read(sc, &dt); 140 if (error) 141 return error; 142 143 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || 144 dt.dt_day > 31 || dt.dt_day == 0 || 145 dt.dt_mon > 12 || dt.dt_mon == 0 || 146 dt.dt_year < POSIX_BASE_YEAR) 147 return EINVAL; 148 149 tv->tv_sec = clock_ymdhms_to_secs(&dt); 150 tv->tv_usec = 0; 151 return 0; 152 } 153 154 int 155 acrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 156 { 157 struct acrtc_softc *sc = handle->cookie; 158 struct clock_ymdhms dt; 159 160 clock_secs_to_ymdhms(tv->tv_sec, &dt); 161 162 return acrtc_clock_write(sc, &dt); 163 } 164 165 int 166 acrtc_clock_read(struct acrtc_softc *sc, struct clock_ymdhms *dt) 167 { 168 uint16_t ctrl; 169 170 dt->dt_sec = FROMBCD(acrtc_read_reg(sc, RTC_SEC)); 171 dt->dt_min = FROMBCD(acrtc_read_reg(sc, RTC_MIN)); 172 dt->dt_hour = FROMBCD(acrtc_read_reg(sc, RTC_HOU)); 173 dt->dt_day = FROMBCD(acrtc_read_reg(sc, RTC_DAY)); 174 dt->dt_mon = FROMBCD(acrtc_read_reg(sc, RTC_MON)); 175 dt->dt_year = FROMBCD(acrtc_read_reg(sc, RTC_YEA)) + 2000; 176 177 #ifdef DEBUG 178 printf("%02d/%02d/%04d %02d:%02d:%0d\n", dt->dt_day, dt->dt_mon, 179 dt->dt_year, dt->dt_hour, dt->dt_min, dt->dt_sec); 180 #endif 181 182 /* Consider the time to be invalid if the clock is in 12H mode. */ 183 ctrl = acrtc_read_reg(sc, RTC_CTRL); 184 if ((ctrl & RTC_CTRL_12H_24H_MODE) == 0) 185 return EINVAL; 186 187 return 0; 188 } 189 190 int 191 acrtc_clock_write(struct acrtc_softc *sc, struct clock_ymdhms *dt) 192 { 193 uint16_t leap = isleap(dt->dt_year) ? RTC_YEA_LEAP_YEAR : 0; 194 195 acrtc_write_reg(sc, RTC_SEC, TOBCD(dt->dt_sec)); 196 acrtc_write_reg(sc, RTC_MIN, TOBCD(dt->dt_min)); 197 acrtc_write_reg(sc, RTC_HOU, TOBCD(dt->dt_hour)); 198 acrtc_write_reg(sc, RTC_WEE, TOBCD(dt->dt_wday)); 199 acrtc_write_reg(sc, RTC_DAY, TOBCD(dt->dt_day)); 200 acrtc_write_reg(sc, RTC_MON, TOBCD(dt->dt_mon)); 201 acrtc_write_reg(sc, RTC_YEA, TOBCD(dt->dt_year - 2000) | leap); 202 acrtc_write_reg(sc, RTC_UPD_TRIG, RTC_UPD_TRIG_UPDATE); 203 204 /* Switch to 24H mode to indicate the time is now valid. */ 205 acrtc_write_reg(sc, RTC_CTRL, RTC_CTRL_12H_24H_MODE); 206 207 return 0; 208 } 209 210 void 211 acrtc_ck32k_enable(void *cookie, uint32_t *cells, int on) 212 { 213 struct acrtc_softc *sc = cookie; 214 uint32_t idx = cells[0]; 215 uint16_t reg; 216 217 reg = acrtc_read_reg(sc, CK32K_OUT_CTRL1 + idx); 218 reg &= ~CK32K_OUT_CTRL_PRE_DIV_MASK; 219 reg &= ~CK32K_OUT_CTRL_MUX_SEL_MASK; 220 reg &= ~CK32K_OUT_CTRL_POST_DIV_MASK; 221 reg |= CK32K_OUT_CTRL_PRE_DIV_32K; 222 reg |= CK32K_OUT_CTRL_MUX_SEL_32K; 223 reg |= CK32K_OUT_CTRL_POST_DIV_32K; 224 if (on) 225 reg |= CK32K_OUT_CTRL_ENA; 226 else 227 reg &= ~CK32K_OUT_CTRL_ENA; 228 acrtc_write_reg(sc, CK32K_OUT_CTRL1 + idx, reg); 229 } 230