Skip to content

Commit e853658

Browse files
jhovoldalexandrebelloni
authored andcommitted
rtc: pm8xxx: mitigate flash wear
On many Qualcomm platforms the PMIC RTC control and time registers are read-only so that the RTC time can not be updated. Instead an offset needs be stored in some machine-specific non-volatile memory, which the driver can take into account. On machines like the Lenovo ThinkPad X13s the PMIC RTC drifts about one second every 3.5 hours, something which leads to repeated updates of the offset when NTP synchronisation is enabled. Reduce wear of the underlying flash storage (used for UEFI variables) by deferring writes until shutdown in case they appear to be due to clock drift. As an example, deferring writes when the new offset differs up to 30 s from the previous one reduces the number of writes on the X13s during a ten day session with the machine not suspending for more than four days in a row from up to 68 writes (every 3.5 h) to at most two (boot and shutdown). Tested-by: Jens Glathe <jens.glathe@oldschoolsolutions.biz> Tested-by: Steev Klimaszewski <steev@kali.org> Tested-by: Joel Stanley <joel@jms.id.au> Tested-by: Sebastian Reichel <sre@kernel.org> # Lenovo T14s Gen6 Signed-off-by: Johan Hovold <johan+linaro@kernel.org> Link: https://lore.kernel.org/r/20250219134118.31017-4-johan+linaro@kernel.org Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
1 parent bba38b8 commit e853658

File tree

1 file changed

+26
-0
lines changed

1 file changed

+26
-0
lines changed

drivers/rtc/rtc-pm8xxx.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct qcom_uefi_rtc_info {
6565
* @rtc_info: qcom uefi rtc-info structure
6666
* @nvmem_cell: nvmem cell for offset
6767
* @offset: offset from epoch in seconds
68+
* @offset_dirty: offset needs to be stored on shutdown
6869
*/
6970
struct pm8xxx_rtc {
7071
struct rtc_device *rtc;
@@ -77,6 +78,7 @@ struct pm8xxx_rtc {
7778
struct qcom_uefi_rtc_info rtc_info;
7879
struct nvmem_cell *nvmem_cell;
7980
u32 offset;
81+
bool offset_dirty;
8082
};
8183

8284
#ifdef CONFIG_EFI
@@ -256,6 +258,15 @@ static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs)
256258
if (offset == rtc_dd->offset)
257259
return 0;
258260

261+
/*
262+
* Reduce flash wear by deferring updates due to clock drift until
263+
* shutdown.
264+
*/
265+
if (abs_diff(offset, rtc_dd->offset) < 30) {
266+
rtc_dd->offset_dirty = true;
267+
goto out;
268+
}
269+
259270
if (rtc_dd->nvmem_cell)
260271
rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset);
261272
else
@@ -264,6 +275,8 @@ static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs)
264275
if (rc)
265276
return rc;
266277

278+
rtc_dd->offset_dirty = false;
279+
out:
267280
rtc_dd->offset = offset;
268281

269282
return 0;
@@ -634,8 +647,21 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
634647
return devm_rtc_register_device(rtc_dd->rtc);
635648
}
636649

650+
static void pm8xxx_shutdown(struct platform_device *pdev)
651+
{
652+
struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
653+
654+
if (rtc_dd->offset_dirty) {
655+
if (rtc_dd->nvmem_cell)
656+
pm8xxx_rtc_write_nvmem_offset(rtc_dd, rtc_dd->offset);
657+
else
658+
pm8xxx_rtc_write_uefi_offset(rtc_dd, rtc_dd->offset);
659+
}
660+
}
661+
637662
static struct platform_driver pm8xxx_rtc_driver = {
638663
.probe = pm8xxx_rtc_probe,
664+
.shutdown = pm8xxx_shutdown,
639665
.driver = {
640666
.name = "rtc-pm8xxx",
641667
.of_match_table = pm8xxx_id_table,

0 commit comments

Comments
 (0)