mc146818.c revision 170844
1139749Simp/*- 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 170844 2007-06-16 23:10:00Z 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> 40146417Smarius#include <sys/lock.h> 41146417Smarius#include <sys/mutex.h> 42137821Smarius 43137821Smarius#include <machine/bus.h> 44137821Smarius 45137821Smarius#include <dev/mc146818/mc146818reg.h> 46137821Smarius#include <dev/mc146818/mc146818var.h> 47137821Smarius 48137821Smarius#include "clock_if.h" 49137821Smarius 50137821Smariusstatic u_int mc146818_def_getcent(device_t); 51137821Smariusstatic void mc146818_def_setcent(device_t, u_int); 52137821Smarius 53137821Smariusint 54137821Smariusmc146818_attach(device_t dev) 55137821Smarius{ 56137821Smarius struct mc146818_softc *sc; 57137821Smarius 58137821Smarius sc = device_get_softc(dev); 59137821Smarius 60146417Smarius if (mtx_initialized(&sc->sc_mtx) == 0) { 61146417Smarius device_printf(dev, "%s: mutex not initialized\n", __func__); 62146417Smarius return (ENXIO); 63146417Smarius } 64146417Smarius 65137821Smarius if (sc->sc_mcread == NULL) 66137821Smarius sc->sc_mcread = mc146818_def_read; 67137821Smarius if (sc->sc_mcwrite == NULL) 68137821Smarius sc->sc_mcwrite = mc146818_def_write; 69137821Smarius 70137821Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 71137821Smarius /* 72137821Smarius * Note that setting MC146818_NO_CENT_ADJUST means that 73137821Smarius * the century has to be stored in NVRAM somewhere. 74137821Smarius */ 75137821Smarius if (sc->sc_getcent == NULL) 76137821Smarius sc->sc_getcent = mc146818_def_getcent; 77137821Smarius if (sc->sc_setcent == NULL) 78137821Smarius sc->sc_setcent = mc146818_def_setcent; 79137821Smarius } 80137821Smarius 81146982Smarius mtx_lock_spin(&sc->sc_mtx); 82137821Smarius if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) { 83146982Smarius mtx_unlock_spin(&sc->sc_mtx); 84146417Smarius device_printf(dev, "%s: battery low\n", __func__); 85137821Smarius return (ENXIO); 86137821Smarius } 87137821Smarius 88137821Smarius sc->sc_rega = MC_BASE_32_KHz; 89137821Smarius (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega); 90137821Smarius 91137821Smarius sc->sc_regb = 0; 92137821Smarius sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY; 93137821Smarius sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR; 94137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 95146982Smarius mtx_unlock_spin(&sc->sc_mtx); 96137821Smarius 97137821Smarius clock_register(dev, 1000000); /* 1 second resolution. */ 98137821Smarius 99137821Smarius return (0); 100137821Smarius} 101137821Smarius 102137821Smarius/* 103137821Smarius * Get time of day and convert it to a struct timespec. 104137821Smarius * Return 0 on success, an error number otherwise. 105137821Smarius */ 106137821Smariusint 107137821Smariusmc146818_gettime(device_t dev, struct timespec *ts) 108137821Smarius{ 109137821Smarius struct mc146818_softc *sc; 110137821Smarius struct clocktime ct; 111137821Smarius int timeout, cent, year; 112137821Smarius 113137821Smarius sc = device_get_softc(dev); 114137821Smarius 115137821Smarius timeout = 1000000; /* XXX how long should we wait? */ 116137821Smarius 117137821Smarius /* 118137821Smarius * If MC_REGA_UIP is 0 we have at least 244us before the next 119137821Smarius * update. If it's 1 an update is imminent. 120137821Smarius */ 121137821Smarius for (;;) { 122146982Smarius mtx_lock_spin(&sc->sc_mtx); 123137821Smarius if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) 124137821Smarius break; 125146982Smarius mtx_unlock_spin(&sc->sc_mtx); 126137821Smarius if (--timeout < 0) { 127146417Smarius device_printf(dev, "%s: timeout\n", __func__); 128137821Smarius return (EBUSY); 129137821Smarius } 130137821Smarius } 131137821Smarius 132137821Smarius#define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) 133137821Smarius 134137821Smarius ct.nsec = 0; 135137821Smarius ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 136137821Smarius ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN)); 137137821Smarius ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR)); 138146417Smarius /* Map dow from 1 - 7 to 0 - 6. */ 139137821Smarius ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1; 140137821Smarius ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM)); 141137821Smarius ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH)); 142137821Smarius year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR)); 143146417Smarius year += sc->sc_year0; 144146417Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 145137821Smarius cent = (*sc->sc_getcent)(dev); 146137821Smarius year += cent * 100; 147146417Smarius } else if (year < POSIX_BASE_YEAR) 148146417Smarius year += 100; 149146982Smarius mtx_unlock_spin(&sc->sc_mtx); 150137821Smarius 151137821Smarius ct.year = year; 152137821Smarius 153137821Smarius return (clock_ct_to_ts(&ct, ts)); 154137821Smarius} 155137821Smarius 156137821Smarius#ifdef notyet 157137821Smariusint 158137821Smariusmc146818_getsecs(device_t dev, int *secp) 159137821Smarius{ 160137821Smarius struct mc146818_softc *sc; 161137821Smarius int sec, timeout; 162137821Smarius 163137821Smarius sc = device_get_softc(dev); 164137821Smarius 165137821Smarius timeout = 1000000; /* XXX how long should we wait? */ 166137821Smarius 167137821Smarius for (;;) { 168146982Smarius mtx_lock_spin(&sc->sc_mtx); 169137821Smarius if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) { 170137821Smarius sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 171146982Smarius mtx_unlock_spin(&sc->sc_mtx); 172137821Smarius break; 173137821Smarius } 174146982Smarius mtx_unlock_spin(&sc->sc_mtx); 175137821Smarius if (--timeout == 0) { 176146417Smarius device_printf(dev, "%s: timeout\n", __func__); 177137821Smarius return (EBUSY); 178137821Smarius } 179137821Smarius } 180137821Smarius 181137821Smarius#undef FROMREG 182137821Smarius 183137821Smarius *secp = sec; 184137821Smarius return (0); 185137821Smarius} 186137821Smarius#endif 187137821Smarius 188137821Smarius/* 189137821Smarius * Set the time of day clock based on the value of the struct timespec arg. 190137821Smarius * Return 0 on success, an error number otherwise. 191137821Smarius */ 192137821Smariusint 193137821Smariusmc146818_settime(device_t dev, struct timespec *ts) 194137821Smarius{ 195137821Smarius struct mc146818_softc *sc; 196137821Smarius struct clocktime ct; 197137821Smarius int cent, year; 198137821Smarius 199137821Smarius sc = device_get_softc(dev); 200137821Smarius 201137821Smarius /* Accuracy is only one second. */ 202137821Smarius if (ts->tv_nsec >= 500000000) 203137821Smarius ts->tv_sec++; 204137821Smarius ts->tv_nsec = 0; 205137821Smarius clock_ts_to_ct(ts, &ct); 206137821Smarius 207146982Smarius mtx_lock_spin(&sc->sc_mtx); 208137821Smarius /* Disable RTC updates and interrupts (if enabled). */ 209137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, 210137821Smarius ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET)); 211137821Smarius 212137821Smarius#define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) 213137821Smarius 214137821Smarius (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec)); 215137821Smarius (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min)); 216137821Smarius (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour)); 217146417Smarius /* Map dow from 0 - 6 to 1 - 7. */ 218137821Smarius (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1)); 219137821Smarius (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day)); 220137821Smarius (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon)); 221137821Smarius 222137821Smarius year = ct.year - sc->sc_year0; 223146417Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 224137821Smarius cent = year / 100; 225137821Smarius (*sc->sc_setcent)(dev, cent); 226137821Smarius year -= cent * 100; 227146417Smarius } else if (year > 99) 228137821Smarius year -= 100; 229137821Smarius (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year)); 230137821Smarius 231137821Smarius /* Reenable RTC updates and interrupts. */ 232137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 233146982Smarius mtx_unlock_spin(&sc->sc_mtx); 234137821Smarius 235137821Smarius#undef TOREG 236137821Smarius 237137821Smarius return (0); 238137821Smarius} 239137821Smarius 240137821Smarius#define MC_ADDR 0 241137821Smarius#define MC_DATA 1 242137821Smarius 243170844Smariusu_int 244137821Smariusmc146818_def_read(device_t dev, u_int reg) 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 return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA)); 251137821Smarius} 252137821Smarius 253170844Smariusvoid 254137821Smariusmc146818_def_write(device_t dev, u_int reg, u_int val) 255137821Smarius{ 256137821Smarius struct mc146818_softc *sc; 257137821Smarius 258137821Smarius sc = device_get_softc(dev); 259137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 260137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val); 261137821Smarius} 262137821Smarius 263137821Smarius/* 264137821Smarius * Looks like it's common even across platforms to store the century at 265137821Smarius * 0x32 in the NVRAM of the mc146818. 266137821Smarius */ 267137821Smarius#define MC_CENT 0x32 268137821Smarius 269137821Smariusstatic u_int 270137821Smariusmc146818_def_getcent(device_t dev) 271137821Smarius{ 272137821Smarius struct mc146818_softc *sc; 273137821Smarius 274137821Smarius sc = device_get_softc(dev); 275137821Smarius return ((*sc->sc_mcread)(dev, MC_CENT)); 276137821Smarius} 277137821Smarius 278137821Smariusstatic void 279137821Smariusmc146818_def_setcent(device_t dev, u_int cent) 280137821Smarius{ 281137821Smarius struct mc146818_softc *sc; 282137821Smarius 283137821Smarius sc = device_get_softc(dev); 284137821Smarius (*sc->sc_mcwrite)(dev, MC_CENT, cent); 285137821Smarius} 286