mc146818.c revision 137821
1137821Smarius/* 2137821Smarius * Copyright (c) 2003 Izumi Tsutsui. All rights reserved. 3137821Smarius * 4137821Smarius * Redistribution and use in source and binary forms, with or without 5137821Smarius * modification, are permitted provided that the following conditions 6137821Smarius * are met: 7137821Smarius * 1. Redistributions of source code must retain the above copyright 8137821Smarius * notice, this list of conditions and the following disclaimer. 9137821Smarius * 2. Redistributions in binary form must reproduce the above copyright 10137821Smarius * notice, this list of conditions and the following disclaimer in the 11137821Smarius * documentation and/or other materials provided with the distribution. 12137821Smarius * 3. The name of the author may not be used to endorse or promote products 13137821Smarius * derived from this software without specific prior written permission. 14137821Smarius * 15137821Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16137821Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17137821Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18137821Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19137821Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20137821Smarius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21137821Smarius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22137821Smarius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23137821Smarius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24137821Smarius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25137821Smarius * 26137821Smarius * from: NetBSD: mc146818.c,v 1.4 2003/11/24 06:20:40 tsutsui Exp 27137821Smarius */ 28137821Smarius 29137821Smarius#include <sys/cdefs.h> 30137821Smarius__FBSDID("$FreeBSD: head/sys/dev/mc146818/mc146818.c 137821 2004-11-17 16:37:25Z marius $"); 31137821Smarius 32137821Smarius/* 33137821Smarius * mc146818 and compatible time of day chip subroutines 34137821Smarius */ 35137821Smarius 36137821Smarius#include <sys/param.h> 37137821Smarius#include <sys/systm.h> 38137821Smarius#include <sys/bus.h> 39137821Smarius#include <sys/clock.h> 40137821Smarius 41137821Smarius#include <machine/bus.h> 42137821Smarius 43137821Smarius#include <dev/mc146818/mc146818reg.h> 44137821Smarius#include <dev/mc146818/mc146818var.h> 45137821Smarius 46137821Smarius#include "clock_if.h" 47137821Smarius 48137821Smariusstatic u_int mc146818_def_getcent(device_t); 49137821Smariusstatic void mc146818_def_setcent(device_t, u_int); 50137821Smariusstatic u_int mc146818_def_read(device_t, u_int); 51137821Smariusstatic void mc146818_def_write(device_t, u_int, u_int); 52137821Smarius 53137821Smariusint 54137821Smariusmc146818_attach(device_t dev) 55137821Smarius{ 56137821Smarius struct mc146818_softc *sc; 57137821Smarius 58137821Smarius sc = device_get_softc(dev); 59137821Smarius 60137821Smarius if (sc->sc_mcread == NULL) 61137821Smarius sc->sc_mcread = mc146818_def_read; 62137821Smarius if (sc->sc_mcwrite == NULL) 63137821Smarius sc->sc_mcwrite = mc146818_def_write; 64137821Smarius 65137821Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 66137821Smarius /* 67137821Smarius * Note that setting MC146818_NO_CENT_ADJUST means that 68137821Smarius * the century has to be stored in NVRAM somewhere. 69137821Smarius */ 70137821Smarius if (sc->sc_getcent == NULL) 71137821Smarius sc->sc_getcent = mc146818_def_getcent; 72137821Smarius if (sc->sc_setcent == NULL) 73137821Smarius sc->sc_setcent = mc146818_def_setcent; 74137821Smarius } 75137821Smarius 76137821Smarius if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) { 77137821Smarius device_printf(dev, "mc146818_attach: battery low\n"); 78137821Smarius return (ENXIO); 79137821Smarius } 80137821Smarius 81137821Smarius sc->sc_rega = MC_BASE_32_KHz; 82137821Smarius (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega); 83137821Smarius 84137821Smarius sc->sc_regb = 0; 85137821Smarius sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY; 86137821Smarius sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR; 87137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 88137821Smarius 89137821Smarius clock_register(dev, 1000000); /* 1 second resolution. */ 90137821Smarius 91137821Smarius return (0); 92137821Smarius} 93137821Smarius 94137821Smarius/* 95137821Smarius * Get time of day and convert it to a struct timespec. 96137821Smarius * Return 0 on success, an error number otherwise. 97137821Smarius */ 98137821Smariusint 99137821Smariusmc146818_gettime(device_t dev, struct timespec *ts) 100137821Smarius{ 101137821Smarius struct mc146818_softc *sc; 102137821Smarius struct clocktime ct; 103137821Smarius int timeout, cent, year; 104137821Smarius 105137821Smarius sc = device_get_softc(dev); 106137821Smarius 107137821Smarius timeout = 1000000; /* XXX how long should we wait? */ 108137821Smarius 109137821Smarius /* 110137821Smarius * XXX: Use a spinlock to mutex register access and increase the 111137821Smarius * likelihood that all registers are read before an update 112137821Smarius * occurs. 113137821Smarius */ 114137821Smarius 115137821Smarius /* 116137821Smarius * If MC_REGA_UIP is 0 we have at least 244us before the next 117137821Smarius * update. If it's 1 an update is imminent. 118137821Smarius */ 119137821Smarius for (;;) { 120137821Smarius if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) 121137821Smarius break; 122137821Smarius if (--timeout < 0) { 123137821Smarius device_printf(dev, "mc146818_gettime: timeout\n"); 124137821Smarius return (EBUSY); 125137821Smarius } 126137821Smarius } 127137821Smarius 128137821Smarius#define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) 129137821Smarius 130137821Smarius ct.nsec = 0; 131137821Smarius ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 132137821Smarius ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN)); 133137821Smarius ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR)); 134137821Smarius ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1; 135137821Smarius ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM)); 136137821Smarius ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH)); 137137821Smarius year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR)); 138137821Smarius if (sc->sc_getcent) { 139137821Smarius cent = (*sc->sc_getcent)(dev); 140137821Smarius year += cent * 100; 141137821Smarius } 142137821Smarius 143137821Smarius year += sc->sc_year0; 144137821Smarius if (year < POSIX_BASE_YEAR && !(sc->sc_flag & MC146818_NO_CENT_ADJUST)) 145137821Smarius year += 100; 146137821Smarius ct.year = year; 147137821Smarius 148137821Smarius return (clock_ct_to_ts(&ct, ts)); 149137821Smarius} 150137821Smarius 151137821Smarius#ifdef notyet 152137821Smariusint 153137821Smariusmc146818_getsecs(device_t dev, int *secp) 154137821Smarius{ 155137821Smarius struct mc146818_softc *sc; 156137821Smarius int sec, timeout; 157137821Smarius 158137821Smarius sc = device_get_softc(dev); 159137821Smarius 160137821Smarius timeout = 1000000; /* XXX how long should we wait? */ 161137821Smarius 162137821Smarius for (;;) { 163137821Smarius if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) { 164137821Smarius sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 165137821Smarius break; 166137821Smarius } 167137821Smarius if (--timeout == 0) { 168137821Smarius device_printf(dev, "mc146818_getsecs: timeout\n"); 169137821Smarius return (EBUSY); 170137821Smarius } 171137821Smarius } 172137821Smarius 173137821Smarius#undef FROMREG 174137821Smarius 175137821Smarius *secp = sec; 176137821Smarius return (0); 177137821Smarius} 178137821Smarius#endif 179137821Smarius 180137821Smarius/* 181137821Smarius * Set the time of day clock based on the value of the struct timespec arg. 182137821Smarius * Return 0 on success, an error number otherwise. 183137821Smarius */ 184137821Smariusint 185137821Smariusmc146818_settime(device_t dev, struct timespec *ts) 186137821Smarius{ 187137821Smarius struct mc146818_softc *sc; 188137821Smarius struct clocktime ct; 189137821Smarius int cent, year; 190137821Smarius 191137821Smarius sc = device_get_softc(dev); 192137821Smarius 193137821Smarius /* Accuracy is only one second. */ 194137821Smarius if (ts->tv_nsec >= 500000000) 195137821Smarius ts->tv_sec++; 196137821Smarius ts->tv_nsec = 0; 197137821Smarius clock_ts_to_ct(ts, &ct); 198137821Smarius 199137821Smarius /* Disable RTC updates and interrupts (if enabled). */ 200137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, 201137821Smarius ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET)); 202137821Smarius 203137821Smarius#define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) 204137821Smarius 205137821Smarius (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec)); 206137821Smarius (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min)); 207137821Smarius (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour)); 208137821Smarius (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1)); 209137821Smarius (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day)); 210137821Smarius (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon)); 211137821Smarius 212137821Smarius year = ct.year - sc->sc_year0; 213137821Smarius if (sc->sc_setcent) { 214137821Smarius cent = year / 100; 215137821Smarius (*sc->sc_setcent)(dev, cent); 216137821Smarius year -= cent * 100; 217137821Smarius } 218137821Smarius if (year > 99 && (sc->sc_flag & MC146818_NO_CENT_ADJUST) == 0) 219137821Smarius year -= 100; 220137821Smarius (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year)); 221137821Smarius 222137821Smarius /* Reenable RTC updates and interrupts. */ 223137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 224137821Smarius 225137821Smarius#undef TOREG 226137821Smarius 227137821Smarius return (0); 228137821Smarius} 229137821Smarius 230137821Smarius#define MC_ADDR 0 231137821Smarius#define MC_DATA 1 232137821Smarius 233137821Smariusstatic u_int 234137821Smariusmc146818_def_read(device_t dev, u_int reg) 235137821Smarius{ 236137821Smarius struct mc146818_softc *sc; 237137821Smarius 238137821Smarius sc = device_get_softc(dev); 239137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 240137821Smarius return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA)); 241137821Smarius} 242137821Smarius 243137821Smariusstatic void 244137821Smariusmc146818_def_write(device_t dev, u_int reg, u_int val) 245137821Smarius{ 246137821Smarius struct mc146818_softc *sc; 247137821Smarius 248137821Smarius sc = device_get_softc(dev); 249137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 250137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val); 251137821Smarius} 252137821Smarius 253137821Smarius/* 254137821Smarius * Looks like it's common even across platforms to store the century at 255137821Smarius * 0x32 in the NVRAM of the mc146818. 256137821Smarius */ 257137821Smarius#define MC_CENT 0x32 258137821Smarius 259137821Smariusstatic u_int 260137821Smariusmc146818_def_getcent(device_t dev) 261137821Smarius{ 262137821Smarius struct mc146818_softc *sc; 263137821Smarius 264137821Smarius sc = device_get_softc(dev); 265137821Smarius return ((*sc->sc_mcread)(dev, MC_CENT)); 266137821Smarius} 267137821Smarius 268137821Smariusstatic void 269137821Smariusmc146818_def_setcent(device_t dev, u_int cent) 270137821Smarius{ 271137821Smarius struct mc146818_softc *sc; 272137821Smarius 273137821Smarius sc = device_get_softc(dev); 274137821Smarius (*sc->sc_mcwrite)(dev, MC_CENT, cent); 275137821Smarius} 276