1 /* $OpenBSD: timekeeper.c,v 1.9 2014/06/07 11:55:35 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 51 struct 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 64 int clock_match(struct device *, void *, void *); 65 void clock_attach(struct device *, struct device *, void *); 66 67 struct cfattach clock_ca = { 68 sizeof (struct timekeeper_softc), clock_match, clock_attach 69 }; 70 71 struct cfdriver clock_cd = { 72 NULL, "clock", DV_DULL 73 }; 74 75 void mkclock_get(struct device *, time_t, struct clock_ymdhms *); 76 void mkclock_set(struct device *, struct clock_ymdhms *); 77 void dsclock_get(struct device *, time_t, struct clock_ymdhms *); 78 void dsclock_set(struct device *, struct clock_ymdhms *); 79 80 const struct clockfns mkclock_clockfns = { 81 NULL /* never used */, mkclock_get, mkclock_set, 82 }; 83 84 const struct clockfns dsclock_clockfns = { 85 NULL /* never used */, dsclock_get, dsclock_set, 86 }; 87 88 int 89 clock_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 98 extern int machtype; /* in machdep.c */ 99 100 void 101 clock_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 */ 136 void 137 mkclock_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 */ 173 void 174 mkclock_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 */ 225 void 226 dsclock_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 */ 273 void 274 dsclock_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