Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: Add support for configuring the UIP timeout for RTC reads

The UIP timeout is hardcoded to 10ms for all RTC reads, but in some
contexts this might not be enough time. Add a timeout parameter to
mc146818_get_time() and mc146818_get_time_callback().

If UIP timeout is configured by caller to be >=100 ms and a call
takes this long, log a warning.

Make all callers use 10ms to ensure no functional changes.

Cc: <stable@vger.kernel.org> # 6.1.y
Fixes: ec5895c0f2d8 ("rtc: mc146818-lib: extract mc146818_avoid_UIP")
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Tested-by: Mateusz Jończyk <mat.jonczyk@o2.pl>
Reviewed-by: Mateusz Jończyk <mat.jonczyk@o2.pl>
Acked-by: Mateusz Jończyk <mat.jonczyk@o2.pl>
Link: https://lore.kernel.org/r/20231128053653.101798-4-mario.limonciello@amd.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Mario Limonciello and committed by
Alexandre Belloni
120931db 1311a8f0

+38 -16
+1 -1
arch/alpha/kernel/rtc.c
··· 80 80 static int 81 81 alpha_rtc_read_time(struct device *dev, struct rtc_time *tm) 82 82 { 83 - int ret = mc146818_get_time(tm); 83 + int ret = mc146818_get_time(tm, 10); 84 84 85 85 if (ret < 0) { 86 86 dev_err_ratelimited(dev, "unable to read current time\n");
+1 -1
arch/x86/kernel/hpet.c
··· 1438 1438 memset(&curr_time, 0, sizeof(struct rtc_time)); 1439 1439 1440 1440 if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) { 1441 - if (unlikely(mc146818_get_time(&curr_time) < 0)) { 1441 + if (unlikely(mc146818_get_time(&curr_time, 10) < 0)) { 1442 1442 pr_err_ratelimited("unable to read current time from RTC\n"); 1443 1443 return IRQ_HANDLED; 1444 1444 }
+1 -1
arch/x86/kernel/rtc.c
··· 67 67 return; 68 68 } 69 69 70 - if (mc146818_get_time(&tm)) { 70 + if (mc146818_get_time(&tm, 10)) { 71 71 pr_err("Unable to read current time from RTC\n"); 72 72 now->tv_sec = now->tv_nsec = 0; 73 73 return;
+1 -1
drivers/base/power/trace.c
··· 120 120 struct rtc_time time; 121 121 unsigned int val; 122 122 123 - if (mc146818_get_time(&time) < 0) { 123 + if (mc146818_get_time(&time, 10) < 0) { 124 124 pr_err("Unable to read current time from RTC\n"); 125 125 return 0; 126 126 }
+3 -3
drivers/rtc/rtc-cmos.c
··· 231 231 if (!pm_trace_rtc_valid()) 232 232 return -EIO; 233 233 234 - ret = mc146818_get_time(t); 234 + ret = mc146818_get_time(t, 10); 235 235 if (ret < 0) { 236 236 dev_err_ratelimited(dev, "unable to read current time\n"); 237 237 return ret; ··· 307 307 * 308 308 * Use the mc146818_avoid_UIP() function to avoid this. 309 309 */ 310 - if (!mc146818_avoid_UIP(cmos_read_alarm_callback, &p)) 310 + if (!mc146818_avoid_UIP(cmos_read_alarm_callback, 10, &p)) 311 311 return -EIO; 312 312 313 313 if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { ··· 556 556 * 557 557 * Use mc146818_avoid_UIP() to avoid this. 558 558 */ 559 - if (!mc146818_avoid_UIP(cmos_set_alarm_callback, &p)) 559 + if (!mc146818_avoid_UIP(cmos_set_alarm_callback, 10, &p)) 560 560 return -ETIMEDOUT; 561 561 562 562 cmos->alarm_expires = rtc_tm_to_time64(&t->time);
+29 -8
drivers/rtc/rtc-mc146818-lib.c
··· 8 8 #include <linux/acpi.h> 9 9 #endif 10 10 11 + #define UIP_RECHECK_DELAY 100 /* usec */ 12 + #define UIP_RECHECK_DELAY_MS (USEC_PER_MSEC / UIP_RECHECK_DELAY) 13 + #define UIP_RECHECK_LOOPS_MS(x) (x / UIP_RECHECK_DELAY_MS) 14 + 11 15 /* 12 16 * Execute a function while the UIP (Update-in-progress) bit of the RTC is 13 - * unset. 17 + * unset. The timeout is configurable by the caller in ms. 14 18 * 15 19 * Warning: callback may be executed more then once. 16 20 */ 17 21 bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), 22 + int timeout, 18 23 void *param) 19 24 { 20 25 int i; 21 26 unsigned long flags; 22 27 unsigned char seconds; 23 28 24 - for (i = 0; i < 100; i++) { 29 + for (i = 0; UIP_RECHECK_LOOPS_MS(i) < timeout; i++) { 25 30 spin_lock_irqsave(&rtc_lock, flags); 26 31 27 32 /* 28 33 * Check whether there is an update in progress during which the 29 34 * readout is unspecified. The maximum update time is ~2ms. Poll 30 - * every 100 usec for completion. 35 + * for completion. 31 36 * 32 37 * Store the second value before checking UIP so a long lasting 33 38 * NMI which happens to hit after the UIP check cannot make ··· 42 37 43 38 if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { 44 39 spin_unlock_irqrestore(&rtc_lock, flags); 45 - udelay(100); 40 + udelay(UIP_RECHECK_DELAY); 46 41 continue; 47 42 } 48 43 ··· 61 56 */ 62 57 if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { 63 58 spin_unlock_irqrestore(&rtc_lock, flags); 64 - udelay(100); 59 + udelay(UIP_RECHECK_DELAY); 65 60 continue; 66 61 } 67 62 ··· 77 72 } 78 73 spin_unlock_irqrestore(&rtc_lock, flags); 79 74 75 + if (UIP_RECHECK_LOOPS_MS(i) >= 100) 76 + pr_warn("Reading current time from RTC took around %li ms\n", 77 + UIP_RECHECK_LOOPS_MS(i)); 78 + 80 79 return true; 81 80 } 82 81 return false; ··· 93 84 */ 94 85 bool mc146818_does_rtc_work(void) 95 86 { 96 - return mc146818_avoid_UIP(NULL, NULL); 87 + return mc146818_avoid_UIP(NULL, 10, NULL); 97 88 } 98 89 EXPORT_SYMBOL_GPL(mc146818_does_rtc_work); 99 90 ··· 139 130 p->ctrl = CMOS_READ(RTC_CONTROL); 140 131 } 141 132 142 - int mc146818_get_time(struct rtc_time *time) 133 + /** 134 + * mc146818_get_time - Get the current time from the RTC 135 + * @time: pointer to struct rtc_time to store the current time 136 + * @timeout: timeout value in ms 137 + * 138 + * This function reads the current time from the RTC and stores it in the 139 + * provided struct rtc_time. The timeout parameter specifies the maximum 140 + * time to wait for the RTC to become ready. 141 + * 142 + * Return: 0 on success, -ETIMEDOUT if the RTC did not become ready within 143 + * the specified timeout, or another error code if an error occurred. 144 + */ 145 + int mc146818_get_time(struct rtc_time *time, int timeout) 143 146 { 144 147 struct mc146818_get_time_callback_param p = { 145 148 .time = time 146 149 }; 147 150 148 - if (!mc146818_avoid_UIP(mc146818_get_time_callback, &p)) { 151 + if (!mc146818_avoid_UIP(mc146818_get_time_callback, timeout, &p)) { 149 152 memset(time, 0, sizeof(*time)); 150 153 return -ETIMEDOUT; 151 154 }
+2 -1
include/linux/mc146818rtc.h
··· 126 126 #endif /* ARCH_RTC_LOCATION */ 127 127 128 128 bool mc146818_does_rtc_work(void); 129 - int mc146818_get_time(struct rtc_time *time); 129 + int mc146818_get_time(struct rtc_time *time, int timeout); 130 130 int mc146818_set_time(struct rtc_time *time); 131 131 132 132 bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), 133 + int timeout, 133 134 void *param); 134 135 135 136 #endif /* _MC146818RTC_H */