18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> 58c2ecf20Sopenharmony_ci * JZ4740 SoC RTC driver 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h> 158c2ecf20Sopenharmony_ci#include <linux/reboot.h> 168c2ecf20Sopenharmony_ci#include <linux/rtc.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define JZ_REG_RTC_CTRL 0x00 218c2ecf20Sopenharmony_ci#define JZ_REG_RTC_SEC 0x04 228c2ecf20Sopenharmony_ci#define JZ_REG_RTC_SEC_ALARM 0x08 238c2ecf20Sopenharmony_ci#define JZ_REG_RTC_REGULATOR 0x0C 248c2ecf20Sopenharmony_ci#define JZ_REG_RTC_HIBERNATE 0x20 258c2ecf20Sopenharmony_ci#define JZ_REG_RTC_WAKEUP_FILTER 0x24 268c2ecf20Sopenharmony_ci#define JZ_REG_RTC_RESET_COUNTER 0x28 278c2ecf20Sopenharmony_ci#define JZ_REG_RTC_SCRATCHPAD 0x34 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* The following are present on the jz4780 */ 308c2ecf20Sopenharmony_ci#define JZ_REG_RTC_WENR 0x3C 318c2ecf20Sopenharmony_ci#define JZ_RTC_WENR_WEN BIT(31) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_WRDY BIT(7) 348c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_1HZ BIT(6) 358c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_1HZ_IRQ BIT(5) 368c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_AF BIT(4) 378c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_AF_IRQ BIT(3) 388c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_AE BIT(2) 398c2ecf20Sopenharmony_ci#define JZ_RTC_CTRL_ENABLE BIT(0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Magic value to enable writes on jz4780 */ 428c2ecf20Sopenharmony_ci#define JZ_RTC_WENR_MAGIC 0xA55A 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 458c2ecf20Sopenharmony_ci#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cienum jz4740_rtc_type { 488c2ecf20Sopenharmony_ci ID_JZ4740, 498c2ecf20Sopenharmony_ci ID_JZ4760, 508c2ecf20Sopenharmony_ci ID_JZ4780, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct jz4740_rtc { 548c2ecf20Sopenharmony_ci void __iomem *base; 558c2ecf20Sopenharmony_ci enum jz4740_rtc_type type; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci struct rtc_device *rtc; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci spinlock_t lock; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct device *dev_for_power_off; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci return readl(rtc->base + reg); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci uint32_t ctrl; 728c2ecf20Sopenharmony_ci int timeout = 10000; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci do { 758c2ecf20Sopenharmony_ci ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); 768c2ecf20Sopenharmony_ci } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return timeout ? 0 : -EIO; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci uint32_t ctrl; 848c2ecf20Sopenharmony_ci int ret, timeout = 10000; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = jz4740_rtc_wait_write_ready(rtc); 878c2ecf20Sopenharmony_ci if (ret != 0) 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci do { 938c2ecf20Sopenharmony_ci ctrl = readl(rtc->base + JZ_REG_RTC_WENR); 948c2ecf20Sopenharmony_ci } while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return timeout ? 0 : -EIO; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, 1008c2ecf20Sopenharmony_ci uint32_t val) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci int ret = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (rtc->type >= ID_JZ4760) 1058c2ecf20Sopenharmony_ci ret = jz4780_rtc_enable_write(rtc); 1068c2ecf20Sopenharmony_ci if (ret == 0) 1078c2ecf20Sopenharmony_ci ret = jz4740_rtc_wait_write_ready(rtc); 1088c2ecf20Sopenharmony_ci if (ret == 0) 1098c2ecf20Sopenharmony_ci writel(val, rtc->base + reg); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, 1158c2ecf20Sopenharmony_ci bool set) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci unsigned long flags; 1198c2ecf20Sopenharmony_ci uint32_t ctrl; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc->lock, flags); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Don't clear interrupt flags by accident */ 1268c2ecf20Sopenharmony_ci ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (set) 1298c2ecf20Sopenharmony_ci ctrl |= mask; 1308c2ecf20Sopenharmony_ci else 1318c2ecf20Sopenharmony_ci ctrl &= ~mask; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc->lock, flags); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = dev_get_drvdata(dev); 1438c2ecf20Sopenharmony_ci uint32_t secs, secs2; 1448c2ecf20Sopenharmony_ci int timeout = 5; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD) != 0x12345678) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* If the seconds register is read while it is updated, it can contain a 1508c2ecf20Sopenharmony_ci * bogus value. This can be avoided by making sure that two consecutive 1518c2ecf20Sopenharmony_ci * reads have the same value. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); 1548c2ecf20Sopenharmony_ci secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci while (secs != secs2 && --timeout) { 1578c2ecf20Sopenharmony_ci secs = secs2; 1588c2ecf20Sopenharmony_ci secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (timeout == 0) 1628c2ecf20Sopenharmony_ci return -EIO; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, time); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int jz4740_rtc_set_time(struct device *dev, struct rtc_time *time) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = dev_get_drvdata(dev); 1728c2ecf20Sopenharmony_ci int ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, rtc_tm_to_time64(time)); 1758c2ecf20Sopenharmony_ci if (ret) 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = dev_get_drvdata(dev); 1848c2ecf20Sopenharmony_ci uint32_t secs; 1858c2ecf20Sopenharmony_ci uint32_t ctrl; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); 1928c2ecf20Sopenharmony_ci alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, &alrm->time); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci int ret; 2028c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = dev_get_drvdata(dev); 2038c2ecf20Sopenharmony_ci uint32_t secs = lower_32_bits(rtc_tm_to_time64(&alrm->time)); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs); 2068c2ecf20Sopenharmony_ci if (!ret) 2078c2ecf20Sopenharmony_ci ret = jz4740_rtc_ctrl_set_bits(rtc, 2088c2ecf20Sopenharmony_ci JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, alrm->enabled); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = dev_get_drvdata(dev); 2168c2ecf20Sopenharmony_ci return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic const struct rtc_class_ops jz4740_rtc_ops = { 2208c2ecf20Sopenharmony_ci .read_time = jz4740_rtc_read_time, 2218c2ecf20Sopenharmony_ci .set_time = jz4740_rtc_set_time, 2228c2ecf20Sopenharmony_ci .read_alarm = jz4740_rtc_read_alarm, 2238c2ecf20Sopenharmony_ci .set_alarm = jz4740_rtc_set_alarm, 2248c2ecf20Sopenharmony_ci .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic irqreturn_t jz4740_rtc_irq(int irq, void *data) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = data; 2308c2ecf20Sopenharmony_ci uint32_t ctrl; 2318c2ecf20Sopenharmony_ci unsigned long events = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (ctrl & JZ_RTC_CTRL_1HZ) 2368c2ecf20Sopenharmony_ci events |= (RTC_UF | RTC_IRQF); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (ctrl & JZ_RTC_CTRL_AF) 2398c2ecf20Sopenharmony_ci events |= (RTC_AF | RTC_IRQF); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci rtc_update_irq(rtc->rtc, 1, events); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void jz4740_rtc_poweroff(struct device *dev) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc = dev_get_drvdata(dev); 2518c2ecf20Sopenharmony_ci jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void jz4740_rtc_power_off(void) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci jz4740_rtc_poweroff(dev_for_power_off); 2578c2ecf20Sopenharmony_ci kernel_halt(); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void jz4740_rtc_clk_disable(void *data) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci clk_disable_unprepare(data); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct of_device_id jz4740_rtc_of_match[] = { 2668c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 }, 2678c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4760-rtc", .data = (void *)ID_JZ4760 }, 2688c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, 2698c2ecf20Sopenharmony_ci {}, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc, 2748c2ecf20Sopenharmony_ci struct device_node *np, 2758c2ecf20Sopenharmony_ci unsigned long rate) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci unsigned long wakeup_ticks, reset_ticks; 2788c2ecf20Sopenharmony_ci unsigned int min_wakeup_pin_assert_time = 60; /* Default: 60ms */ 2798c2ecf20Sopenharmony_ci unsigned int reset_pin_assert_time = 100; /* Default: 100ms */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci of_property_read_u32(np, "ingenic,reset-pin-assert-time-ms", 2828c2ecf20Sopenharmony_ci &reset_pin_assert_time); 2838c2ecf20Sopenharmony_ci of_property_read_u32(np, "ingenic,min-wakeup-pin-assert-time-ms", 2848c2ecf20Sopenharmony_ci &min_wakeup_pin_assert_time); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * Set minimum wakeup pin assertion time: 100 ms. 2888c2ecf20Sopenharmony_ci * Range is 0 to 2 sec if RTC is clocked at 32 kHz. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci wakeup_ticks = (min_wakeup_pin_assert_time * rate) / 1000; 2918c2ecf20Sopenharmony_ci if (wakeup_ticks < JZ_RTC_WAKEUP_FILTER_MASK) 2928c2ecf20Sopenharmony_ci wakeup_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; 2938c2ecf20Sopenharmony_ci else 2948c2ecf20Sopenharmony_ci wakeup_ticks = JZ_RTC_WAKEUP_FILTER_MASK; 2958c2ecf20Sopenharmony_ci jz4740_rtc_reg_write(rtc, JZ_REG_RTC_WAKEUP_FILTER, wakeup_ticks); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* 2988c2ecf20Sopenharmony_ci * Set reset pin low-level assertion time after wakeup: 60 ms. 2998c2ecf20Sopenharmony_ci * Range is 0 to 125 ms if RTC is clocked at 32 kHz. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci reset_ticks = (reset_pin_assert_time * rate) / 1000; 3028c2ecf20Sopenharmony_ci if (reset_ticks < JZ_RTC_RESET_COUNTER_MASK) 3038c2ecf20Sopenharmony_ci reset_ticks &= JZ_RTC_RESET_COUNTER_MASK; 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci reset_ticks = JZ_RTC_RESET_COUNTER_MASK; 3068c2ecf20Sopenharmony_ci jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, reset_ticks); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int jz4740_rtc_probe(struct platform_device *pdev) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3128c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 3138c2ecf20Sopenharmony_ci struct jz4740_rtc *rtc; 3148c2ecf20Sopenharmony_ci unsigned long rate; 3158c2ecf20Sopenharmony_ci struct clk *clk; 3168c2ecf20Sopenharmony_ci int ret, irq; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); 3198c2ecf20Sopenharmony_ci if (!rtc) 3208c2ecf20Sopenharmony_ci return -ENOMEM; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci rtc->type = (enum jz4740_rtc_type)device_get_match_data(dev); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3258c2ecf20Sopenharmony_ci if (irq < 0) 3268c2ecf20Sopenharmony_ci return irq; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci rtc->base = devm_platform_ioremap_resource(pdev, 0); 3298c2ecf20Sopenharmony_ci if (IS_ERR(rtc->base)) 3308c2ecf20Sopenharmony_ci return PTR_ERR(rtc->base); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, "rtc"); 3338c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 3348c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get RTC clock\n"); 3358c2ecf20Sopenharmony_ci return PTR_ERR(clk); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 3398c2ecf20Sopenharmony_ci if (ret) { 3408c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable clock\n"); 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, jz4740_rtc_clk_disable, clk); 3458c2ecf20Sopenharmony_ci if (ret) { 3468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register devm action\n"); 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci spin_lock_init(&rtc->lock); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rtc); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci device_init_wakeup(dev, 1); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = dev_pm_set_wake_irq(dev, irq); 3578c2ecf20Sopenharmony_ci if (ret) { 3588c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set wake irq: %d\n", ret); 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci rtc->rtc = devm_rtc_allocate_device(dev); 3638c2ecf20Sopenharmony_ci if (IS_ERR(rtc->rtc)) { 3648c2ecf20Sopenharmony_ci ret = PTR_ERR(rtc->rtc); 3658c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate rtc device: %d\n", ret); 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci rtc->rtc->ops = &jz4740_rtc_ops; 3708c2ecf20Sopenharmony_ci rtc->rtc->range_max = U32_MAX; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 3738c2ecf20Sopenharmony_ci jz4740_rtc_set_wakeup_params(rtc, np, rate); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* Each 1 Hz pulse should happen after (rate) ticks */ 3768c2ecf20Sopenharmony_ci jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, rate - 1); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ret = rtc_register_device(rtc->rtc); 3798c2ecf20Sopenharmony_ci if (ret) 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, jz4740_rtc_irq, 0, 3838c2ecf20Sopenharmony_ci pdev->name, rtc); 3848c2ecf20Sopenharmony_ci if (ret) { 3858c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request rtc irq: %d\n", ret); 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (of_device_is_system_power_controller(np)) { 3908c2ecf20Sopenharmony_ci dev_for_power_off = dev; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (!pm_power_off) 3938c2ecf20Sopenharmony_ci pm_power_off = jz4740_rtc_power_off; 3948c2ecf20Sopenharmony_ci else 3958c2ecf20Sopenharmony_ci dev_warn(dev, "Poweroff handler already present!\n"); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic struct platform_driver jz4740_rtc_driver = { 4028c2ecf20Sopenharmony_ci .probe = jz4740_rtc_probe, 4038c2ecf20Sopenharmony_ci .driver = { 4048c2ecf20Sopenharmony_ci .name = "jz4740-rtc", 4058c2ecf20Sopenharmony_ci .of_match_table = jz4740_rtc_of_match, 4068c2ecf20Sopenharmony_ci }, 4078c2ecf20Sopenharmony_ci}; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cimodule_platform_driver(jz4740_rtc_driver); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 4128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); 4148c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:jz4740-rtc"); 415