Centos-kernel-stream-9/drivers/rtc/rtc-mc146818-lib.c

291 lines
7.0 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/mc146818rtc.h>
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
#endif
rtc: mc146818-lib: extract mc146818_avoid_UIP Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:27 2021 +0100 rtc: mc146818-lib: extract mc146818_avoid_UIP Function mc146818_get_time() contains an elaborate mechanism of reading the RTC time while no RTC update is in progress. It turns out that reading the RTC alarm clock also requires avoiding the RTC update. Therefore, the mechanism in mc146818_get_time() should be reused - so extract it into a separate function. The logic in mc146818_avoid_UIP() is same as in mc146818_get_time() except that after every if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { there is now "mdelay(1)". To avoid producing a very unreadable patch, mc146818_get_time() will be refactored to use mc146818_avoid_UIP() in the next patch. Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-6-mat.jonczyk@o2.pl (cherry picked from commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:00 +00:00
/*
* Execute a function while the UIP (Update-in-progress) bit of the RTC is
* unset.
*
* Warning: callback may be executed more then once.
*/
bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
void *param)
{
int i;
unsigned long flags;
unsigned char seconds;
rtc: mc146818-lib: reduce RTC_UIP polling period Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2183349 commit d2a632a8a11756197deb1341bbb09c09abaf20ce Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Feb 25 22:50:09 2022 +0100 rtc: mc146818-lib: reduce RTC_UIP polling period Waiting 1ms every time is not necessary, for example on some AMD boxes the RTC_UIP bit is documented as being high for around 270 microseconds in some cases [1], which agreed with experiments on an SB710 southbridge. So 100us seems optimal. This in preparation for mach_get_cmos_time() refactoring. The functions mc146818_get_time() and mach_get_cmos_time() in arch/x86/kernel/rtc.c perform the same function and the code is duplicated. mach_get_cmos_time() is busy waiting for the RTC_UIP bit to clear, so make mc146818_get_time() more similar to it by reducing the polling period. [1] AMD SB700/710/750 Register Reference Guide, page 307, https://developer.amd.com/wordpress/media/2012/10/43009_sb7xx_rrg_pub_1.00.pdf "SB700 A12: The UIP high pulse is 270 μS Typical when SS on SRC clock is OFF and 100μ min when SRC SS is ON." [sic] Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: x86@kernel.org Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20220225215011.861477-2-mat.jonczyk@o2.pl (cherry picked from commit d2a632a8a11756197deb1341bbb09c09abaf20ce) Signed-off-by: Gavin Shan <gshan@redhat.com>
2023-05-30 03:35:04 +00:00
for (i = 0; i < 100; i++) {
rtc: mc146818-lib: extract mc146818_avoid_UIP Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:27 2021 +0100 rtc: mc146818-lib: extract mc146818_avoid_UIP Function mc146818_get_time() contains an elaborate mechanism of reading the RTC time while no RTC update is in progress. It turns out that reading the RTC alarm clock also requires avoiding the RTC update. Therefore, the mechanism in mc146818_get_time() should be reused - so extract it into a separate function. The logic in mc146818_avoid_UIP() is same as in mc146818_get_time() except that after every if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { there is now "mdelay(1)". To avoid producing a very unreadable patch, mc146818_get_time() will be refactored to use mc146818_avoid_UIP() in the next patch. Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-6-mat.jonczyk@o2.pl (cherry picked from commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:00 +00:00
spin_lock_irqsave(&rtc_lock, flags);
/*
* Check whether there is an update in progress during which the
* readout is unspecified. The maximum update time is ~2ms. Poll
rtc: mc146818-lib: reduce RTC_UIP polling period Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2183349 commit d2a632a8a11756197deb1341bbb09c09abaf20ce Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Feb 25 22:50:09 2022 +0100 rtc: mc146818-lib: reduce RTC_UIP polling period Waiting 1ms every time is not necessary, for example on some AMD boxes the RTC_UIP bit is documented as being high for around 270 microseconds in some cases [1], which agreed with experiments on an SB710 southbridge. So 100us seems optimal. This in preparation for mach_get_cmos_time() refactoring. The functions mc146818_get_time() and mach_get_cmos_time() in arch/x86/kernel/rtc.c perform the same function and the code is duplicated. mach_get_cmos_time() is busy waiting for the RTC_UIP bit to clear, so make mc146818_get_time() more similar to it by reducing the polling period. [1] AMD SB700/710/750 Register Reference Guide, page 307, https://developer.amd.com/wordpress/media/2012/10/43009_sb7xx_rrg_pub_1.00.pdf "SB700 A12: The UIP high pulse is 270 μS Typical when SS on SRC clock is OFF and 100μ min when SRC SS is ON." [sic] Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: x86@kernel.org Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20220225215011.861477-2-mat.jonczyk@o2.pl (cherry picked from commit d2a632a8a11756197deb1341bbb09c09abaf20ce) Signed-off-by: Gavin Shan <gshan@redhat.com>
2023-05-30 03:35:04 +00:00
* every 100 usec for completion.
rtc: mc146818-lib: extract mc146818_avoid_UIP Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:27 2021 +0100 rtc: mc146818-lib: extract mc146818_avoid_UIP Function mc146818_get_time() contains an elaborate mechanism of reading the RTC time while no RTC update is in progress. It turns out that reading the RTC alarm clock also requires avoiding the RTC update. Therefore, the mechanism in mc146818_get_time() should be reused - so extract it into a separate function. The logic in mc146818_avoid_UIP() is same as in mc146818_get_time() except that after every if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { there is now "mdelay(1)". To avoid producing a very unreadable patch, mc146818_get_time() will be refactored to use mc146818_avoid_UIP() in the next patch. Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-6-mat.jonczyk@o2.pl (cherry picked from commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:00 +00:00
*
* Store the second value before checking UIP so a long lasting
* NMI which happens to hit after the UIP check cannot make
* an update cycle invisible.
*/
seconds = CMOS_READ(RTC_SECONDS);
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
spin_unlock_irqrestore(&rtc_lock, flags);
rtc: mc146818-lib: reduce RTC_UIP polling period Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2183349 commit d2a632a8a11756197deb1341bbb09c09abaf20ce Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Feb 25 22:50:09 2022 +0100 rtc: mc146818-lib: reduce RTC_UIP polling period Waiting 1ms every time is not necessary, for example on some AMD boxes the RTC_UIP bit is documented as being high for around 270 microseconds in some cases [1], which agreed with experiments on an SB710 southbridge. So 100us seems optimal. This in preparation for mach_get_cmos_time() refactoring. The functions mc146818_get_time() and mach_get_cmos_time() in arch/x86/kernel/rtc.c perform the same function and the code is duplicated. mach_get_cmos_time() is busy waiting for the RTC_UIP bit to clear, so make mc146818_get_time() more similar to it by reducing the polling period. [1] AMD SB700/710/750 Register Reference Guide, page 307, https://developer.amd.com/wordpress/media/2012/10/43009_sb7xx_rrg_pub_1.00.pdf "SB700 A12: The UIP high pulse is 270 μS Typical when SS on SRC clock is OFF and 100μ min when SRC SS is ON." [sic] Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: x86@kernel.org Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20220225215011.861477-2-mat.jonczyk@o2.pl (cherry picked from commit d2a632a8a11756197deb1341bbb09c09abaf20ce) Signed-off-by: Gavin Shan <gshan@redhat.com>
2023-05-30 03:35:04 +00:00
udelay(100);
rtc: mc146818-lib: extract mc146818_avoid_UIP Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:27 2021 +0100 rtc: mc146818-lib: extract mc146818_avoid_UIP Function mc146818_get_time() contains an elaborate mechanism of reading the RTC time while no RTC update is in progress. It turns out that reading the RTC alarm clock also requires avoiding the RTC update. Therefore, the mechanism in mc146818_get_time() should be reused - so extract it into a separate function. The logic in mc146818_avoid_UIP() is same as in mc146818_get_time() except that after every if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { there is now "mdelay(1)". To avoid producing a very unreadable patch, mc146818_get_time() will be refactored to use mc146818_avoid_UIP() in the next patch. Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-6-mat.jonczyk@o2.pl (cherry picked from commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:00 +00:00
continue;
}
/* Revalidate the above readout */
if (seconds != CMOS_READ(RTC_SECONDS)) {
spin_unlock_irqrestore(&rtc_lock, flags);
continue;
}
if (callback)
callback(seconds, param);
/*
* Check for the UIP bit again. If it is set now then
* the above values may contain garbage.
*/
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
spin_unlock_irqrestore(&rtc_lock, flags);
rtc: mc146818-lib: reduce RTC_UIP polling period Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2183349 commit d2a632a8a11756197deb1341bbb09c09abaf20ce Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Feb 25 22:50:09 2022 +0100 rtc: mc146818-lib: reduce RTC_UIP polling period Waiting 1ms every time is not necessary, for example on some AMD boxes the RTC_UIP bit is documented as being high for around 270 microseconds in some cases [1], which agreed with experiments on an SB710 southbridge. So 100us seems optimal. This in preparation for mach_get_cmos_time() refactoring. The functions mc146818_get_time() and mach_get_cmos_time() in arch/x86/kernel/rtc.c perform the same function and the code is duplicated. mach_get_cmos_time() is busy waiting for the RTC_UIP bit to clear, so make mc146818_get_time() more similar to it by reducing the polling period. [1] AMD SB700/710/750 Register Reference Guide, page 307, https://developer.amd.com/wordpress/media/2012/10/43009_sb7xx_rrg_pub_1.00.pdf "SB700 A12: The UIP high pulse is 270 μS Typical when SS on SRC clock is OFF and 100μ min when SRC SS is ON." [sic] Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: x86@kernel.org Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20220225215011.861477-2-mat.jonczyk@o2.pl (cherry picked from commit d2a632a8a11756197deb1341bbb09c09abaf20ce) Signed-off-by: Gavin Shan <gshan@redhat.com>
2023-05-30 03:35:04 +00:00
udelay(100);
rtc: mc146818-lib: extract mc146818_avoid_UIP Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:27 2021 +0100 rtc: mc146818-lib: extract mc146818_avoid_UIP Function mc146818_get_time() contains an elaborate mechanism of reading the RTC time while no RTC update is in progress. It turns out that reading the RTC alarm clock also requires avoiding the RTC update. Therefore, the mechanism in mc146818_get_time() should be reused - so extract it into a separate function. The logic in mc146818_avoid_UIP() is same as in mc146818_get_time() except that after every if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { there is now "mdelay(1)". To avoid producing a very unreadable patch, mc146818_get_time() will be refactored to use mc146818_avoid_UIP() in the next patch. Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-6-mat.jonczyk@o2.pl (cherry picked from commit ec5895c0f2d87b9bf4185db1915e40fa6fcfc0ac) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:00 +00:00
continue;
}
/*
* A NMI might have interrupted the above sequence so check
* whether the seconds value has changed which indicates that
* the NMI took longer than the UIP bit was set. Unlikely, but
* possible and there is also virt...
*/
if (seconds != CMOS_READ(RTC_SECONDS)) {
spin_unlock_irqrestore(&rtc_lock, flags);
continue;
}
spin_unlock_irqrestore(&rtc_lock, flags);
return true;
}
return false;
}
EXPORT_SYMBOL_GPL(mc146818_avoid_UIP);
rtc: mc146818-lib: fix RTC presence check Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. Conflicts: drivers/rtc/rtc-mc146818-lib.c include/linux/mc146818rtc.h Earlier changes affected the return type for mc146818_get_time() which in turn threw off the merge. Adapted the patch by hand. commit ea6fa4961aab8f90a8aa03575a98b4bda368d4b6 Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:26 2021 +0100 rtc: mc146818-lib: fix RTC presence check To prevent an infinite loop in mc146818_get_time(), commit 211e5db19d15 ("rtc: mc146818: Detect and handle broken RTCs") added a check for RTC availability. Together with a later fix, it checked if bit 6 in register 0x0d is cleared. This, however, caused a false negative on a motherboard with an AMD SB710 southbridge; according to the specification [1], bit 6 of register 0x0d of this chipset is a scratchbit. This caused a regression in Linux 5.11 - the RTC was determined broken by the kernel and not used by rtc-cmos.c [3]. This problem was also reported in Fedora [4]. As a better alternative, check whether the UIP ("Update-in-progress") bit is set for longer then 10ms. If that is the case, then apparently the RTC is either absent (and all register reads return 0xff) or broken. Also limit the number of loop iterations in mc146818_get_time() to 10 to prevent an infinite loop there. The functions mc146818_get_time() and mc146818_does_rtc_work() will be refactored later in this patch series, in order to fix a separate problem with reading / setting the RTC alarm time. This is done so to avoid a confusion about what is being fixed when. In a previous approach to this problem, I implemented a check whether the RTC_HOURS register contains a value <= 24. This, however, sometimes did not work correctly on my Intel Kaby Lake laptop. According to Intel's documentation [2], "the time and date RAM locations (0-9) are disconnected from the external bus" during the update cycle so reading this register without checking the UIP bit is incorrect. [1] AMD SB700/710/750 Register Reference Guide, page 308, https://developer.amd.com/wordpress/media/2012/10/43009_sb7xx_rrg_pub_1.00.pdf [2] 7th Generation Intel ® Processor Family I/O for U/Y Platforms [...] Datasheet Volume 1 of 2, page 209 Intel's Document Number: 334658-006, https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/7th-and-8th-gen-core-family-mobile-u-y-processor-lines-i-o-datasheet-vol-1.pdf [3] Functions in arch/x86/kernel/rtc.c apparently were using it. [4] https://bugzilla.redhat.com/show_bug.cgi?id=1936688 Fixes: 211e5db19d15 ("rtc: mc146818: Detect and handle broken RTCs") Fixes: ebb22a059436 ("rtc: mc146818: Dont test for bit 0-5 in Register D") Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-5-mat.jonczyk@o2.pl (cherry picked from commit ea6fa4961aab8f90a8aa03575a98b4bda368d4b6) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:33:59 +00:00
/*
* If the UIP (Update-in-progress) bit of the RTC is set for more then
* 10ms, the RTC is apparently broken or not present.
*/
bool mc146818_does_rtc_work(void)
{
return mc146818_avoid_UIP(NULL, NULL);
rtc: mc146818-lib: fix RTC presence check Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. Conflicts: drivers/rtc/rtc-mc146818-lib.c include/linux/mc146818rtc.h Earlier changes affected the return type for mc146818_get_time() which in turn threw off the merge. Adapted the patch by hand. commit ea6fa4961aab8f90a8aa03575a98b4bda368d4b6 Author: Mateusz Jończyk <mat.jonczyk@o2.pl> Date: Fri Dec 10 21:01:26 2021 +0100 rtc: mc146818-lib: fix RTC presence check To prevent an infinite loop in mc146818_get_time(), commit 211e5db19d15 ("rtc: mc146818: Detect and handle broken RTCs") added a check for RTC availability. Together with a later fix, it checked if bit 6 in register 0x0d is cleared. This, however, caused a false negative on a motherboard with an AMD SB710 southbridge; according to the specification [1], bit 6 of register 0x0d of this chipset is a scratchbit. This caused a regression in Linux 5.11 - the RTC was determined broken by the kernel and not used by rtc-cmos.c [3]. This problem was also reported in Fedora [4]. As a better alternative, check whether the UIP ("Update-in-progress") bit is set for longer then 10ms. If that is the case, then apparently the RTC is either absent (and all register reads return 0xff) or broken. Also limit the number of loop iterations in mc146818_get_time() to 10 to prevent an infinite loop there. The functions mc146818_get_time() and mc146818_does_rtc_work() will be refactored later in this patch series, in order to fix a separate problem with reading / setting the RTC alarm time. This is done so to avoid a confusion about what is being fixed when. In a previous approach to this problem, I implemented a check whether the RTC_HOURS register contains a value <= 24. This, however, sometimes did not work correctly on my Intel Kaby Lake laptop. According to Intel's documentation [2], "the time and date RAM locations (0-9) are disconnected from the external bus" during the update cycle so reading this register without checking the UIP bit is incorrect. [1] AMD SB700/710/750 Register Reference Guide, page 308, https://developer.amd.com/wordpress/media/2012/10/43009_sb7xx_rrg_pub_1.00.pdf [2] 7th Generation Intel ® Processor Family I/O for U/Y Platforms [...] Datasheet Volume 1 of 2, page 209 Intel's Document Number: 334658-006, https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/7th-and-8th-gen-core-family-mobile-u-y-processor-lines-i-o-datasheet-vol-1.pdf [3] Functions in arch/x86/kernel/rtc.c apparently were using it. [4] https://bugzilla.redhat.com/show_bug.cgi?id=1936688 Fixes: 211e5db19d15 ("rtc: mc146818: Detect and handle broken RTCs") Fixes: ebb22a059436 ("rtc: mc146818: Dont test for bit 0-5 in Register D") Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20211210200131.153887-5-mat.jonczyk@o2.pl (cherry picked from commit ea6fa4961aab8f90a8aa03575a98b4bda368d4b6) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:33:59 +00:00
}
EXPORT_SYMBOL_GPL(mc146818_does_rtc_work);
struct mc146818_get_time_callback_param {
struct rtc_time *time;
unsigned char ctrl;
#ifdef CONFIG_ACPI
unsigned char century;
#endif
#ifdef CONFIG_MACH_DECSTATION
unsigned int real_year;
#endif
};
static void mc146818_get_time_callback(unsigned char seconds, void *param_in)
{
struct mc146818_get_time_callback_param *p = param_in;
/*
* Only the values that we read from the RTC are set. We leave
* tm_wday, tm_yday and tm_isdst untouched. Even though the
* RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
* by the RTC when initially set to a non-zero value.
*/
p->time->tm_sec = seconds;
p->time->tm_min = CMOS_READ(RTC_MINUTES);
p->time->tm_hour = CMOS_READ(RTC_HOURS);
p->time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
p->time->tm_mon = CMOS_READ(RTC_MONTH);
p->time->tm_year = CMOS_READ(RTC_YEAR);
#ifdef CONFIG_MACH_DECSTATION
p->real_year = CMOS_READ(RTC_DEC_YEAR);
#endif
#ifdef CONFIG_ACPI
if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
acpi_gbl_FADT.century) {
p->century = CMOS_READ(acpi_gbl_FADT.century);
} else {
p->century = 0;
}
#endif
rtc: mc146818: Prevent reading garbage The MC146818 driver is prone to read garbage from the RTC. There are several issues all related to the update cycle of the MC146818. The chip increments seconds obviously once per second and indicates that by a bit in a register. The bit goes high 244us before the actual update starts. During the update the readout of the time values is undefined. The code just checks whether the update in progress bit (UIP) is set before reading the clock. If it's set it waits arbitrary 20ms before retrying, which is ample because the maximum update time is ~2ms. But this check does not guarantee that the UIP bit goes high and the actual update happens during the readout. So the following can happen 0.997 UIP = False -> Interrupt/NMI/preemption 0.998 UIP -> True 0.999 Readout <- Undefined To prevent this rework the code so it checks UIP before and after the readout and if set after the readout try again. But that's not enough to cover the following: 0.997 UIP = False Readout seconds -> NMI (or vCPU scheduled out) 0.998 UIP -> True update completes UIP -> False 1.000 Readout minutes,.... UIP check succeeds That can make the readout wrong up to 59 seconds. To prevent this, read the seconds value before the first UIP check, validate it after checking UIP and after reading out the rest. It's amazing that the original i386 code had this actually correct and the generic implementation of the MC146818 driver got it wrong in 2002 and it stayed that way until today. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20201206220541.594826678@linutronix.de
2020-12-06 21:46:14 +00:00
p->ctrl = CMOS_READ(RTC_CONTROL);
}
int mc146818_get_time(struct rtc_time *time)
{
struct mc146818_get_time_callback_param p = {
.time = time
};
if (!mc146818_avoid_UIP(mc146818_get_time_callback, &p)) {
memset(time, 0, sizeof(*time));
return -EIO;
}
rtc: mc146818: Prevent reading garbage The MC146818 driver is prone to read garbage from the RTC. There are several issues all related to the update cycle of the MC146818. The chip increments seconds obviously once per second and indicates that by a bit in a register. The bit goes high 244us before the actual update starts. During the update the readout of the time values is undefined. The code just checks whether the update in progress bit (UIP) is set before reading the clock. If it's set it waits arbitrary 20ms before retrying, which is ample because the maximum update time is ~2ms. But this check does not guarantee that the UIP bit goes high and the actual update happens during the readout. So the following can happen 0.997 UIP = False -> Interrupt/NMI/preemption 0.998 UIP -> True 0.999 Readout <- Undefined To prevent this rework the code so it checks UIP before and after the readout and if set after the readout try again. But that's not enough to cover the following: 0.997 UIP = False Readout seconds -> NMI (or vCPU scheduled out) 0.998 UIP -> True update completes UIP -> False 1.000 Readout minutes,.... UIP check succeeds That can make the readout wrong up to 59 seconds. To prevent this, read the seconds value before the first UIP check, validate it after checking UIP and after reading out the rest. It's amazing that the original i386 code had this actually correct and the generic implementation of the MC146818 driver got it wrong in 2002 and it stayed that way until today. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20201206220541.594826678@linutronix.de
2020-12-06 21:46:14 +00:00
if (!(p.ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
time->tm_sec = bcd2bin(time->tm_sec);
time->tm_min = bcd2bin(time->tm_min);
time->tm_hour = bcd2bin(time->tm_hour);
time->tm_mday = bcd2bin(time->tm_mday);
time->tm_mon = bcd2bin(time->tm_mon);
time->tm_year = bcd2bin(time->tm_year);
#ifdef CONFIG_ACPI
p.century = bcd2bin(p.century);
#endif
}
#ifdef CONFIG_MACH_DECSTATION
time->tm_year += p.real_year - 72;
#endif
#ifdef CONFIG_ACPI
if (p.century > 19)
time->tm_year += (p.century - 19) * 100;
#endif
/*
* Account for differences between how the RTC uses the values
* and how they are defined in a struct rtc_time;
*/
if (time->tm_year <= 69)
time->tm_year += 100;
time->tm_mon--;
return 0;
}
EXPORT_SYMBOL_GPL(mc146818_get_time);
rtc: mc146818-lib: Fix the AltCentury for AMD platforms Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit 3ae8fd41573af4fb3a490c9ed947fc936ba87190 Author: Mario Limonciello <mario.limonciello@amd.com> Date: Tue Jan 11 16:57:50 2022 -0600 rtc: mc146818-lib: Fix the AltCentury for AMD platforms Setting the century forward has been failing on AMD platforms. There was a previous attempt at fixing this for family 0x17 as part of commit 7ad295d5196a ("rtc: Fix the AltCentury value on AMD/Hygon platform") but this was later reverted due to some problems reported that appeared to stem from an FW bug on a family 0x17 desktop system. The same comments mentioned in the previous commit continue to apply to the newer platforms as well. ``` MC146818 driver use function mc146818_set_time() to set register RTC_FREQ_SELECT(RTC_REG_A)'s bit4-bit6 field which means divider stage reset value on Intel platform to 0x7. While AMD/Hygon RTC_REG_A(0Ah)'s bit4 is defined as DV0 [Reference]: DV0 = 0 selects Bank 0, DV0 = 1 selects Bank 1. Bit5-bit6 is defined as reserved. DV0 is set to 1, it will select Bank 1, which will disable AltCentury register(0x32) access. As UEFI pass acpi_gbl_FADT.century 0x32 (AltCentury), the CMOS write will be failed on code: CMOS_WRITE(century, acpi_gbl_FADT.century). Correct RTC_REG_A bank select bit(DV0) to 0 on AMD/Hygon CPUs, it will enable AltCentury(0x32) register writing and finally setup century as expected. ``` However in closer examination the change previously submitted was also modifying bits 5 & 6 which are declared reserved in the AMD documentation. So instead modify just the DV0 bank selection bit. Being cognizant that there was a failure reported before, split the code change out to a static function that can also be used for exclusions if any regressions such as Mikhail's pop up again. Cc: Jinke Fan <fanjinke@hygon.cn> Cc: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com> Link: https://lore.kernel.org/all/CABXGCsMLob0DC25JS8wwAYydnDoHBSoMh2_YLPfqm3TTvDE-Zw@mail.gmail.com/ Link: https://www.amd.com/system/files/TechDocs/51192_Bolton_FCH_RRG.pdf Signed-off-by: Raul E Rangel <rrangel@chromium.org> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20220111225750.1699-1-mario.limonciello@amd.com (cherry picked from commit 3ae8fd41573af4fb3a490c9ed947fc936ba87190) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:09 +00:00
/* AMD systems don't allow access to AltCentury with DV1 */
static bool apply_amd_register_a_behavior(void)
{
#ifdef CONFIG_X86
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
return true;
#endif
return false;
}
/* Set the current date and time in the real time clock. */
int mc146818_set_time(struct rtc_time *time)
{
unsigned long flags;
unsigned char mon, day, hrs, min, sec;
unsigned char save_control, save_freq_select;
unsigned int yrs;
#ifdef CONFIG_MACH_DECSTATION
unsigned int real_yrs, leap_yr;
#endif
unsigned char century = 0;
yrs = time->tm_year;
mon = time->tm_mon + 1; /* tm_mon starts at zero */
day = time->tm_mday;
hrs = time->tm_hour;
min = time->tm_min;
sec = time->tm_sec;
if (yrs > 255) /* They are unsigned */
return -EINVAL;
#ifdef CONFIG_MACH_DECSTATION
real_yrs = yrs;
leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) ||
!((yrs + 1900) % 400));
yrs = 72;
/*
* We want to keep the year set to 73 until March
* for non-leap years, so that Feb, 29th is handled
* correctly.
*/
if (!leap_yr && mon < 3) {
real_yrs--;
yrs = 73;
}
#endif
#ifdef CONFIG_ACPI
if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
acpi_gbl_FADT.century) {
century = (yrs + 1900) / 100;
yrs %= 100;
}
#endif
/* These limits and adjustments are independent of
* whether the chip is in binary mode or not.
*/
if (yrs > 169)
return -EINVAL;
if (yrs >= 100)
yrs -= 100;
spin_lock_irqsave(&rtc_lock, flags);
save_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irqrestore(&rtc_lock, flags);
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
sec = bin2bcd(sec);
min = bin2bcd(min);
hrs = bin2bcd(hrs);
day = bin2bcd(day);
mon = bin2bcd(mon);
yrs = bin2bcd(yrs);
century = bin2bcd(century);
}
spin_lock_irqsave(&rtc_lock, flags);
save_control = CMOS_READ(RTC_CONTROL);
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
rtc: mc146818-lib: Fix the AltCentury for AMD platforms Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071847 Tested: This is one of a series of patch sets to enable Arm SystemReady IR support in the kernel for compliant platforms. This set updates the RTC drivers as needed in drivers/rtc. This set has been tested via simple boot tests, and of course the CI loop. commit 3ae8fd41573af4fb3a490c9ed947fc936ba87190 Author: Mario Limonciello <mario.limonciello@amd.com> Date: Tue Jan 11 16:57:50 2022 -0600 rtc: mc146818-lib: Fix the AltCentury for AMD platforms Setting the century forward has been failing on AMD platforms. There was a previous attempt at fixing this for family 0x17 as part of commit 7ad295d5196a ("rtc: Fix the AltCentury value on AMD/Hygon platform") but this was later reverted due to some problems reported that appeared to stem from an FW bug on a family 0x17 desktop system. The same comments mentioned in the previous commit continue to apply to the newer platforms as well. ``` MC146818 driver use function mc146818_set_time() to set register RTC_FREQ_SELECT(RTC_REG_A)'s bit4-bit6 field which means divider stage reset value on Intel platform to 0x7. While AMD/Hygon RTC_REG_A(0Ah)'s bit4 is defined as DV0 [Reference]: DV0 = 0 selects Bank 0, DV0 = 1 selects Bank 1. Bit5-bit6 is defined as reserved. DV0 is set to 1, it will select Bank 1, which will disable AltCentury register(0x32) access. As UEFI pass acpi_gbl_FADT.century 0x32 (AltCentury), the CMOS write will be failed on code: CMOS_WRITE(century, acpi_gbl_FADT.century). Correct RTC_REG_A bank select bit(DV0) to 0 on AMD/Hygon CPUs, it will enable AltCentury(0x32) register writing and finally setup century as expected. ``` However in closer examination the change previously submitted was also modifying bits 5 & 6 which are declared reserved in the AMD documentation. So instead modify just the DV0 bank selection bit. Being cognizant that there was a failure reported before, split the code change out to a static function that can also be used for exclusions if any regressions such as Mikhail's pop up again. Cc: Jinke Fan <fanjinke@hygon.cn> Cc: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com> Link: https://lore.kernel.org/all/CABXGCsMLob0DC25JS8wwAYydnDoHBSoMh2_YLPfqm3TTvDE-Zw@mail.gmail.com/ Link: https://www.amd.com/system/files/TechDocs/51192_Bolton_FCH_RRG.pdf Signed-off-by: Raul E Rangel <rrangel@chromium.org> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20220111225750.1699-1-mario.limonciello@amd.com (cherry picked from commit 3ae8fd41573af4fb3a490c9ed947fc936ba87190) Signed-off-by: Al Stone <ahs3@redhat.com>
2022-10-04 15:34:09 +00:00
if (apply_amd_register_a_behavior())
CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT);
else
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
#ifdef CONFIG_MACH_DECSTATION
CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
#endif
CMOS_WRITE(yrs, RTC_YEAR);
CMOS_WRITE(mon, RTC_MONTH);
CMOS_WRITE(day, RTC_DAY_OF_MONTH);
CMOS_WRITE(hrs, RTC_HOURS);
CMOS_WRITE(min, RTC_MINUTES);
CMOS_WRITE(sec, RTC_SECONDS);
#ifdef CONFIG_ACPI
if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
acpi_gbl_FADT.century)
CMOS_WRITE(century, acpi_gbl_FADT.century);
#endif
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(mc146818_set_time);