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