diff options
Diffstat (limited to 'drivers/rtc/rtc-jz4740.c')
-rw-r--r-- | drivers/rtc/rtc-jz4740.c | 94 |
1 files changed, 69 insertions, 25 deletions
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index c383719292c7..59d279e3e6f5 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -6,12 +6,15 @@ */ #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #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> @@ -25,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 @@ -44,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, @@ -56,6 +63,8 @@ struct jz4740_rtc { struct rtc_device *rtc; + struct clk_hw clk32k; + spinlock_t lock; }; @@ -69,19 +78,15 @@ static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) { uint32_t ctrl; - int timeout = 10000; - do { - ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); - } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout); - - return timeout ? 0 : -EIO; + return readl_poll_timeout(rtc->base + JZ_REG_RTC_CTRL, ctrl, + ctrl & JZ_RTC_CTRL_WRDY, 0, 1000); } static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) { uint32_t ctrl; - int ret, timeout = 10000; + int ret; ret = jz4740_rtc_wait_write_ready(rtc); if (ret != 0) @@ -89,11 +94,8 @@ static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR); - do { - ctrl = readl(rtc->base + JZ_REG_RTC_WENR); - } while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout); - - return timeout ? 0 : -EIO; + return readl_poll_timeout(rtc->base + JZ_REG_RTC_WENR, ctrl, + ctrl & JZ_RTC_WENR_WEN, 0, 1000); } static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, @@ -260,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 }, {}, }; @@ -301,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; @@ -335,17 +370,13 @@ static int jz4740_rtc_probe(struct platform_device *pdev) device_init_wakeup(dev, 1); ret = dev_pm_set_wake_irq(dev, irq); - if (ret) { - dev_err(dev, "Failed to set wake irq: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to set wake irq\n"); rtc->rtc = devm_rtc_allocate_device(dev); - if (IS_ERR(rtc->rtc)) { - ret = PTR_ERR(rtc->rtc); - dev_err(dev, "Failed to allocate rtc device: %d\n", ret); - return ret; - } + if (IS_ERR(rtc->rtc)) + return dev_err_probe(dev, PTR_ERR(rtc->rtc), + "Failed to allocate rtc device\n"); rtc->rtc->ops = &jz4740_rtc_ops; rtc->rtc->range_max = U32_MAX; @@ -362,10 +393,8 @@ static int jz4740_rtc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, jz4740_rtc_irq, 0, pdev->name, rtc); - if (ret) { - dev_err(dev, "Failed to request rtc irq: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request rtc irq\n"); if (of_device_is_system_power_controller(np)) { dev_for_power_off = dev; @@ -376,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; } |