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 * 13137821Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14137821Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15137821Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16137821Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17137821Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18137821Smarius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19137821Smarius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20137821Smarius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21137821Smarius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22137821Smarius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23137821Smarius * 24201009Smarius * $NetBSD: mc146818.c,v 1.16 2008/05/14 13:29:28 tsutsui Exp $ 25137821Smarius */ 26137821Smarius 27137821Smarius#include <sys/cdefs.h> 28137821Smarius__FBSDID("$FreeBSD: releng/10.3/sys/dev/mc146818/mc146818.c 201009 2009-12-25 22:58:43Z marius $"); 29137821Smarius 30137821Smarius/* 31137821Smarius * mc146818 and compatible time of day chip subroutines 32137821Smarius */ 33137821Smarius 34137821Smarius#include <sys/param.h> 35137821Smarius#include <sys/systm.h> 36137821Smarius#include <sys/bus.h> 37137821Smarius#include <sys/clock.h> 38146417Smarius#include <sys/lock.h> 39146417Smarius#include <sys/mutex.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); 50137821Smarius 51137821Smariusint 52137821Smariusmc146818_attach(device_t dev) 53137821Smarius{ 54137821Smarius struct mc146818_softc *sc; 55137821Smarius 56137821Smarius sc = device_get_softc(dev); 57137821Smarius 58146417Smarius if (mtx_initialized(&sc->sc_mtx) == 0) { 59146417Smarius device_printf(dev, "%s: mutex not initialized\n", __func__); 60146417Smarius return (ENXIO); 61146417Smarius } 62146417Smarius 63137821Smarius if (sc->sc_mcread == NULL) 64137821Smarius sc->sc_mcread = mc146818_def_read; 65137821Smarius if (sc->sc_mcwrite == NULL) 66137821Smarius sc->sc_mcwrite = mc146818_def_write; 67137821Smarius 68137821Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 69137821Smarius /* 70137821Smarius * Note that setting MC146818_NO_CENT_ADJUST means that 71137821Smarius * the century has to be stored in NVRAM somewhere. 72137821Smarius */ 73137821Smarius if (sc->sc_getcent == NULL) 74137821Smarius sc->sc_getcent = mc146818_def_getcent; 75137821Smarius if (sc->sc_setcent == NULL) 76137821Smarius sc->sc_setcent = mc146818_def_setcent; 77137821Smarius } 78137821Smarius 79146982Smarius mtx_lock_spin(&sc->sc_mtx); 80137821Smarius if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) { 81146982Smarius mtx_unlock_spin(&sc->sc_mtx); 82146417Smarius device_printf(dev, "%s: battery low\n", __func__); 83137821Smarius return (ENXIO); 84137821Smarius } 85137821Smarius 86137821Smarius sc->sc_rega = MC_BASE_32_KHz; 87137821Smarius (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega); 88137821Smarius 89137821Smarius sc->sc_regb = 0; 90137821Smarius sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY; 91137821Smarius sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR; 92137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 93146982Smarius mtx_unlock_spin(&sc->sc_mtx); 94137821Smarius 95201008Smarius clock_register(dev, 1000000); /* 1 second resolution */ 96137821Smarius 97137821Smarius return (0); 98137821Smarius} 99137821Smarius 100137821Smarius/* 101137821Smarius * Get time of day and convert it to a struct timespec. 102137821Smarius * Return 0 on success, an error number otherwise. 103137821Smarius */ 104137821Smariusint 105137821Smariusmc146818_gettime(device_t dev, struct timespec *ts) 106137821Smarius{ 107137821Smarius struct mc146818_softc *sc; 108137821Smarius struct clocktime ct; 109137821Smarius int timeout, cent, year; 110137821Smarius 111137821Smarius sc = device_get_softc(dev); 112137821Smarius 113137821Smarius timeout = 1000000; /* XXX how long should we wait? */ 114137821Smarius 115137821Smarius /* 116137821Smarius * If MC_REGA_UIP is 0 we have at least 244us before the next 117201008Smarius * update. If it's 1 an update is imminent. 118137821Smarius */ 119137821Smarius for (;;) { 120146982Smarius mtx_lock_spin(&sc->sc_mtx); 121137821Smarius if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) 122137821Smarius break; 123146982Smarius mtx_unlock_spin(&sc->sc_mtx); 124137821Smarius if (--timeout < 0) { 125146417Smarius device_printf(dev, "%s: timeout\n", __func__); 126137821Smarius return (EBUSY); 127137821Smarius } 128137821Smarius } 129137821Smarius 130137821Smarius#define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) 131137821Smarius 132137821Smarius ct.nsec = 0; 133137821Smarius ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 134137821Smarius ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN)); 135137821Smarius ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR)); 136146417Smarius /* Map dow from 1 - 7 to 0 - 6. */ 137137821Smarius ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1; 138137821Smarius ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM)); 139137821Smarius ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH)); 140137821Smarius year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR)); 141146417Smarius year += sc->sc_year0; 142146417Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 143137821Smarius cent = (*sc->sc_getcent)(dev); 144137821Smarius year += cent * 100; 145146417Smarius } else if (year < POSIX_BASE_YEAR) 146146417Smarius year += 100; 147146982Smarius mtx_unlock_spin(&sc->sc_mtx); 148137821Smarius 149137821Smarius ct.year = year; 150137821Smarius 151137821Smarius return (clock_ct_to_ts(&ct, ts)); 152137821Smarius} 153137821Smarius 154137821Smarius#ifdef notyet 155137821Smariusint 156137821Smariusmc146818_getsecs(device_t dev, int *secp) 157137821Smarius{ 158137821Smarius struct mc146818_softc *sc; 159137821Smarius int sec, timeout; 160137821Smarius 161137821Smarius sc = device_get_softc(dev); 162137821Smarius 163137821Smarius timeout = 1000000; /* XXX how long should we wait? */ 164137821Smarius 165137821Smarius for (;;) { 166146982Smarius mtx_lock_spin(&sc->sc_mtx); 167137821Smarius if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) { 168137821Smarius sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 169146982Smarius mtx_unlock_spin(&sc->sc_mtx); 170137821Smarius break; 171137821Smarius } 172146982Smarius mtx_unlock_spin(&sc->sc_mtx); 173137821Smarius if (--timeout == 0) { 174146417Smarius device_printf(dev, "%s: timeout\n", __func__); 175137821Smarius return (EBUSY); 176137821Smarius } 177137821Smarius } 178137821Smarius 179137821Smarius#undef FROMREG 180137821Smarius 181137821Smarius *secp = sec; 182137821Smarius return (0); 183137821Smarius} 184137821Smarius#endif 185137821Smarius 186137821Smarius/* 187137821Smarius * Set the time of day clock based on the value of the struct timespec arg. 188137821Smarius * Return 0 on success, an error number otherwise. 189137821Smarius */ 190137821Smariusint 191137821Smariusmc146818_settime(device_t dev, struct timespec *ts) 192137821Smarius{ 193137821Smarius struct mc146818_softc *sc; 194137821Smarius struct clocktime ct; 195137821Smarius int cent, year; 196137821Smarius 197137821Smarius sc = device_get_softc(dev); 198137821Smarius 199137821Smarius /* Accuracy is only one second. */ 200137821Smarius if (ts->tv_nsec >= 500000000) 201137821Smarius ts->tv_sec++; 202137821Smarius ts->tv_nsec = 0; 203137821Smarius clock_ts_to_ct(ts, &ct); 204137821Smarius 205146982Smarius mtx_lock_spin(&sc->sc_mtx); 206137821Smarius /* Disable RTC updates and interrupts (if enabled). */ 207137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, 208137821Smarius ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET)); 209137821Smarius 210137821Smarius#define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) 211137821Smarius 212137821Smarius (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec)); 213137821Smarius (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min)); 214137821Smarius (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour)); 215146417Smarius /* Map dow from 0 - 6 to 1 - 7. */ 216137821Smarius (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1)); 217137821Smarius (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day)); 218137821Smarius (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon)); 219137821Smarius 220137821Smarius year = ct.year - sc->sc_year0; 221146417Smarius if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 222137821Smarius cent = year / 100; 223137821Smarius (*sc->sc_setcent)(dev, cent); 224137821Smarius year -= cent * 100; 225146417Smarius } else if (year > 99) 226137821Smarius year -= 100; 227137821Smarius (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year)); 228137821Smarius 229137821Smarius /* Reenable RTC updates and interrupts. */ 230137821Smarius (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 231146982Smarius mtx_unlock_spin(&sc->sc_mtx); 232137821Smarius 233137821Smarius#undef TOREG 234137821Smarius 235137821Smarius return (0); 236137821Smarius} 237137821Smarius 238137821Smarius#define MC_ADDR 0 239137821Smarius#define MC_DATA 1 240137821Smarius 241170844Smariusu_int 242137821Smariusmc146818_def_read(device_t dev, u_int reg) 243137821Smarius{ 244137821Smarius struct mc146818_softc *sc; 245137821Smarius 246137821Smarius sc = device_get_softc(dev); 247137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 248137821Smarius return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA)); 249137821Smarius} 250137821Smarius 251170844Smariusvoid 252137821Smariusmc146818_def_write(device_t dev, u_int reg, u_int val) 253137821Smarius{ 254137821Smarius struct mc146818_softc *sc; 255137821Smarius 256137821Smarius sc = device_get_softc(dev); 257137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 258137821Smarius bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val); 259137821Smarius} 260137821Smarius 261201008Smarius#undef MC_ADDR 262201008Smarius#undef MC_DATA 263201008Smarius 264137821Smarius/* 265137821Smarius * Looks like it's common even across platforms to store the century at 266137821Smarius * 0x32 in the NVRAM of the mc146818. 267137821Smarius */ 268137821Smarius#define MC_CENT 0x32 269137821Smarius 270137821Smariusstatic u_int 271137821Smariusmc146818_def_getcent(device_t dev) 272137821Smarius{ 273137821Smarius struct mc146818_softc *sc; 274137821Smarius 275137821Smarius sc = device_get_softc(dev); 276137821Smarius return ((*sc->sc_mcread)(dev, MC_CENT)); 277137821Smarius} 278137821Smarius 279137821Smariusstatic void 280137821Smariusmc146818_def_setcent(device_t dev, u_int cent) 281137821Smarius{ 282137821Smarius struct mc146818_softc *sc; 283137821Smarius 284137821Smarius sc = device_get_softc(dev); 285137821Smarius (*sc->sc_mcwrite)(dev, MC_CENT, cent); 286137821Smarius} 287201008Smarius 288201008Smarius#undef MC_CENT 289