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