1 /*- 2 * Copyright (c) 2008 Poul-Henning Kamp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "opt_isa.h" 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 #include <sys/kernel.h> 41 #include <sys/module.h> 42 43 #include <isa/rtc.h> 44 #ifdef DEV_ISA 45 #include <isa/isareg.h> 46 #include <isa/isavar.h> 47 #endif 48 49 #define RTC_LOCK mtx_lock_spin(&clock_lock) 50 #define RTC_UNLOCK mtx_unlock_spin(&clock_lock) 51 52 int atrtcclock_disable = 0; 53 54 static int rtc_reg = -1; 55 static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; 56 static u_char rtc_statusb = RTCSB_24HR; 57 58 /* 59 * RTC support routines 60 */ 61 62 int 63 rtcin(int reg) 64 { 65 u_char val; 66 67 RTC_LOCK; 68 if (rtc_reg != reg) { 69 inb(0x84); 70 outb(IO_RTC, reg); 71 rtc_reg = reg; 72 inb(0x84); 73 } 74 val = inb(IO_RTC + 1); 75 RTC_UNLOCK; 76 return (val); 77 } 78 79 void 80 writertc(int reg, u_char val) 81 { 82 83 RTC_LOCK; 84 if (rtc_reg != reg) { 85 inb(0x84); 86 outb(IO_RTC, reg); 87 rtc_reg = reg; 88 inb(0x84); 89 } 90 outb(IO_RTC + 1, val); 91 inb(0x84); 92 RTC_UNLOCK; 93 } 94 95 static __inline int 96 readrtc(int port) 97 { 98 return(bcd2bin(rtcin(port))); 99 } 100 101 void 102 atrtc_start(void) 103 { 104 105 writertc(RTC_STATUSA, rtc_statusa); 106 writertc(RTC_STATUSB, RTCSB_24HR); 107 } 108 109 void 110 atrtc_rate(unsigned rate) 111 { 112 113 rtc_statusa = RTCSA_DIVIDER | rate; 114 writertc(RTC_STATUSA, rtc_statusa); 115 } 116 117 void 118 atrtc_enable_intr(void) 119 { 120 121 rtc_statusb |= RTCSB_PINTR; 122 writertc(RTC_STATUSB, rtc_statusb); 123 rtcin(RTC_INTR); 124 } 125 126 void 127 atrtc_restore(void) 128 { 129 130 /* Restore all of the RTC's "status" (actually, control) registers. */ 131 rtcin(RTC_STATUSA); /* dummy to get rtc_reg set */ 132 writertc(RTC_STATUSB, RTCSB_24HR); 133 writertc(RTC_STATUSA, rtc_statusa); 134 writertc(RTC_STATUSB, rtc_statusb); 135 rtcin(RTC_INTR); 136 } 137 138 int 139 atrtc_setup_clock(void) 140 { 141 int diag; 142 143 if (atrtcclock_disable) 144 return (0); 145 146 diag = rtcin(RTC_DIAG); 147 if (diag != 0) { 148 printf("RTC BIOS diagnostic error %b\n", 149 diag, RTCDG_BITS); 150 return (0); 151 } 152 153 stathz = RTC_NOPROFRATE; 154 profhz = RTC_PROFRATE; 155 156 return (1); 157 } 158 159 /********************************************************************** 160 * RTC driver for subr_rtc 161 */ 162 163 #include "clock_if.h" 164 165 #include <sys/rman.h> 166 167 struct atrtc_softc { 168 int port_rid, intr_rid; 169 struct resource *port_res; 170 struct resource *intr_res; 171 }; 172 173 /* 174 * Attach to the ISA PnP descriptors for the timer and realtime clock. 175 */ 176 static struct isa_pnp_id atrtc_ids[] = { 177 { 0x000bd041 /* PNP0B00 */, "AT realtime clock" }, 178 { 0 } 179 }; 180 181 static int 182 atrtc_probe(device_t dev) 183 { 184 int result; 185 186 device_set_desc(dev, "AT Real Time Clock"); 187 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atrtc_ids); 188 /* ENXIO if wrong PnP-ID, ENOENT ifno PnP-ID, zero if good PnP-iD */ 189 if (result != ENOENT) 190 return(result); 191 /* All PC's have an RTC, and we're hosed without it, so... */ 192 return (BUS_PROBE_LOW_PRIORITY); 193 } 194 195 static int 196 atrtc_attach(device_t dev) 197 { 198 struct atrtc_softc *sc; 199 int i; 200 201 /* 202 * Not that we need them or anything, but grab our resources 203 * so they show up, correctly attributed, in the big picture. 204 */ 205 206 sc = device_get_softc(dev); 207 if (!(sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, 208 &sc->port_rid, IO_RTC, IO_RTC + 1, 2, RF_ACTIVE))) 209 device_printf(dev,"Warning: Couldn't map I/O.\n"); 210 if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, 211 &sc->intr_rid, 8, 8, 1, RF_ACTIVE))) 212 device_printf(dev,"Warning: Couldn't map Interrupt.\n"); 213 clock_register(dev, 1000000); 214 if (resource_int_value("atrtc", 0, "clock", &i) == 0 && i == 0) 215 atrtcclock_disable = 1; 216 return(0); 217 } 218 219 static int 220 atrtc_resume(device_t dev) 221 { 222 223 atrtc_restore(); 224 return(0); 225 } 226 227 static int 228 atrtc_settime(device_t dev __unused, struct timespec *ts) 229 { 230 struct clocktime ct; 231 232 clock_ts_to_ct(ts, &ct); 233 234 /* Disable RTC updates and interrupts. */ 235 writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); 236 237 writertc(RTC_SEC, bin2bcd(ct.sec)); /* Write back Seconds */ 238 writertc(RTC_MIN, bin2bcd(ct.min)); /* Write back Minutes */ 239 writertc(RTC_HRS, bin2bcd(ct.hour)); /* Write back Hours */ 240 241 writertc(RTC_WDAY, ct.dow + 1); /* Write back Weekday */ 242 writertc(RTC_DAY, bin2bcd(ct.day)); /* Write back Day */ 243 writertc(RTC_MONTH, bin2bcd(ct.mon)); /* Write back Month */ 244 writertc(RTC_YEAR, bin2bcd(ct.year % 100)); /* Write back Year */ 245 #ifdef USE_RTC_CENTURY 246 writertc(RTC_CENTURY, bin2bcd(ct.year / 100)); /* ... and Century */ 247 #endif 248 249 /* Reenable RTC updates and interrupts. */ 250 writertc(RTC_STATUSB, rtc_statusb); 251 rtcin(RTC_INTR); 252 return (0); 253 } 254 255 static int 256 atrtc_gettime(device_t dev, struct timespec *ts) 257 { 258 struct clocktime ct; 259 int s; 260 261 /* Look if we have a RTC present and the time is valid */ 262 if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) { 263 device_printf(dev, "WARNING: Battery failure indication\n"); 264 return (EINVAL); 265 } 266 267 /* wait for time update to complete */ 268 /* If RTCSA_TUP is zero, we have at least 244us before next update */ 269 s = splhigh(); 270 while (rtcin(RTC_STATUSA) & RTCSA_TUP) { 271 splx(s); 272 s = splhigh(); 273 } 274 ct.nsec = 0; 275 ct.sec = readrtc(RTC_SEC); 276 ct.min = readrtc(RTC_MIN); 277 ct.hour = readrtc(RTC_HRS); 278 ct.day = readrtc(RTC_DAY); 279 ct.dow = readrtc(RTC_WDAY) - 1; 280 ct.mon = readrtc(RTC_MONTH); 281 ct.year = readrtc(RTC_YEAR); 282 #ifdef USE_RTC_CENTURY 283 ct.year += readrtc(RTC_CENTURY) * 100; 284 #else 285 ct.year += 2000; 286 #endif 287 /* Set dow = -1 because some clocks don't set it correctly. */ 288 ct.dow = -1; 289 return (clock_ct_to_ts(&ct, ts)); 290 } 291 292 static device_method_t atrtc_methods[] = { 293 /* Device interface */ 294 DEVMETHOD(device_probe, atrtc_probe), 295 DEVMETHOD(device_attach, atrtc_attach), 296 DEVMETHOD(device_detach, bus_generic_detach), 297 DEVMETHOD(device_shutdown, bus_generic_shutdown), 298 DEVMETHOD(device_suspend, bus_generic_suspend), 299 /* XXX stop statclock? */ 300 DEVMETHOD(device_resume, atrtc_resume), 301 302 /* clock interface */ 303 DEVMETHOD(clock_gettime, atrtc_gettime), 304 DEVMETHOD(clock_settime, atrtc_settime), 305 306 { 0, 0 } 307 }; 308 309 static driver_t atrtc_driver = { 310 "atrtc", 311 atrtc_methods, 312 sizeof(struct atrtc_softc), 313 }; 314 315 static devclass_t atrtc_devclass; 316 317 DRIVER_MODULE(atrtc, isa, atrtc_driver, atrtc_devclass, 0, 0); 318 DRIVER_MODULE(atrtc, acpi, atrtc_driver, atrtc_devclass, 0, 0); 319 320 #include "opt_ddb.h" 321 #ifdef DDB 322 #include <ddb/ddb.h> 323 324 DB_SHOW_COMMAND(rtc, rtc) 325 { 326 printf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n", 327 rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY), 328 rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC), 329 rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR)); 330 } 331 #endif /* DDB */ 332