[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20220418184933.13172-6-paul@crapouillou.net>
Date: Mon, 18 Apr 2022 19:49:33 +0100
From: Paul Cercueil <paul@...pouillou.net>
To: Alessandro Zummo <a.zummo@...ertech.it>,
Alexandre Belloni <alexandre.belloni@...tlin.com>
Cc: list@...ndingux.net, linux-rtc@...r.kernel.org,
devicetree@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-mips@...r.kernel.org, Paul Cercueil <paul@...pouillou.net>
Subject: [PATCH 5/5] rtc: jz4740: Support for fine-tuning the RTC clock
Write the NC1HZ and ADJC register fields, which allow to tweak the
frequency of the RTC clock, so that it can run as accurately as
possible.
Signed-off-by: Paul Cercueil <paul@...pouillou.net>
---
drivers/rtc/rtc-jz4740.c | 45 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index f4c9b6058f07..f275e58a9cea 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -5,6 +5,7 @@
* JZ4740 SoC RTC driver
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
@@ -41,6 +42,9 @@
#define JZ_RTC_CTRL_AE BIT(2)
#define JZ_RTC_CTRL_ENABLE BIT(0)
+#define JZ_RTC_REGULATOR_NC1HZ_MASK GENMASK(15, 0)
+#define JZ_RTC_REGULATOR_ADJC_MASK GENMASK(25, 16)
+
/* Magic value to enable writes on jz4780 */
#define JZ_RTC_WENR_MAGIC 0xA55A
@@ -64,6 +68,7 @@ struct jz4740_rtc {
enum jz4740_rtc_type type;
struct rtc_device *rtc;
+ struct clk *clk;
struct clk_hw clk32k;
@@ -222,12 +227,51 @@ static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable);
}
+static int jz4740_rtc_read_offset(struct device *dev, long *offset)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ long rate = clk_get_rate(rtc->clk);
+ s32 nc1hz, adjc, offset1k;
+ u32 reg;
+
+ reg = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_REGULATOR);
+ nc1hz = FIELD_GET(JZ_RTC_REGULATOR_NC1HZ_MASK, reg);
+ adjc = FIELD_GET(JZ_RTC_REGULATOR_ADJC_MASK, reg);
+
+ offset1k = (nc1hz - rate + 1) * 1024L + adjc;
+ *offset = offset1k * 1000000L / (rate * 1024L);
+
+ return 0;
+}
+
+static int jz4740_rtc_set_offset(struct device *dev, long offset)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ long rate = clk_get_rate(rtc->clk);
+ s32 offset1k, adjc, nc1hz;
+
+ offset1k = div_s64_rem(offset * rate * 1024LL, 1000000LL, &adjc);
+ nc1hz = rate - 1 + offset1k / 1024L;
+
+ if (adjc < 0) {
+ nc1hz--;
+ adjc += 1024;
+ }
+
+ nc1hz = FIELD_PREP(JZ_RTC_REGULATOR_NC1HZ_MASK, nc1hz);
+ adjc = FIELD_PREP(JZ_RTC_REGULATOR_ADJC_MASK, adjc);
+
+ return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, nc1hz | adjc);
+}
+
static const struct rtc_class_ops jz4740_rtc_ops = {
.read_time = jz4740_rtc_read_time,
.set_time = jz4740_rtc_set_time,
.read_alarm = jz4740_rtc_read_alarm,
.set_alarm = jz4740_rtc_set_alarm,
.alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
+ .read_offset = jz4740_rtc_read_offset,
+ .set_offset = jz4740_rtc_set_offset,
};
static irqreturn_t jz4740_rtc_irq(int irq, void *data)
@@ -378,6 +422,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
spin_lock_init(&rtc->lock);
+ rtc->clk = clk;
platform_set_drvdata(pdev, rtc);
device_init_wakeup(dev, 1);
--
2.35.1
Powered by blists - more mailing lists