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