blob: b036ff33fbe61c49274978661f95dd03f7d8e186 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-only
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002#include <linux/bcd.h>
3#include <linux/delay.h>
4#include <linux/export.h>
5#include <linux/mc146818rtc.h>
6
7#ifdef CONFIG_ACPI
8#include <linux/acpi.h>
9#endif
10
11/*
12 * Returns true if a clock update is in progress
13 */
14static inline unsigned char mc146818_is_updating(void)
15{
16 unsigned char uip;
17 unsigned long flags;
18
19 spin_lock_irqsave(&rtc_lock, flags);
20 uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
21 spin_unlock_irqrestore(&rtc_lock, flags);
22 return uip;
23}
24
25unsigned int mc146818_get_time(struct rtc_time *time)
26{
27 unsigned char ctrl;
28 unsigned long flags;
29 unsigned char century = 0;
30
31#ifdef CONFIG_MACH_DECSTATION
32 unsigned int real_year;
33#endif
34
35 /*
36 * read RTC once any update in progress is done. The update
37 * can take just over 2ms. We wait 20ms. There is no need to
38 * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
39 * If you need to know *exactly* when a second has started, enable
40 * periodic update complete interrupts, (via ioctl) and then
41 * immediately read /dev/rtc which will block until you get the IRQ.
42 * Once the read clears, read the RTC time (again via ioctl). Easy.
43 */
44 if (mc146818_is_updating())
45 mdelay(20);
46
47 /*
48 * Only the values that we read from the RTC are set. We leave
49 * tm_wday, tm_yday and tm_isdst untouched. Even though the
50 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
51 * by the RTC when initially set to a non-zero value.
52 */
53 spin_lock_irqsave(&rtc_lock, flags);
54 time->tm_sec = CMOS_READ(RTC_SECONDS);
55 time->tm_min = CMOS_READ(RTC_MINUTES);
56 time->tm_hour = CMOS_READ(RTC_HOURS);
57 time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
58 time->tm_mon = CMOS_READ(RTC_MONTH);
59 time->tm_year = CMOS_READ(RTC_YEAR);
60#ifdef CONFIG_MACH_DECSTATION
61 real_year = CMOS_READ(RTC_DEC_YEAR);
62#endif
63#ifdef CONFIG_ACPI
64 if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
65 acpi_gbl_FADT.century)
66 century = CMOS_READ(acpi_gbl_FADT.century);
67#endif
68 ctrl = CMOS_READ(RTC_CONTROL);
69 spin_unlock_irqrestore(&rtc_lock, flags);
70
71 if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
72 {
73 time->tm_sec = bcd2bin(time->tm_sec);
74 time->tm_min = bcd2bin(time->tm_min);
75 time->tm_hour = bcd2bin(time->tm_hour);
76 time->tm_mday = bcd2bin(time->tm_mday);
77 time->tm_mon = bcd2bin(time->tm_mon);
78 time->tm_year = bcd2bin(time->tm_year);
79 century = bcd2bin(century);
80 }
81
82#ifdef CONFIG_MACH_DECSTATION
83 time->tm_year += real_year - 72;
84#endif
85
Olivier Deprez157378f2022-04-04 15:47:50 +020086 if (century > 19)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000087 time->tm_year += (century - 19) * 100;
88
89 /*
90 * Account for differences between how the RTC uses the values
91 * and how they are defined in a struct rtc_time;
92 */
93 if (time->tm_year <= 69)
94 time->tm_year += 100;
95
96 time->tm_mon--;
97
98 return RTC_24H;
99}
100EXPORT_SYMBOL_GPL(mc146818_get_time);
101
Olivier Deprez92d4c212022-12-06 15:05:30 +0100102/* AMD systems don't allow access to AltCentury with DV1 */
103static bool apply_amd_register_a_behavior(void)
104{
105#ifdef CONFIG_X86
106 if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
107 boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
108 return true;
109#endif
110 return false;
111}
112
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000113/* Set the current date and time in the real time clock. */
114int mc146818_set_time(struct rtc_time *time)
115{
116 unsigned long flags;
117 unsigned char mon, day, hrs, min, sec;
118 unsigned char save_control, save_freq_select;
119 unsigned int yrs;
120#ifdef CONFIG_MACH_DECSTATION
121 unsigned int real_yrs, leap_yr;
122#endif
123 unsigned char century = 0;
124
125 yrs = time->tm_year;
126 mon = time->tm_mon + 1; /* tm_mon starts at zero */
127 day = time->tm_mday;
128 hrs = time->tm_hour;
129 min = time->tm_min;
130 sec = time->tm_sec;
131
132 if (yrs > 255) /* They are unsigned */
133 return -EINVAL;
134
135 spin_lock_irqsave(&rtc_lock, flags);
136#ifdef CONFIG_MACH_DECSTATION
137 real_yrs = yrs;
138 leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) ||
139 !((yrs + 1900) % 400));
140 yrs = 72;
141
142 /*
143 * We want to keep the year set to 73 until March
144 * for non-leap years, so that Feb, 29th is handled
145 * correctly.
146 */
147 if (!leap_yr && mon < 3) {
148 real_yrs--;
149 yrs = 73;
150 }
151#endif
152
153#ifdef CONFIG_ACPI
154 if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
155 acpi_gbl_FADT.century) {
156 century = (yrs + 1900) / 100;
157 yrs %= 100;
158 }
159#endif
160
161 /* These limits and adjustments are independent of
162 * whether the chip is in binary mode or not.
163 */
164 if (yrs > 169) {
165 spin_unlock_irqrestore(&rtc_lock, flags);
166 return -EINVAL;
167 }
168
169 if (yrs >= 100)
170 yrs -= 100;
171
172 if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
173 || RTC_ALWAYS_BCD) {
174 sec = bin2bcd(sec);
175 min = bin2bcd(min);
176 hrs = bin2bcd(hrs);
177 day = bin2bcd(day);
178 mon = bin2bcd(mon);
179 yrs = bin2bcd(yrs);
180 century = bin2bcd(century);
181 }
182
183 save_control = CMOS_READ(RTC_CONTROL);
184 CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
185 save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
Olivier Deprez92d4c212022-12-06 15:05:30 +0100186 if (apply_amd_register_a_behavior())
187 CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT);
188 else
189 CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000190
191#ifdef CONFIG_MACH_DECSTATION
192 CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
193#endif
194 CMOS_WRITE(yrs, RTC_YEAR);
195 CMOS_WRITE(mon, RTC_MONTH);
196 CMOS_WRITE(day, RTC_DAY_OF_MONTH);
197 CMOS_WRITE(hrs, RTC_HOURS);
198 CMOS_WRITE(min, RTC_MINUTES);
199 CMOS_WRITE(sec, RTC_SECONDS);
200#ifdef CONFIG_ACPI
201 if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
202 acpi_gbl_FADT.century)
203 CMOS_WRITE(century, acpi_gbl_FADT.century);
204#endif
205
206 CMOS_WRITE(save_control, RTC_CONTROL);
207 CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
208
209 spin_unlock_irqrestore(&rtc_lock, flags);
210
211 return 0;
212}
213EXPORT_SYMBOL_GPL(mc146818_set_time);