1 /* $NetBSD: mc146818.c,v 1.7 2005/12/11 12:21:27 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Izumi Tsutsui. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * mc146818 and compatible time of day chip subroutines 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: mc146818.c,v 1.7 2005/12/11 12:21:27 christos Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/errno.h> 40 41 #include <machine/bus.h> 42 43 #include <dev/clock_subr.h> 44 45 #include <dev/ic/mc146818reg.h> 46 #include <dev/ic/mc146818var.h> 47 48 int mc146818_gettime(todr_chip_handle_t, volatile struct timeval *); 49 int mc146818_settime(todr_chip_handle_t, volatile struct timeval *); 50 int mc146818_getcal(todr_chip_handle_t, int *); 51 int mc146818_setcal(todr_chip_handle_t, int); 52 53 void 54 mc146818_attach(struct mc146818_softc *sc) 55 { 56 todr_chip_handle_t handle; 57 58 #ifdef DIAGNOSTIC 59 if (sc->sc_mcread == NULL || 60 sc->sc_mcwrite == NULL) 61 panic("mc146818_attach: invalid read/write functions"); 62 #endif 63 64 printf(": mc146818 compatible time-of-day clock"); 65 66 handle = &sc->sc_handle; 67 handle->cookie = sc; 68 handle->todr_gettime = mc146818_gettime; 69 handle->todr_settime = mc146818_settime; 70 handle->todr_getcal = mc146818_getcal; 71 handle->todr_setcal = mc146818_setcal; 72 handle->todr_setwen = NULL; 73 } 74 75 /* 76 * todr_gettime function: 77 * Get time of day and convert it to a struct timeval. 78 * Return 0 on success, an error number othersize. 79 */ 80 int 81 mc146818_gettime(todr_chip_handle_t handle, volatile struct timeval *tv) 82 { 83 struct mc146818_softc *sc; 84 struct clock_ymdhms dt; 85 int s, timeout, cent, year; 86 87 sc = handle->cookie; 88 89 s = splclock(); /* XXX really needed? */ 90 91 todr_wenable(handle, 1); 92 93 timeout = 1000000; /* XXX how long should we wait? */ 94 for (;;) { 95 if ((*sc->sc_mcread)(sc, MC_REGA) & MC_REGA_UIP) 96 break; 97 if (--timeout < 0) { 98 printf("mc146818_gettime: timeout\n"); 99 return EBUSY; 100 } 101 } 102 103 #define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) 104 105 dt.dt_sec = FROMREG((*sc->sc_mcread)(sc, MC_SEC)); 106 dt.dt_min = FROMREG((*sc->sc_mcread)(sc, MC_MIN)); 107 dt.dt_hour = FROMREG((*sc->sc_mcread)(sc, MC_HOUR)); 108 dt.dt_wday = FROMREG((*sc->sc_mcread)(sc, MC_DOW)); 109 dt.dt_day = FROMREG((*sc->sc_mcread)(sc, MC_DOM)); 110 dt.dt_mon = FROMREG((*sc->sc_mcread)(sc, MC_MONTH)); 111 year = FROMREG((*sc->sc_mcread)(sc, MC_YEAR)); 112 if (sc->sc_getcent) { 113 cent = (*sc->sc_getcent)(sc); 114 year += cent * 100; 115 } 116 117 #undef FROMREG 118 119 year += sc->sc_year0; 120 if (year < POSIX_BASE_YEAR && 121 (sc->sc_flag & MC146818_NO_CENT_ADJUST) == 0) 122 year += 100; 123 dt.dt_year = year; 124 125 todr_wenable(handle, 0); 126 127 splx(s); 128 129 /* simple sanity checks */ 130 if (dt.dt_mon > 12 || dt.dt_day > 31 || 131 dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60) 132 return EIO; 133 134 tv->tv_sec = clock_ymdhms_to_secs(&dt); 135 tv->tv_usec = 0; 136 return 0; 137 } 138 139 /* 140 * todr_settime function: 141 * Set the time of day clock based on the value of the struct timeval arg. 142 * Return 0 on success, an error number othersize. 143 */ 144 int 145 mc146818_settime(todr_chip_handle_t handle, volatile struct timeval *tv) 146 { 147 struct mc146818_softc *sc; 148 struct clock_ymdhms dt; 149 int s, timeout, cent, year; 150 151 sc = handle->cookie; 152 153 /* Note: we ignore `tv_usec' */ 154 clock_secs_to_ymdhms(tv->tv_sec, &dt); 155 156 s = splclock(); /* XXX really needed? */ 157 158 todr_wenable(handle, 1); 159 160 timeout = 1000000; /* XXX how long should we wait? */ 161 for (;;) { 162 if ((*sc->sc_mcread)(sc, MC_REGA) & MC_REGA_UIP) 163 break; 164 if (--timeout < 0) { 165 printf("mc146818_settime: timeout\n"); 166 return EBUSY; 167 } 168 } 169 170 #define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) 171 172 (*sc->sc_mcwrite)(sc, MC_SEC, TOREG(dt.dt_sec)); 173 (*sc->sc_mcwrite)(sc, MC_MIN, TOREG(dt.dt_min)); 174 (*sc->sc_mcwrite)(sc, MC_HOUR, TOREG(dt.dt_hour)); 175 (*sc->sc_mcwrite)(sc, MC_DOW, TOREG(dt.dt_wday)); 176 (*sc->sc_mcwrite)(sc, MC_DOM, TOREG(dt.dt_day)); 177 (*sc->sc_mcwrite)(sc, MC_MONTH, TOREG(dt.dt_mon)); 178 179 year = dt.dt_year - sc->sc_year0; 180 if (sc->sc_setcent) { 181 cent = year / 100; 182 (*sc->sc_setcent)(sc, cent); 183 year -= cent * 100; 184 } 185 if (year > 99 && 186 (sc->sc_flag & MC146818_NO_CENT_ADJUST) == 0) 187 year -= 100; 188 (*sc->sc_mcwrite)(sc, MC_YEAR, TOREG(year)); 189 190 #undef TOREG 191 192 todr_wenable(handle, 0); 193 194 splx(s); 195 196 return 0; 197 } 198 199 int 200 mc146818_getcal(todr_chip_handle_t handle, int *vp) 201 { 202 203 return EOPNOTSUPP; 204 } 205 206 int 207 mc146818_setcal(todr_chip_handle_t handle, int v) 208 { 209 210 return EOPNOTSUPP; 211 } 212