1 // SPDX-License-Identifier: GPL-2.0-only
2 #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  * Execute a function while the UIP (Update-in-progress) bit of the RTC is
13  * unset.
14  *
15  * Warning: callback may be executed more then once.
16  */
17 bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
18 			void *param)
19 {
20 	int i;
21 	unsigned long flags;
22 	unsigned char seconds;
23 
24 	for (i = 0; i < 10; i++) {
25 		spin_lock_irqsave(&rtc_lock, flags);
26 
27 		/*
28 		 * Check whether there is an update in progress during which the
29 		 * readout is unspecified. The maximum update time is ~2ms. Poll
30 		 * every msec for completion.
31 		 *
32 		 * Store the second value before checking UIP so a long lasting
33 		 * NMI which happens to hit after the UIP check cannot make
34 		 * an update cycle invisible.
35 		 */
36 		seconds = CMOS_READ(RTC_SECONDS);
37 
38 		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
39 			spin_unlock_irqrestore(&rtc_lock, flags);
40 			mdelay(1);
41 			continue;
42 		}
43 
44 		/* Revalidate the above readout */
45 		if (seconds != CMOS_READ(RTC_SECONDS)) {
46 			spin_unlock_irqrestore(&rtc_lock, flags);
47 			continue;
48 		}
49 
50 		if (callback)
51 			callback(seconds, param);
52 
53 		/*
54 		 * Check for the UIP bit again. If it is set now then
55 		 * the above values may contain garbage.
56 		 */
57 		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
58 			spin_unlock_irqrestore(&rtc_lock, flags);
59 			mdelay(1);
60 			continue;
61 		}
62 
63 		/*
64 		 * A NMI might have interrupted the above sequence so check
65 		 * whether the seconds value has changed which indicates that
66 		 * the NMI took longer than the UIP bit was set. Unlikely, but
67 		 * possible and there is also virt...
68 		 */
69 		if (seconds != CMOS_READ(RTC_SECONDS)) {
70 			spin_unlock_irqrestore(&rtc_lock, flags);
71 			continue;
72 		}
73 		spin_unlock_irqrestore(&rtc_lock, flags);
74 
75 		return true;
76 	}
77 	return false;
78 }
79 EXPORT_SYMBOL_GPL(mc146818_avoid_UIP);
80 
81 /*
82  * If the UIP (Update-in-progress) bit of the RTC is set for more then
83  * 10ms, the RTC is apparently broken or not present.
84  */
85 bool mc146818_does_rtc_work(void)
86 {
87 	return mc146818_avoid_UIP(NULL, NULL);
88 }
89 EXPORT_SYMBOL_GPL(mc146818_does_rtc_work);
90 
91 struct mc146818_get_time_callback_param {
92 	struct rtc_time *time;
93 	unsigned char ctrl;
94 #ifdef CONFIG_ACPI
95 	unsigned char century;
96 #endif
97 #ifdef CONFIG_MACH_DECSTATION
98 	unsigned int real_year;
99 #endif
100 };
101 
102 static void mc146818_get_time_callback(unsigned char seconds, void *param_in)
103 {
104 	struct mc146818_get_time_callback_param *p = param_in;
105 
106 	/*
107 	 * Only the values that we read from the RTC are set. We leave
108 	 * tm_wday, tm_yday and tm_isdst untouched. Even though the
109 	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
110 	 * by the RTC when initially set to a non-zero value.
111 	 */
112 	p->time->tm_sec = seconds;
113 	p->time->tm_min = CMOS_READ(RTC_MINUTES);
114 	p->time->tm_hour = CMOS_READ(RTC_HOURS);
115 	p->time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
116 	p->time->tm_mon = CMOS_READ(RTC_MONTH);
117 	p->time->tm_year = CMOS_READ(RTC_YEAR);
118 #ifdef CONFIG_MACH_DECSTATION
119 	p->real_year = CMOS_READ(RTC_DEC_YEAR);
120 #endif
121 #ifdef CONFIG_ACPI
122 	if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
123 	    acpi_gbl_FADT.century) {
124 		p->century = CMOS_READ(acpi_gbl_FADT.century);
125 	} else {
126 		p->century = 0;
127 	}
128 #endif
129 
130 	p->ctrl = CMOS_READ(RTC_CONTROL);
131 }
132 
133 int mc146818_get_time(struct rtc_time *time)
134 {
135 	struct mc146818_get_time_callback_param p = {
136 		.time = time
137 	};
138 
139 	if (!mc146818_avoid_UIP(mc146818_get_time_callback, &p)) {
140 		memset(time, 0, sizeof(*time));
141 		return -EIO;
142 	}
143 
144 	if (!(p.ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
145 	{
146 		time->tm_sec = bcd2bin(time->tm_sec);
147 		time->tm_min = bcd2bin(time->tm_min);
148 		time->tm_hour = bcd2bin(time->tm_hour);
149 		time->tm_mday = bcd2bin(time->tm_mday);
150 		time->tm_mon = bcd2bin(time->tm_mon);
151 		time->tm_year = bcd2bin(time->tm_year);
152 #ifdef CONFIG_ACPI
153 		p.century = bcd2bin(p.century);
154 #endif
155 	}
156 
157 #ifdef CONFIG_MACH_DECSTATION
158 	time->tm_year += p.real_year - 72;
159 #endif
160 
161 #ifdef CONFIG_ACPI
162 	if (p.century > 19)
163 		time->tm_year += (p.century - 19) * 100;
164 #endif
165 
166 	/*
167 	 * Account for differences between how the RTC uses the values
168 	 * and how they are defined in a struct rtc_time;
169 	 */
170 	if (time->tm_year <= 69)
171 		time->tm_year += 100;
172 
173 	time->tm_mon--;
174 
175 	return 0;
176 }
177 EXPORT_SYMBOL_GPL(mc146818_get_time);
178 
179 /* AMD systems don't allow access to AltCentury with DV1 */
180 static bool apply_amd_register_a_behavior(void)
181 {
182 #ifdef CONFIG_X86
183 	if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
184 	    boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
185 		return true;
186 #endif
187 	return false;
188 }
189 
190 /* Set the current date and time in the real time clock. */
191 int mc146818_set_time(struct rtc_time *time)
192 {
193 	unsigned long flags;
194 	unsigned char mon, day, hrs, min, sec;
195 	unsigned char save_control, save_freq_select;
196 	unsigned int yrs;
197 #ifdef CONFIG_MACH_DECSTATION
198 	unsigned int real_yrs, leap_yr;
199 #endif
200 	unsigned char century = 0;
201 
202 	yrs = time->tm_year;
203 	mon = time->tm_mon + 1;   /* tm_mon starts at zero */
204 	day = time->tm_mday;
205 	hrs = time->tm_hour;
206 	min = time->tm_min;
207 	sec = time->tm_sec;
208 
209 	if (yrs > 255)	/* They are unsigned */
210 		return -EINVAL;
211 
212 #ifdef CONFIG_MACH_DECSTATION
213 	real_yrs = yrs;
214 	leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) ||
215 			!((yrs + 1900) % 400));
216 	yrs = 72;
217 
218 	/*
219 	 * We want to keep the year set to 73 until March
220 	 * for non-leap years, so that Feb, 29th is handled
221 	 * correctly.
222 	 */
223 	if (!leap_yr && mon < 3) {
224 		real_yrs--;
225 		yrs = 73;
226 	}
227 #endif
228 
229 #ifdef CONFIG_ACPI
230 	if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
231 	    acpi_gbl_FADT.century) {
232 		century = (yrs + 1900) / 100;
233 		yrs %= 100;
234 	}
235 #endif
236 
237 	/* These limits and adjustments are independent of
238 	 * whether the chip is in binary mode or not.
239 	 */
240 	if (yrs > 169)
241 		return -EINVAL;
242 
243 	if (yrs >= 100)
244 		yrs -= 100;
245 
246 	spin_lock_irqsave(&rtc_lock, flags);
247 	save_control = CMOS_READ(RTC_CONTROL);
248 	spin_unlock_irqrestore(&rtc_lock, flags);
249 	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
250 		sec = bin2bcd(sec);
251 		min = bin2bcd(min);
252 		hrs = bin2bcd(hrs);
253 		day = bin2bcd(day);
254 		mon = bin2bcd(mon);
255 		yrs = bin2bcd(yrs);
256 		century = bin2bcd(century);
257 	}
258 
259 	spin_lock_irqsave(&rtc_lock, flags);
260 	save_control = CMOS_READ(RTC_CONTROL);
261 	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
262 	save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
263 	if (apply_amd_register_a_behavior())
264 		CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT);
265 	else
266 		CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
267 
268 #ifdef CONFIG_MACH_DECSTATION
269 	CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
270 #endif
271 	CMOS_WRITE(yrs, RTC_YEAR);
272 	CMOS_WRITE(mon, RTC_MONTH);
273 	CMOS_WRITE(day, RTC_DAY_OF_MONTH);
274 	CMOS_WRITE(hrs, RTC_HOURS);
275 	CMOS_WRITE(min, RTC_MINUTES);
276 	CMOS_WRITE(sec, RTC_SECONDS);
277 #ifdef CONFIG_ACPI
278 	if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
279 	    acpi_gbl_FADT.century)
280 		CMOS_WRITE(century, acpi_gbl_FADT.century);
281 #endif
282 
283 	CMOS_WRITE(save_control, RTC_CONTROL);
284 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
285 
286 	spin_unlock_irqrestore(&rtc_lock, flags);
287 
288 	return 0;
289 }
290 EXPORT_SYMBOL_GPL(mc146818_set_time);
291