Linux RTC
 help / color / mirror / Atom feed
* Re: [PATCH v2] dt-bindings: arm: vt8500: via,vt8500-pmc: Convert to DT Schema
From: Alexey Charkov @ 2026-06-01  8:47 UTC (permalink / raw)
  To: Udaya Kiran Challa
  Cc: krzk, robh, krzk+dt, conor+dt, skhan, me, linux-rtc, devicetree,
	linux-kernel
In-Reply-To: <20260531171612.4536-1-challauday369@gmail.com>

On Sun, May 31, 2026 at 9:16 PM Udaya Kiran Challa
<challauday369@gmail.com> wrote:
>
> Convert the VIA/Wondermedia VT8500 Power Management controller binding
> from the legacy text format to DT schema.
>
> Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
> ---
> Changelog:
> Changes since v1:
> - Make child node binding constrained
> - Fix example to include the clock container node
> - Fix maintainers list
>
> Link to v1:https://lore.kernel.org/all/20260524110047.37590-1-challauday369@gmail.com/
> ---
>  .../bindings/arm/vt8500/via,vt8500-pmc.txt    | 13 -----
>  .../bindings/arm/vt8500/via,vt8500-pmc.yaml   | 53 +++++++++++++++++++
>  2 files changed, 53 insertions(+), 13 deletions(-)
>  delete mode 100644 Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt
>  create mode 100644 Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.yaml
>
> diff --git a/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt
> deleted file mode 100644
> index 521b9c7de933..000000000000
> --- a/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -VIA/Wondermedia VT8500 Power Management Controller
> ------------------------------------------------------
> -
> -Required properties:
> -- compatible : "via,vt8500-pmc"
> -- reg : Should contain 1 register ranges(address and length)
> -
> -Example:
> -
> -       pmc@d8130000 {
> -               compatible = "via,vt8500-pmc";
> -               reg = <0xd8130000 0x1000>;
> -       };
> diff --git a/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.yaml b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.yaml
> new file mode 100644
> index 000000000000..e340281e5726
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.yaml
> @@ -0,0 +1,53 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/arm/vt8500/via,vt8500-pmc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: VIA/Wondermedia VT8500 Power Management Controller
> +
> +maintainers:
> +  - Alexey Charkov <alchark@gmail.com>
> +  - Krzysztof Kozlowski <krzk@kernel.org>
> +
> +description:
> +  The VIA/Wondermedia Power Management Controller provides register access for
> +  clock and power management functions on VT8500 and WM8xxx series SoCs.
> +
> +properties:
> +  compatible:
> +    const: via,vt8500-pmc

Each SoC version had different registers within the PMC node (even
though key components such as the OS timer and reset control kept the
same offsets and function). So this really should be a SoC-versioned
enum rather than a single "compatible", as they were never really
mutually compatible (despite what current DTs say).

> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    type: object
> +    description: Container node for VT8500/WM8xxx clock provider nodes.
> +    properties:
> +      "#address-cells":
> +        const: 1
> +      "#size-cells":
> +        const: 0
> +    required:
> +      - "#address-cells"
> +      - "#size-cells"

#address-cells and #size-cells are automatically required once the
subnodes have regs, so it seems that this whole section could become
just "clocks: true" with no particular loss of meaning.

> +    additionalProperties: true

If the PMC binding ends up separate from the clock control bindings
(which I'm somewhat skeptical of, see [1]), shouldn't the subnodes of
this clocks node be explicitly specified to follow that binding? The
fixed 25 MHz reference clock is not part of the PMC and doesn't belong
there (despite what current device trees contain), but the rest do.

[1] https://lore.kernel.org/all/CABjd4YzJoHrx1suMbhNvrMP+X=d=thvXYwSY=UD-4Qy=7-qmjw@mail.gmail.com/

Best regards,
Alexey

> +
> +required:
> +  - compatible
> +  - reg
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    pmc@d8130000 {
> +        compatible = "via,vt8500-pmc";
> +        reg = <0xd8130000 0x1000>;
> +
> +        clocks {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +        };
> +    };
> --
> 2.43.0

^ permalink raw reply

* [PATCH 0/2] rtc: aspeed: Add AST2700 RTC support
From: Tommy Huang @ 2026-06-01  9:14 UTC (permalink / raw)
  To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery
  Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel,
	linux-aspeed, Tommy Huang

This patch series adds support for the ASPEED AST2700 RTC define,
includeing dt-binding in the Document and rtc-aspeed.c

Signed-off-by: Tommy Huang <tommy_huang@aspeedtech.com>
---
Tommy Huang (2):
      dt-bindings: rtc: add ASPEED AST2700 compatible
      rtc: aspeed: add AST2700 compatible

 Documentation/devicetree/bindings/rtc/trivial-rtc.yaml | 2 ++
 drivers/rtc/rtc-aspeed.c                               | 1 +
 2 files changed, 3 insertions(+)
---
base-commit: e43ffb69e0438cddd72aaa30898b4dc446f664f8
change-id: 20260601-ast2700-rtc-d4adb51d2148

Best regards,
-- 
Tommy Huang <tommy_huang@aspeedtech.com>


^ permalink raw reply

* [PATCH 1/2] dt-bindings: rtc: add ASPEED AST2700 compatible
From: Tommy Huang @ 2026-06-01  9:14 UTC (permalink / raw)
  To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery
  Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel,
	linux-aspeed, Tommy Huang
In-Reply-To: <20260601-ast2700-rtc-v1-0-15d4ca46500a@aspeedtech.com>

Document the compatible string for the RTC controller found on
ASPEED AST2700 SoCs.

Signed-off-by: Tommy Huang <tommy_huang@aspeedtech.com>
---
 Documentation/devicetree/bindings/rtc/trivial-rtc.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml
index 722176c831aa..f4d0eed98a08 100644
--- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml
+++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml
@@ -30,6 +30,8 @@ properties:
       - aspeed,ast2500-rtc
       # ASPEED BMC ast2600 Real-time Clock
       - aspeed,ast2600-rtc
+      # ASPEED BMC ast2700 Real-time Clock
+      - aspeed,ast2700-rtc
       # Conexant Digicolor Real Time Clock Controller
       - cnxt,cx92755-rtc
       # I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output

-- 
2.34.1


^ permalink raw reply related

* [PATCH 2/2] rtc: aspeed: add AST2700 compatible
From: Tommy Huang @ 2026-06-01  9:14 UTC (permalink / raw)
  To: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery
  Cc: linux-rtc, devicetree, linux-kernel, linux-arm-kernel,
	linux-aspeed, Tommy Huang
In-Reply-To: <20260601-ast2700-rtc-v1-0-15d4ca46500a@aspeedtech.com>

Add support for matching the RTC controller on ASPEED AST2700 SoCs.

The AST2700 RTC controller is compatible with the existing ASPEED
RTC driver implementation.

Signed-off-by: Tommy Huang <tommy_huang@aspeedtech.com>
---
 drivers/rtc/rtc-aspeed.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c
index 0d0053b52f9b..8f5b440f8c0a 100644
--- a/drivers/rtc/rtc-aspeed.c
+++ b/drivers/rtc/rtc-aspeed.c
@@ -111,6 +111,7 @@ static const struct of_device_id aspeed_rtc_match[] = {
 	{ .compatible = "aspeed,ast2400-rtc", },
 	{ .compatible = "aspeed,ast2500-rtc", },
 	{ .compatible = "aspeed,ast2600-rtc", },
+	{ .compatible = "aspeed,ast2700-rtc", },
 	{}
 };
 MODULE_DEVICE_TABLE(of, aspeed_rtc_match);

-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH v5] dt-bindings: clock: via,vt8500: Convert to DT Schema
From: Krzysztof Kozlowski @ 2026-06-01 11:40 UTC (permalink / raw)
  To: Udaya Kiran Challa
  Cc: alchark, robh, krzk+dt, conor+dt, skhan, me, linux-rtc,
	devicetree, linux-kernel
In-Reply-To: <20260531171041.4149-1-challauday369@gmail.com>

On Sun, May 31, 2026 at 10:39:55PM +0530, Udaya Kiran Challa wrote:
> Convert the VIA/Wondermedia VT8500 and Wondermedia WM8xxx series SoCs clock
> controller binding from the legacy text format to DT schema.
> 
> Signed-off-by: Udaya Kiran Challa <challauday369@gmail.com>
> ---
> Changelog:
> Changes since v4:

My comments from v2 still apply. Almost all of them.

You a PMC binding and preferrably all this goes to that binding.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v2] dt-bindings: arm: vt8500: via,vt8500-pmc: Convert to DT Schema
From: Krzysztof Kozlowski @ 2026-06-01 11:42 UTC (permalink / raw)
  To: Udaya Kiran Challa, alchark, robh, krzk+dt, conor+dt
  Cc: skhan, me, linux-rtc, devicetree, linux-kernel
In-Reply-To: <20260531171612.4536-1-challauday369@gmail.com>

On 31/05/2026 19:15, Udaya Kiran Challa wrote:
> +  clocks:
> +    type: object
> +    description: Container node for VT8500/WM8xxx clock provider nodes.
> +    properties:
> +      "#address-cells":
> +        const: 1
> +      "#size-cells":
> +        const: 0
> +    required:
> +      - "#address-cells"
> +      - "#size-cells"
> +    additionalProperties: true


No improvements. Which part was confusing? Or let's rephrase, from where
did you take such syntax?

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH] dt-bindings: arm: vt8500: via,vt8500-pmc: Convert to DT Schema
From: Krzysztof Kozlowski @ 2026-06-01 11:42 UTC (permalink / raw)
  To: Uday Kiran
  Cc: linux, robh, krzk+dt, conor+dt, skhan, me, linux-rtc, devicetree,
	linux-kernel
In-Reply-To: <CAAj-GBnDiiKPwCQz1KDp6kMx9mQzmxSJpAuanbWsjt4iSzoZQQ@mail.gmail.com>

On 31/05/2026 18:51, Uday Kiran wrote:
>>> +properties:
>>> +  compatible:
>>> +    const: via,vt8500-pmc
>>> +
>>> +  reg:
>>> +    maxItems: 1
>>> +
>>> +  clocks:
>>> +    type: object
>>> +    additionalProperties: true
>>
>> No, binding must be constrained. See writing-bindings or any other
>> binding. If you are unsure how to do something - open other existing
>> bindings. Do you see anywhere such syntax?
> 
> Agreed. The clocks child node is now fully constrained: #address-cells and

No, it is not. What are you speaking about? Open this patch and look -
how anything "IS" there constrained?



Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v4] dt-bindings: clock: via,vt8500: Convert to DT Schema
From: Krzysztof Kozlowski @ 2026-06-01 11:44 UTC (permalink / raw)
  To: Uday Kiran
  Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, skhan, me, linux-rtc,
	devicetree, linux-kernel
In-Reply-To: <CAAj-GBmNXvTNbnQ8hOzsjnQd0Oi4a17LY8yjb8HTbD4PpTXH3Q@mail.gmail.com>

On 31/05/2026 18:49, Uday Kiran wrote:
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/clock/via,vt8500-clock.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: VIA/Wondermedia VT8500 Clock Controller
>>
>> How PMC is a clock controller? Really?
> 
> No Krzysztof, actually that was a wrong direction in v4.
> 
>>> +
>>> +maintainers:
>>> +  - Michael Turquette <mturquette@baylibre.com>
>>> +  - Stephen Boyd <sboyd@kernel.org>
>>
>>
>> Subsystem maintainers do not care about PMC. This can be platform
>> maintainer.
> 
> I agree with you. I changed maintainers accordingly.
> 
>>> +
>>> +description:
>>> +  Clock controller bindings for VIA/Wondermedia VT8500 and Wondermedia WM8xxx
>>> +  series SoCs.
>>> +
>>> +select:
>>> +  properties:
>>> +    compatible:
>>> +      const: via,vt8500-pmc
>>> +
>>> +  required:
>>> +    - compatible
>>
>> Why do you have select?
>>
>> I don't understand your changes. This was not at v2 and I did not ask to
>> change that.
> 
> The select: block with via,vt8500-pmc and the clocks: type: object were
> mistakenly added to via,vt8500-clock.yaml in v4 — leftover confusion from
> trying to handle the PMC node's clock container in the same schema. In v5 these
> are removed from the clock schema entirely. The PMC binding is now a separate
> patch (via,vt8500-pmc.yaml) which is the right place for the clock container
> node description.
> 
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: via,vt8500-pmc
>>
>>
>> So via,vt8500-clock.yaml or pmc? Why aren't you removing the pmc file?
>> Why is this located at clocks?
> 
> In the next revision, this patch is scope only to the clock provider bindings
> (via,vt8500-device-clock, via,vt8500-pll-clock, wm,*-pll-clock). It no longer
> models PMC/top-level node properties and does not modify PMC binding files.
> 
>>> +
>>> +  reg:
>>> +    maxItems: 1
>>> +
>>> +  clocks:
>>> +    type: object
>>> +    additionalProperties: true
>>
>> No, this cannot be "true".
> 
> Agreed, I dropped that structure and kept strict schema validation.

So open v5 and tell me how did you solve "this cannot be 'true'", part?

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v1] rtc: m41t80: clean up watchdog on probe failure
From: Yuho Choi @ 2026-06-01 19:46 UTC (permalink / raw)
  To: Alexandre Belloni; +Cc: linux-rtc, linux-kernel, Yuho Choi

m41t80_probe() registers the watchdog misc device and reboot notifier
before registering the RTC device. If RTC device registration fails,
probe returns without calling m41t80_remove(), leaving the watchdog misc
device and reboot notifier registered.

Both watchdog paths use the global save_client pointer, which can
outlive the failed probe and point at driver state that has been
released by devres.

Unregister the watchdog misc device and reboot notifier before returning
from the RTC registration failure path.

Fixes: 10d0c768cc6d ("rtc: m41t80: fix race conditions")
Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
---
 drivers/rtc/rtc-m41t80.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index b26afef37d9c..f4a30320c6ed 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -1009,9 +1009,17 @@ static int m41t80_probe(struct i2c_client *client)
 
 	rc = devm_rtc_register_device(m41t80_data->rtc);
 	if (rc)
-		return rc;
+		goto err_wdt;
 
 	return 0;
+err_wdt:
+#ifdef CONFIG_RTC_DRV_M41T80_WDT
+	if (m41t80_data->features & M41T80_FEATURE_HT) {
+		misc_deregister(&wdt_dev);
+		unregister_reboot_notifier(&wdt_notifier);
+	}
+#endif
+	return rc;
 }
 
 static void m41t80_remove(struct i2c_client *client)
-- 
2.43.0


^ permalink raw reply related

* [PATCH] rtc: pcf8563: fix clock provider leak on unbind
From: Yi Ding @ 2026-06-02  3:51 UTC (permalink / raw)
  To: Alexandre Belloni; +Cc: Heiko Schocher, linux-rtc, linux-kernel, Yi Ding

pcf8563_clkout_register_clk() registers the CLKOUT clock provider with
of_clk_add_provider(), but nothing ever unwinds it: there is no
of_clk_del_provider() call and the driver has no remove callback. Each
of_clk_add_provider() allocates a struct of_clk_provider, takes a
reference on the OF node and adds an entry to the global of_clk_providers
list, none of which is released when the device is unbound. Every
bind/unbind (or module reload) therefore leaks a provider structure and
an of_node reference.

The clock itself is already device-managed (devm_clk_register()); only
the provider registration was not. Use devm_of_clk_add_hw_provider() so
the provider is removed automatically on unbind. Tie it to the parent
i2c device, whose OF node carries the #clock-cells and clock-output-names
properties (the RTC class device has no OF node of its own).

Fixes: a39a6405d5f9 ("rtc: pcf8563: add CLKOUT to common clock framework")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Yi Ding <yi.s.ding@gmail.com>
---
 drivers/rtc/rtc-pcf8563.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index b281e9489..7083d9278 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -449,7 +449,9 @@ static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
 	clk = devm_clk_register(&pcf8563->rtc->dev, &pcf8563->clkout_hw);
 
 	if (!IS_ERR(clk))
-		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+		devm_of_clk_add_hw_provider(pcf8563->rtc->dev.parent,
+					    of_clk_hw_simple_get,
+					    &pcf8563->clkout_hw);
 
 	return clk;
 }
-- 
2.47.3


^ permalink raw reply related

* [PATCH] rtc: mpc5121: replace in_8/out_8 with generic accessors
From: Rosen Penev @ 2026-06-02  4:00 UTC (permalink / raw)
  To: linux-rtc; +Cc: Alexandre Belloni, chleroy, open list

Convert ppc4xx-specific in_8/out_8, in_be16/out_be16 and in_be32/
out_be32 to the portable ioread8/iowrite8, ioread16be/iowrite16be
and ioread32be/iowrite32be respectively.

Add COMPILE_TEST as a result for greater compile coverage.

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 drivers/rtc/Kconfig       |  2 +-
 drivers/rtc/rtc-mpc5121.c | 80 +++++++++++++++++++--------------------
 2 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 364afc73f8ab..6d8935250c82 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1779,7 +1779,7 @@ config RTC_DRV_MC13XXX
 
 config RTC_DRV_MPC5121
 	tristate "Freescale MPC5121 built-in RTC"
-	depends on PPC_MPC512x || PPC_MPC52xx
+	depends on PPC_MPC512x || PPC_MPC52xx || COMPILE_TEST
 	help
 	  If you say yes here you will get support for the
 	  built-in RTC on MPC5121 or on MPC5200.
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index b90f8337a7e6..c038301fb86b 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -87,15 +87,15 @@ struct mpc5121_rtc_data {
 static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
 				   struct rtc_time *tm)
 {
-	out_8(&regs->second_set, tm->tm_sec);
-	out_8(&regs->minute_set, tm->tm_min);
-	out_8(&regs->hour_set, tm->tm_hour);
+	iowrite8(tm->tm_sec, &regs->second_set);
+	iowrite8(tm->tm_min, &regs->minute_set);
+	iowrite8(tm->tm_hour, &regs->hour_set);
 
 	/* set time sequence */
-	out_8(&regs->set_time, 0x1);
-	out_8(&regs->set_time, 0x3);
-	out_8(&regs->set_time, 0x1);
-	out_8(&regs->set_time, 0x0);
+	iowrite8(0x1, &regs->set_time);
+	iowrite8(0x3, &regs->set_time);
+	iowrite8(0x1, &regs->set_time);
+	iowrite8(0x0, &regs->set_time);
 }
 
 static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -107,7 +107,7 @@ static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
 	/*
 	 * linux time is actual_time plus the offset saved in target_time
 	 */
-	now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
+	now = ioread32be(&regs->actual_time) + ioread32be(&regs->target_time);
 
 	rtc_time64_to_tm(now, tm);
 
@@ -131,7 +131,7 @@ static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	 * between it and linux time to the target_time register.
 	 */
 	now = rtc_tm_to_time64(tm);
-	out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
+	iowrite32be(now - ioread32be(&regs->actual_time), &regs->target_time);
 
 	/*
 	 * update second minute hour registers
@@ -148,20 +148,20 @@ static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 	int tmp;
 
-	tm->tm_sec = in_8(&regs->second);
-	tm->tm_min = in_8(&regs->minute);
+	tm->tm_sec = ioread8(&regs->second);
+	tm->tm_min = ioread8(&regs->minute);
 
 	/* 12 hour format? */
-	if (in_8(&regs->hour) & 0x20)
-		tm->tm_hour = (in_8(&regs->hour) >> 1) +
-			(in_8(&regs->hour) & 1 ? 12 : 0);
+	if (ioread8(&regs->hour) & 0x20)
+		tm->tm_hour = (ioread8(&regs->hour) >> 1) +
+			(ioread8(&regs->hour) & 1 ? 12 : 0);
 	else
-		tm->tm_hour = in_8(&regs->hour);
+		tm->tm_hour = ioread8(&regs->hour);
 
-	tmp = in_8(&regs->wday_mday);
+	tmp = ioread8(&regs->wday_mday);
 	tm->tm_mday = tmp & 0x1f;
-	tm->tm_mon = in_8(&regs->month) - 1;
-	tm->tm_year = in_be16(&regs->year) - 1900;
+	tm->tm_mon = ioread8(&regs->month) - 1;
+	tm->tm_year = ioread16be(&regs->year) - 1900;
 	tm->tm_wday = (tmp >> 5) % 7;
 	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
 	tm->tm_isdst = 0;
@@ -177,16 +177,16 @@ static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	mpc5121_rtc_update_smh(regs, tm);
 
 	/* date */
-	out_8(&regs->month_set, tm->tm_mon + 1);
-	out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
-	out_8(&regs->date_set, tm->tm_mday);
-	out_be16(&regs->year_set, tm->tm_year + 1900);
+	iowrite8(tm->tm_mon + 1, &regs->month_set);
+	iowrite8(tm->tm_wday ? tm->tm_wday : 7, &regs->weekday_set);
+	iowrite8(tm->tm_mday, &regs->date_set);
+	iowrite16be(tm->tm_year + 1900, &regs->year_set);
 
 	/* set date sequence */
-	out_8(&regs->set_date, 0x1);
-	out_8(&regs->set_date, 0x3);
-	out_8(&regs->set_date, 0x1);
-	out_8(&regs->set_date, 0x0);
+	iowrite8(0x1, &regs->set_date);
+	iowrite8(0x3, &regs->set_date);
+	iowrite8(0x1, &regs->set_date);
+	iowrite8(0x0, &regs->set_date);
 
 	return 0;
 }
@@ -198,7 +198,7 @@ static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 
 	*alarm = rtc->wkalarm;
 
-	alarm->pending = in_8(&regs->alm_status);
+	alarm->pending = ioread8(&regs->alm_status);
 
 	return 0;
 }
@@ -212,10 +212,10 @@ static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 	alarm->time.tm_mon = -1;
 	alarm->time.tm_year = -1;
 
-	out_8(&regs->alm_min_set, alarm->time.tm_min);
-	out_8(&regs->alm_hour_set, alarm->time.tm_hour);
+	iowrite8(alarm->time.tm_min, &regs->alm_min_set);
+	iowrite8(alarm->time.tm_hour, &regs->alm_hour_set);
 
-	out_8(&regs->alm_enable, alarm->enabled);
+	iowrite8(alarm->enabled, &regs->alm_enable);
 
 	rtc->wkalarm = *alarm;
 	return 0;
@@ -226,10 +226,10 @@ static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
 	struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 
-	if (in_8(&regs->int_alm)) {
+	if (ioread8(&regs->int_alm)) {
 		/* acknowledge and clear status */
-		out_8(&regs->int_alm, 1);
-		out_8(&regs->alm_status, 1);
+		iowrite8(1, &regs->int_alm);
+		iowrite8(1, &regs->alm_status);
 
 		rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
 		return IRQ_HANDLED;
@@ -243,9 +243,9 @@ static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
 	struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 
-	if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
+	if (ioread8(&regs->int_sec) && (ioread8(&regs->int_enable) & 0x1)) {
 		/* acknowledge */
-		out_8(&regs->int_sec, 1);
+		iowrite8(1, &regs->int_sec);
 
 		rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
 		return IRQ_HANDLED;
@@ -266,7 +266,7 @@ static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
 	else
 		val = 0;
 
-	out_8(&regs->alm_enable, val);
+	iowrite8(val, &regs->alm_enable);
 	rtc->wkalarm.enabled = val;
 
 	return 0;
@@ -340,11 +340,11 @@ static int mpc5121_rtc_probe(struct platform_device *op)
 
 	if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
 		u32 ka;
-		ka = in_be32(&rtc->regs->keep_alive);
+		ka = ioread32be(&rtc->regs->keep_alive);
 		if (ka & 0x02) {
 			dev_warn(&op->dev,
 				"mpc5121-rtc: Battery or oscillator failure!\n");
-			out_be32(&rtc->regs->keep_alive, ka);
+			iowrite32be(ka, &rtc->regs->keep_alive);
 		}
 		rtc->rtc->ops = &mpc5121_rtc_ops;
 		/*
@@ -376,8 +376,8 @@ static void mpc5121_rtc_remove(struct platform_device *op)
 	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 
 	/* disable interrupt, so there are no nasty surprises */
-	out_8(&regs->alm_enable, 0);
-	out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
+	iowrite8(0, &regs->alm_enable);
+	iowrite8(ioread8(&regs->int_enable) & ~0x1, &regs->int_enable);
 
 	irq_dispose_mapping(rtc->irq);
 	irq_dispose_mapping(rtc->irq_periodic);
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH 1/5] rtc: renesas-rtca3: Fix PIE clear polling condition in alarm setup error path
From: Claudiu Beznea @ 2026-06-02  8:31 UTC (permalink / raw)
  To: Prabhakar, Alexandre Belloni, Claudiu Beznea, Geert Uytterhoeven
  Cc: linux-rtc, linux-renesas-soc, linux-kernel, Biju Das,
	Fabrizio Castro, Lad Prabhakar
In-Reply-To: <20260506164914.3987293-2-prabhakar.mahadev-lad.rj@bp.renesas.com>

Hi, Prabhakar,

On 5/6/26 19:49, Prabhakar wrote:
> From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> In rtca3_set_alarm(), the setup_failed path attempts to disable the
> Periodic Interrupt Enable (PIE) bit and wait until it is cleared.
> However, the polling condition passed to readb_poll_timeout_atomic()
> uses an incorrect expression:
> 
>      !(tmp & ~RTCA3_RCR1_PIE)
> 
> As ~RTCA3_RCR1_PIE evaluates to a mask of all bits except PIE, the
> condition effectively waits for all non-PIE bits to become zero, which
> is unrelated to the intended operation and is unlikely to ever be true.
> This causes the poll to time out unnecessarily.
> 
> Fix the condition to check for the PIE bit itself being cleared:
> 
>      !(tmp & RTCA3_RCR1_PIE)
> 
> This correctly waits until PIE is deasserted after being cleared.
> 
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> # on RZ/G3S

I think it also deserves a Fixes tag?

Thank you,
Claudiu

^ permalink raw reply

* Re: [PATCH 2/5] rtc: renesas-rtca3: Check RADJ poll result during initial setup
From: Claudiu Beznea @ 2026-06-02  8:32 UTC (permalink / raw)
  To: Prabhakar, Alexandre Belloni, Claudiu Beznea, Geert Uytterhoeven
  Cc: linux-rtc, linux-renesas-soc, linux-kernel, Biju Das,
	Fabrizio Castro, Lad Prabhakar
In-Reply-To: <20260506164914.3987293-3-prabhakar.mahadev-lad.rj@bp.renesas.com>



On 5/6/26 19:49, Prabhakar wrote:
> From: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> In rtca3_initial_setup(), the driver clears the RTCA3_RADJ register and
> waits for it to reach zero using readb_poll_timeout(). Check the return
> value of readb_poll_timeout() and propagate the error if the poll fails.
> 
> Signed-off-by: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>

Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> # on RZ/G3S

^ permalink raw reply

* Re: [PATCH 3/5] rtc: renesas-rtca3: Fix incorrect error message for reset assert
From: Claudiu Beznea @ 2026-06-02  8:32 UTC (permalink / raw)
  To: Prabhakar, Alexandre Belloni, Claudiu Beznea, Geert Uytterhoeven
  Cc: linux-rtc, linux-renesas-soc, linux-kernel, Biju Das,
	Fabrizio Castro, Lad Prabhakar
In-Reply-To: <20260506164914.3987293-4-prabhakar.mahadev-lad.rj@bp.renesas.com>



On 5/6/26 19:49, Prabhakar wrote:
> From: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> Update the message to "assert reset" to accurately reflect the
> operation being performed.
> 
> Signed-off-by: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>

Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> # on RZ/G3S

^ permalink raw reply

* Re: [PATCH 4/5] rtc: renesas-rtca3: Fix typo in rtca3_ppb_per_cycle documentation
From: Claudiu Beznea @ 2026-06-02  8:32 UTC (permalink / raw)
  To: Prabhakar, Alexandre Belloni, Claudiu Beznea, Geert Uytterhoeven
  Cc: linux-rtc, linux-renesas-soc, linux-kernel, Biju Das,
	Fabrizio Castro, Lad Prabhakar
In-Reply-To: <20260506164914.3987293-5-prabhakar.mahadev-lad.rj@bp.renesas.com>



On 5/6/26 19:49, Prabhakar wrote:
> From: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> Correct a typo in the kernel-doc comment for struct
> rtca3_ppb_per_cycle by fixing "adjutment" to "adjustment".
> 
> Signed-off-by: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>

Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> # on RZ/G3S

^ permalink raw reply

* Re: [PATCH 5/5] rtc: renesas-rtca3: Factor out year decoding helper
From: Claudiu Beznea @ 2026-06-02  8:32 UTC (permalink / raw)
  To: Prabhakar, Alexandre Belloni, Claudiu Beznea, Geert Uytterhoeven
  Cc: linux-rtc, linux-renesas-soc, linux-kernel, Biju Das,
	Fabrizio Castro, Lad Prabhakar
In-Reply-To: <20260506164914.3987293-6-prabhakar.mahadev-lad.rj@bp.renesas.com>



On 5/6/26 19:49, Prabhakar wrote:
> From: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> The logic to decode the year value from the hardware registers is
> duplicated in both rtca3_read_time() and rtca3_read_alarm().
> 
> Introduce a helper rtca3_decode_year() to centralize this conversion.
> 
> Signed-off-by: Lad Prabhakar<prabhakar.mahadev-lad.rj@bp.renesas.com>

Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> # on RZ/G3S

^ permalink raw reply

* [PATCH 2/2] m68k: coldfire/m5441x: register the on-chip RTC
From: Jean-Michel Hautbois @ 2026-06-02  8:36 UTC (permalink / raw)
  To: Alexandre Belloni, Greg Ungerer, Geert Uytterhoeven
  Cc: linux-kernel, linux-rtc, linux-m68k, Jean-Michel Hautbois
In-Reply-To: <20260602-jmh-upstream-coldfire-rtc-v1-0-1e129a177d2f@yoseli.org>

Register the MCF5441x on-chip RTC platform device from the SoC code so
every MCF5441x board gets the rtc-m5441x driver (time/calendar plus the
battery-backed standby RAM via nvmem) without a per-board file.

Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
---
 arch/m68k/coldfire/m5441x.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/m68k/coldfire/m5441x.c b/arch/m68k/coldfire/m5441x.c
index 7a25cfc7ac07..b79aebdfd567 100644
--- a/arch/m68k/coldfire/m5441x.c
+++ b/arch/m68k/coldfire/m5441x.c
@@ -10,6 +10,7 @@
 #include <linux/param.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <asm/machdep.h>
 #include <asm/coldfire.h>
@@ -239,6 +240,34 @@ static void __init m5441x_fec_init(void)
 	__raw_writeb(0x03, MCFGPIO_PAR_FEC);
 }
 
+/*
+ * On-chip "robust" RTC. Exposes the time/calendar and the 2KB
+ * battery-backed standby RAM (rtc-m5441x driver).
+ */
+static struct resource m5441x_rtc_resource[] = {
+	{
+		.start	= MCFRTC_BASE,
+		.end	= MCFRTC_BASE + MCFRTC_SIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= MCF_IRQ_RTC,
+		.end	= MCF_IRQ_RTC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static int __init m5441x_rtc_init(void)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("rtc-m5441x", -1,
+					       m5441x_rtc_resource,
+					       ARRAY_SIZE(m5441x_rtc_resource));
+	return PTR_ERR_OR_ZERO(pdev);
+}
+arch_initcall(m5441x_rtc_init);
+
 void __init config_BSP(char *commandp, int size)
 {
 	m5441x_clk_init();

-- 
2.39.5


^ permalink raw reply related

* [PATCH 0/2] rtc: add a driver for the Freescale MCF5441x on-chip RTC
From: Jean-Michel Hautbois @ 2026-06-02  8:36 UTC (permalink / raw)
  To: Alexandre Belloni, Greg Ungerer, Geert Uytterhoeven
  Cc: linux-kernel, linux-rtc, linux-m68k, Jean-Michel Hautbois

This series adds support for the "robust" real time clock found on the
Freescale MCF5441x family of ColdFire SoCs.

Patch 1 adds the rtc-class driver. Besides the usual time/calendar and a
one-shot alarm, the block contains 2KB of battery-backed standby RAM that
survives a main-power loss while VSTBY_RTC is supplied; the driver exposes
it through the nvmem framework so userspace can persist data (for example a
reset/reboot counter) across power cycles. Register and standby-RAM writes
go through the RTC_CR[WE] knock sequence, the time counters are read under
the RTC_SR[INVAL] guard described in the reference manual, and the time/date
encoding is forced to binary at probe.

Patch 2 registers the platform device from the m5441x ColdFire SoC support
code so that every MCF5441x board gets the RTC. The MCFRTC_BASE, MCFRTC_SIZE
and MCF_IRQ_RTC definitions it relies on already exist in
arch/m68k/include/asm/m5441xsim.h.

The driver has been exercised on an MCF54418-based board: it registers
as an rtc device, exposes the standby RAM as an nvmem device, and a value
written to the standby RAM survives a mains power-cycle.

Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
---
Jean-Michel Hautbois (2):
      rtc: m5441x: add MCF5441x on-chip RTC driver
      m68k: coldfire/m5441x: register the on-chip RTC

 MAINTAINERS                 |   6 +
 arch/m68k/coldfire/m5441x.c |  29 +++
 drivers/rtc/Kconfig         |  12 +
 drivers/rtc/Makefile        |   1 +
 drivers/rtc/rtc-m5441x.c    | 582 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 630 insertions(+)
---
base-commit: 6f3ed7fec72fc8979b2a8c7219c0a9fcfc8d07b5
change-id: 20260529-jmh-upstream-coldfire-rtc-88b095b2cf28

Best regards,
--  
Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>


^ permalink raw reply

* [PATCH 1/2] rtc: m5441x: add MCF5441x on-chip RTC driver
From: Jean-Michel Hautbois @ 2026-06-02  8:36 UTC (permalink / raw)
  To: Alexandre Belloni, Greg Ungerer, Geert Uytterhoeven
  Cc: linux-kernel, linux-rtc, linux-m68k, Jean-Michel Hautbois
In-Reply-To: <20260602-jmh-upstream-coldfire-rtc-v1-0-1e129a177d2f@yoseli.org>

Add an rtc-class driver for the Freescale MCF5441x on-chip "robust" RTC.
It provides the time/calendar and alarm, and exposes the 2KB
battery-backed standby RAM through the nvmem framework so userspace can
preserve data across a main-power loss (the RAM is retained while
VSTBY_RTC is supplied).

Register and standby-RAM writes go through the RTC_CR[WE] knock
sequence; the base-2112 year encoding and register map follow the
MCF54418 reference manual. Based on the out-of-tree Freescale 3.0.x
rtc-m5441x driver, rewritten for the current RTC and nvmem APIs.

Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
---
 MAINTAINERS              |   6 +
 drivers/rtc/Kconfig      |  12 +
 drivers/rtc/Makefile     |   1 +
 drivers/rtc/rtc-m5441x.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 601 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9ec290e38b44..e8001adfb6ca 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10196,6 +10196,12 @@ S:	Maintained
 F:	drivers/mmc/host/sdhci-esdhc-mcf.c
 F:	include/linux/platform_data/mmc-esdhc-mcf.h
 
+FREESCALE COLDFIRE M5441X RTC DRIVER
+M:	Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
+L:	linux-rtc@vger.kernel.org
+S:	Maintained
+F:	drivers/rtc/rtc-m5441x.c
+
 FREESCALE DIU FRAMEBUFFER DRIVER
 M:	Timur Tabi <timur@kernel.org>
 L:	linux-fbdev@vger.kernel.org
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 364afc73f8ab..45362247a56c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1427,6 +1427,18 @@ config RTC_DRV_NTXEC
 
 comment "on-CPU RTC drivers"
 
+config RTC_DRV_M5441X
+	tristate "Freescale MCF5441x on-chip RTC"
+	depends on M5441x || COMPILE_TEST
+	help
+	  If you say yes here you get support for the on-chip "robust"
+	  real time clock found on Freescale MCF5441x ColdFire SoCs,
+	  including its 2KB battery-backed standby RAM exposed through the
+	  nvmem framework.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-m5441x.
+
 config RTC_DRV_ASM9260
 	tristate "Alphascale asm9260 RTC"
 	depends on MACH_ASM9260 || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6cf7e066314e..4c7cb67fbc3f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o
 obj-$(CONFIG_RTC_DRV_M48T35)	+= rtc-m48t35.o
 obj-$(CONFIG_RTC_DRV_M48T59)	+= rtc-m48t59.o
 obj-$(CONFIG_RTC_DRV_M48T86)	+= rtc-m48t86.o
+obj-$(CONFIG_RTC_DRV_M5441X)	+= rtc-m5441x.o
 obj-$(CONFIG_RTC_DRV_MA35D1)	+= rtc-ma35d1.o
 obj-$(CONFIG_RTC_DRV_MACSMC)	+= rtc-macsmc.o
 obj-$(CONFIG_RTC_DRV_MAX31335)	+= rtc-max31335.o
diff --git a/drivers/rtc/rtc-m5441x.c b/drivers/rtc/rtc-m5441x.c
new file mode 100644
index 000000000000..b3d1bf4c1153
--- /dev/null
+++ b/drivers/rtc/rtc-m5441x.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RTC driver for the Freescale MCF5441x on-chip "robust" RTC.
+ *
+ * Exposes the time/calendar and alarm, plus the 2KB battery-backed
+ * standby RAM as an nvmem device. The standby RAM survives main-power
+ * loss while VSTBY_RTC is supplied, which makes it usable to persist a
+ * reset/reboot counter across power cycles.
+ *
+ * Register layout and the base-2112 year encoding are taken from the
+ * MCF54418 reference manual (chapter "Robust Real Time Clock").
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2026 Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
+ *
+ * Based on the out-of-tree Freescale 3.0.x rtc-m5441x driver by
+ * Lanttor Guo <Lanttor.Guo@freescale.com>, rewritten for the current
+ * RTC and nvmem frameworks.
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+
+/* Register offsets from the RTC base. */
+#define M5441X_RTC_YEARMON	0x00
+#define M5441X_RTC_DAYS		0x02
+#define M5441X_RTC_HOURMIN	0x04
+#define M5441X_RTC_SECONDS	0x06
+#define M5441X_RTC_ALM_YRMON	0x08
+#define M5441X_RTC_ALM_DAYS	0x0a
+#define M5441X_RTC_ALM_HM	0x0c
+#define M5441X_RTC_ALM_SEC	0x0e
+#define M5441X_RTC_CR		0x10
+#define M5441X_RTC_SR		0x12
+#define M5441X_RTC_ISR		0x14
+#define M5441X_RTC_IER		0x16
+#define M5441X_RTC_CFG_DATA	0x20
+
+/* RTC_CR bits. Only WE is freely writable; the rest need the WE knock. */
+#define M5441X_RTC_CR_RSVD	0x8000	/* bit 15, reserved, must be set */
+#define M5441X_RTC_CR_BCDEN	0x0080	/* 0 = binary, 1 = BCD time/date */
+#define M5441X_RTC_CR_DSTEN	0x0040	/* daylight-saving auto-adjust */
+#define M5441X_RTC_CR_AM_MASK	0x000c	/* alarm match field */
+#define M5441X_RTC_CR_AM_FULL	0x000c	/* match s,m,h,day,month,year */
+#define M5441X_RTC_CR_WE_MASK	0x0003	/* write-enable knock field */
+
+#define M5441X_RTC_SR_INVAL	0x0001	/* time invalid / changing */
+#define M5441X_RTC_SR_WPE	0x0010	/* write protection enabled */
+#define M5441X_RTC_ISR_ALM	0x0004	/* alarm interrupt status/enable */
+#define M5441X_RTC_IER_RSVD	0x0001	/* IER/ISR bit 0 reserved, must be set */
+#define M5441X_RTC_CFG_OSCEN	0x0008	/* oscillator enable */
+
+/* Battery-backed standby RAM: 2KB at this offset within the block. */
+#define M5441X_RTC_SRAM_OFFSET	0x40
+#define M5441X_RTC_SRAM_SIZE	2048
+#define M5441X_RTC_SRAM_CHUNK	64	/* lock-drop granularity for SRAM I/O */
+
+#define M5441X_RTC_YEAR_BASE	2112
+
+struct m5441x_rtc {
+	struct rtc_device *rtc;
+	void __iomem *base;
+	spinlock_t lock;	/* serialises register access + WP window */
+	bool osc_dead;		/* 32kHz oscillator never started; no timekeeping */
+};
+
+static inline u16 rtc_rd(struct m5441x_rtc *p, unsigned int reg)
+{
+	return ioread16be(p->base + reg);
+}
+
+static inline void rtc_wr(struct m5441x_rtc *p, unsigned int reg, u16 val)
+{
+	iowrite16be(val, p->base + reg);
+}
+
+/*
+ * Issue the RTC_CR[WE] knock (00 -> 01 -> 11 -> 10) that opens the
+ * write-protection window. Writes to the registers and the standby RAM
+ * are rejected unless this sequence has been issued; the space then
+ * stays writable for ~2 seconds. Caller holds p->lock.
+ */
+static void m5441x_rtc_knock(struct m5441x_rtc *p)
+{
+	rtc_wr(p, M5441X_RTC_CR, 0x0000);
+	rtc_wr(p, M5441X_RTC_CR, 0x0001);
+	rtc_wr(p, M5441X_RTC_CR, 0x0003);
+	rtc_wr(p, M5441X_RTC_CR, 0x0002);
+}
+
+/* Open the write window only if protection is currently enabled. */
+static void m5441x_rtc_unlock(struct m5441x_rtc *p)
+{
+	if (rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_WPE)
+		m5441x_rtc_knock(p);
+}
+
+/*
+ * Write the data bits of RTC_CR (BCDEN/DSTEN/AM). Caller holds p->lock
+ * and must have opened the write window first. The reserved bit 15 is
+ * kept set and the WE field kept clear so the write updates the data
+ * bits without re-arming write protection (a lone WE=10 would).
+ */
+static void m5441x_rtc_write_cr(struct m5441x_rtc *p, u16 val)
+{
+	val &= ~M5441X_RTC_CR_WE_MASK;
+	val |= M5441X_RTC_CR_RSVD;
+	rtc_wr(p, M5441X_RTC_CR, val);
+}
+
+/*
+ * The time counters are unstable for an oscillator cycle either side of
+ * the one-second boundary. RTC_SR[INVAL] flags this; reads during the
+ * window return 0xffff and writes are nullified. Spin until it clears.
+ * The window is only a couple of 32kHz cycles (~60us), so bound the
+ * busy-wait tightly: it runs with the lock held and interrupts off.
+ * Caller holds p->lock.
+ */
+static int m5441x_rtc_wait_valid(struct m5441x_rtc *p)
+{
+	unsigned int tries = 10;
+
+	while (rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_INVAL) {
+		if (!--tries)
+			return -EIO;
+		udelay(10);
+	}
+
+	return 0;
+}
+
+static int m5441x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct m5441x_rtc *p = dev_get_drvdata(dev);
+	u16 yearmon, days, hourmin, seconds;
+	unsigned int tries = 3;
+	unsigned long flags;
+	int ret;
+
+	/*
+	 * Without a running oscillator the counters are frozen; refuse the
+	 * read rather than hand userspace a stale time that hwclock --hctosys
+	 * would propagate into the system clock.
+	 */
+	if (p->osc_dead)
+		return -ENODATA;
+
+	/*
+	 * INVAL only guarantees the counters are stable at the moment it is
+	 * checked; it does not freeze them across the four reads. If a 1Hz
+	 * tick re-asserts INVAL mid-burst the fields can straddle a second
+	 * boundary (e.g. 23:59:59 -> 00:00:00), so re-check it afterwards and
+	 * retry the whole burst on observed re-entry.
+	 */
+	spin_lock_irqsave(&p->lock, flags);
+	do {
+		ret = m5441x_rtc_wait_valid(p);
+		if (ret)
+			break;
+		yearmon = rtc_rd(p, M5441X_RTC_YEARMON);
+		days    = rtc_rd(p, M5441X_RTC_DAYS);
+		hourmin = rtc_rd(p, M5441X_RTC_HOURMIN);
+		seconds = rtc_rd(p, M5441X_RTC_SECONDS);
+		/* Stable across the whole burst -> the snapshot is coherent. */
+		if (!(rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_INVAL))
+			break;
+		ret = -EAGAIN;
+	} while (--tries);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	if (ret)
+		return ret;
+
+	tm->tm_year = M5441X_RTC_YEAR_BASE + (s8)((yearmon >> 8) & 0xff) - 1900;
+	tm->tm_mon  = (yearmon & 0xff) - 1;
+	tm->tm_mday = days & 0xff;
+	tm->tm_wday = (days >> 8) & 0x7;
+	tm->tm_hour = (hourmin >> 8) & 0x1f;
+	tm->tm_min  = hourmin & 0x3f;
+	tm->tm_sec  = seconds & 0x3f;
+
+	return 0;
+}
+
+static int m5441x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct m5441x_rtc *p = dev_get_drvdata(dev);
+	s8 yoff = (tm->tm_year + 1900) - M5441X_RTC_YEAR_BASE;
+	unsigned int tries = 3;
+	unsigned long flags;
+	int ret;
+
+	/* A frozen oscillator would never latch the new time; reject it. */
+	if (p->osc_dead)
+		return -ENODATA;
+
+	/*
+	 * Writes are nullified while INVAL is asserted. wait_valid() clears
+	 * the path before the burst, but the knock plus four writes can cross
+	 * a second boundary on a slow RTC bus, silently dropping fields.
+	 * Re-check INVAL afterwards and rewrite on observed re-entry.
+	 */
+	spin_lock_irqsave(&p->lock, flags);
+	do {
+		ret = m5441x_rtc_wait_valid(p);
+		if (ret)
+			break;
+		m5441x_rtc_unlock(p);
+		rtc_wr(p, M5441X_RTC_YEARMON,
+		       ((u16)((u8)yoff) << 8) | ((tm->tm_mon + 1) & 0xff));
+		rtc_wr(p, M5441X_RTC_DAYS,
+		       ((tm->tm_wday & 0x7) << 8) | (tm->tm_mday & 0x1f));
+		rtc_wr(p, M5441X_RTC_HOURMIN,
+		       ((tm->tm_hour & 0x1f) << 8) | (tm->tm_min & 0x3f));
+		rtc_wr(p, M5441X_RTC_SECONDS, tm->tm_sec & 0x3f);
+		/* No boundary crossed mid-burst -> the writes all took. */
+		if (!(rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_INVAL))
+			break;
+		ret = -EAGAIN;
+	} while (--tries);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	return ret;
+}
+
+static int m5441x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct m5441x_rtc *p = dev_get_drvdata(dev);
+	u16 yearmon, days, hourmin, seconds, ier, isr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&p->lock, flags);
+	yearmon = rtc_rd(p, M5441X_RTC_ALM_YRMON);
+	days    = rtc_rd(p, M5441X_RTC_ALM_DAYS);
+	hourmin = rtc_rd(p, M5441X_RTC_ALM_HM);
+	seconds = rtc_rd(p, M5441X_RTC_ALM_SEC);
+	ier     = rtc_rd(p, M5441X_RTC_IER);
+	isr     = rtc_rd(p, M5441X_RTC_ISR);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	alrm->time.tm_year = M5441X_RTC_YEAR_BASE +
+			     (s8)((yearmon >> 8) & 0xff) - 1900;
+	alrm->time.tm_mon  = (yearmon & 0xff) - 1;
+	alrm->time.tm_mday = days & 0xff;
+	alrm->time.tm_hour = (hourmin >> 8) & 0x1f;
+	alrm->time.tm_min  = hourmin & 0x3f;
+	alrm->time.tm_sec  = seconds & 0x3f;
+	alrm->enabled = !!(ier & M5441X_RTC_ISR_ALM);
+	alrm->pending = !!(isr & M5441X_RTC_ISR_ALM);
+
+	return 0;
+}
+
+static int m5441x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct m5441x_rtc *p = dev_get_drvdata(dev);
+	unsigned long flags;
+	u16 ier;
+
+	spin_lock_irqsave(&p->lock, flags);
+	m5441x_rtc_unlock(p);
+	ier = rtc_rd(p, M5441X_RTC_IER);
+	if (enabled) {
+		/*
+		 * The comparator latches ISR_ALM even while IER masks delivery,
+		 * so a match from a previous arming (or one surviving a reboot)
+		 * would fire the moment IER is unmasked. Clear it first.
+		 */
+		rtc_wr(p, M5441X_RTC_ISR, M5441X_RTC_ISR_ALM);
+		ier |= M5441X_RTC_ISR_ALM;
+	} else {
+		ier &= ~M5441X_RTC_ISR_ALM;
+	}
+	rtc_wr(p, M5441X_RTC_IER, ier);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	return 0;
+}
+
+static int m5441x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct m5441x_rtc *p = dev_get_drvdata(dev);
+	s8 yoff = (alrm->time.tm_year + 1900) - M5441X_RTC_YEAR_BASE;
+	unsigned long flags;
+	u16 ier, cr;
+
+	spin_lock_irqsave(&p->lock, flags);
+	m5441x_rtc_unlock(p);
+	/* acknowledge any pending alarm (write-1-to-clear) */
+	rtc_wr(p, M5441X_RTC_ISR, M5441X_RTC_ISR_ALM);
+
+	/*
+	 * Match the full date so the alarm is a one-shot rather than the
+	 * hardware default of a daily hour:minute:second match.
+	 */
+	cr = rtc_rd(p, M5441X_RTC_CR);
+	m5441x_rtc_write_cr(p, (cr & ~M5441X_RTC_CR_AM_MASK) |
+				M5441X_RTC_CR_AM_FULL);
+
+	rtc_wr(p, M5441X_RTC_ALM_YRMON,
+	       ((u16)((u8)yoff) << 8) | ((alrm->time.tm_mon + 1) & 0xff));
+	/* RTC_ALM_DAYS has no day-of-week field; bits [15:8] are reserved. */
+	rtc_wr(p, M5441X_RTC_ALM_DAYS, alrm->time.tm_mday & 0x1f);
+	rtc_wr(p, M5441X_RTC_ALM_HM,
+	       ((alrm->time.tm_hour & 0x1f) << 8) | (alrm->time.tm_min & 0x3f));
+	rtc_wr(p, M5441X_RTC_ALM_SEC, alrm->time.tm_sec & 0x3f);
+
+	ier = rtc_rd(p, M5441X_RTC_IER);
+	if (alrm->enabled)
+		ier |= M5441X_RTC_ISR_ALM;
+	else
+		ier &= ~M5441X_RTC_ISR_ALM;
+	rtc_wr(p, M5441X_RTC_IER, ier);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	return 0;
+}
+
+static irqreturn_t m5441x_rtc_irq(int irq, void *dev_id)
+{
+	struct m5441x_rtc *p = dev_id;
+	u16 status;
+
+	spin_lock(&p->lock);
+	/*
+	 * Mask the reserved bit 0 (always reads set) so only a real source
+	 * can claim the interrupt.
+	 */
+	status = rtc_rd(p, M5441X_RTC_ISR) & rtc_rd(p, M5441X_RTC_IER) &
+		 ~M5441X_RTC_IER_RSVD;
+	if (status) {
+		m5441x_rtc_unlock(p);
+		rtc_wr(p, M5441X_RTC_ISR, status);	/* write-1-to-clear */
+	}
+	spin_unlock(&p->lock);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & M5441X_RTC_ISR_ALM)
+		rtc_update_irq(p->rtc, 1, RTC_AF | RTC_IRQF);
+
+	/*
+	 * A hardware source was acknowledged above; claim the IRQ even when it
+	 * was not the alarm so genirq's spurious detector does not disable the
+	 * shared line.
+	 */
+	return IRQ_HANDLED;
+}
+
+static int m5441x_rtc_nvram_read(void *priv, unsigned int offset,
+				 void *val, size_t bytes)
+{
+	struct m5441x_rtc *p = priv;
+	u8 *buf = val;
+	size_t done;
+
+	/*
+	 * In-kernel nvmem_device_read() forwards offset/bytes verbatim, so
+	 * range-check here rather than trust the caller.
+	 */
+	if (offset >= M5441X_RTC_SRAM_SIZE ||
+	    bytes > M5441X_RTC_SRAM_SIZE - offset)
+		return -EINVAL;
+
+	/*
+	 * Process the transfer in chunks, releasing the lock between them, so
+	 * a full 2KB access does not keep hard interrupts disabled across
+	 * thousands of slow on-chip MMIO cycles and wreck IRQ latency.
+	 */
+	for (done = 0; done < bytes; done += M5441X_RTC_SRAM_CHUNK) {
+		size_t chunk = min_t(size_t, bytes - done, M5441X_RTC_SRAM_CHUNK);
+		unsigned long flags;
+		size_t i;
+
+		spin_lock_irqsave(&p->lock, flags);
+		for (i = 0; i < chunk; i++)
+			buf[done + i] = ioread8(p->base + M5441X_RTC_SRAM_OFFSET +
+						offset + done + i);
+		spin_unlock_irqrestore(&p->lock, flags);
+	}
+
+	return 0;
+}
+
+static int m5441x_rtc_nvram_write(void *priv, unsigned int offset,
+				  void *val, size_t bytes)
+{
+	struct m5441x_rtc *p = priv;
+	u8 *buf = val;
+	size_t done;
+
+	/* See the read path: validate the range, the core does not. */
+	if (offset >= M5441X_RTC_SRAM_SIZE ||
+	    bytes > M5441X_RTC_SRAM_SIZE - offset)
+		return -EINVAL;
+
+	/*
+	 * Chunk the write and drop the lock between chunks (see the read
+	 * path). The write window stays open for ~2 seconds, far longer than
+	 * one chunk takes, so re-knock at the start of each chunk to keep it
+	 * open across the lock-drop.
+	 */
+	for (done = 0; done < bytes; done += M5441X_RTC_SRAM_CHUNK) {
+		size_t chunk = min_t(size_t, bytes - done, M5441X_RTC_SRAM_CHUNK);
+		unsigned long flags;
+		size_t i;
+
+		spin_lock_irqsave(&p->lock, flags);
+		m5441x_rtc_knock(p);
+		for (i = 0; i < chunk; i++)
+			iowrite8(buf[done + i], p->base + M5441X_RTC_SRAM_OFFSET +
+				 offset + done + i);
+		spin_unlock_irqrestore(&p->lock, flags);
+	}
+
+	return 0;
+}
+
+static const struct rtc_class_ops m5441x_rtc_ops = {
+	.read_time		= m5441x_rtc_read_time,
+	.set_time		= m5441x_rtc_set_time,
+	.read_alarm		= m5441x_rtc_read_alarm,
+	.set_alarm		= m5441x_rtc_set_alarm,
+	.alarm_irq_enable	= m5441x_rtc_alarm_irq_enable,
+};
+
+static int m5441x_rtc_probe(struct platform_device *pdev)
+{
+	struct nvmem_config nvmem_cfg = {
+		.name		= "m5441x_rtc_sram",
+		.type		= NVMEM_TYPE_BATTERY_BACKED,
+		.word_size	= 1,
+		.stride		= 1,
+		.size		= M5441X_RTC_SRAM_SIZE,
+		.reg_read	= m5441x_rtc_nvram_read,
+		.reg_write	= m5441x_rtc_nvram_write,
+	};
+	struct m5441x_rtc *p;
+	unsigned long flags;
+	bool osc_dead = false;
+	int irq, ret;
+	u16 cr, cfg;
+
+	p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	spin_lock_init(&p->lock);
+
+	p->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(p->base))
+		return PTR_ERR(p->base);
+
+	platform_set_drvdata(pdev, p);
+
+	/* Enable the oscillator if needed and mask/clear stale interrupts. */
+	spin_lock_irqsave(&p->lock, flags);
+	m5441x_rtc_unlock(p);
+	/*
+	 * Force binary, DST-free time/date encoding. The driver does not
+	 * handle the BCD format, and Linux expects the RTC to keep monotonic
+	 * time with local-time/DST conversion done in userspace, so clear
+	 * BCDEN and DSTEN in case a bootloader or prior OS left them set. Also
+	 * default the alarm match field to a full one-shot match: the reset
+	 * value (AM=00) is a daily hh:mm:ss match, which would fire on the
+	 * wrong day if userspace enables the alarm via RTC_AIE_ON without
+	 * first programming it through set_alarm().
+	 */
+	cr = rtc_rd(p, M5441X_RTC_CR);
+	cr &= ~(M5441X_RTC_CR_BCDEN | M5441X_RTC_CR_DSTEN | M5441X_RTC_CR_AM_MASK);
+	cr |= M5441X_RTC_CR_AM_FULL;
+	m5441x_rtc_write_cr(p, cr);
+
+	/*
+	 * The alarm registers are battery-backed and survive a power cycle,
+	 * so a previously armed match could re-fire before set_alarm() runs.
+	 * Clear them now (the write window is open).
+	 */
+	rtc_wr(p, M5441X_RTC_ALM_YRMON, 0);
+	rtc_wr(p, M5441X_RTC_ALM_DAYS, 0);
+	rtc_wr(p, M5441X_RTC_ALM_HM, 0);
+	rtc_wr(p, M5441X_RTC_ALM_SEC, 0);
+
+	/*
+	 * Enable the 32kHz oscillator that drives the time counters. This only
+	 * checks that the OSCEN enable bit latches: when it reads back clear
+	 * the oscillator block rejected the enable, which in practice means no
+	 * RTC crystal is fitted and the counters will never advance. It cannot
+	 * tell a dead crystal from one still starting up, so it is a best-
+	 * effort hint, not a liveness guarantee. Warn but keep probing, since
+	 * the battery-backed standby RAM stays usable without the oscillator.
+	 */
+	cfg = rtc_rd(p, M5441X_RTC_CFG_DATA);
+	if (!(cfg & M5441X_RTC_CFG_OSCEN)) {
+		rtc_wr(p, M5441X_RTC_CFG_DATA, cfg | M5441X_RTC_CFG_OSCEN);
+		if (!(rtc_rd(p, M5441X_RTC_CFG_DATA) & M5441X_RTC_CFG_OSCEN))
+			osc_dead = true;
+	}
+
+	/* Mask every source but keep the reserved bit 0 set as required. */
+	rtc_wr(p, M5441X_RTC_IER, M5441X_RTC_IER_RSVD);
+	rtc_wr(p, M5441X_RTC_ISR, rtc_rd(p, M5441X_RTC_ISR));
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	/*
+	 * Keep the device (and its standby-RAM nvmem) usable, but record the
+	 * dead oscillator so read_time/set_time refuse to expose a clock that
+	 * cannot tick.
+	 */
+	p->osc_dead = osc_dead;
+	if (osc_dead)
+		dev_warn(&pdev->dev,
+			 "RTC oscillator enable did not latch; timekeeping unavailable (no 32kHz crystal?)\n");
+
+	p->rtc = devm_rtc_allocate_device(&pdev->dev);
+	if (IS_ERR(p->rtc))
+		return PTR_ERR(p->rtc);
+
+	p->rtc->ops = &m5441x_rtc_ops;
+	p->rtc->range_min = mktime64(1984, 1, 1, 0, 0, 0);
+	p->rtc->range_max = mktime64(2239, 12, 31, 23, 59, 59);
+
+	irq = platform_get_irq_optional(pdev, 0);
+	if (irq > 0) {
+		ret = devm_request_irq(&pdev->dev, irq, m5441x_rtc_irq, 0,
+				       dev_name(&pdev->dev), p);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"failed to request IRQ %d: %d, alarm disabled\n",
+				irq, ret);
+			clear_bit(RTC_FEATURE_ALARM, p->rtc->features);
+		} else {
+			/*
+			 * The alarm comparator runs on standby power, so let it
+			 * wake a suspended system. The PM core keeps the IRQ
+			 * enabled across suspend once it is the device's wake
+			 * IRQ; both are device-managed.
+			 */
+			devm_device_init_wakeup(&pdev->dev);
+			devm_pm_set_wake_irq(&pdev->dev, irq);
+		}
+	} else {
+		clear_bit(RTC_FEATURE_ALARM, p->rtc->features);
+	}
+
+	/*
+	 * Register the RTC device before the nvmem provider:
+	 * devm_rtc_nvmem_register() copies rtc->owner into the nvmem config,
+	 * and that field is only assigned by devm_rtc_register_device().
+	 * Registering nvmem first would leave its owner NULL and fail to pin
+	 * this module while standby-RAM consumers hold references.
+	 */
+	ret = devm_rtc_register_device(p->rtc);
+	if (ret)
+		return ret;
+
+	nvmem_cfg.priv = p;
+	ret = devm_rtc_nvmem_register(p->rtc, &nvmem_cfg);
+	if (ret)
+		dev_warn(&pdev->dev, "standby RAM nvmem unavailable: %d\n", ret);
+
+	return 0;
+}
+
+static struct platform_driver m5441x_rtc_driver = {
+	.driver = {
+		.name = "rtc-m5441x",
+	},
+	.probe = m5441x_rtc_probe,
+};
+module_platform_driver(m5441x_rtc_driver);
+
+MODULE_DESCRIPTION("Freescale MCF5441x on-chip RTC driver");
+MODULE_AUTHOR("Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-m5441x");

-- 
2.39.5


^ permalink raw reply related

* Re: [PATCH v1] rtc: mpfs: fix counter upload completion condition
From: Conor Dooley @ 2026-06-02  9:16 UTC (permalink / raw)
  To: linux-riscv
  Cc: Conor Dooley, stable, Valentina.FernandezAlanis, Daire McNamara,
	Alexandre Belloni, linux-rtc, linux-kernel
In-Reply-To: <20260513-panhandle-ashy-70c6abf84d59@spud>

[-- Attachment #1: Type: text/plain, Size: 2269 bytes --]

Hey Alexandrew,

On Wed, May 13, 2026 at 06:55:55PM +0100, Conor Dooley wrote:
> From: Conor Dooley <conor.dooley@microchip.com>
> 
> The condition that needs to be checked for upload completion is the
> UPLOAD bit in the completion register going low. The original iterations
> of this driver used a do-while and this was converted to a
> read_poll_timeout() during upstreaming without the condition being
> inverted as it should have been.
> 
> I suspect that this went unnoticed until now because a) the first read
> was done when the bit was still set, immediately completing the
> read_poll_timeout() and b) because the RTC doesn't hold time when power
> is removed from the SoC reducing its utility (I for one keep it
> disabled). If my first suspicion was true when the driver was
> upstreamed, it's not true any longer though, hence the detection of the
> problem.
> 
> Fixes: 0b31d703598dc ("rtc: Add driver for Microchip PolarFire SoC")
> CC: stable@vger.kernel.org
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>

Any chance this could be applied as 7.1 fixes material?

Apologies if I missed an application mail somewhere,
Conor.

> ---
> CC: Valentina.FernandezAlanis@microchip.com
> CC: Conor Dooley <conor.dooley@microchip.com>
> CC: Daire McNamara <daire.mcnamara@microchip.com>
> CC: Alexandre Belloni <alexandre.belloni@bootlin.com>
> CC: linux-riscv@lists.infradead.org
> CC: linux-rtc@vger.kernel.org
> CC: linux-kernel@vger.kernel.org
> ---
>  drivers/rtc/rtc-mpfs.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c
> index 6aa3eae575d2a..ece6de4a6adbd 100644
> --- a/drivers/rtc/rtc-mpfs.c
> +++ b/drivers/rtc/rtc-mpfs.c
> @@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm)
>  	ctrl |= CONTROL_UPLOAD_BIT;
>  	writel(ctrl, rtcdev->base + CONTROL_REG);
>  
> -	ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US,
> +	ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US,
>  				false, rtcdev->base + CONTROL_REG);
>  	if (ret) {
>  		dev_err(dev, "timed out uploading time to rtc");
> -- 
> 2.53.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH 1/2] rtc: m5441x: add MCF5441x on-chip RTC driver
From: Geert Uytterhoeven @ 2026-06-02 10:04 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Alexandre Belloni, Greg Ungerer, linux-kernel, linux-rtc,
	linux-m68k
In-Reply-To: <20260602-jmh-upstream-coldfire-rtc-v1-1-1e129a177d2f@yoseli.org>

Hi Jean-Michel,

On Tue, 2 Jun 2026 at 10:36, Jean-Michel Hautbois
<jeanmichel.hautbois@yoseli.org> wrote:
> Add an rtc-class driver for the Freescale MCF5441x on-chip "robust" RTC.
> It provides the time/calendar and alarm, and exposes the 2KB
> battery-backed standby RAM through the nvmem framework so userspace can
> preserve data across a main-power loss (the RAM is retained while
> VSTBY_RTC is supplied).
>
> Register and standby-RAM writes go through the RTC_CR[WE] knock
> sequence; the base-2112 year encoding and register map follow the
> MCF54418 reference manual. Based on the out-of-tree Freescale 3.0.x
> rtc-m5441x driver, rewritten for the current RTC and nvmem APIs.
>
> Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>

Thanks for your patch!

> +/*
> + * The time counters are unstable for an oscillator cycle either side of
> + * the one-second boundary. RTC_SR[INVAL] flags this; reads during the
> + * window return 0xffff and writes are nullified. Spin until it clears.
> + * The window is only a couple of 32kHz cycles (~60us), so bound the
> + * busy-wait tightly: it runs with the lock held and interrupts off.
> + * Caller holds p->lock.
> + */
> +static int m5441x_rtc_wait_valid(struct m5441x_rtc *p)
> +{
> +       unsigned int tries = 10;
> +
> +       while (rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_INVAL) {
> +               if (!--tries)
> +                       return -EIO;
> +               udelay(10);
> +       }

Please use read_poll_timeout().

> +
> +       return 0;
> +}

> +static int m5441x_rtc_nvram_read(void *priv, unsigned int offset,
> +                                void *val, size_t bytes)
> +{
> +       struct m5441x_rtc *p = priv;
> +       u8 *buf = val;
> +       size_t done;
> +
> +       /*
> +        * In-kernel nvmem_device_read() forwards offset/bytes verbatim, so
> +        * range-check here rather than trust the caller.
> +        */
> +       if (offset >= M5441X_RTC_SRAM_SIZE ||
> +           bytes > M5441X_RTC_SRAM_SIZE - offset)
> +               return -EINVAL;
> +
> +       /*
> +        * Process the transfer in chunks, releasing the lock between them, so
> +        * a full 2KB access does not keep hard interrupts disabled across
> +        * thousands of slow on-chip MMIO cycles and wreck IRQ latency.
> +        */
> +       for (done = 0; done < bytes; done += M5441X_RTC_SRAM_CHUNK) {
> +               size_t chunk = min_t(size_t, bytes - done, M5441X_RTC_SRAM_CHUNK);

size_t looks like overkill to me.

> +               unsigned long flags;
> +               size_t i;

Likewise

> +
> +               spin_lock_irqsave(&p->lock, flags);

scoped_guard(spinlock, &p->lock)?

> +               for (i = 0; i < chunk; i++)
> +                       buf[done + i] = ioread8(p->base + M5441X_RTC_SRAM_OFFSET +
> +                                               offset + done + i);
> +               spin_unlock_irqrestore(&p->lock, flags);
> +       }
> +
> +       return 0;
> +}

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH 1/2] rtc: m5441x: add MCF5441x on-chip RTC driver
From: Jean-Michel Hautbois @ 2026-06-02 11:05 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Alexandre Belloni, Greg Ungerer, linux-kernel, linux-rtc,
	linux-m68k
In-Reply-To: <CAMuHMdX=njpXOQu=LZCba0PjHRKbcbpCDMHjat-e+atMtNmSRQ@mail.gmail.com>

Hi Geert,

Le 02/06/2026 à 12:04, Geert Uytterhoeven a écrit :
> Hi Jean-Michel,
> 
> On Tue, 2 Jun 2026 at 10:36, Jean-Michel Hautbois
> <jeanmichel.hautbois@yoseli.org> wrote:
>> Add an rtc-class driver for the Freescale MCF5441x on-chip "robust" RTC.
>> It provides the time/calendar and alarm, and exposes the 2KB
>> battery-backed standby RAM through the nvmem framework so userspace can
>> preserve data across a main-power loss (the RAM is retained while
>> VSTBY_RTC is supplied).
>>
>> Register and standby-RAM writes go through the RTC_CR[WE] knock
>> sequence; the base-2112 year encoding and register map follow the
>> MCF54418 reference manual. Based on the out-of-tree Freescale 3.0.x
>> rtc-m5441x driver, rewritten for the current RTC and nvmem APIs.
>>
>> Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
> 
> Thanks for your patch!
> 
>> +/*
>> + * The time counters are unstable for an oscillator cycle either side of
>> + * the one-second boundary. RTC_SR[INVAL] flags this; reads during the
>> + * window return 0xffff and writes are nullified. Spin until it clears.
>> + * The window is only a couple of 32kHz cycles (~60us), so bound the
>> + * busy-wait tightly: it runs with the lock held and interrupts off.
>> + * Caller holds p->lock.
>> + */
>> +static int m5441x_rtc_wait_valid(struct m5441x_rtc *p)
>> +{
>> +       unsigned int tries = 10;
>> +
>> +       while (rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_INVAL) {
>> +               if (!--tries)
>> +                       return -EIO;
>> +               udelay(10);
>> +       }
> 
> Please use read_poll_timeout().

As wait_valid() is called after spin_lock_irqsave() is called, I suppose 
I should use read_poll_timeout_atomic() ? Because read_poll_timeout() 
sleeps ?

> 
>> +
>> +       return 0;
>> +}
> 
>> +static int m5441x_rtc_nvram_read(void *priv, unsigned int offset,
>> +                                void *val, size_t bytes)
>> +{
>> +       struct m5441x_rtc *p = priv;
>> +       u8 *buf = val;
>> +       size_t done;
>> +
>> +       /*
>> +        * In-kernel nvmem_device_read() forwards offset/bytes verbatim, so
>> +        * range-check here rather than trust the caller.
>> +        */
>> +       if (offset >= M5441X_RTC_SRAM_SIZE ||
>> +           bytes > M5441X_RTC_SRAM_SIZE - offset)
>> +               return -EINVAL;
>> +
>> +       /*
>> +        * Process the transfer in chunks, releasing the lock between them, so
>> +        * a full 2KB access does not keep hard interrupts disabled across
>> +        * thousands of slow on-chip MMIO cycles and wreck IRQ latency.
>> +        */
>> +       for (done = 0; done < bytes; done += M5441X_RTC_SRAM_CHUNK) {
>> +               size_t chunk = min_t(size_t, bytes - done, M5441X_RTC_SRAM_CHUNK);
> 
> size_t looks like overkill to me.
> 
>> +               unsigned long flags;
>> +               size_t i;
> 
> Likewise
> 

Done.

>> +
>> +               spin_lock_irqsave(&p->lock, flags);
> 
> scoped_guard(spinlock, &p->lock)?

Sure, thanks !

> 
>> +               for (i = 0; i < chunk; i++)
>> +                       buf[done + i] = ioread8(p->base + M5441X_RTC_SRAM_OFFSET +
>> +                                               offset + done + i);
>> +               spin_unlock_irqrestore(&p->lock, flags);
>> +       }
>> +
>> +       return 0;
>> +}
> 
> Gr{oetje,eeting}s,
> 
>                          Geert
> 


^ permalink raw reply

* Re: [PATCH 1/2] rtc: m5441x: add MCF5441x on-chip RTC driver
From: Geert Uytterhoeven @ 2026-06-02 11:08 UTC (permalink / raw)
  To: Jean-Michel Hautbois
  Cc: Alexandre Belloni, Greg Ungerer, linux-kernel, linux-rtc,
	linux-m68k
In-Reply-To: <42ab8311-0bd1-4094-aca7-0ca108c3a919@yoseli.org>

Hi Jean-Michel,

On Tue, 2 Jun 2026 at 13:05, Jean-Michel Hautbois
<jeanmichel.hautbois@yoseli.org> wrote:
> Le 02/06/2026 à 12:04, Geert Uytterhoeven a écrit :
> > On Tue, 2 Jun 2026 at 10:36, Jean-Michel Hautbois
> > <jeanmichel.hautbois@yoseli.org> wrote:
> >> Add an rtc-class driver for the Freescale MCF5441x on-chip "robust" RTC.
> >> It provides the time/calendar and alarm, and exposes the 2KB
> >> battery-backed standby RAM through the nvmem framework so userspace can
> >> preserve data across a main-power loss (the RAM is retained while
> >> VSTBY_RTC is supplied).
> >>
> >> Register and standby-RAM writes go through the RTC_CR[WE] knock
> >> sequence; the base-2112 year encoding and register map follow the
> >> MCF54418 reference manual. Based on the out-of-tree Freescale 3.0.x
> >> rtc-m5441x driver, rewritten for the current RTC and nvmem APIs.
> >>
> >> Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
> >
> > Thanks for your patch!
> >
> >> +/*
> >> + * The time counters are unstable for an oscillator cycle either side of
> >> + * the one-second boundary. RTC_SR[INVAL] flags this; reads during the
> >> + * window return 0xffff and writes are nullified. Spin until it clears.
> >> + * The window is only a couple of 32kHz cycles (~60us), so bound the
> >> + * busy-wait tightly: it runs with the lock held and interrupts off.
> >> + * Caller holds p->lock.
> >> + */
> >> +static int m5441x_rtc_wait_valid(struct m5441x_rtc *p)
> >> +{
> >> +       unsigned int tries = 10;
> >> +
> >> +       while (rtc_rd(p, M5441X_RTC_SR) & M5441X_RTC_SR_INVAL) {
> >> +               if (!--tries)
> >> +                       return -EIO;
> >> +               udelay(10);
> >> +       }
> >
> > Please use read_poll_timeout().
>
> As wait_valid() is called after spin_lock_irqsave() is called, I suppose
> I should use read_poll_timeout_atomic() ? Because read_poll_timeout()
> sleeps ?

Sorry, I copy and pasted the wrong function name, so you're right.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v1] rtc: mpfs: fix counter upload completion condition
From: Valentina.FernandezAlanis @ 2026-06-02 13:14 UTC (permalink / raw)
  To: conor, linux-riscv
  Cc: Conor.Dooley, stable, Daire.McNamara, alexandre.belloni,
	linux-rtc, linux-kernel, Valentina.FernandezAlanis
In-Reply-To: <20260513-panhandle-ashy-70c6abf84d59@spud>

On 13/05/2026 18:55, Conor Dooley wrote:
> From: Conor Dooley <conor.dooley@microchip.com>
>
> The condition that needs to be checked for upload completion is the
> UPLOAD bit in the completion register going low. The original iterations
> of this driver used a do-while and this was converted to a
> read_poll_timeout() during upstreaming without the condition being
> inverted as it should have been.
>
> I suspect that this went unnoticed until now because a) the first read
> was done when the bit was still set, immediately completing the
> read_poll_timeout() and b) because the RTC doesn't hold time when power
> is removed from the SoC reducing its utility (I for one keep it
> disabled). If my first suspicion was true when the driver was
> upstreamed, it's not true any longer though, hence the detection of the
> problem.
>
> Fixes: 0b31d703598dc ("rtc: Add driver for Microchip PolarFire SoC")
> CC: stable@vger.kernel.org
> Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Tested-by: Valentina Fernandez <valentina.fernandezalanis@microchip.com>
> ---
> CC: Valentina.FernandezAlanis@microchip.com
> CC: Conor Dooley <conor.dooley@microchip.com>
> CC: Daire McNamara <daire.mcnamara@microchip.com>
> CC: Alexandre Belloni <alexandre.belloni@bootlin.com>
> CC: linux-riscv@lists.infradead.org
> CC: linux-rtc@vger.kernel.org
> CC: linux-kernel@vger.kernel.org
> ---
>   drivers/rtc/rtc-mpfs.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c
> index 6aa3eae575d2a..ece6de4a6adbd 100644
> --- a/drivers/rtc/rtc-mpfs.c
> +++ b/drivers/rtc/rtc-mpfs.c
> @@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm)
>   	ctrl |= CONTROL_UPLOAD_BIT;
>   	writel(ctrl, rtcdev->base + CONTROL_REG);
>   
> -	ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US,
> +	ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US,
>   				false, rtcdev->base + CONTROL_REG);
>   	if (ret) {
>   		dev_err(dev, "timed out uploading time to rtc");



^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: rtc: add ASPEED AST2700 compatible
From: Conor Dooley @ 2026-06-02 16:59 UTC (permalink / raw)
  To: Tommy Huang
  Cc: Alexandre Belloni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery, linux-rtc, devicetree, linux-kernel,
	linux-arm-kernel, linux-aspeed
In-Reply-To: <20260601-ast2700-rtc-v1-1-15d4ca46500a@aspeedtech.com>

[-- Attachment #1: Type: text/plain, Size: 75 bytes --]

Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox