Annotation of sys/arch/solbourne/dev/tod.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: tod.c,v 1.1 2005/04/20 01:00:16 miod Exp $ */
! 2: /*
! 3: * Copyright (c) 2005, Miodrag Vallat
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: *
! 14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 15: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
! 16: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
! 17: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
! 18: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
! 19: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
! 20: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 21: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
! 22: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
! 23: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 24: * POSSIBILITY OF SUCH DAMAGE.
! 25: */
! 26:
! 27: /*
! 28: * TODclock driver. We only use it to know the current time during boot,
! 29: * as we do not get interrupts from it.
! 30: *
! 31: * The clock in the IDT machines is the Oki MSM62X42BRS.
! 32: *
! 33: * A datasheet for this chip is available from:
! 34: * http://www.datasheetarchive.com/datasheet/pdf/19/196099.html
! 35: */
! 36:
! 37: #include <sys/param.h>
! 38: #include <sys/kernel.h>
! 39: #include <sys/device.h>
! 40: #include <sys/systm.h>
! 41:
! 42: #include <machine/autoconf.h>
! 43: #include <machine/cpu.h>
! 44:
! 45: #include <solbourne/dev/todreg.h>
! 46: #include <dev/clock_subr.h>
! 47:
! 48: #include <machine/idt.h>
! 49: #include <machine/kap.h>
! 50:
! 51: int todmatch(struct device *, void *, void *);
! 52: void todattach(struct device *, struct device *, void *);
! 53:
! 54: struct cfattach tod_ca = {
! 55: sizeof(struct device), todmatch, todattach
! 56: };
! 57:
! 58: struct cfdriver tod_cd = {
! 59: NULL, "tod", DV_DULL
! 60: };
! 61:
! 62: volatile u_char *tod_regs;
! 63:
! 64: u_char msm_read(u_int);
! 65: void msm_write(u_int, u_char);
! 66:
! 67: int
! 68: todmatch(parent, vcf, aux)
! 69: struct device *parent;
! 70: void *vcf, *aux;
! 71: {
! 72: struct confargs *ca = aux;
! 73:
! 74: return (strcmp(tod_cd.cd_name, ca->ca_ra.ra_name) == 0);
! 75: }
! 76:
! 77: void
! 78: todattach(parent, self, aux)
! 79: struct device *parent, *self;
! 80: void *aux;
! 81: {
! 82: printf(": OKI MSM62X42BRS\n");
! 83:
! 84: /* the register are already mapped 1:1 by pmap_bootstrap() */
! 85: tod_regs = (volatile u_char *)TODCLOCK_BASE;
! 86: }
! 87:
! 88: /*
! 89: * Read or write a register of the Oki clock.
! 90: *
! 91: * The clock registers are not directly accessible (while control registers
! 92: * are). We need to freeze them first. To do so, we set the hold bit in
! 93: * D, and if the busy bit clears, we are free to proceed. If the busy bit
! 94: * is still set, we need to clear the hold bit and retry.
! 95: */
! 96: u_char
! 97: msm_read(u_int regno)
! 98: {
! 99: u_char d, r;
! 100:
! 101: /* no need to do the hold dance for control registers */
! 102: if (regno >= MSM_D)
! 103: return (tod_regs[regno] & 0x0f);
! 104:
! 105: d = tod_regs[MSM_D] & 0x0f & ~MSM_D_HOLD;
! 106: for (;;) {
! 107: tod_regs[MSM_D] = d | MSM_D_HOLD;
! 108: if (!ISSET(tod_regs[MSM_D], MSM_D_BUSY))
! 109: break;
! 110: tod_regs[MSM_D] = d;
! 111: }
! 112:
! 113: r = tod_regs[regno] & 0x0f;
! 114: tod_regs[MSM_D] = d;
! 115:
! 116: return (r);
! 117: }
! 118:
! 119: void
! 120: msm_write(u_int regno, u_char value)
! 121: {
! 122: u_char d;
! 123:
! 124: /* no need to do the hold dance for control registers */
! 125: if (regno >= MSM_D) {
! 126: tod_regs[regno] = value;
! 127: return;
! 128: }
! 129:
! 130: d = tod_regs[MSM_D] & 0x0f & ~MSM_D_HOLD;
! 131: for (;;) {
! 132: tod_regs[MSM_D] = d | MSM_D_HOLD;
! 133: if (!ISSET(tod_regs[MSM_D], MSM_D_BUSY))
! 134: break;
! 135: tod_regs[MSM_D] = d;
! 136: }
! 137:
! 138: tod_regs[regno] = value;
! 139: tod_regs[MSM_D] = d;
! 140: }
! 141:
! 142: void
! 143: inittodr(base)
! 144: time_t base;
! 145: {
! 146: struct clock_ymdhms dt;
! 147:
! 148: dt.dt_sec = msm_read(MSM_SEC_UNITS) + 10 * msm_read(MSM_SEC_TENS);
! 149: dt.dt_min = msm_read(MSM_MIN_UNITS) + 10 * msm_read(MSM_MIN_TENS);
! 150: #if 0
! 151: dt.dt_hour = msm_read(MSM_HOUR_UNITS) + 10 * msm_read(MSM_HOUR_TENS);
! 152: #else
! 153: dt.dt_hour = msm_read(MSM_HOUR_TENS);
! 154: if (dt.dt_hour & MSM_HOUR_PM)
! 155: dt.dt_hour = 12 + 10 * (dt.dt_hour & ~MSM_HOUR_TENS);
! 156: else
! 157: dt.dt_hour *= 10;
! 158: dt.dt_hour += msm_read(MSM_HOUR_UNITS);
! 159: #endif
! 160: dt.dt_day = msm_read(MSM_DAY_UNITS) + 10 * msm_read(MSM_DAY_TENS);
! 161: dt.dt_mon = msm_read(MSM_MONTH_UNITS) + 10 * msm_read(MSM_MONTH_TENS);
! 162: dt.dt_year = msm_read(MSM_YEAR_UNITS) + 10 * msm_read(MSM_YEAR_TENS);
! 163: dt.dt_year += CLOCK_YEAR_BASE;
! 164: /* dt_wday left uninitialized */
! 165:
! 166: time.tv_sec = clock_ymdhms_to_secs(&dt);
! 167:
! 168: if (time.tv_sec == 0) {
! 169: /*
! 170: * Believe the time in the file system for lack of
! 171: * anything better, resetting the clock.
! 172: */
! 173: if (base < 35 * SECYR) {/* this port did not exist until 2005 */
! 174: /*
! 175: * If base is 0, assume filesystem time is just unknown
! 176: * in stead of preposterous. Don't bark.
! 177: */
! 178: if (base != 0)
! 179: printf("WARNING: preposterous time in file system\n");
! 180: /* not going to use it anyway, if the chip is readable */
! 181: time.tv_sec = 35 * SECYR + 90 * SECDAY + SECDAY / 2;
! 182: } else {
! 183: printf("WARNING: bad date in battery clock");
! 184: time.tv_sec = base;
! 185: resettodr();
! 186: }
! 187: } else {
! 188: int deltat = time.tv_sec - base;
! 189:
! 190: if (deltat < 0)
! 191: deltat = -deltat;
! 192: if (deltat < 2 * SECDAY)
! 193: return;
! 194:
! 195: #ifndef SMALL_KERNEL
! 196: printf("WARNING: clock %s %d days",
! 197: time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
! 198: #endif
! 199: }
! 200: printf(" -- CHECK AND RESET THE DATE!\n");
! 201: }
! 202:
! 203: void
! 204: resettodr()
! 205: {
! 206: struct clock_ymdhms dt;
! 207:
! 208: if (time.tv_sec == 0 || tod_regs == NULL)
! 209: return;
! 210:
! 211: clock_secs_to_ymdhms(time.tv_sec, &dt);
! 212:
! 213: /*
! 214: * Since we don't know if the clock is in AM/PM or 24 hour mode,
! 215: * we need to reset it and force one mode. Being an evil european
! 216: * person, I'll force 24 hour mode, of course.
! 217: */
! 218: msm_write(MSM_F, MSM_F_RESET | MSM_F_24HR);
! 219: msm_write(MSM_F, MSM_F_STOP); /* leave reset mode, but stop clock */
! 220:
! 221: dt.dt_year -= CLOCK_YEAR_BASE;
! 222: msm_write(MSM_YEAR_TENS, dt.dt_year / 10);
! 223: msm_write(MSM_YEAR_UNITS, dt.dt_year % 10);
! 224: msm_write(MSM_MONTH_TENS, dt.dt_mon / 10);
! 225: msm_write(MSM_MONTH_UNITS, dt.dt_mon % 10);
! 226: msm_write(MSM_DAY_TENS, dt.dt_day / 10);
! 227: msm_write(MSM_DAY_UNITS, dt.dt_day % 10);
! 228: msm_write(MSM_HOUR_TENS, dt.dt_hour / 10);
! 229: msm_write(MSM_HOUR_UNITS, dt.dt_hour % 10);
! 230: msm_write(MSM_MIN_TENS, dt.dt_min / 10);
! 231: msm_write(MSM_MIN_UNITS, dt.dt_min % 10);
! 232: msm_write(MSM_SEC_TENS, dt.dt_sec / 10);
! 233: msm_write(MSM_SEC_UNITS, dt.dt_sec % 10);
! 234:
! 235: msm_write(MSM_F, 0); /* restart clock */
! 236: }
CVSweb