]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rtc: rzn1: support input frequencies other than 32768Hz
authorWolfram Sang <wsa+renesas@sang-engineering.com>
Mon, 26 May 2025 09:58:04 +0000 (11:58 +0200)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Sun, 1 Jun 2025 21:53:02 +0000 (23:53 +0200)
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 <wsa+renesas@sang-engineering.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/r/20250526095801.35781-8-wsa+renesas@sang-engineering.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/rtc-rzn1.c

index 7777df1e34266432eb82ffc2fc0299ba7ae42689..c4ed4373545741d5f27095765989f4b139811249 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <linux/bcd.h>
+#include <linux/clk.h>
 #include <linux/init.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
@@ -22,7 +23,6 @@
 #include <linux/spinlock.h>
 
 #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);