rtc: jz4740: Register clock provider for the CLK32K pin
authorPaul Cercueil <paul@crapouillou.net>
Sun, 29 Jan 2023 12:04:42 +0000 (12:04 +0000)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Thu, 9 Feb 2023 22:38:00 +0000 (23:38 +0100)
On JZ4770 and JZ4780, the CLK32K pin is configurable. By default, it is
configured as a GPIO in input mode, and its value can be read through
GPIO PD14.

With this change, clients can now request the 32 kHz clock on the CLK32K
pin, through Device Tree. This clock is simply a pass-through of the
input oscillator's clock with enable/disable operations.

This will permit the WiFi/Bluetooth chip to work on the MIPS CI20 board,
which does source one of its clocks from the CLK32K pin.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Link: https://lore.kernel.org/r/20230129120442.22858-5-paul@crapouillou.net
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/Kconfig
drivers/rtc/rtc-jz4740.c

index 677d2601d3057135d9fb3d76390c372da14252fb..d2b6d20a674548822e8d8c9feff3b8753643aa7b 100644 (file)
@@ -1690,7 +1690,7 @@ config RTC_DRV_MPC5121
 config RTC_DRV_JZ4740
        tristate "Ingenic JZ4740 SoC"
        depends on MIPS || COMPILE_TEST
-       depends on OF
+       depends on OF && COMMON_CLK
        help
          If you say yes here you get support for the Ingenic JZ47xx SoCs RTC
          controllers.
index 9ffa764aa71ebe964d7a0536783d1ee5b87d396c..59d279e3e6f5bb2c709d87c8cb8fed9bb9d55161 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
@@ -13,6 +14,7 @@
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pm_wakeirq.h>
+#include <linux/property.h>
 #include <linux/reboot.h>
 #include <linux/rtc.h>
 #include <linux/slab.h>
@@ -26,6 +28,7 @@
 #define JZ_REG_RTC_WAKEUP_FILTER       0x24
 #define JZ_REG_RTC_RESET_COUNTER       0x28
 #define JZ_REG_RTC_SCRATCHPAD  0x34
+#define JZ_REG_RTC_CKPCR       0x40
 
 /* The following are present on the jz4780 */
 #define JZ_REG_RTC_WENR        0x3C
@@ -45,6 +48,9 @@
 #define JZ_RTC_WAKEUP_FILTER_MASK      0x0000FFE0
 #define JZ_RTC_RESET_COUNTER_MASK      0x00000FE0
 
+#define JZ_RTC_CKPCR_CK32PULL_DIS      BIT(4)
+#define JZ_RTC_CKPCR_CK32CTL_EN                (BIT(2) | BIT(1))
+
 enum jz4740_rtc_type {
        ID_JZ4740,
        ID_JZ4760,
@@ -57,6 +63,8 @@ struct jz4740_rtc {
 
        struct rtc_device *rtc;
 
+       struct clk_hw clk32k;
+
        spinlock_t lock;
 };
 
@@ -254,6 +262,7 @@ static void jz4740_rtc_power_off(void)
 static const struct of_device_id jz4740_rtc_of_match[] = {
        { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 },
        { .compatible = "ingenic,jz4760-rtc", .data = (void *)ID_JZ4760 },
+       { .compatible = "ingenic,jz4770-rtc", .data = (void *)ID_JZ4780 },
        { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 },
        {},
 };
@@ -295,6 +304,38 @@ static void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc,
        jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, reset_ticks);
 }
 
+static int jz4740_rtc_clk32k_enable(struct clk_hw *hw)
+{
+       struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k);
+
+       return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR,
+                                   JZ_RTC_CKPCR_CK32PULL_DIS |
+                                   JZ_RTC_CKPCR_CK32CTL_EN);
+}
+
+static void jz4740_rtc_clk32k_disable(struct clk_hw *hw)
+{
+       struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k);
+
+       jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, 0);
+}
+
+static int jz4740_rtc_clk32k_is_enabled(struct clk_hw *hw)
+{
+       struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k);
+       u32 ckpcr;
+
+       ckpcr = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CKPCR);
+
+       return !!(ckpcr & JZ_RTC_CKPCR_CK32CTL_EN);
+}
+
+static const struct clk_ops jz4740_rtc_clk32k_ops = {
+       .enable = jz4740_rtc_clk32k_enable,
+       .disable = jz4740_rtc_clk32k_disable,
+       .is_enabled = jz4740_rtc_clk32k_is_enabled,
+};
+
 static int jz4740_rtc_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -364,6 +405,21 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
                        dev_warn(dev, "Poweroff handler already present!\n");
        }
 
+       if (device_property_present(dev, "#clock-cells")) {
+               rtc->clk32k.init = CLK_HW_INIT_HW("clk32k", __clk_get_hw(clk),
+                                                 &jz4740_rtc_clk32k_ops, 0);
+
+               ret = devm_clk_hw_register(dev, &rtc->clk32k);
+               if (ret)
+                       return dev_err_probe(dev, ret,
+                                            "Unable to register clk32k clock\n");
+
+               ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &rtc->clk32k);
+               if (ret)
+                       return dev_err_probe(dev, ret,
+                                            "Unable to register clk32k clock provider\n");
+       }
+
        return 0;
 }