mc146818.c revision 201008
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 201008 2009-12-25 22:53:46Z 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); 52 53int 54mc146818_attach(device_t dev) 55{ 56 struct mc146818_softc *sc; 57 58 sc = device_get_softc(dev); 59 60 if (mtx_initialized(&sc->sc_mtx) == 0) { 61 device_printf(dev, "%s: mutex not initialized\n", __func__); 62 return (ENXIO); 63 } 64 65 if (sc->sc_mcread == NULL) 66 sc->sc_mcread = mc146818_def_read; 67 if (sc->sc_mcwrite == NULL) 68 sc->sc_mcwrite = mc146818_def_write; 69 70 if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 71 /* 72 * Note that setting MC146818_NO_CENT_ADJUST means that 73 * the century has to be stored in NVRAM somewhere. 74 */ 75 if (sc->sc_getcent == NULL) 76 sc->sc_getcent = mc146818_def_getcent; 77 if (sc->sc_setcent == NULL) 78 sc->sc_setcent = mc146818_def_setcent; 79 } 80 81 mtx_lock_spin(&sc->sc_mtx); 82 if (!(*sc->sc_mcread)(dev, MC_REGD) & MC_REGD_VRT) { 83 mtx_unlock_spin(&sc->sc_mtx); 84 device_printf(dev, "%s: battery low\n", __func__); 85 return (ENXIO); 86 } 87 88 sc->sc_rega = MC_BASE_32_KHz; 89 (*sc->sc_mcwrite)(dev, MC_REGA, sc->sc_rega); 90 91 sc->sc_regb = 0; 92 sc->sc_regb |= (sc->sc_flag & MC146818_BCD) ? 0 : MC_REGB_BINARY; 93 sc->sc_regb |= (sc->sc_flag & MC146818_12HR) ? 0 : MC_REGB_24HR; 94 (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 95 mtx_unlock_spin(&sc->sc_mtx); 96 97 clock_register(dev, 1000000); /* 1 second resolution */ 98 99 return (0); 100} 101 102/* 103 * Get time of day and convert it to a struct timespec. 104 * Return 0 on success, an error number otherwise. 105 */ 106int 107mc146818_gettime(device_t dev, struct timespec *ts) 108{ 109 struct mc146818_softc *sc; 110 struct clocktime ct; 111 int timeout, cent, year; 112 113 sc = device_get_softc(dev); 114 115 timeout = 1000000; /* XXX how long should we wait? */ 116 117 /* 118 * If MC_REGA_UIP is 0 we have at least 244us before the next 119 * update. If it's 1 an update is imminent. 120 */ 121 for (;;) { 122 mtx_lock_spin(&sc->sc_mtx); 123 if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) 124 break; 125 mtx_unlock_spin(&sc->sc_mtx); 126 if (--timeout < 0) { 127 device_printf(dev, "%s: timeout\n", __func__); 128 return (EBUSY); 129 } 130 } 131 132#define FROMREG(x) ((sc->sc_flag & MC146818_BCD) ? FROMBCD(x) : (x)) 133 134 ct.nsec = 0; 135 ct.sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 136 ct.min = FROMREG((*sc->sc_mcread)(dev, MC_MIN)); 137 ct.hour = FROMREG((*sc->sc_mcread)(dev, MC_HOUR)); 138 /* Map dow from 1 - 7 to 0 - 6. */ 139 ct.dow = FROMREG((*sc->sc_mcread)(dev, MC_DOW)) - 1; 140 ct.day = FROMREG((*sc->sc_mcread)(dev, MC_DOM)); 141 ct.mon = FROMREG((*sc->sc_mcread)(dev, MC_MONTH)); 142 year = FROMREG((*sc->sc_mcread)(dev, MC_YEAR)); 143 year += sc->sc_year0; 144 if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 145 cent = (*sc->sc_getcent)(dev); 146 year += cent * 100; 147 } else if (year < POSIX_BASE_YEAR) 148 year += 100; 149 mtx_unlock_spin(&sc->sc_mtx); 150 151 ct.year = year; 152 153 return (clock_ct_to_ts(&ct, ts)); 154} 155 156#ifdef notyet 157int 158mc146818_getsecs(device_t dev, int *secp) 159{ 160 struct mc146818_softc *sc; 161 int sec, timeout; 162 163 sc = device_get_softc(dev); 164 165 timeout = 1000000; /* XXX how long should we wait? */ 166 167 for (;;) { 168 mtx_lock_spin(&sc->sc_mtx); 169 if (!((*sc->sc_mcread)(dev, MC_REGA) & MC_REGA_UIP)) { 170 sec = FROMREG((*sc->sc_mcread)(dev, MC_SEC)); 171 mtx_unlock_spin(&sc->sc_mtx); 172 break; 173 } 174 mtx_unlock_spin(&sc->sc_mtx); 175 if (--timeout == 0) { 176 device_printf(dev, "%s: timeout\n", __func__); 177 return (EBUSY); 178 } 179 } 180 181#undef FROMREG 182 183 *secp = sec; 184 return (0); 185} 186#endif 187 188/* 189 * Set the time of day clock based on the value of the struct timespec arg. 190 * Return 0 on success, an error number otherwise. 191 */ 192int 193mc146818_settime(device_t dev, struct timespec *ts) 194{ 195 struct mc146818_softc *sc; 196 struct clocktime ct; 197 int cent, year; 198 199 sc = device_get_softc(dev); 200 201 /* Accuracy is only one second. */ 202 if (ts->tv_nsec >= 500000000) 203 ts->tv_sec++; 204 ts->tv_nsec = 0; 205 clock_ts_to_ct(ts, &ct); 206 207 mtx_lock_spin(&sc->sc_mtx); 208 /* Disable RTC updates and interrupts (if enabled). */ 209 (*sc->sc_mcwrite)(dev, MC_REGB, 210 ((sc->sc_regb & (MC_REGB_BINARY | MC_REGB_24HR)) | MC_REGB_SET)); 211 212#define TOREG(x) ((sc->sc_flag & MC146818_BCD) ? TOBCD(x) : (x)) 213 214 (*sc->sc_mcwrite)(dev, MC_SEC, TOREG(ct.sec)); 215 (*sc->sc_mcwrite)(dev, MC_MIN, TOREG(ct.min)); 216 (*sc->sc_mcwrite)(dev, MC_HOUR, TOREG(ct.hour)); 217 /* Map dow from 0 - 6 to 1 - 7. */ 218 (*sc->sc_mcwrite)(dev, MC_DOW, TOREG(ct.dow + 1)); 219 (*sc->sc_mcwrite)(dev, MC_DOM, TOREG(ct.day)); 220 (*sc->sc_mcwrite)(dev, MC_MONTH, TOREG(ct.mon)); 221 222 year = ct.year - sc->sc_year0; 223 if (sc->sc_flag & MC146818_NO_CENT_ADJUST) { 224 cent = year / 100; 225 (*sc->sc_setcent)(dev, cent); 226 year -= cent * 100; 227 } else if (year > 99) 228 year -= 100; 229 (*sc->sc_mcwrite)(dev, MC_YEAR, TOREG(year)); 230 231 /* Reenable RTC updates and interrupts. */ 232 (*sc->sc_mcwrite)(dev, MC_REGB, sc->sc_regb); 233 mtx_unlock_spin(&sc->sc_mtx); 234 235#undef TOREG 236 237 return (0); 238} 239 240#define MC_ADDR 0 241#define MC_DATA 1 242 243u_int 244mc146818_def_read(device_t dev, u_int reg) 245{ 246 struct mc146818_softc *sc; 247 248 sc = device_get_softc(dev); 249 bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 250 return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, MC_DATA)); 251} 252 253void 254mc146818_def_write(device_t dev, u_int reg, u_int val) 255{ 256 struct mc146818_softc *sc; 257 258 sc = device_get_softc(dev); 259 bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_ADDR, reg); 260 bus_space_write_1(sc->sc_bst, sc->sc_bsh, MC_DATA, val); 261} 262 263#undef MC_ADDR 264#undef MC_DATA 265 266/* 267 * Looks like it's common even across platforms to store the century at 268 * 0x32 in the NVRAM of the mc146818. 269 */ 270#define MC_CENT 0x32 271 272static u_int 273mc146818_def_getcent(device_t dev) 274{ 275 struct mc146818_softc *sc; 276 277 sc = device_get_softc(dev); 278 return ((*sc->sc_mcread)(dev, MC_CENT)); 279} 280 281static void 282mc146818_def_setcent(device_t dev, u_int cent) 283{ 284 struct mc146818_softc *sc; 285 286 sc = device_get_softc(dev); 287 (*sc->sc_mcwrite)(dev, MC_CENT, cent); 288} 289 290#undef MC_CENT 291