mc146818.c revision 146417
1/*- 2 * Copyright (c) 2003 Izumi Tsutsui. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * from: NetBSD: mc146818.c,v 1.4 2003/11/24 06:20:40 tsutsui Exp 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sys/dev/mc146818/mc146818.c 146417 2005-05-19 21:20:42Z marius $"); 31 32/* 33 * mc146818 and compatible time of day chip subroutines 34 */ 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/clock.h> 40#include <sys/lock.h> 41#include <sys/mutex.h> 42 43#include <machine/bus.h> 44 45#include <dev/mc146818/mc146818reg.h> 46#include <dev/mc146818/mc146818var.h> 47 48#include "clock_if.h" 49 50static u_int mc146818_def_getcent(device_t); 51static void mc146818_def_setcent(device_t, u_int); 52static u_int mc146818_def_read(device_t, u_int); 53static void mc146818_def_write(device_t, u_int, u_int); 54 55int 56mc146818_attach(device_t dev) 57{ 58 struct mc146818_softc *sc; 59 60 sc = device_get_softc(dev); 61 62 if (mtx_initialized(&sc->sc_mtx) == 0) { 63 device_printf(dev, "%s: mutex not initialized\n", __func__); 64 return (ENXIO); 65 } 66 67 if (sc->sc_mcread == NULL) 68 sc->sc_mcread = mc146818_def_read; 69 if (sc->sc_mcwrite == NULL) 70 sc->sc_mcwrite = mc146818_def_write; 71 72 if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 73 /* 74 * Note that setting MC146818_NO_CENT_ADJUST means that 75 * the century has to be stored in NVRAM somewhere. 76 */ 77 if (sc->sc_getcent == NULL) 78 sc->sc_getcent = mc146818_def_getcent; 79 if (sc->sc_setcent == NULL) 80 sc->sc_setcent = mc146818_def_setcent; 81 } 82 83 mtx_lock(&sc->sc_mtx); 84 if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) { 85 mtx_unlock(&sc->sc_mtx); 86 device_printf(dev, "%s: battery low\n", __func__); 87 return (ENXIO); 88 } 89 90 sc->sc_rega = MC_BASE_32_KHz; 91 (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega); 92 93 sc->sc_regb = 0; 94 sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY; 95 sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR; 96 (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 97 mtx_unlock(&sc->sc_mtx); 98 99 clock_register(dev, 1000000); /* 1 second resolution. */ 100 101 return (0); 102} 103 104/* 105 * Get time of day and convert it to a struct timespec. 106 * Return 0 on success, an error number otherwise. 107 */ 108int 109mc146818_gettime(device_t dev, struct timespec *ts) 110{ 111 struct mc146818_softc *sc; 112 struct clocktime ct; 113 int timeout, cent, year; 114 115 sc = device_get_softc(dev); 116 117 timeout = 1000000; /* XXX how long should we wait? */ 118 119 mtx_lock(&sc->sc_mtx); 120 /* 121 * If MC_REGA_UIP is 0 we have at least 244us before the next 122 * update. If it's 1 an update is imminent. 123 */ 124 for (;;) { 125 if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) 126 break; 127 if (--timeout < 0) { 128 mtx_unlock(&sc->sc_mtx); 129 device_printf(dev, "%s: timeout\n", __func__); 130 return (EBUSY); 131 } 132 } 133 134#define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) 135 136 ct.nsec = 0; 137 ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 138 ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN)); 139 ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR)); 140 /* Map dow from 1 - 7 to 0 - 6. */ 141 ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1; 142 ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM)); 143 ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH)); 144 year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR)); 145 year += sc->sc_year0; 146 if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 147 cent = (*sc->sc_getcent)(dev); 148 year += cent * 100; 149 } else if (year < POSIX_BASE_YEAR) 150 year += 100; 151 mtx_unlock(&sc->sc_mtx); 152 153 ct.year = year; 154 155 return (clock_ct_to_ts(&ct, ts)); 156} 157 158#ifdef notyet 159int 160mc146818_getsecs(device_t dev, int *secp) 161{ 162 struct mc146818_softc *sc; 163 int sec, timeout; 164 165 sc = device_get_softc(dev); 166 167 timeout = 1000000; /* XXX how long should we wait? */ 168 169 mtx_lock(&sc->sc_mtx); 170 for (;;) { 171 if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) { 172 sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 173 break; 174 } 175 if (--timeout == 0) { 176 mtx_unlock(&sc->sc_mtx); 177 device_printf(dev, "%s: timeout\n", __func__); 178 return (EBUSY); 179 } 180 } 181 mtx_unlock(&sc->sc_mtx); 182 183#undef FROMREG 184 185 *secp = sec; 186 return (0); 187} 188#endif 189 190/* 191 * Set the time of day clock based on the value of the struct timespec arg. 192 * Return 0 on success, an error number otherwise. 193 */ 194int 195mc146818_settime(device_t dev, struct timespec *ts) 196{ 197 struct mc146818_softc *sc; 198 struct clocktime ct; 199 int cent, year; 200 201 sc = device_get_softc(dev); 202 203 /* Accuracy is only one second. */ 204 if (ts->tv_nsec >= 500000000) 205 ts->tv_sec++; 206 ts->tv_nsec = 0; 207 clock_ts_to_ct(ts, &ct); 208 209 mtx_lock(&sc->sc_mtx); 210 /* Disable RTC updates and interrupts (if enabled). */ 211 (*sc->sc_mcwrite)(dev, MC_REGB, 212 ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET)); 213 214#define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) 215 216 (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec)); 217 (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min)); 218 (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour)); 219 /* Map dow from 0 - 6 to 1 - 7. */ 220 (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1)); 221 (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day)); 222 (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon)); 223 224 year = ct.year - sc->sc_year0; 225 if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 226 cent = year / 100; 227 (*sc->sc_setcent)(dev, cent); 228 year -= cent * 100; 229 } else if (year > 99) 230 year -= 100; 231 (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year)); 232 233 /* Reenable RTC updates and interrupts. */ 234 (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 235 mtx_unlock(&sc->sc_mtx); 236 237#undef TOREG 238 239 return (0); 240} 241 242#define MC_ADDR 0 243#define MC_DATA 1 244 245static u_int 246mc146818_def_read(device_t dev, u_int reg) 247{ 248 struct mc146818_softc *sc; 249 250 sc = device_get_softc(dev); 251 bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 252 return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA)); 253} 254 255static void 256mc146818_def_write(device_t dev, u_int reg, u_int val) 257{ 258 struct mc146818_softc *sc; 259 260 sc = device_get_softc(dev); 261 bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 262 bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val); 263} 264 265/* 266 * Looks like it's common even across platforms to store the century at 267 * 0x32 in the NVRAM of the mc146818. 268 */ 269#define MC_CENT 0x32 270 271static u_int 272mc146818_def_getcent(device_t dev) 273{ 274 struct mc146818_softc *sc; 275 276 sc = device_get_softc(dev); 277 return ((*sc->sc_mcread)(dev, MC_CENT)); 278} 279 280static void 281mc146818_def_setcent(device_t dev, u_int cent) 282{ 283 struct mc146818_softc *sc; 284 285 sc = device_get_softc(dev); 286 (*sc->sc_mcwrite)(dev, MC_CENT, cent); 287} 288