Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* mc146818rtc.h - register definitions for the Real-Time-Clock / CMOS RAM |
| 2 | * Copyright Torsten Duwe <duwe@informatik.uni-erlangen.de> 1993 |
| 3 | * derived from Data Sheet, Copyright Motorola 1984 (!). |
| 4 | * It was written to be part of the Linux operating system. |
| 5 | */ |
| 6 | /* permission is hereby granted to copy, modify and redistribute this code |
| 7 | * in terms of the GNU Library General Public License, Version 2 or later, |
| 8 | * at your option. |
| 9 | */ |
| 10 | |
| 11 | #ifndef _MC146818RTC_H |
| 12 | #define _MC146818RTC_H |
| 13 | |
| 14 | #include <asm/io.h> |
| 15 | #include <linux/rtc.h> /* get the user-level API */ |
| 16 | #include <asm/mc146818rtc.h> /* register access macros */ |
Arnd Bergmann | 5ab788d | 2016-05-30 20:57:50 +0200 | [diff] [blame^] | 17 | #include <linux/bcd.h> |
| 18 | #include <linux/delay.h> |
| 19 | |
| 20 | #ifdef CONFIG_ACPI |
| 21 | #include <linux/acpi.h> |
| 22 | #endif |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | |
| 24 | #ifdef __KERNEL__ |
| 25 | #include <linux/spinlock.h> /* spinlock_t */ |
| 26 | extern spinlock_t rtc_lock; /* serialize CMOS RAM access */ |
David Brownell | 7be2c7c | 2007-02-10 01:46:02 -0800 | [diff] [blame] | 27 | |
| 28 | /* Some RTCs extend the mc146818 register set to support alarms of more |
| 29 | * than 24 hours in the future; or dates that include a century code. |
| 30 | * This platform_data structure can pass this information to the driver. |
David Brownell | 87ac84f | 2007-05-08 00:34:00 -0700 | [diff] [blame] | 31 | * |
| 32 | * Also, some platforms need suspend()/resume() hooks to kick in special |
| 33 | * handling of wake alarms, e.g. activating ACPI BIOS hooks or setting up |
| 34 | * a separate wakeup alarm used by some almost-clone chips. |
David Brownell | 7be2c7c | 2007-02-10 01:46:02 -0800 | [diff] [blame] | 35 | */ |
| 36 | struct cmos_rtc_board_info { |
David Brownell | 87ac84f | 2007-05-08 00:34:00 -0700 | [diff] [blame] | 37 | void (*wake_on)(struct device *dev); |
| 38 | void (*wake_off)(struct device *dev); |
| 39 | |
Maciej W. Rozycki | 31632db | 2014-06-06 14:35:49 -0700 | [diff] [blame] | 40 | u32 flags; |
| 41 | #define CMOS_RTC_FLAGS_NOFREQ (1 << 0) |
| 42 | int address_space; |
| 43 | |
David Brownell | 7be2c7c | 2007-02-10 01:46:02 -0800 | [diff] [blame] | 44 | u8 rtc_day_alarm; /* zero, or register index */ |
| 45 | u8 rtc_mon_alarm; /* zero, or register index */ |
| 46 | u8 rtc_century; /* zero, or register index */ |
| 47 | }; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | #endif |
| 49 | |
| 50 | /********************************************************************** |
| 51 | * register summary |
| 52 | **********************************************************************/ |
| 53 | #define RTC_SECONDS 0 |
| 54 | #define RTC_SECONDS_ALARM 1 |
| 55 | #define RTC_MINUTES 2 |
| 56 | #define RTC_MINUTES_ALARM 3 |
| 57 | #define RTC_HOURS 4 |
| 58 | #define RTC_HOURS_ALARM 5 |
| 59 | /* RTC_*_alarm is always true if 2 MSBs are set */ |
| 60 | # define RTC_ALARM_DONT_CARE 0xC0 |
| 61 | |
| 62 | #define RTC_DAY_OF_WEEK 6 |
| 63 | #define RTC_DAY_OF_MONTH 7 |
| 64 | #define RTC_MONTH 8 |
| 65 | #define RTC_YEAR 9 |
| 66 | |
| 67 | /* control registers - Moto names |
| 68 | */ |
| 69 | #define RTC_REG_A 10 |
| 70 | #define RTC_REG_B 11 |
| 71 | #define RTC_REG_C 12 |
| 72 | #define RTC_REG_D 13 |
| 73 | |
| 74 | /********************************************************************** |
| 75 | * register details |
| 76 | **********************************************************************/ |
| 77 | #define RTC_FREQ_SELECT RTC_REG_A |
| 78 | |
| 79 | /* update-in-progress - set to "1" 244 microsecs before RTC goes off the bus, |
| 80 | * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete, |
| 81 | * totalling to a max high interval of 2.228 ms. |
| 82 | */ |
| 83 | # define RTC_UIP 0x80 |
| 84 | # define RTC_DIV_CTL 0x70 |
| 85 | /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */ |
| 86 | # define RTC_REF_CLCK_4MHZ 0x00 |
| 87 | # define RTC_REF_CLCK_1MHZ 0x10 |
| 88 | # define RTC_REF_CLCK_32KHZ 0x20 |
| 89 | /* 2 values for divider stage reset, others for "testing purposes only" */ |
| 90 | # define RTC_DIV_RESET1 0x60 |
| 91 | # define RTC_DIV_RESET2 0x70 |
| 92 | /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */ |
| 93 | # define RTC_RATE_SELECT 0x0F |
| 94 | |
| 95 | /**********************************************************************/ |
| 96 | #define RTC_CONTROL RTC_REG_B |
| 97 | # define RTC_SET 0x80 /* disable updates for clock setting */ |
| 98 | # define RTC_PIE 0x40 /* periodic interrupt enable */ |
| 99 | # define RTC_AIE 0x20 /* alarm interrupt enable */ |
| 100 | # define RTC_UIE 0x10 /* update-finished interrupt enable */ |
| 101 | # define RTC_SQWE 0x08 /* enable square-wave output */ |
| 102 | # define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */ |
| 103 | # define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */ |
| 104 | # define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */ |
| 105 | |
| 106 | /**********************************************************************/ |
| 107 | #define RTC_INTR_FLAGS RTC_REG_C |
| 108 | /* caution - cleared by read */ |
| 109 | # define RTC_IRQF 0x80 /* any of the following 3 is active */ |
| 110 | # define RTC_PF 0x40 |
| 111 | # define RTC_AF 0x20 |
| 112 | # define RTC_UF 0x10 |
| 113 | |
| 114 | /**********************************************************************/ |
| 115 | #define RTC_VALID RTC_REG_D |
| 116 | # define RTC_VRT 0x80 /* valid RAM and time */ |
| 117 | /**********************************************************************/ |
| 118 | |
Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 119 | #ifndef ARCH_RTC_LOCATION /* Override by <asm/mc146818rtc.h>? */ |
| 120 | |
| 121 | #define RTC_IO_EXTENT 0x8 |
Bjorn Helgaas | 9626f1f1 | 2007-11-14 16:59:57 -0800 | [diff] [blame] | 122 | #define RTC_IO_EXTENT_USED 0x2 |
Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 123 | #define RTC_IOMAPPED 1 /* Default to I/O mapping. */ |
| 124 | |
Bjorn Helgaas | 9626f1f1 | 2007-11-14 16:59:57 -0800 | [diff] [blame] | 125 | #else |
| 126 | #define RTC_IO_EXTENT_USED RTC_IO_EXTENT |
Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 127 | #endif /* ARCH_RTC_LOCATION */ |
| 128 | |
Arnd Bergmann | 5ab788d | 2016-05-30 20:57:50 +0200 | [diff] [blame^] | 129 | /* |
| 130 | * Returns true if a clock update is in progress |
| 131 | */ |
| 132 | static inline unsigned char mc146818_is_updating(void) |
| 133 | { |
| 134 | unsigned char uip; |
| 135 | unsigned long flags; |
| 136 | |
| 137 | spin_lock_irqsave(&rtc_lock, flags); |
| 138 | uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); |
| 139 | spin_unlock_irqrestore(&rtc_lock, flags); |
| 140 | return uip; |
| 141 | } |
| 142 | |
| 143 | static inline unsigned int mc146818_get_time(struct rtc_time *time) |
| 144 | { |
| 145 | unsigned char ctrl; |
| 146 | unsigned long flags; |
| 147 | unsigned char century = 0; |
| 148 | |
| 149 | #ifdef CONFIG_MACH_DECSTATION |
| 150 | unsigned int real_year; |
| 151 | #endif |
| 152 | |
| 153 | /* |
| 154 | * read RTC once any update in progress is done. The update |
| 155 | * can take just over 2ms. We wait 20ms. There is no need to |
| 156 | * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. |
| 157 | * If you need to know *exactly* when a second has started, enable |
| 158 | * periodic update complete interrupts, (via ioctl) and then |
| 159 | * immediately read /dev/rtc which will block until you get the IRQ. |
| 160 | * Once the read clears, read the RTC time (again via ioctl). Easy. |
| 161 | */ |
| 162 | if (mc146818_is_updating()) |
| 163 | mdelay(20); |
| 164 | |
| 165 | /* |
| 166 | * Only the values that we read from the RTC are set. We leave |
| 167 | * tm_wday, tm_yday and tm_isdst untouched. Even though the |
| 168 | * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated |
| 169 | * by the RTC when initially set to a non-zero value. |
| 170 | */ |
| 171 | spin_lock_irqsave(&rtc_lock, flags); |
| 172 | time->tm_sec = CMOS_READ(RTC_SECONDS); |
| 173 | time->tm_min = CMOS_READ(RTC_MINUTES); |
| 174 | time->tm_hour = CMOS_READ(RTC_HOURS); |
| 175 | time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); |
| 176 | time->tm_mon = CMOS_READ(RTC_MONTH); |
| 177 | time->tm_year = CMOS_READ(RTC_YEAR); |
| 178 | #ifdef CONFIG_MACH_DECSTATION |
| 179 | real_year = CMOS_READ(RTC_DEC_YEAR); |
| 180 | #endif |
| 181 | #ifdef CONFIG_ACPI |
| 182 | if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && |
| 183 | acpi_gbl_FADT.century) |
| 184 | century = CMOS_READ(acpi_gbl_FADT.century); |
| 185 | #endif |
| 186 | ctrl = CMOS_READ(RTC_CONTROL); |
| 187 | spin_unlock_irqrestore(&rtc_lock, flags); |
| 188 | |
| 189 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) |
| 190 | { |
| 191 | time->tm_sec = bcd2bin(time->tm_sec); |
| 192 | time->tm_min = bcd2bin(time->tm_min); |
| 193 | time->tm_hour = bcd2bin(time->tm_hour); |
| 194 | time->tm_mday = bcd2bin(time->tm_mday); |
| 195 | time->tm_mon = bcd2bin(time->tm_mon); |
| 196 | time->tm_year = bcd2bin(time->tm_year); |
| 197 | century = bcd2bin(century); |
| 198 | } |
| 199 | |
| 200 | #ifdef CONFIG_MACH_DECSTATION |
| 201 | time->tm_year += real_year - 72; |
| 202 | #endif |
| 203 | |
| 204 | if (century) |
| 205 | time->tm_year += (century - 19) * 100; |
| 206 | |
| 207 | /* |
| 208 | * Account for differences between how the RTC uses the values |
| 209 | * and how they are defined in a struct rtc_time; |
| 210 | */ |
| 211 | if (time->tm_year <= 69) |
| 212 | time->tm_year += 100; |
| 213 | |
| 214 | time->tm_mon--; |
| 215 | |
| 216 | return RTC_24H; |
| 217 | } |
| 218 | |
| 219 | /* Set the current date and time in the real time clock. */ |
| 220 | static inline int mc146818_set_time(struct rtc_time *time) |
| 221 | { |
| 222 | unsigned long flags; |
| 223 | unsigned char mon, day, hrs, min, sec; |
| 224 | unsigned char save_control, save_freq_select; |
| 225 | unsigned int yrs; |
| 226 | #ifdef CONFIG_MACH_DECSTATION |
| 227 | unsigned int real_yrs, leap_yr; |
| 228 | #endif |
| 229 | unsigned char century = 0; |
| 230 | |
| 231 | yrs = time->tm_year; |
| 232 | mon = time->tm_mon + 1; /* tm_mon starts at zero */ |
| 233 | day = time->tm_mday; |
| 234 | hrs = time->tm_hour; |
| 235 | min = time->tm_min; |
| 236 | sec = time->tm_sec; |
| 237 | |
| 238 | if (yrs > 255) /* They are unsigned */ |
| 239 | return -EINVAL; |
| 240 | |
| 241 | spin_lock_irqsave(&rtc_lock, flags); |
| 242 | #ifdef CONFIG_MACH_DECSTATION |
| 243 | real_yrs = yrs; |
| 244 | leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) || |
| 245 | !((yrs + 1900) % 400)); |
| 246 | yrs = 72; |
| 247 | |
| 248 | /* |
| 249 | * We want to keep the year set to 73 until March |
| 250 | * for non-leap years, so that Feb, 29th is handled |
| 251 | * correctly. |
| 252 | */ |
| 253 | if (!leap_yr && mon < 3) { |
| 254 | real_yrs--; |
| 255 | yrs = 73; |
| 256 | } |
| 257 | #endif |
| 258 | |
| 259 | #ifdef CONFIG_ACPI |
| 260 | if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && |
| 261 | acpi_gbl_FADT.century) { |
| 262 | century = (yrs + 1900) / 100; |
| 263 | yrs %= 100; |
| 264 | } |
| 265 | #endif |
| 266 | |
| 267 | /* These limits and adjustments are independent of |
| 268 | * whether the chip is in binary mode or not. |
| 269 | */ |
| 270 | if (yrs > 169) { |
| 271 | spin_unlock_irqrestore(&rtc_lock, flags); |
| 272 | return -EINVAL; |
| 273 | } |
| 274 | |
| 275 | if (yrs >= 100) |
| 276 | yrs -= 100; |
| 277 | |
| 278 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) |
| 279 | || RTC_ALWAYS_BCD) { |
| 280 | sec = bin2bcd(sec); |
| 281 | min = bin2bcd(min); |
| 282 | hrs = bin2bcd(hrs); |
| 283 | day = bin2bcd(day); |
| 284 | mon = bin2bcd(mon); |
| 285 | yrs = bin2bcd(yrs); |
| 286 | century = bin2bcd(century); |
| 287 | } |
| 288 | |
| 289 | save_control = CMOS_READ(RTC_CONTROL); |
| 290 | CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); |
| 291 | save_freq_select = CMOS_READ(RTC_FREQ_SELECT); |
| 292 | CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); |
| 293 | |
| 294 | #ifdef CONFIG_MACH_DECSTATION |
| 295 | CMOS_WRITE(real_yrs, RTC_DEC_YEAR); |
| 296 | #endif |
| 297 | CMOS_WRITE(yrs, RTC_YEAR); |
| 298 | CMOS_WRITE(mon, RTC_MONTH); |
| 299 | CMOS_WRITE(day, RTC_DAY_OF_MONTH); |
| 300 | CMOS_WRITE(hrs, RTC_HOURS); |
| 301 | CMOS_WRITE(min, RTC_MINUTES); |
| 302 | CMOS_WRITE(sec, RTC_SECONDS); |
| 303 | #ifdef CONFIG_ACPI |
| 304 | if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && |
| 305 | acpi_gbl_FADT.century) |
| 306 | CMOS_WRITE(century, acpi_gbl_FADT.century); |
| 307 | #endif |
| 308 | |
| 309 | CMOS_WRITE(save_control, RTC_CONTROL); |
| 310 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); |
| 311 | |
| 312 | spin_unlock_irqrestore(&rtc_lock, flags); |
| 313 | |
| 314 | return 0; |
| 315 | } |
| 316 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | #endif /* _MC146818RTC_H */ |