From 5af9f1fa576874b24627d4c05e3a84672204c200 Mon Sep 17 00:00:00 2001 From: Liu Dalin Date: Fri, 9 May 2025 16:44:16 +0800 Subject: [PATCH 01/16] rtc: loongson: Add missing alarm notifications for ACPI RTC events When an application sets and enables an alarm on Loongson RTC devices, the alarm notification fails to propagate to userspace because the ACPI event handler omits calling rtc_update_irq(). As a result, processes waiting via select() or poll() on RTC device files fail to receive alarm notifications. The ACPI interrupt is also triggered multiple times. In loongson_rtc_handler, we need to clear TOY_MATCH0_REG to resolve this issue. Fixes: 09471d8f5b39 ("rtc: loongson: clear TOY_MATCH0_REG in loongson_rtc_isr()") Fixes: 1b733a9ebc3d ("rtc: Add rtc driver for the Loongson family chips") Signed-off-by: Liu Dalin Reviewed-by: Binbin Zhou Link: https://lore.kernel.org/r/20250509084416.7979-1-liudalin@kylinsec.com.cn Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-loongson.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/rtc/rtc-loongson.c b/drivers/rtc/rtc-loongson.c index 97e5625c064c..2ca7ffd5d7a9 100644 --- a/drivers/rtc/rtc-loongson.c +++ b/drivers/rtc/rtc-loongson.c @@ -129,6 +129,14 @@ static u32 loongson_rtc_handler(void *id) { struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id; + rtc_update_irq(priv->rtcdev, 1, RTC_AF | RTC_IRQF); + + /* + * The TOY_MATCH0_REG should be cleared 0 here, + * otherwise the interrupt cannot be cleared. + */ + regmap_write(priv->regmap, TOY_MATCH0_REG, 0); + spin_lock(&priv->lock); /* Disable RTC alarm wakeup and interrupt */ writel(readl(priv->pm_base + PM1_EN_REG) & ~RTC_EN, -- 2.51.0 From 0a68f5be7883ecda536a62f4877a373cfec05089 Mon Sep 17 00:00:00 2001 From: Ryan Wanner Date: Mon, 14 Apr 2025 14:41:21 -0700 Subject: [PATCH 02/16] dt-bindings: rtc: at91rm9200: add microchip,sama7d65-rtc Add SAMA7D65 RTC compatible to DT bindings documentation. Signed-off-by: Ryan Wanner Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/a274485331628be0bcf382b1ba489d4555fa49c8.1744666011.git.Ryan.Wanner@microchip.com Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml b/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml index c8bb2eef442d..7c5b13caa40b 100644 --- a/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml @@ -23,7 +23,9 @@ properties: - microchip,sam9x60-rtc - microchip,sama7g5-rtc - items: - - const: microchip,sam9x7-rtc + - enum: + - microchip,sam9x7-rtc + - microchip,sama7d65-rtc - const: microchip,sam9x60-rtc reg: -- 2.51.0 From bf1c27c6d540c84f2de00e43b2efcbd214c8d63c Mon Sep 17 00:00:00 2001 From: Ryan Wanner Date: Mon, 14 Apr 2025 14:41:22 -0700 Subject: [PATCH 03/16] dt-bindings: at91rm9260-rtt: add microchip,sama7d65-rtt Add SAMA7D65 RTT compatible to DT bindings documentation. Signed-off-by: Ryan Wanner Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/183474a65377f4030360166a5f2659af7323e82b.1744666011.git.Ryan.Wanner@microchip.com Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml b/Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml index a7f6c1d1a08a..9c9b981fe38b 100644 --- a/Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml +++ b/Documentation/devicetree/bindings/rtc/atmel,at91sam9260-rtt.yaml @@ -22,6 +22,7 @@ properties: - enum: - microchip,sam9x60-rtt - microchip,sam9x7-rtt + - microchip,sama7d65-rtt - const: atmel,at91sam9260-rtt - items: - const: microchip,sama7g5-rtt -- 2.51.0 From 8568d8b4daecb8c1ac6a7a883ee2da7b70f11b20 Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Thu, 3 Apr 2025 13:33:43 +0300 Subject: [PATCH 04/16] dt-bindings: rtc: add schema for NXP S32G2/S32G3 SoCs RTC tracks clock time during system suspend and it is used as a wakeup source on S32G2/S32G3 architecture. RTC from S32G2/S32G3 is not battery-powered and it is not kept alive during system reset. Co-developed-by: Bogdan-Gabriel Roman Signed-off-by: Bogdan-Gabriel Roman Co-developed-by: Ghennadi Procopciuc Signed-off-by: Ghennadi Procopciuc Signed-off-by: Ciprian Marian Costea Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250403103346.3064895-2-ciprianmarian.costea@oss.nxp.com Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/nxp,s32g-rtc.yaml | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml diff --git a/Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml b/Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml new file mode 100644 index 000000000000..40fd2fa298fe --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/nxp,s32g-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP S32G2/S32G3 Real Time Clock (RTC) + +maintainers: + - Bogdan Hamciuc + - Ciprian Marian Costea + +description: + RTC hardware module present on S32G2/S32G3 SoCs is used as a wakeup source. + It is not kept alive during system reset and it is not battery-powered. + +allOf: + - $ref: rtc.yaml# + +properties: + compatible: + oneOf: + - enum: + - nxp,s32g2-rtc + - items: + - const: nxp,s32g3-rtc + - const: nxp,s32g2-rtc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: ipg clock drives the access to the RTC iomapped registers + - description: Clock source for the RTC module. Can be selected between + 4 different clock sources using an integrated hardware mux. + On S32G2/S32G3 SoCs, 'source0' is the SIRC clock (~32KHz) and it is + available during standby and runtime. 'source1' is reserved and cannot + be used. 'source2' is the FIRC clock and it is only available during + runtime providing a better resolution (~48MHz). 'source3' is an external + RTC clock source which can be additionally added in hardware. + + clock-names: + items: + - const: ipg + - enum: [ source0, source1, source2, source3 ] + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + + rtc@40060000 { + compatible = "nxp,s32g3-rtc", + "nxp,s32g2-rtc"; + reg = <0x40060000 0x1000>; + interrupts = ; + clocks = <&clks 54>, <&clks 55>; + clock-names = "ipg", "source0"; + }; -- 2.51.0 From 818b6709e1cb0637cbdc67402c17759df39471b4 Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Thu, 3 Apr 2025 13:33:44 +0300 Subject: [PATCH 05/16] rtc: s32g: add NXP S32G2/S32G3 SoC support Add a RTC driver for NXP S32G2/S32G3 SoCs. RTC tracks clock time during system suspend. It can be a wakeup source for the S32G2/S32G3 SoC based boards. The RTC module from S32G2/S32G3 is not battery-powered and it is not kept alive during system reset. Co-developed-by: Bogdan Hamciuc Signed-off-by: Bogdan Hamciuc Co-developed-by: Ghennadi Procopciuc Signed-off-by: Ghennadi Procopciuc Signed-off-by: Ciprian Marian Costea Reviewed-by: Frank Li Tested-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20250403103346.3064895-3-ciprianmarian.costea@oss.nxp.com Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-s32g.c | 385 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 drivers/rtc/rtc-s32g.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 668a28ac3107..9aec922613ce 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -2108,4 +2108,15 @@ config RTC_DRV_AMLOGIC_A4 This driver can also be built as a module. If so, the module will be called "rtc-amlogic-a4". +config RTC_DRV_S32G + tristate "RTC driver for S32G2/S32G3 SoCs" + depends on ARCH_S32 || COMPILE_TEST + depends on COMMON_CLK + help + Say yes to enable RTC driver for platforms based on the + S32G2/S32G3 SoC family. + + This RTC module can be used as a wakeup source. + Please note that it is not battery-powered. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index d0a6bfaa5ebc..4619aa2ac469 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c new file mode 100644 index 000000000000..3a0818e972eb --- /dev/null +++ b/drivers/rtc/rtc-s32g.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025 NXP + */ + +#include +#include +#include +#include +#include +#include + +#define RTCC_OFFSET 0x4ul +#define RTCS_OFFSET 0x8ul +#define APIVAL_OFFSET 0x10ul + +/* RTCC fields */ +#define RTCC_CNTEN BIT(31) +#define RTCC_APIEN BIT(15) +#define RTCC_APIIE BIT(14) +#define RTCC_CLKSEL_MASK GENMASK(13, 12) +#define RTCC_DIV512EN BIT(11) +#define RTCC_DIV32EN BIT(10) + +/* RTCS fields */ +#define RTCS_INV_API BIT(17) +#define RTCS_APIF BIT(13) + +#define APIVAL_MAX_VAL GENMASK(31, 0) +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) + +/* + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and + * should not be used. + */ +#define RTC_CLK_SRC1_RESERVED BIT(1) + +/* + * S32G RTC module has a 512 value and a 32 value hardware frequency + * divisors (DIV512 and DIV32) which could be used to achieve higher + * counter ranges by lowering the RTC frequency. + */ +enum { + DIV1 = 1, + DIV32 = 32, + DIV512 = 512, + DIV512_32 = 16384 +}; + +static const char *const rtc_clk_src[] = { + "source0", + "source1", + "source2", + "source3" +}; + +struct rtc_priv { + struct rtc_device *rdev; + void __iomem *rtc_base; + struct clk *ipg; + struct clk *clk_src; + const struct rtc_soc_data *rtc_data; + u64 rtc_hz; + time64_t sleep_sec; + int irq; + u32 clk_src_idx; +}; + +struct rtc_soc_data { + u32 clk_div; + u32 reserved_clk_mask; +}; + +static const struct rtc_soc_data rtc_s32g2_data = { + .clk_div = DIV512_32, + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, +}; + +static irqreturn_t s32g_rtc_handler(int irq, void *dev) +{ + struct rtc_priv *priv = platform_get_drvdata(dev); + u32 status; + + status = readl(priv->rtc_base + RTCS_OFFSET); + + if (status & RTCS_APIF) { + writel(0x0, priv->rtc_base + APIVAL_OFFSET); + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); + } + + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +/* + * The function is not really getting time from the RTC since the S32G RTC + * has several limitations. Thus, to setup alarm use system time. + */ +static int s32g_rtc_read_time(struct device *dev, + struct rtc_time *tm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + time64_t sec; + + if (check_add_overflow(ktime_get_real_seconds(), + priv->sleep_sec, &sec)) + return -ERANGE; + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc, rtcs; + + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcs = readl(priv->rtc_base + RTCS_OFFSET); + + alrm->enabled = rtcc & RTCC_APIIE; + if (alrm->enabled) + alrm->pending = !(rtcs & RTCS_APIF); + + return 0; +} + +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc; + + /* RTC API functionality is used both for triggering interrupts + * and as a wakeup event. Hence it should always be enabled. + */ + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcc |= RTCC_APIEN | RTCC_APIIE; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + + return 0; +} + +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + unsigned long long cycles; + long long t_offset; + time64_t alrm_time; + u32 rtcs; + int ret; + + alrm_time = rtc_tm_to_time64(&alrm->time); + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; + if (t_offset < 0) + return -ERANGE; + + cycles = t_offset * priv->rtc_hz; + if (cycles > APIVAL_MAX_VAL) + return -ERANGE; + + /* APIVAL could have been reset from the IRQ handler. + * Hence, we wait in case there is a synchronization process. + */ + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); + if (ret) + return ret; + + writel(cycles, priv->rtc_base + APIVAL_OFFSET); + + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); +} + +/* + * Disable the 32-bit free running counter. + * This allows Clock Source and Divisors selection + * to be performed without causing synchronization issues. + */ +static void s32g_rtc_disable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc &= ~RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static void s32g_rtc_enable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc |= RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static int rtc_clk_src_setup(struct rtc_priv *priv) +{ + u32 rtcc; + + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); + + switch (priv->rtc_data->clk_div) { + case DIV512_32: + rtcc |= RTCC_DIV512EN; + rtcc |= RTCC_DIV32EN; + break; + case DIV512: + rtcc |= RTCC_DIV512EN; + break; + case DIV32: + rtcc |= RTCC_DIV32EN; + break; + case DIV1: + break; + default: + return -EINVAL; + } + + rtcc |= RTCC_APIEN | RTCC_APIIE; + /* + * Make sure the CNTEN is 0 before we configure + * the clock source and dividers. + */ + s32g_rtc_disable(priv); + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + s32g_rtc_enable(priv); + + return 0; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = s32g_rtc_read_time, + .read_alarm = s32g_rtc_read_alarm, + .set_alarm = s32g_rtc_set_alarm, + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, +}; + +static int rtc_clk_dts_setup(struct rtc_priv *priv, + struct device *dev) +{ + u32 i; + + priv->ipg = devm_clk_get_enabled(dev, "ipg"); + if (IS_ERR(priv->ipg)) + return dev_err_probe(dev, PTR_ERR(priv->ipg), + "Failed to get 'ipg' clock\n"); + + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { + if (priv->rtc_data->reserved_clk_mask & BIT(i)) + return -EOPNOTSUPP; + + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); + if (!IS_ERR(priv->clk_src)) { + priv->clk_src_idx = i; + break; + } + } + + if (IS_ERR(priv->clk_src)) + return dev_err_probe(dev, PTR_ERR(priv->clk_src), + "Failed to get rtc module clock source\n"); + + return 0; +} + +static int s32g_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_priv *priv; + unsigned long rtc_hz; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rtc_data = of_device_get_match_data(dev); + if (!priv->rtc_data) + return -ENODEV; + + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->rtc_base)) + return PTR_ERR(priv->rtc_base); + + device_init_wakeup(dev, true); + + ret = rtc_clk_dts_setup(priv, dev); + if (ret) + return ret; + + priv->rdev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rdev)) + return PTR_ERR(priv->rdev); + + ret = rtc_clk_src_setup(priv); + if (ret) + return ret; + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + ret = priv->irq; + goto disable_rtc; + } + + rtc_hz = clk_get_rate(priv->clk_src); + if (!rtc_hz) { + dev_err(dev, "Failed to get RTC frequency\n"); + ret = -EINVAL; + goto disable_rtc; + } + + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); + + platform_set_drvdata(pdev, priv); + priv->rdev->ops = &rtc_ops; + + ret = devm_request_irq(dev, priv->irq, + s32g_rtc_handler, 0, dev_name(dev), pdev); + if (ret) { + dev_err(dev, "Request interrupt %d failed, error: %d\n", + priv->irq, ret); + goto disable_rtc; + } + + ret = devm_rtc_register_device(priv->rdev); + if (ret) + goto disable_rtc; + + return 0; + +disable_rtc: + s32g_rtc_disable(priv); + return ret; +} + +static int s32g_rtc_suspend(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); + + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), + &priv->sleep_sec)) { + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); + priv->sleep_sec = 0; + } + + return 0; +} + +static int s32g_rtc_resume(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + + /* The transition from resume to run is a reset event. + * This leads to the RTC registers being reset after resume from + * suspend. It is uncommon, but this behaviour has been observed + * on S32G RTC after issuing a Suspend to RAM operation. + * Thus, reconfigure RTC registers on the resume path. + */ + return rtc_clk_src_setup(priv); +} + +static const struct of_device_id rtc_dt_ids[] = { + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, + { /* sentinel */ }, +}; + +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, + s32g_rtc_suspend, s32g_rtc_resume); + +static struct platform_driver s32g_rtc_driver = { + .driver = { + .name = "s32g-rtc", + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), + .of_match_table = rtc_dt_ids, + }, + .probe = s32g_rtc_probe, +}; +module_platform_driver(s32g_rtc_driver); + +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); +MODULE_LICENSE("GPL"); -- 2.51.0 From 1a7ed2fffbe3096f1b15787b913547db887733c3 Mon Sep 17 00:00:00 2001 From: "A. Niyas Ahamed Mydeen" Date: Wed, 2 Apr 2025 17:35:46 +0530 Subject: [PATCH 06/16] rtc: m41t80: kickstart ocillator upon failure The ocillator on the m41t62 (and other chips of this type) needs a kickstart upon a failure; the RTC read routine will notice the oscillator failure and fail reads. This is added in the RTC write routine; this allows the system to know that the time in the RTC is accurate. This is following the procedure described in section 3.11 of "https://www.st.com/resource/en/datasheet/m41t62.pdf" Signed-off-by: A. Niyas Ahamed Mydeen Reviewed-by: Corey Minyard Link: https://lore.kernel.org/r/20250402120546.336657-2-nmydeen@mvista.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m41t80.c | 68 ++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 1f58ae8b151e..7074d086f1c8 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef CONFIG_RTC_DRV_M41T80_WDT #include #include @@ -204,7 +205,7 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) return flags; if (flags & M41T80_FLAGS_OF) { - dev_err(&client->dev, "Oscillator failure, data is invalid.\n"); + dev_err(&client->dev, "Oscillator failure, time may not be accurate, write time to RTC to fix it.\n"); return -EINVAL; } @@ -227,21 +228,31 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) return 0; } -static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm) +static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm) { struct i2c_client *client = to_i2c_client(dev); struct m41t80_data *clientdata = i2c_get_clientdata(client); + struct rtc_time tm = *in_tm; unsigned char buf[8]; int err, flags; + time64_t time = 0; + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + if (flags & M41T80_FLAGS_OF) { + /* add 4sec of oscillator stablize time otherwise we are behind 4sec */ + time = rtc_tm_to_time64(&tm); + rtc_time64_to_tm(time + 4, &tm); + } buf[M41T80_REG_SSEC] = 0; - buf[M41T80_REG_SEC] = bin2bcd(tm->tm_sec); - buf[M41T80_REG_MIN] = bin2bcd(tm->tm_min); - buf[M41T80_REG_HOUR] = bin2bcd(tm->tm_hour); - buf[M41T80_REG_DAY] = bin2bcd(tm->tm_mday); - buf[M41T80_REG_MON] = bin2bcd(tm->tm_mon + 1); - buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100); - buf[M41T80_REG_WDAY] = tm->tm_wday; + buf[M41T80_REG_SEC] = bin2bcd(tm.tm_sec); + buf[M41T80_REG_MIN] = bin2bcd(tm.tm_min); + buf[M41T80_REG_HOUR] = bin2bcd(tm.tm_hour); + buf[M41T80_REG_DAY] = bin2bcd(tm.tm_mday); + buf[M41T80_REG_MON] = bin2bcd(tm.tm_mon + 1); + buf[M41T80_REG_YEAR] = bin2bcd(tm.tm_year - 100); + buf[M41T80_REG_WDAY] = tm.tm_wday; /* If the square wave output is controlled in the weekday register */ if (clientdata->features & M41T80_FEATURE_SQ_ALT) { @@ -260,17 +271,34 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm) dev_err(&client->dev, "Unable to write to date registers\n"); return err; } - - /* Clear the OF bit of Flags Register */ - flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); - if (flags < 0) - return flags; - - err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, - flags & ~M41T80_FLAGS_OF); - if (err < 0) { - dev_err(&client->dev, "Unable to write flags register\n"); - return err; + if (flags & M41T80_FLAGS_OF) { + /* OF cannot be immediately reset: oscillator has to be restarted. */ + dev_warn(&client->dev, "OF bit is still set, kickstarting clock.\n"); + err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, M41T80_SEC_ST); + if (err < 0) { + dev_err(&client->dev, "Can't set ST bit\n"); + return err; + } + err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, flags & ~M41T80_SEC_ST); + if (err < 0) { + dev_err(&client->dev, "Can't clear ST bit\n"); + return err; + } + /* oscillator must run for 4sec before we attempt to reset OF bit */ + msleep(4000); + /* Clear the OF bit of Flags Register */ + err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags & ~M41T80_FLAGS_OF); + if (err < 0) { + dev_err(&client->dev, "Unable to write flags register\n"); + return err; + } + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) { + return flags; + } else if (flags & M41T80_FLAGS_OF) { + dev_err(&client->dev, "Can't clear the OF bit check battery\n"); + return err; + } } return err; -- 2.51.0 From c7622a4e44d9d008e0e5edcc46c71854c50cf4a8 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 26 May 2025 00:21:52 +0200 Subject: [PATCH 07/16] rtc: m41t80: reduce verbosity The driver currently uses dev_err for messages that have a very low probability of being read by the user as the error will probably never happen and the systems with the RTC probably don't have any user able to read the message. Moreover, the only user action after getting this message is the restart the action so drop the level to dev_dbg. Link: https://lore.kernel.org/r/20250525222153.1472917-1-alexandre.belloni@bootlin.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m41t80.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 7074d086f1c8..c568639d2151 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -212,7 +212,7 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) err = i2c_smbus_read_i2c_block_data(client, M41T80_REG_SSEC, sizeof(buf), buf); if (err < 0) { - dev_err(&client->dev, "Unable to read date\n"); + dev_dbg(&client->dev, "Unable to read date\n"); return err; } @@ -268,7 +268,7 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm) err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC, sizeof(buf), buf); if (err < 0) { - dev_err(&client->dev, "Unable to write to date registers\n"); + dev_dbg(&client->dev, "Unable to write to date registers\n"); return err; } if (flags & M41T80_FLAGS_OF) { @@ -276,12 +276,12 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm) dev_warn(&client->dev, "OF bit is still set, kickstarting clock.\n"); err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, M41T80_SEC_ST); if (err < 0) { - dev_err(&client->dev, "Can't set ST bit\n"); + dev_dbg(&client->dev, "Can't set ST bit\n"); return err; } err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, flags & ~M41T80_SEC_ST); if (err < 0) { - dev_err(&client->dev, "Can't clear ST bit\n"); + dev_dbg(&client->dev, "Can't clear ST bit\n"); return err; } /* oscillator must run for 4sec before we attempt to reset OF bit */ @@ -289,14 +289,14 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm) /* Clear the OF bit of Flags Register */ err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags & ~M41T80_FLAGS_OF); if (err < 0) { - dev_err(&client->dev, "Unable to write flags register\n"); + dev_dbg(&client->dev, "Unable to write flags register\n"); return err; } flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); if (flags < 0) { return flags; } else if (flags & M41T80_FLAGS_OF) { - dev_err(&client->dev, "Can't clear the OF bit check battery\n"); + dev_dbg(&client->dev, "Can't clear the OF bit check battery\n"); return err; } } @@ -336,7 +336,7 @@ static int m41t80_alarm_irq_enable(struct device *dev, unsigned int enabled) retval = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, flags); if (retval < 0) { - dev_err(dev, "Unable to enable alarm IRQ %d\n", retval); + dev_dbg(dev, "Unable to enable alarm IRQ %d\n", retval); return retval; } return 0; @@ -361,7 +361,7 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret & ~(M41T80_ALMON_AFE)); if (err < 0) { - dev_err(dev, "Unable to clear AFE bit\n"); + dev_dbg(dev, "Unable to clear AFE bit\n"); return err; } @@ -375,7 +375,7 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, ret & ~(M41T80_FLAGS_AF)); if (err < 0) { - dev_err(dev, "Unable to clear AF bit\n"); + dev_dbg(dev, "Unable to clear AF bit\n"); return err; } -- 2.51.0 From ae95a7e3214092900fc0688224626b203f95f165 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 May 2025 11:58:02 +0200 Subject: [PATCH 08/16] dt-bindings: rtc: rzn1: add optional second clock The external crystal can be a second clock input. It is needed for the SCMP counting method which allows using crystals different than 32768Hz. It is also needed for an upcoming SoC which only supports the SCMP method. Signed-off-by: Wolfram Sang Reviewed-by: Krzysztof Kozlowski Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20250526095801.35781-6-wsa+renesas@sang-engineering.com Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/renesas,rzn1-rtc.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml b/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml index f6e0c613af67..f6fdcc7090b6 100644 --- a/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml @@ -33,10 +33,14 @@ properties: - const: pps clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 clock-names: - const: hclk + minItems: 1 + items: + - const: hclk + - const: xtal power-domains: maxItems: 1 -- 2.51.0 From a50f00cb87b1c69eb4a08ae5c79e9536da839dfd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 May 2025 11:58:03 +0200 Subject: [PATCH 09/16] rtc: rzn1: Disable controller before initialization Datasheet says that the controller must be disabled before setting up either SUBU or SCMP. This did not matter so far because the driver only supported SUBU which was the default, too. It is good practice to follow datasheet recommendations, though. It will also be needed because SCMP mode will be added in a later patch. Signed-off-by: Wolfram Sang Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/r/20250526095801.35781-7-wsa+renesas@sang-engineering.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rzn1.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c index 3c2861983ff1..7777df1e3426 100644 --- a/drivers/rtc/rtc-rzn1.c +++ b/drivers/rtc/rtc-rzn1.c @@ -25,6 +25,7 @@ #define RZN1_RTC_CTL0_SLSB_SUBU 0 #define RZN1_RTC_CTL0_SLSB_SCMP BIT(4) #define RZN1_RTC_CTL0_AMPM BIT(5) +#define RZN1_RTC_CTL0_CEST BIT(6) #define RZN1_RTC_CTL0_CE BIT(7) #define RZN1_RTC_CTL1 0x04 @@ -369,6 +370,7 @@ static const struct rtc_class_ops rzn1_rtc_ops = { static int rzn1_rtc_probe(struct platform_device *pdev) { struct rzn1_rtc *rtc; + u32 val; int irq; int ret; @@ -406,6 +408,14 @@ static int rzn1_rtc_probe(struct platform_device *pdev) * Ensure the clock counter is enabled. * Set 24-hour mode and possible oscillator offset compensation in SUBU mode. */ + val = readl(rtc->base + RZN1_RTC_CTL0) & ~RZN1_RTC_CTL0_CE; + writel(val, rtc->base + RZN1_RTC_CTL0); + /* Wait 2-4 32k clock cycles for the disabled controller */ + ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL0, val, + !(val & RZN1_RTC_CTL0_CEST), 62, 123); + if (ret) + goto dis_runtime_pm; + writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU, rtc->base + RZN1_RTC_CTL0); -- 2.51.0 From f096bdbf08c9c696a6b5c18a36b75c43f8d86eba Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 May 2025 11:58:04 +0200 Subject: [PATCH 10/16] rtc: rzn1: support input frequencies other than 32768Hz When using the SCMP mode instead of SUBU, this RTC can also support other input frequencies than 32768Hz. Also, upcoming SoCs will only support SCMP. Signed-off-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/r/20250526095801.35781-8-wsa+renesas@sang-engineering.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rzn1.c | 58 +++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c index 7777df1e3426..c4ed43735457 100644 --- a/drivers/rtc/rtc-rzn1.c +++ b/drivers/rtc/rtc-rzn1.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #define RZN1_RTC_CTL0 0x00 -#define RZN1_RTC_CTL0_SLSB_SUBU 0 #define RZN1_RTC_CTL0_SLSB_SCMP BIT(4) #define RZN1_RTC_CTL0_AMPM BIT(5) #define RZN1_RTC_CTL0_CEST BIT(6) @@ -50,6 +50,8 @@ #define RZN1_RTC_SUBU_DEV BIT(7) #define RZN1_RTC_SUBU_DECR BIT(6) +#define RZN1_RTC_SCMP 0x3c + #define RZN1_RTC_ALM 0x40 #define RZN1_RTC_ALH 0x44 #define RZN1_RTC_ALW 0x48 @@ -357,7 +359,7 @@ static int rzn1_rtc_set_offset(struct device *dev, long offset) return 0; } -static const struct rtc_class_ops rzn1_rtc_ops = { +static const struct rtc_class_ops rzn1_rtc_ops_subu = { .read_time = rzn1_rtc_read_time, .set_time = rzn1_rtc_set_time, .read_alarm = rzn1_rtc_read_alarm, @@ -367,12 +369,21 @@ static const struct rtc_class_ops rzn1_rtc_ops = { .set_offset = rzn1_rtc_set_offset, }; +static const struct rtc_class_ops rzn1_rtc_ops_scmp = { + .read_time = rzn1_rtc_read_time, + .set_time = rzn1_rtc_set_time, + .read_alarm = rzn1_rtc_read_alarm, + .set_alarm = rzn1_rtc_set_alarm, + .alarm_irq_enable = rzn1_rtc_alarm_irq_enable, +}; + static int rzn1_rtc_probe(struct platform_device *pdev) { struct rzn1_rtc *rtc; - u32 val; - int irq; - int ret; + u32 val, scmp_val = 0; + struct clk *xtal; + unsigned long rate; + int irq, ret; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) @@ -395,7 +406,6 @@ static int rzn1_rtc_probe(struct platform_device *pdev) rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; rtc->rtcdev->alarm_offset_max = 7 * 86400; - rtc->rtcdev->ops = &rzn1_rtc_ops; ret = devm_pm_runtime_enable(&pdev->dev); if (ret < 0) @@ -404,10 +414,24 @@ static int rzn1_rtc_probe(struct platform_device *pdev) if (ret < 0) return ret; - /* - * Ensure the clock counter is enabled. - * Set 24-hour mode and possible oscillator offset compensation in SUBU mode. - */ + /* Only switch to scmp if we have an xtal clock with a valid rate and != 32768 */ + xtal = devm_clk_get_optional(&pdev->dev, "xtal"); + if (IS_ERR(xtal)) { + ret = PTR_ERR(xtal); + goto dis_runtime_pm; + } else if (xtal) { + rate = clk_get_rate(xtal); + + if (rate < 32000 || rate > BIT(22)) { + ret = -EOPNOTSUPP; + goto dis_runtime_pm; + } + + if (rate != 32768) + scmp_val = RZN1_RTC_CTL0_SLSB_SCMP; + } + + /* Disable controller during SUBU/SCMP setup */ val = readl(rtc->base + RZN1_RTC_CTL0) & ~RZN1_RTC_CTL0_CE; writel(val, rtc->base + RZN1_RTC_CTL0); /* Wait 2-4 32k clock cycles for the disabled controller */ @@ -416,8 +440,18 @@ static int rzn1_rtc_probe(struct platform_device *pdev) if (ret) goto dis_runtime_pm; - writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU, - rtc->base + RZN1_RTC_CTL0); + /* Set desired modes leaving the controller disabled */ + writel(RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0); + + if (scmp_val) { + writel(rate - 1, rtc->base + RZN1_RTC_SCMP); + rtc->rtcdev->ops = &rzn1_rtc_ops_scmp; + } else { + rtc->rtcdev->ops = &rzn1_rtc_ops_subu; + } + + /* Enable controller finally */ + writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0); /* Disable all interrupts */ writel(0, rtc->base + RZN1_RTC_CTL1); -- 2.51.0 From 1e15a6803df01471e0065dabd8b9563692c47219 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Sat, 31 May 2025 15:20:40 +0800 Subject: [PATCH 11/16] rtc: pcf8563: fix wrong alarm register When the regmap framework was introduced to this driver, the PCF8563_REG_AMN register within the set_alarm function was incorrectly changed to PCF8563_REG_SC. The PCF8563_REG_SC register is the seconds register. This caused alarm values to be written to the seconds register when an alarm was set. Which means the alarm would not trigger as expected and the seconds register would be overwritten with an incorrect value. Signed-off-by: Troy Mitchell Link: https://lore.kernel.org/r/20250531-pcf8563-fix-alarm-v2-1-cac4b1716167@gmail.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf8563.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 5a084d426e58..b2611697fa5e 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -285,7 +285,7 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) buf[2] = bin2bcd(tm->time.tm_mday); buf[3] = tm->time.tm_wday & 0x07; - err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, buf, + err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_AMN, buf, sizeof(buf)); if (err) return err; -- 2.51.0 From 7df4cfef8b351fec3156160bedfc7d6d29de4cce Mon Sep 17 00:00:00 2001 From: Alexandre Mergnat Date: Mon, 28 Apr 2025 12:06:47 +0200 Subject: [PATCH 12/16] rtc: Make rtc_time64_to_tm() support dates before 1970 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Conversion of dates before 1970 is still relevant today because these dates are reused on some hardwares to store dates bigger than the maximal date that is representable in the device's native format. This prominently and very soon affects the hardware covered by the rtc-mt6397 driver that can only natively store dates in the interval 1900-01-01 up to 2027-12-31. So to store the date 2028-01-01 00:00:00 to such a device, rtc_time64_to_tm() must do the right thing for time=-2208988800. Signed-off-by: Alexandre Mergnat Reviewed-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250428-enable-rtc-v4-1-2b2f7e3f9349@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/lib.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c index fe361652727a..13b5b1f20465 100644 --- a/drivers/rtc/lib.c +++ b/drivers/rtc/lib.c @@ -46,24 +46,38 @@ EXPORT_SYMBOL(rtc_year_days); * rtc_time64_to_tm - converts time64_t to rtc_time. * * @time: The number of seconds since 01-01-1970 00:00:00. - * (Must be positive.) + * Works for values since at least 1900 * @tm: Pointer to the struct rtc_time. */ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) { - unsigned int secs; - int days; + int days, secs; u64 u64tmp; u32 u32tmp, udays, century, day_of_century, year_of_century, year, day_of_year, month, day; bool is_Jan_or_Feb, is_leap_year; - /* time must be positive */ + /* + * Get days and seconds while preserving the sign to + * handle negative time values (dates before 1970-01-01) + */ days = div_s64_rem(time, 86400, &secs); + /* + * We need 0 <= secs < 86400 which isn't given for negative + * values of time. Fixup accordingly. + */ + if (secs < 0) { + days -= 1; + secs += 86400; + } + /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; + /* Ensure tm_wday is always positive */ + if (tm->tm_wday < 0) + tm->tm_wday += 7; /* * The following algorithm is, basically, Proposition 6.3 of Neri @@ -93,7 +107,7 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) * thus, is slightly different from [1]. */ - udays = ((u32) days) + 719468; + udays = days + 719468; u32tmp = 4 * udays + 3; century = u32tmp / 146097; -- 2.51.0 From fe9f5f96cfe8b82d0f24cbfa93718925560f4f8d Mon Sep 17 00:00:00 2001 From: Alexandre Mergnat Date: Mon, 28 Apr 2025 12:06:48 +0200 Subject: [PATCH 13/16] rtc: Fix offset calculation for .start_secs < 0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The comparison rtc->start_secs > rtc->range_max has a signed left-hand side and an unsigned right-hand side. So the comparison might become true for negative start_secs which is interpreted as a (possibly very large) positive value. As a negative value can never be bigger than an unsigned value the correct representation of the (mathematical) comparison rtc->start_secs > rtc->range_max in C is: rtc->start_secs >= 0 && rtc->start_secs > rtc->range_max Use that to fix the offset calculation currently used in the rtc-mt6397 driver. Fixes: 989515647e783 ("rtc: Add one offset seconds to expand RTC range") Signed-off-by: Alexandre Mergnat Reviewed-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250428-enable-rtc-v4-2-2b2f7e3f9349@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index b88cd4fb295b..b1a2be1f9e3b 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -326,7 +326,7 @@ static void rtc_device_get_offset(struct rtc_device *rtc) * * Otherwise the offset seconds should be 0. */ - if (rtc->start_secs > rtc->range_max || + if ((rtc->start_secs >= 0 && rtc->start_secs > rtc->range_max) || rtc->start_secs + range_secs - 1 < rtc->range_min) rtc->offset_secs = rtc->start_secs - rtc->range_min; else if (rtc->start_secs > rtc->range_min) -- 2.51.0 From 46351921cbe11403a4fcab00e76fa03d99a7aef2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 28 Apr 2025 12:06:49 +0200 Subject: [PATCH 14/16] rtc: test: Emit the seconds-since-1970 value instead of days-since-1970 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is easier to handle because you can just consult date(1) to convert between a seconds-since-1970 value and a date string: $ date --utc -d @3661 Thu Jan 1 01:01:01 AM UTC 1970 $ date -d "Jan 1 12:00:00 AM UTC 1900" +%s -2208988800 The intended side effect is that this prepares the test for dates before 1970. The division of a negative value by 86400 doesn't result in the desired days-since-1970 value as e.g. secs=-82739 should map to days=-1. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Mergnat Link: https://lore.kernel.org/r/20250428-enable-rtc-v4-3-2b2f7e3f9349@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/lib_test.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/rtc/lib_test.c b/drivers/rtc/lib_test.c index c30c759662e3..c0faddc04c9b 100644 --- a/drivers/rtc/lib_test.c +++ b/drivers/rtc/lib_test.c @@ -46,16 +46,13 @@ static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years) struct rtc_time result; time64_t secs; - s64 days; for (secs = 0; secs <= total_secs; secs += 86400) { rtc_time64_to_tm(secs, &result); - days = div_s64(secs, 86400); - #define FAIL_MSG "%d/%02d/%02d (%2d) : %lld", \ - year, month, mday, yday, days + year, month, mday, yday, secs KUNIT_ASSERT_EQ_MSG(test, year - 1900, result.tm_year, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, month - 1, result.tm_mon, FAIL_MSG); -- 2.51.0 From da62b49830f83d1c80bfa1f2208db0cc6c19a1f9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 28 Apr 2025 12:06:50 +0200 Subject: [PATCH 15/16] rtc: test: Also test time and wday outcome of rtc_time64_to_tm() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit To cover calculation of the time and wday in the rtc kunit test also check tm_hour, tm_min, tm_sec and tm_wday of the rtc_time calculated by rtc_time64_to_tm(). There are no surprises, the two tests making use of rtc_time64_to_tm_test_date_range() continue to succeed. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Mergnat Link: https://lore.kernel.org/r/20250428-enable-rtc-v4-4-2b2f7e3f9349@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/lib_test.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/lib_test.c b/drivers/rtc/lib_test.c index c0faddc04c9b..b1ac0701d42e 100644 --- a/drivers/rtc/lib_test.c +++ b/drivers/rtc/lib_test.c @@ -6,8 +6,10 @@ /* * Advance a date by one day. */ -static void advance_date(int *year, int *month, int *mday, int *yday) +static void advance_date(int *year, int *month, int *mday, int *yday, int *wday) { + *wday = (*wday + 1) % 7; + if (*mday != rtc_month_days(*month - 1, *year)) { ++*mday; ++*yday; @@ -43,23 +45,29 @@ static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years) int month = 1; int mday = 1; int yday = 1; + int wday = 4; /* Jan 1st 1970 was a Thursday */ struct rtc_time result; time64_t secs; + const time64_t sec_offset = ((1 * 60) + 2) * 60 + 3; for (secs = 0; secs <= total_secs; secs += 86400) { - rtc_time64_to_tm(secs, &result); + rtc_time64_to_tm(secs + sec_offset, &result); - #define FAIL_MSG "%d/%02d/%02d (%2d) : %lld", \ - year, month, mday, yday, secs + #define FAIL_MSG "%d/%02d/%02d (%2d, %d) : %lld", \ + year, month, mday, yday, wday, secs + sec_offset KUNIT_ASSERT_EQ_MSG(test, year - 1900, result.tm_year, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, month - 1, result.tm_mon, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, mday, result.tm_mday, FAIL_MSG); KUNIT_ASSERT_EQ_MSG(test, yday, result.tm_yday, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 1, result.tm_hour, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 2, result.tm_min, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 3, result.tm_sec, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, wday, result.tm_wday, FAIL_MSG); - advance_date(&year, &month, &mday, &yday); + advance_date(&year, &month, &mday, &yday, &wday); } } -- 2.51.0 From ccb2dba3c19f04d2203a4b630180bf50fe710d22 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 28 Apr 2025 12:06:51 +0200 Subject: [PATCH 16/16] rtc: test: Test date conversion for dates starting in 1900 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit While the RTC framework intends to only handle dates after 1970 for consumers, time conversion must also work for earlier dates to cover e.g. storing dates beyond an RTC's range_max. This is most relevant for the rtc-mt6397 driver that has range_min = RTC_TIMESTAMP_BEGIN_1900; range_max = mktime64(2027, 12, 31, 23, 59, 59); and so needs working support for timestamps in 1900 starting in less than three years. So shift the tested interval of timestamps to also cover years 1900 to 1970. Signed-off-by: Uwe Kleine-König Signed-off-by: Alexandre Mergnat Link: https://lore.kernel.org/r/20250428-enable-rtc-v4-5-2b2f7e3f9349@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/lib_test.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/lib_test.c b/drivers/rtc/lib_test.c index b1ac0701d42e..0eebad1fe2a0 100644 --- a/drivers/rtc/lib_test.c +++ b/drivers/rtc/lib_test.c @@ -41,15 +41,15 @@ static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years) */ time64_t total_secs = ((time64_t)years) / 400 * 146097 * 86400; - int year = 1970; + int year = 1900; int month = 1; int mday = 1; int yday = 1; - int wday = 4; /* Jan 1st 1970 was a Thursday */ + int wday = 1; /* Jan 1st 1900 was a Monday */ struct rtc_time result; time64_t secs; - const time64_t sec_offset = ((1 * 60) + 2) * 60 + 3; + const time64_t sec_offset = RTC_TIMESTAMP_BEGIN_1900 + ((1 * 60) + 2) * 60 + 3; for (secs = 0; secs <= total_secs; secs += 86400) { @@ -72,7 +72,7 @@ static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years) } /* - * Checks every day in a 160000 years interval starting on 1970-01-01 + * Checks every day in a 160000 years interval starting on 1900-01-01 * against the expected result. */ static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test) @@ -81,7 +81,7 @@ static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test) } /* - * Checks every day in a 1000 years interval starting on 1970-01-01 + * Checks every day in a 1000 years interval starting on 1900-01-01 * against the expected result. */ static void rtc_time64_to_tm_test_date_range_1000(struct kunit *test) -- 2.51.0