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

rtc: pm8xxx: add support for uefi offset

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.

Add support for storing a 32-bit offset from the GPS time epoch in a
UEFI variable so that the RTC time can be set on such platforms.

The UEFI variable is

882f8c2b-9646-435f-8de5-f208ff80c1bd-RTCInfo

and holds a 12-byte structure where the first four bytes is a GPS time
offset in little-endian byte order.

Note that this format is not arbitrary as the variable is shared with
the UEFI firmware (and Windows).

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-3-johan+linaro@kernel.org
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Johan Hovold and committed by
Alexandre Belloni
bba38b87 931a8891

+133 -24
+132 -24
drivers/rtc/rtc-pm8xxx.c
··· 5 5 * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 6 6 * Copyright (c) 2023, Linaro Limited 7 7 */ 8 + #include <linux/efi.h> 8 9 #include <linux/of.h> 9 10 #include <linux/module.h> 10 11 #include <linux/nvmem-consumer.h> ··· 17 16 #include <linux/regmap.h> 18 17 #include <linux/slab.h> 19 18 #include <linux/spinlock.h> 20 - 21 19 #include <linux/unaligned.h> 20 + 21 + #include <asm/byteorder.h> 22 22 23 23 /* RTC_CTRL register bit fields */ 24 24 #define PM8xxx_RTC_ENABLE BIT(7) ··· 48 46 unsigned int alarm_en; 49 47 }; 50 48 49 + struct qcom_uefi_rtc_info { 50 + __le32 offset_gps; 51 + u8 reserved[8]; 52 + } __packed; 53 + 51 54 /** 52 55 * struct pm8xxx_rtc - RTC driver internal structure 53 56 * @rtc: RTC device 54 57 * @regmap: regmap used to access registers 55 58 * @allow_set_time: whether the time can be set 59 + * @use_uefi: use UEFI variable as fallback for offset 56 60 * @alarm_irq: alarm irq number 57 61 * @regs: register description 58 62 * @dev: device structure 63 + * @rtc_info: qcom uefi rtc-info structure 59 64 * @nvmem_cell: nvmem cell for offset 60 65 * @offset: offset from epoch in seconds 61 66 */ ··· 70 61 struct rtc_device *rtc; 71 62 struct regmap *regmap; 72 63 bool allow_set_time; 64 + bool use_uefi; 73 65 int alarm_irq; 74 66 const struct pm8xxx_rtc_regs *regs; 75 67 struct device *dev; 68 + struct qcom_uefi_rtc_info rtc_info; 76 69 struct nvmem_cell *nvmem_cell; 77 70 u32 offset; 78 71 }; 72 + 73 + #ifdef CONFIG_EFI 74 + 75 + MODULE_IMPORT_NS("EFIVAR"); 76 + 77 + #define QCOM_UEFI_NAME L"RTCInfo" 78 + #define QCOM_UEFI_GUID EFI_GUID(0x882f8c2b, 0x9646, 0x435f, \ 79 + 0x8d, 0xe5, 0xf2, 0x08, 0xff, 0x80, 0xc1, 0xbd) 80 + #define QCOM_UEFI_ATTRS (EFI_VARIABLE_NON_VOLATILE | \ 81 + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 82 + EFI_VARIABLE_RUNTIME_ACCESS) 83 + 84 + static int pm8xxx_rtc_read_uefi_offset(struct pm8xxx_rtc *rtc_dd) 85 + { 86 + struct qcom_uefi_rtc_info *rtc_info = &rtc_dd->rtc_info; 87 + unsigned long size = sizeof(*rtc_info); 88 + struct device *dev = rtc_dd->dev; 89 + efi_status_t status; 90 + u32 offset_gps; 91 + int rc; 92 + 93 + rc = efivar_lock(); 94 + if (rc) 95 + return rc; 96 + 97 + status = efivar_get_variable(QCOM_UEFI_NAME, &QCOM_UEFI_GUID, NULL, 98 + &size, rtc_info); 99 + efivar_unlock(); 100 + 101 + if (status != EFI_SUCCESS) { 102 + dev_dbg(dev, "failed to read UEFI offset: %lu\n", status); 103 + return efi_status_to_err(status); 104 + } 105 + 106 + if (size != sizeof(*rtc_info)) { 107 + dev_dbg(dev, "unexpected UEFI structure size %lu\n", size); 108 + return -EINVAL; 109 + } 110 + 111 + dev_dbg(dev, "uefi_rtc_info = %*ph\n", (int)size, rtc_info); 112 + 113 + /* Convert from GPS to Unix time offset */ 114 + offset_gps = le32_to_cpu(rtc_info->offset_gps); 115 + rtc_dd->offset = offset_gps + (u32)RTC_TIMESTAMP_EPOCH_GPS; 116 + 117 + return 0; 118 + } 119 + 120 + static int pm8xxx_rtc_write_uefi_offset(struct pm8xxx_rtc *rtc_dd, u32 offset) 121 + { 122 + struct qcom_uefi_rtc_info *rtc_info = &rtc_dd->rtc_info; 123 + unsigned long size = sizeof(*rtc_info); 124 + struct device *dev = rtc_dd->dev; 125 + efi_status_t status; 126 + u32 offset_gps; 127 + 128 + /* Convert from Unix to GPS time offset */ 129 + offset_gps = offset - (u32)RTC_TIMESTAMP_EPOCH_GPS; 130 + 131 + rtc_info->offset_gps = cpu_to_le32(offset_gps); 132 + 133 + dev_dbg(dev, "efi_rtc_info = %*ph\n", (int)size, rtc_info); 134 + 135 + status = efivar_set_variable(QCOM_UEFI_NAME, &QCOM_UEFI_GUID, 136 + QCOM_UEFI_ATTRS, size, rtc_info); 137 + if (status != EFI_SUCCESS) { 138 + dev_dbg(dev, "failed to write UEFI offset: %lx\n", status); 139 + return efi_status_to_err(status); 140 + } 141 + 142 + return 0; 143 + } 144 + 145 + #else /* CONFIG_EFI */ 146 + 147 + static int pm8xxx_rtc_read_uefi_offset(struct pm8xxx_rtc *rtc_dd) 148 + { 149 + return -ENODEV; 150 + } 151 + 152 + static int pm8xxx_rtc_write_uefi_offset(struct pm8xxx_rtc *rtc_dd, u32 offset) 153 + { 154 + return -ENODEV; 155 + } 156 + 157 + #endif /* CONFIG_EFI */ 79 158 80 159 static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd) 81 160 { ··· 207 110 return 0; 208 111 } 209 112 210 - static int pm8xxx_rtc_read_offset(struct pm8xxx_rtc *rtc_dd) 211 - { 212 - if (!rtc_dd->nvmem_cell) 213 - return 0; 214 - 215 - return pm8xxx_rtc_read_nvmem_offset(rtc_dd); 216 - } 217 - 218 113 static int pm8xxx_rtc_read_raw(struct pm8xxx_rtc *rtc_dd, u32 *secs) 219 114 { 220 115 const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; ··· 244 155 u32 offset; 245 156 int rc; 246 157 247 - if (!rtc_dd->nvmem_cell) 158 + if (!rtc_dd->nvmem_cell && !rtc_dd->use_uefi) 248 159 return -ENODEV; 249 160 250 161 rc = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs); ··· 256 167 if (offset == rtc_dd->offset) 257 168 return 0; 258 169 259 - rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset); 170 + if (rtc_dd->nvmem_cell) 171 + rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset); 172 + else 173 + rc = pm8xxx_rtc_write_uefi_offset(rtc_dd, offset); 174 + 260 175 if (rc) 261 176 return rc; 262 177 ··· 548 455 }; 549 456 MODULE_DEVICE_TABLE(of, pm8xxx_id_table); 550 457 458 + static int pm8xxx_rtc_probe_offset(struct pm8xxx_rtc *rtc_dd) 459 + { 460 + int rc; 461 + 462 + rtc_dd->nvmem_cell = devm_nvmem_cell_get(rtc_dd->dev, "offset"); 463 + if (IS_ERR(rtc_dd->nvmem_cell)) { 464 + rc = PTR_ERR(rtc_dd->nvmem_cell); 465 + if (rc != -ENOENT) 466 + return rc; 467 + rtc_dd->nvmem_cell = NULL; 468 + } else { 469 + return pm8xxx_rtc_read_nvmem_offset(rtc_dd); 470 + } 471 + 472 + /* Use UEFI storage as fallback if available */ 473 + if (efivar_is_available()) { 474 + rc = pm8xxx_rtc_read_uefi_offset(rtc_dd); 475 + if (rc == 0) 476 + rtc_dd->use_uefi = true; 477 + } 478 + 479 + return 0; 480 + } 481 + 551 482 static int pm8xxx_rtc_probe(struct platform_device *pdev) 552 483 { 553 484 const struct of_device_id *match; ··· 586 469 if (rtc_dd == NULL) 587 470 return -ENOMEM; 588 471 472 + rtc_dd->regs = match->data; 473 + rtc_dd->dev = &pdev->dev; 474 + 589 475 rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL); 590 476 if (!rtc_dd->regmap) 591 477 return -ENXIO; ··· 599 479 600 480 rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node, 601 481 "allow-set-time"); 602 - 603 - rtc_dd->nvmem_cell = devm_nvmem_cell_get(&pdev->dev, "offset"); 604 - if (IS_ERR(rtc_dd->nvmem_cell)) { 605 - rc = PTR_ERR(rtc_dd->nvmem_cell); 606 - if (rc != -ENOENT) 607 - return rc; 608 - rtc_dd->nvmem_cell = NULL; 609 - } 610 - 611 - rtc_dd->regs = match->data; 612 - rtc_dd->dev = &pdev->dev; 613 - 614 482 if (!rtc_dd->allow_set_time) { 615 - rc = pm8xxx_rtc_read_offset(rtc_dd); 483 + rc = pm8xxx_rtc_probe_offset(rtc_dd); 616 484 if (rc) 617 485 return rc; 618 486 }
+1
include/linux/rtc.h
··· 170 170 /* useful timestamps */ 171 171 #define RTC_TIMESTAMP_BEGIN_0000 -62167219200ULL /* 0000-01-01 00:00:00 */ 172 172 #define RTC_TIMESTAMP_BEGIN_1900 -2208988800LL /* 1900-01-01 00:00:00 */ 173 + #define RTC_TIMESTAMP_EPOCH_GPS 315964800LL /* 1980-01-06 00:00:00 */ 173 174 #define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */ 174 175 #define RTC_TIMESTAMP_END_2063 2966371199LL /* 2063-12-31 23:59:59 */ 175 176 #define RTC_TIMESTAMP_END_2079 3471292799LL /* 2079-12-31 23:59:59 */