timekeeper.c revision 1.10
1/* $OpenBSD: timekeeper.c,v 1.10 2017/11/03 06:54:06 aoyama Exp $ */ 2/* $NetBSD: timekeeper.c,v 1.1 2000/01/05 08:48:56 nisimura Exp $ */ 3 4/*- 5 * Copyright (c) 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Tohru Nishimura. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/device.h> 36#include <sys/kernel.h> 37#include <sys/evcount.h> 38 39#include <machine/autoconf.h> 40#include <machine/board.h> /* machtype value */ 41#include <machine/cpu.h> 42 43#include <dev/clock_subr.h> 44 45#include <luna88k/luna88k/clockvar.h> 46#include <luna88k/dev/timekeeper.h> 47 48#define MK_YEAR0 1970 /* year offset of MK */ 49#define DS_YEAR0 1990 /* year offset of DS */ 50 51struct timekeeper_softc { 52 struct device sc_dev; 53 void *sc_clock, *sc_nvram; 54 int sc_nvramsize; 55 struct evcount sc_count; 56}; 57 58/* 59 * BCD to decimal and decimal to BCD. 60 */ 61#define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) 62#define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) 63 64int clock_match(struct device *, void *, void *); 65void clock_attach(struct device *, struct device *, void *); 66 67struct cfattach clock_ca = { 68 sizeof (struct timekeeper_softc), clock_match, clock_attach 69}; 70 71struct cfdriver clock_cd = { 72 NULL, "clock", DV_DULL 73}; 74 75void mkclock_get(struct device *, time_t, struct clock_ymdhms *); 76void mkclock_set(struct device *, struct clock_ymdhms *); 77void dsclock_get(struct device *, time_t, struct clock_ymdhms *); 78void dsclock_set(struct device *, struct clock_ymdhms *); 79 80const struct clockfns mkclock_clockfns = { 81 NULL /* never used */, mkclock_get, mkclock_set, 82}; 83 84const struct clockfns dsclock_clockfns = { 85 NULL /* never used */, dsclock_get, dsclock_set, 86}; 87 88int 89clock_match(struct device *parent, void *match, void *aux) 90{ 91 struct mainbus_attach_args *ma = aux; 92 93 if (strcmp(ma->ma_name, clock_cd.cd_name)) 94 return 0; 95 return 1; 96} 97 98extern int machtype; /* in machdep.c */ 99 100void 101clock_attach(struct device *parent, struct device *self, void *aux) 102{ 103 struct timekeeper_softc *sc = (void *)self; 104 struct mainbus_attach_args *ma = aux; 105 const struct clockfns *clockwork; 106 107 switch (machtype) { 108 default: 109 case LUNA_88K: /* Mostek MK48T02 */ 110 sc->sc_clock = (void *)(ma->ma_addr + MK_NVRAM_SPACE); 111 sc->sc_nvram = (void *)ma->ma_addr; 112 sc->sc_nvramsize = MK_NVRAM_SPACE; 113 clockwork = &mkclock_clockfns; 114 printf(": MK48T02\n"); 115 break; 116 case LUNA_88K2: /* Dallas DS1397 */ 117 sc->sc_clock = (void *)ma->ma_addr; 118 sc->sc_nvram = (void *)(ma->ma_addr + 50); 119 sc->sc_nvramsize = 50; 120 clockwork = &dsclock_clockfns; 121 printf(": DS1397\n"); 122 break; 123 } 124 125 evcount_attach(&sc->sc_count, self->dv_xname, &ma->ma_ilvl); 126 127 clockattach(&sc->sc_dev, clockwork, &sc->sc_count); 128} 129 130/* 131 * On LUNA-88K, NVRAM contents and Timekeeper registers are mapped on the 132 * most significant byte of each 32bit word. (i.e. 4-bytes stride) 133 * 134 * Get the time of day, based on the clock's value and/or the base value. 135 */ 136void 137mkclock_get(struct device *dev, time_t base, struct clock_ymdhms *dt) 138{ 139 struct timekeeper_softc *sc = (void *)dev; 140 volatile u_int32_t *chiptime = (void *)sc->sc_clock; 141 int s; 142 143 s = splclock(); 144 145 /* enable read (stop time) */ 146 chiptime[MK_CSR] |= (MK_CSR_READ << 24); 147 148 dt->dt_sec = FROMBCD(chiptime[MK_SEC] >> 24); 149 dt->dt_min = FROMBCD(chiptime[MK_MIN] >> 24); 150 dt->dt_hour = FROMBCD(chiptime[MK_HOUR] >> 24); 151 dt->dt_wday = FROMBCD(chiptime[MK_DOW] >> 24); 152 dt->dt_day = FROMBCD(chiptime[MK_DOM] >> 24); 153 dt->dt_mon = FROMBCD(chiptime[MK_MONTH] >> 24); 154 dt->dt_year = FROMBCD(chiptime[MK_YEAR] >> 24); 155 156 chiptime[MK_CSR] &= (~MK_CSR_READ << 24); /* time wears on */ 157 158 /* UniOS-Mach doesn't set the correct BCD year after Y2K */ 159 if (dt->dt_year > 100) dt->dt_year -= (MK_YEAR0 % 100); 160 161 dt->dt_year += MK_YEAR0; 162 splx(s); 163#ifdef TIMEKEEPER_DEBUG 164 printf("get %02d/%02d/%02d %02d:%02d:%02d\n", 165 dt->dt_year, dt->dt_mon, dt->dt_day, 166 dt->dt_hour, dt->dt_min, dt->dt_sec); 167#endif 168} 169 170/* 171 * Reset the TODR based on the time value. 172 */ 173void 174mkclock_set(struct device *dev, struct clock_ymdhms *dt) 175{ 176 struct timekeeper_softc *sc = (void *)dev; 177 volatile u_int32_t *chiptime = (void *)sc->sc_clock; 178 volatile u_int32_t *stamp = (void *)(sc->sc_nvram + (4 * 0x10)); 179 int s; 180 181 s = splclock(); 182 chiptime[MK_CSR] |= (MK_CSR_WRITE << 24); /* enable write */ 183 184 chiptime[MK_SEC] = TOBCD(dt->dt_sec) << 24; 185 chiptime[MK_MIN] = TOBCD(dt->dt_min) << 24; 186 chiptime[MK_HOUR] = TOBCD(dt->dt_hour) << 24; 187 chiptime[MK_DOW] = TOBCD(dt->dt_wday) << 24; 188 chiptime[MK_DOM] = TOBCD(dt->dt_day) << 24; 189 chiptime[MK_MONTH] = TOBCD(dt->dt_mon) << 24; 190 /* XXX: We don't consider UniOS-Mach Y2K problem */ 191 chiptime[MK_YEAR] = TOBCD(dt->dt_year - MK_YEAR0) << 24; 192 193 chiptime[MK_CSR] &= (~MK_CSR_WRITE << 24); /* load them up */ 194 splx(s); 195#ifdef TIMEKEEPER_DEBUG 196 printf("set %02d/%02d/%02d %02d:%02d:%02d\n", 197 dt->dt_year, dt->dt_mon, dt->dt_day, 198 dt->dt_hour, dt->dt_min, dt->dt_sec); 199#endif 200 201 /* Write a stamp at NVRAM address 0x10-0x13 */ 202 stamp[0] = 'R' << 24; stamp[1] = 'T' << 24; 203 stamp[2] = 'C' << 24; stamp[3] = '\0' << 24; 204} 205 206#define _DS_GET(off, data) \ 207 do { *chiptime = (off); (data) = (*chipdata); } while (0) 208#define _DS_SET(off, data) \ 209 do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0) 210#define _DS_GET_BCD(off, data) \ 211 do { \ 212 u_int8_t c; \ 213 *chiptime = (off); \ 214 c = *chipdata; (data) = FROMBCD(c); \ 215 } while (0) 216#define _DS_SET_BCD(off, data) \ 217 do { \ 218 *chiptime = (off); \ 219 *chipdata = TOBCD((u_int8_t)(data)); \ 220 } while (0) 221 222/* 223 * Get the time of day, based on the clock's value and/or the base value. 224 */ 225void 226dsclock_get(struct device *dev, time_t base, struct clock_ymdhms *dt) 227{ 228 struct timekeeper_softc *sc = (void *)dev; 229 volatile u_int8_t *chiptime = (void *)sc->sc_clock; 230 volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 231 int s; 232 u_int8_t c; 233 234 s = splclock(); 235 236 /* specify 24hr and BCD mode */ 237 _DS_GET(DS_REGB, c); 238 c |= DS_REGB_24HR; 239 c &= ~DS_REGB_BINARY; 240 _DS_SET(DS_REGB, c); 241 242 /* update in progress; spin loop */ 243 for (;;) { 244 *chiptime = DS_REGA; 245 if ((*chipdata & DS_REGA_UIP) == 0) 246 break; 247 } 248 249 _DS_GET_BCD(DS_SEC, dt->dt_sec); 250 _DS_GET_BCD(DS_MIN, dt->dt_min); 251 _DS_GET_BCD(DS_HOUR, dt->dt_hour); 252 _DS_GET_BCD(DS_DOW, dt->dt_wday); 253 _DS_GET_BCD(DS_DOM, dt->dt_day); 254 _DS_GET_BCD(DS_MONTH, dt->dt_mon); 255 _DS_GET_BCD(DS_YEAR, dt->dt_year); 256 257 /* UniOS-Mach doesn't set the correct BCD year after Y2K */ 258 if (dt->dt_year > 100) dt->dt_year -= (DS_YEAR0 % 100); 259 260 dt->dt_year += DS_YEAR0; 261 splx(s); 262 263#ifdef TIMEKEEPER_DEBUG 264 printf("get %02d/%02d/%02d %02d:%02d:%02d\n", 265 dt->dt_year, dt->dt_mon, dt->dt_day, 266 dt->dt_hour, dt->dt_min, dt->dt_sec); 267#endif 268} 269 270/* 271 * Reset the TODR based on the time value. 272 */ 273void 274dsclock_set(struct device *dev, struct clock_ymdhms *dt) 275{ 276 struct timekeeper_softc *sc = (void *)dev; 277 volatile u_int8_t *chiptime = (void *)sc->sc_clock; 278 volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); 279 int s; 280 u_int8_t c; 281 282 s = splclock(); 283 284 /* enable write */ 285 _DS_GET(DS_REGB, c); 286 c |= DS_REGB_SET; 287 _DS_SET(DS_REGB, c); 288 289 _DS_SET_BCD(DS_SEC, dt->dt_sec); 290 _DS_SET_BCD(DS_MIN, dt->dt_min); 291 _DS_SET_BCD(DS_HOUR, dt->dt_hour); 292 _DS_SET_BCD(DS_DOW, dt->dt_wday); 293 _DS_SET_BCD(DS_DOM, dt->dt_day); 294 _DS_SET_BCD(DS_MONTH, dt->dt_mon); 295 /* XXX: We don't consider UniOS-Mach Y2K problem */ 296 _DS_SET_BCD(DS_YEAR, dt->dt_year - DS_YEAR0); 297 298 _DS_GET(DS_REGB, c); 299 c &= ~DS_REGB_SET; 300 _DS_SET(DS_REGB, c); 301 302 splx(s); 303 304#ifdef TIMEKEEPER_DEBUG 305 printf("set %02d/%02d/%02d %02d:%02d:%02d\n", 306 dt->dt_year, dt->dt_mon, dt->dt_day, 307 dt->dt_hour, dt->dt_min, dt->dt_sec); 308#endif 309} 310