Files
pmaports-rtwo/device/community/linux-pine64-pinephonepro/megi_patches/0015-rtc-rockchip-Add-support-for-RTC-present-in-RV1106-S.patch
T
Rudraksha Gupta bbd72c2711 linux-pine64-pinephonepro: update to v7.1
The reason why we have patches instead of a separate repo are because of
the following reasons:

    1. We don't want to make a repo to which users contribute to. While
        people haven't tried to commit to my repository yet, this is
        yet another incentive for people to upstream into Linux kernel
        mainline. Most of the functionality is here. We just need to
        upstream it.
    2. I assume that all the patches here aren't needed. This calls
        for a scream test. People who find functionalality missing will
        open an issue. It becomes trivial to add/remove patches from
        the `source=...` var rather than repackaging the entire kernel.
        The next commit aims to remove a lot of patches and see if there
        are people who rely on said functionality. If not, there is no
        requirement to upstream it.

Signed-off-by: Rudraksha Gupta <guptarud@gmail.com>
Part-of: <https://gitlab.postmarketos.org/postmarketOS/pmaports/-/merge_requests/9136>
2026-08-04 09:06:21 +00:00

869 lines
25 KiB
Diff

From eea2b3dbea7c25240cd594f028c5a33d92cf70df Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Tue, 25 Jun 2024 00:01:40 +0200
Subject: [PATCH 15/84] rtc: rockchip: Add support for RTC present in RV1106
SoC
This driver adds support for RTC present in Rockchip RV1106 SoC.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-rockchip.c | 810 +++++++++++++++++++++++++++++++++++++
3 files changed, 821 insertions(+)
create mode 100644 drivers/rtc/rtc-rockchip.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 364afc73f8ab..bc755773a31d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -456,6 +456,16 @@ config RTC_DRV_RK808
This driver can also be built as a module. If so, the module
will be called rk808-rtc.
+config RTC_DRV_ROCKCHIP
+ tristate "Rockchip RTC"
+ depends on ARCH_ROCKCHIP
+ help
+ If you say yes here you will get support for the
+ RTC of Rockchip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rockchip.
+
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6cf7e066314e..7428e7f6274f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o
obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
+obj-$(CONFIG_RTC_DRV_ROCKCHIP) += rtc-rockchip.o
obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
diff --git a/drivers/rtc/rtc-rockchip.c b/drivers/rtc/rtc-rockchip.c
new file mode 100644
index 000000000000..3998463565b8
--- /dev/null
+++ b/drivers/rtc/rtc-rockchip.c
@@ -0,0 +1,810 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd
+ */
+#include <linux/bcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+/* RTC_CTRL_REG bitfields */
+
+#define RTC_REG(x) ((x))
+#define RTC_SET_SECONDS RTC_REG(0x0)
+#define RTC_SET_MINUTES RTC_REG(0x4)
+#define RTC_SET_HOURS RTC_REG(0x8)
+#define RTC_SET_DAYS RTC_REG(0xc)
+#define RTC_SET_MONTHS RTC_REG(0x10)
+#define RTC_SET_YEARL RTC_REG(0x14)
+#define RTC_SET_YEARH RTC_REG(0x18)
+#define RTC_SET_WEEKS RTC_REG(0x1c)
+#define RTC_ALARM_SECONDS RTC_REG(0x20)
+#define RTC_ALARM_MINUTES RTC_REG(0x24)
+#define RTC_ALARM_HOURS RTC_REG(0x28)
+#define RTC_ALARM_DAYS RTC_REG(0x2c)
+#define RTC_ALARM_MONTHS RTC_REG(0x30)
+#define RTC_ALARM_YEARL RTC_REG(0x34)
+#define RTC_ALARM_YEARH RTC_REG(0x38)
+#define RTC_CTRL RTC_REG(0x3C)
+#define RTC_STATUS0 RTC_REG(0x40)
+#define RTC_STATUS1 RTC_REG(0x44)
+#define RTC_INT0_EN RTC_REG(0x48)
+#define RTC_INT1_EN RTC_REG(0x4c)
+#define RTC_MSEC_CTRL RTC_REG(0x50)
+#define RTC_MSEC_CNT RTC_REG(0x54)
+#define RTC_COMP_H RTC_REG(0x58)
+#define RTC_COMP_D RTC_REG(0x5c)
+#define RTC_COMP_M RTC_REG(0x60)
+#define RTC_ANALOG_CTRL RTC_REG(0x64)
+#define RTC_ANALOG_TEST RTC_REG(0x68)
+#define RTC_LDO_CTRL RTC_REG(0x6c)
+#define RTC_XO_TRIM0 RTC_REG(0x70)
+#define RTC_XO_TRIM1 RTC_REG(0x74)
+#define RTC_VPTAT_TRIM RTC_REG(0x78)
+#define RTC_ANALOG_EN RTC_REG(0x7c)
+#define RTC_CLK32K_TEST RTC_REG(0x80)
+#define RTC_TEST_ST RTC_REG(0x84)
+#define RTC_TEST_LEN RTC_REG(0x88)
+#define RTC_CNT_0 RTC_REG(0x8c)
+#define RTC_CNT_1 RTC_REG(0x90)
+#define RTC_CNT_2 RTC_REG(0x94)
+#define RTC_CNT_3 RTC_REG(0x98)
+#define RTC_MAX_REGISTER RTC_CNT_3
+
+#define VI_GRF_VI_MISC_CON0 0x50000
+#define RTC_CLAMP_EN BIT(6)
+
+/* RTC_CTRL_REG bitfields */
+#define RTC_CTRL_REG_START_RTC BIT(0)
+#define RTC_TIMEOUT (3000 * 1000)
+
+/* RK630 has a shadowed register for saving a "frozen" RTC time.
+ * When user setting "GET_TIME" to 1, the time will save in this shadowed
+ * register. If set "READSEL" to 1, user read rtc time register, actually
+ * get the time of that moment. If we need the real time, clr this bit.
+ */
+#define RTC_CTRL_REG_RTC_GET_TIME BIT(6)
+#define RTC_CTRL_REG_RTC_READSEL_M BIT(7)
+#define RTC_INT_REG_ALARM_EN BIT(7)
+#define RTC_D2A_XO_EN BIT(0)
+#define RTC_D2A_CLK_OUT_EN BIT(5)
+
+#define RTC_STATUS_MASK 0xFF
+
+#define SECONDS_REG_MSK 0x7F
+#define MINUTES_REG_MAK 0x7F
+#define HOURS_REG_MSK 0x3F
+#define DAYS_REG_MSK 0x3F
+#define MONTHS_REG_MSK 0x1F
+#define YEARS_REG_MSK 0xFF
+#define WEEKS_REG_MSK 0x7
+
+#define RTC_VREF_INIT 0x40
+
+#define D2A_POR_REG_SEL1 BIT(4)
+#define D2A_POR_REG_SEL0 BIT(1)
+
+#define NUM_TIME_REGS 8
+#define NUM_ALARM_REGS 7
+
+#define DISABLE_ALARM_INT 0x3F
+#define ENABLE_ALARM_INT 0xFF
+#define ALARM_INT_STATUS BIT(4)
+
+#define CLK32K_TEST_EN BIT(0)
+#define CLK32K_TEST_START BIT(0)
+#define CLK32K_TEST_STATUS BIT(1)
+#define CLK32K_TEST_DONE BIT(2)
+#define CLK32K_TEST_LEN 1
+
+#define CLK32K_COMP_DIR_ADD BIT(7)
+#define CLK32K_COMP_EN BIT(2)
+#define CLK32K_NO_COMP 0x1
+
+#define CLK32K_TEST_REF_CLK 24000000
+
+#define RTC_WRITE_MASK 0xc4522900
+
+enum {
+ ROCKCHIP_RV1106_RTC = 1,
+};
+
+struct rockchip_rtc {
+ struct regmap *regmap;
+ struct rtc_device *rtc;
+ struct regmap *grf;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ int irq;
+ unsigned int flag;
+ unsigned int mode;
+ struct delayed_work trim_work;
+};
+
+static unsigned int rockchip_rtc_write(struct regmap *map,
+ u32 offset, u32 val)
+{
+ return regmap_write(map, offset, val | RTC_WRITE_MASK);
+}
+
+static unsigned int rockchip_rtc_update_bits(struct regmap *map,
+ u32 offset, u32 mask,
+ u32 set)
+{
+ unsigned int val;
+
+ regmap_read(map, offset, &val);
+ return regmap_write(map, offset, (val & ~mask) | set | RTC_WRITE_MASK);
+}
+
+/* Read current time and date in RTC */
+static int rockchip_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rockchip_rtc *rtc = dev_get_drvdata(dev);
+ u32 rtc_data[NUM_TIME_REGS];
+ int ret;
+ int yearl, yearh;
+
+ /* No shadowed registers, need read time three time to update time */
+ ret = regmap_bulk_read(rtc->regmap, RTC_SET_SECONDS,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_bulk_read(rtc->regmap, RTC_SET_SECONDS,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_bulk_read(rtc->regmap, RTC_SET_SECONDS,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK);
+ tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK);
+ tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK);
+ tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK);
+ tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1;
+ yearl = (bcd2bin(rtc_data[5] & YEARS_REG_MSK));
+ yearh = (bcd2bin(rtc_data[6] & YEARS_REG_MSK));
+ tm->tm_year = yearh * 100 + yearl + 100;
+ tm->tm_wday = bcd2bin(rtc_data[7] & WEEKS_REG_MSK);
+
+ dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return ret;
+}
+
+/* Set current time and date in RTC */
+static int rockchip_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rockchip_rtc *rtc = dev_get_drvdata(dev);
+ u32 rtc_data[NUM_TIME_REGS];
+ int ret, status = 0;
+ int yearl, yearh;
+
+ dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ rtc_data[0] = bin2bcd(tm->tm_sec) | RTC_WRITE_MASK;
+ rtc_data[1] = bin2bcd(tm->tm_min) | RTC_WRITE_MASK;
+ rtc_data[2] = bin2bcd(tm->tm_hour) | RTC_WRITE_MASK;
+ rtc_data[3] = bin2bcd(tm->tm_mday) | RTC_WRITE_MASK;
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1) | RTC_WRITE_MASK;
+ if (tm->tm_year > 199) {
+ yearh = (tm->tm_year - 100) / 100;
+ yearl = tm->tm_year - 100 - yearh * 100;
+ } else {
+ yearh = 0;
+ yearl = tm->tm_year - 100 - yearh * 100;
+ }
+ rtc_data[5] = bin2bcd(yearl) | RTC_WRITE_MASK;
+ rtc_data[6] = bin2bcd(yearh) | RTC_WRITE_MASK;
+ rtc_data[7] = bin2bcd(tm->tm_wday) | RTC_WRITE_MASK;
+
+ /* Stop RTC while updating the RTC registers */
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL,
+ RTC_CTRL_REG_START_RTC, 0);
+ if (ret) {
+ dev_err(dev, "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(rtc->regmap, RTC_STATUS1, status,
+ !(status & RTC_CTRL_REG_START_RTC),
+ 1, RTC_TIMEOUT);
+ if (ret)
+ dev_err(dev,
+ "%s:timeout Update RTC_STATUS1 : %d\n",
+ __func__, ret);
+
+ ret = regmap_bulk_write(rtc->regmap, RTC_SET_SECONDS,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
+ return ret;
+ }
+
+ /* Start RTC again */
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL,
+ RTC_CTRL_REG_RTC_READSEL_M |
+ RTC_CTRL_REG_START_RTC,
+ RTC_CTRL_REG_RTC_READSEL_M |
+ RTC_CTRL_REG_START_RTC);
+ if (ret) {
+ dev_err(dev, "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(rtc->regmap, RTC_STATUS1, status,
+ (status & RTC_CTRL_REG_START_RTC),
+ 1, RTC_TIMEOUT);
+ if (ret)
+ dev_err(dev,
+ "%s:timeout Update RTC_STATUS1 : %d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+/* Read alarm time and date in RTC */
+static int rockchip_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rockchip_rtc *rtc = dev_get_drvdata(dev);
+ u32 alrm_data[NUM_ALARM_REGS];
+ u32 int_reg;
+ int yearl, yearh;
+ int ret;
+
+ ret = regmap_bulk_read(rtc->regmap,
+ RTC_ALARM_SECONDS,
+ alrm_data, NUM_ALARM_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to read RTC alarm date REG: %d\n", ret);
+ return ret;
+ }
+
+ alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
+ alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK);
+ alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK);
+ alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK);
+ alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1;
+ yearl = (bcd2bin(alrm_data[5] & YEARS_REG_MSK));
+ yearh = (bcd2bin(alrm_data[6] & YEARS_REG_MSK));
+ alrm->time.tm_year = yearh * 100 + yearl + 100;
+
+ ret = regmap_read(rtc->regmap, RTC_INT0_EN, &int_reg);
+ if (ret) {
+ dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev,
+ "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec);
+
+ alrm->enabled = (int_reg & RTC_INT_REG_ALARM_EN) ? 1 : 0;
+
+ return 0;
+}
+
+static int rockchip_rtc_stop_alarm(struct rockchip_rtc *rtc)
+{
+ int ret;
+
+ ret = rockchip_rtc_write(rtc->regmap, RTC_INT0_EN, 0);
+
+ return ret;
+}
+
+static int rockchip_rtc_start_alarm(struct rockchip_rtc *rtc)
+{
+ int ret;
+
+ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, RTC_STATUS_MASK);
+ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, 0);
+ ret = rockchip_rtc_write(rtc->regmap, RTC_INT0_EN, ENABLE_ALARM_INT);
+
+ return ret;
+}
+
+static int rockchip_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rockchip_rtc *rtc = dev_get_drvdata(dev);
+ u32 alrm_data[NUM_ALARM_REGS];
+ int yearl, yearh;
+ int ret;
+
+ ret = rockchip_rtc_stop_alarm(rtc);
+ if (ret) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev,
+ "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec);
+
+ alrm_data[0] = bin2bcd(alrm->time.tm_sec) | RTC_WRITE_MASK;
+ alrm_data[1] = bin2bcd(alrm->time.tm_min) | RTC_WRITE_MASK;
+ alrm_data[2] = bin2bcd(alrm->time.tm_hour) | RTC_WRITE_MASK;
+ alrm_data[3] = bin2bcd(alrm->time.tm_mday) | RTC_WRITE_MASK;
+ alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1) | RTC_WRITE_MASK;
+ if (alrm->time.tm_year > 199) {
+ yearh = (alrm->time.tm_year - 100) / 100;
+ yearl = alrm->time.tm_year - 100 - yearh * 100;
+ } else {
+ yearh = 0;
+ yearl = alrm->time.tm_year - 100 - yearh * 100;
+ }
+ alrm_data[5] = bin2bcd(yearl) | RTC_WRITE_MASK;
+ alrm_data[6] = bin2bcd(yearh) | RTC_WRITE_MASK;
+
+ ret = regmap_bulk_write(rtc->regmap,
+ RTC_ALARM_SECONDS,
+ alrm_data, NUM_ALARM_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk write: %d\n", ret);
+ return ret;
+ }
+ if (alrm->enabled) {
+ ret = rockchip_rtc_start_alarm(rtc);
+ if (ret) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int rockchip_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct rockchip_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ return rockchip_rtc_start_alarm(rtc);
+
+ return rockchip_rtc_stop_alarm(rtc);
+}
+
+/*
+ * We will just handle setting the frequency and make use the framework for
+ * reading the periodic interrupts.
+ *
+ */
+static irqreturn_t rockchip_rtc_alarm_irq(int irq, void *data)
+{
+ struct rockchip_rtc *rtc = data;
+ int ret, status;
+
+ ret = regmap_read(rtc->regmap, RTC_STATUS0, &status);
+ if (ret) {
+ pr_err("Failed to read RTC INT REG: %d\n", ret);
+ return ret;
+ }
+
+ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, status);
+ if (ret) {
+ pr_err("%s:Failed to update RTC status: %d\n", __func__, ret);
+ return ret;
+ }
+ if (status & ALARM_INT_STATUS) {
+ pr_info("Alarm by: %s\n", __func__);
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops rockchip_rtc_ops = {
+ .read_time = rockchip_rtc_read_time,
+ .set_time = rockchip_rtc_set_time,
+ .read_alarm = rockchip_rtc_readalarm,
+ .set_alarm = rockchip_rtc_setalarm,
+ .alarm_irq_enable = rockchip_rtc_alarm_irq_enable,
+};
+
+/*
+ * Due to the analog generator 32k clock affected by
+ * temperature, voltage, clock precision need test
+ * with the environment change. In rtc test,
+ * use 24M clock as reference clock to measure the 32k clock.
+ * Before start test 32k clock, we should enable clk32k test(0x80),
+ * and configure test length, when rtc test done(0x84[2]),
+ * latch the 24M clock domain counter,
+ * and read out the counter from rtc_test
+ * registers(0x8c~0x98) via apb bus.
+ * In RTC digital design, we set three level compensation,
+ * the compensation value due to the
+ * RTC 32k clock test result, and if we need compensation,
+ * we need configure the compensation enable bit.
+ * Comp every hour, compensation at last minute every hour,
+ * and support add time and sub time by the MSB bit.
+ * Comp every day, compensation at last minute in last hour every day,
+ * and support add time and sub time by the MSB bit.
+ * Comp every month, compensation at last minute
+ * in last hour in last day every month,
+ * and support add time and sub time by the MSB bit.
+ */
+static void rockchip_rtc_compensation_delay_work(struct work_struct *work)
+{
+ struct rockchip_rtc *rtc = container_of(work, struct rockchip_rtc, trim_work.work);
+ u64 camp;
+ u32 count[4], counts, g_ref, tcamp;
+ int ret, done = 0, trim_dir, c_hour,
+ c_day, c_det_day, c_mon, c_det_mon;
+
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CLK32K_TEST,
+ CLK32K_TEST_EN, CLK32K_TEST_EN);
+ if (ret) {
+ pr_err("%s:Failed to update RTC CLK32K TEST: %d\n",
+ __func__, ret);
+ return;
+ }
+
+ ret = rockchip_rtc_write(rtc->regmap, RTC_TEST_LEN,
+ CLK32K_TEST_LEN);
+ if (ret) {
+ pr_err("%s:Failed to update RTC CLK32K TEST LEN: %d\n",
+ __func__, ret);
+ return;
+ }
+
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_TEST_ST,
+ CLK32K_TEST_START,
+ CLK32K_TEST_START);
+ if (ret) {
+ pr_err("%s:Failed to update RTC CLK32K TEST STATUS : %d\n",
+ __func__, ret);
+ return;
+ }
+ ret = regmap_read_poll_timeout(rtc->regmap, RTC_TEST_ST, done,
+ (done & CLK32K_TEST_DONE), 20000, RTC_TIMEOUT);
+ if (ret)
+ pr_err("%s:timeout waiting for RTC TEST STATUS : %d\n",
+ __func__, ret);
+
+ ret = regmap_bulk_read(rtc->regmap,
+ RTC_CNT_0,
+ count, 4);
+ if (ret) {
+ pr_err("Failed to read RTC count REG: %d\n", ret);
+ return;
+ }
+
+ counts = count[0] | (count[1] << 8) |
+ (count[2] << 16) | (count[3] << 24);
+ g_ref = CLK32K_TEST_REF_CLK * (CLK32K_TEST_LEN + 1);
+
+ if (counts > g_ref) {
+ trim_dir = 0;
+ camp = 36ULL * (32768 * (counts - g_ref));
+ do_div(camp, (g_ref / 100));
+ } else {
+ trim_dir = CLK32K_COMP_DIR_ADD;
+ camp = 36ULL * (32768 * (g_ref - counts));
+ do_div(camp, (g_ref / 100));
+ }
+ tcamp = (u32)camp;
+ c_hour = DIV_ROUND_CLOSEST(tcamp, 32768);
+ c_day = DIV_ROUND_CLOSEST(24 * tcamp, 32768);
+ c_mon = DIV_ROUND_CLOSEST(30 * 24 * tcamp, 32768);
+
+ if (c_hour > 1)
+ rockchip_rtc_write(rtc->regmap, RTC_COMP_H, bin2bcd((c_hour - 1)) | trim_dir);
+ else
+ rockchip_rtc_write(rtc->regmap, RTC_COMP_H, CLK32K_NO_COMP);
+
+ if (c_day > c_hour * 23) {
+ c_det_day = c_day - c_hour * 23;
+ trim_dir = CLK32K_COMP_DIR_ADD;
+ } else {
+ c_det_day = c_hour * 24 - c_day;
+ trim_dir = 0;
+ }
+
+ if (c_det_day > 1)
+ rockchip_rtc_write(rtc->regmap, RTC_COMP_D,
+ bin2bcd((c_det_day - 1)) | trim_dir);
+ else
+ rockchip_rtc_write(rtc->regmap, RTC_COMP_D, CLK32K_NO_COMP);
+
+ if (c_mon > (29 * c_day + 23 * c_hour)) {
+ c_det_mon = c_mon - 29 * c_day - 23 * c_hour;
+ trim_dir = CLK32K_COMP_DIR_ADD;
+ } else {
+ c_det_mon = 29 * c_day + 23 * c_hour - c_mon;
+ trim_dir = 0;
+ }
+
+ if (c_det_mon)
+ rockchip_rtc_write(rtc->regmap, RTC_COMP_M,
+ bin2bcd((c_det_mon - 1)) | trim_dir);
+ else
+ rockchip_rtc_write(rtc->regmap, RTC_COMP_M, CLK32K_NO_COMP);
+
+ ret = regmap_read(rtc->regmap, RTC_CTRL, &done);
+ if (ret) {
+ pr_err("Failed to read RTC_CTRL: %d\n", ret);
+ return;
+ }
+
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL,
+ CLK32K_COMP_EN, CLK32K_COMP_EN);
+ if (ret) {
+ pr_err("%s:Failed to update RTC CTRL : %d\n", __func__, ret);
+ return;
+ }
+ return;
+}
+
+static bool rockchip_rtc_is_trimed(struct rockchip_rtc *rtc)
+{
+ int ret, comp_done;
+
+ ret = regmap_read(rtc->regmap, RTC_CTRL, &comp_done);
+ if (ret) {
+ pr_err("%s: Failed to read RTC_CTRL: %d\n", __func__, ret);
+ return false;
+ }
+ return (comp_done & CLK32K_COMP_EN) == CLK32K_COMP_EN;
+}
+
+static void rockchip_rtc_trim_start(struct rockchip_rtc *rtc)
+{
+ if (!rockchip_rtc_is_trimed(rtc))
+ queue_delayed_work(system_long_wq, &rtc->trim_work,
+ msecs_to_jiffies(5000));
+}
+
+static void __maybe_unused rockchip_rtc_trim_close(struct rockchip_rtc *rtc)
+{
+ if (!rockchip_rtc_is_trimed(rtc))
+ cancel_delayed_work_sync(&rtc->trim_work);
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+#ifdef CONFIG_PM_SLEEP
+/* Turn off the alarm if it should not be a wake source. */
+static int rockchip_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rockchip_rtc *rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->irq);
+
+ rockchip_rtc_trim_close(rtc);
+
+ if (rtc->grf) {
+ switch (rtc->mode) {
+ case ROCKCHIP_RV1106_RTC:
+ regmap_write(rtc->grf, VI_GRF_VI_MISC_CON0,
+ (RTC_CLAMP_EN << 16));
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ clk_bulk_disable_unprepare(rtc->num_clks, rtc->clks);
+
+ return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int rockchip_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rockchip_rtc *rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq);
+
+ if (rtc->grf) {
+ switch (rtc->mode) {
+ case ROCKCHIP_RV1106_RTC:
+ regmap_write(rtc->grf, VI_GRF_VI_MISC_CON0,
+ (RTC_CLAMP_EN << 16) | RTC_CLAMP_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ ret = clk_bulk_prepare_enable(rtc->num_clks, rtc->clks);
+ if (ret) {
+ dev_err(dev, "Cannot enable clock.\n");
+ return ret;
+ }
+ rockchip_rtc_trim_start(rtc);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rockchip_rtc_pm_ops,
+ rockchip_rtc_suspend, rockchip_rtc_resume);
+
+static const struct of_device_id rockchip_rtc_of_match[] = {
+ {
+ .compatible = "rockchip,rv1106-rtc",
+ .data = (void *)ROCKCHIP_RV1106_RTC
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_rtc_of_match);
+
+static void rockchip_rtc_clk_disable(void *data)
+{
+ struct rockchip_rtc *rtc = data;
+
+ clk_bulk_disable_unprepare(rtc->num_clks, rtc->clks);
+}
+
+static int rockchip_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct rockchip_rtc *rtc;
+ int ret;
+ struct rtc_time tm_read, tm = {
+ .tm_wday = 0,
+ .tm_year = 121,
+ .tm_mon = 0,
+ .tm_mday = 1,
+ .tm_hour = 12,
+ .tm_min = 0,
+ .tm_sec = 0,
+ };
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regmap = device_node_to_regmap(np);
+ if (IS_ERR(rtc->regmap))
+ return dev_err_probe(dev, PTR_ERR(rtc->regmap),
+ "no regmap available\n");
+
+ rtc->mode = (uintptr_t)device_get_match_data(dev);
+ rtc->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(rtc->grf)) {
+ dev_warn(dev, "Missing rockchip,grf property\n");
+ rtc->grf = NULL;
+ } else {
+ switch (rtc->mode) {
+ case ROCKCHIP_RV1106_RTC:
+ regmap_write(rtc->grf, VI_GRF_VI_MISC_CON0,
+ (RTC_CLAMP_EN << 16) | RTC_CLAMP_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rtc->clks);
+ if (rtc->num_clks < 1)
+ return -ENODEV;
+ ret = clk_bulk_prepare_enable(rtc->num_clks, rtc->clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot enable clock.\n");
+ ret = devm_add_action_or_reset(dev, rockchip_rtc_clk_disable, rtc);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add clk disable action.");
+
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_VPTAT_TRIM,
+ D2A_POR_REG_SEL1,
+ D2A_POR_REG_SEL1);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to write RTC_VPTAT_TRIM\n");
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_ANALOG_EN,
+ D2A_POR_REG_SEL0,
+ 0x00);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to write RTC_ANALOG_EN\n");
+
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_LDO_CTRL,
+ RTC_D2A_XO_EN,
+ RTC_D2A_XO_EN);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to update RTC_LDO_CTRL\n");
+
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_ANALOG_EN,
+ RTC_D2A_CLK_OUT_EN,
+ RTC_D2A_CLK_OUT_EN);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to update RTC_ANALOG_EN\n");
+
+ /* start rtc running by default, and use shadowed timer. */
+ ret = rockchip_rtc_update_bits(rtc->regmap, RTC_CTRL,
+ RTC_CTRL_REG_START_RTC |
+ RTC_CTRL_REG_RTC_READSEL_M,
+ RTC_CTRL_REG_RTC_READSEL_M |
+ RTC_CTRL_REG_START_RTC);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to update RTC control\n");
+
+ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, RTC_STATUS_MASK);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to write RTC status0\n");
+
+ ret = rockchip_rtc_write(rtc->regmap, RTC_STATUS0, 0);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to write RTC status0\n");
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rockchip_rtc_read_time(&pdev->dev, &tm_read);
+ if (rtc_valid_tm(&tm_read) != 0)
+ rockchip_rtc_set_time(&pdev->dev, &tm);
+
+ rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc->rtc))
+ return PTR_ERR(rtc->rtc);
+
+ rtc->rtc->ops = &rockchip_rtc_ops;
+
+ rtc->irq = platform_get_irq(pdev, 0);
+ if (rtc->irq < 0)
+ return dev_err_probe(&pdev->dev, rtc->irq, "No IRQ resource\n");
+
+ /* request alarm irq of rtc */
+ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+ &rockchip_rtc_alarm_irq, IRQF_ONESHOT,
+ "RTC alarm", rtc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to request alarm IRQ %d\n",
+ rtc->irq);
+
+ INIT_DELAYED_WORK(&rtc->trim_work, rockchip_rtc_compensation_delay_work);
+ rockchip_rtc_trim_start(rtc);
+
+ return devm_rtc_register_device(rtc->rtc);
+}
+
+static struct platform_driver rockchip_rtc_driver = {
+ .probe = rockchip_rtc_probe,
+ .driver = {
+ .name = "rockchip-rtc",
+ .pm = &rockchip_rtc_pm_ops,
+ .of_match_table = rockchip_rtc_of_match,
+ },
+};
+
+module_platform_driver(rockchip_rtc_driver);
+
+MODULE_DESCRIPTION("RTC driver for the rockchip");
+MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
+MODULE_LICENSE("GPL");
--
2.55.0