18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * An RTC driver for the NVIDIA Tegra 200 series internal RTC.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2019, NVIDIA Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/irq.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/pm.h>
188c2ecf20Sopenharmony_ci#include <linux/rtc.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Set to 1 = busy every eight 32 kHz clocks during copy of sec+msec to AHB. */
228c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_BUSY			0x004
238c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_SECONDS			0x008
248c2ecf20Sopenharmony_ci/* When msec is read, the seconds are buffered into shadow seconds. */
258c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_SHADOW_SECONDS		0x00c
268c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_MILLI_SECONDS		0x010
278c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_SECONDS_ALARM0		0x014
288c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_SECONDS_ALARM1		0x018
298c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_MILLI_SECONDS_ALARM0	0x01c
308c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_INTR_MASK			0x028
318c2ecf20Sopenharmony_ci/* write 1 bits to clear status bits */
328c2ecf20Sopenharmony_ci#define TEGRA_RTC_REG_INTR_STATUS		0x02c
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* bits in INTR_MASK */
358c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_MASK_MSEC_CDN_ALARM	(1<<4)
368c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_MASK_SEC_CDN_ALARM	(1<<3)
378c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_MASK_MSEC_ALARM		(1<<2)
388c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_MASK_SEC_ALARM1		(1<<1)
398c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_MASK_SEC_ALARM0		(1<<0)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* bits in INTR_STATUS */
428c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_STATUS_MSEC_CDN_ALARM	(1<<4)
438c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM	(1<<3)
448c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_STATUS_MSEC_ALARM	(1<<2)
458c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_STATUS_SEC_ALARM1	(1<<1)
468c2ecf20Sopenharmony_ci#define TEGRA_RTC_INTR_STATUS_SEC_ALARM0	(1<<0)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct tegra_rtc_info {
498c2ecf20Sopenharmony_ci	struct platform_device *pdev;
508c2ecf20Sopenharmony_ci	struct rtc_device *rtc;
518c2ecf20Sopenharmony_ci	void __iomem *base; /* NULL if not initialized */
528c2ecf20Sopenharmony_ci	struct clk *clk;
538c2ecf20Sopenharmony_ci	int irq; /* alarm and periodic IRQ */
548c2ecf20Sopenharmony_ci	spinlock_t lock;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * RTC hardware is busy when it is updating its values over AHB once every
598c2ecf20Sopenharmony_ci * eight 32 kHz clocks (~250 us). Outside of these updates the CPU is free to
608c2ecf20Sopenharmony_ci * write. CPU is always free to read.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic inline u32 tegra_rtc_check_busy(struct tegra_rtc_info *info)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return readl(info->base + TEGRA_RTC_REG_BUSY) & 1;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci * Wait for hardware to be ready for writing. This function tries to maximize
698c2ecf20Sopenharmony_ci * the amount of time before the next update. It does this by waiting for the
708c2ecf20Sopenharmony_ci * RTC to become busy with its periodic update, then returning once the RTC
718c2ecf20Sopenharmony_ci * first becomes not busy.
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci * This periodic update (where the seconds and milliseconds are copied to the
748c2ecf20Sopenharmony_ci * AHB side) occurs every eight 32 kHz clocks (~250 us). The behavior of this
758c2ecf20Sopenharmony_ci * function allows us to make some assumptions without introducing a race,
768c2ecf20Sopenharmony_ci * because 250 us is plenty of time to read/write a value.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_cistatic int tegra_rtc_wait_while_busy(struct device *dev)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
818c2ecf20Sopenharmony_ci	int retries = 500; /* ~490 us is the worst case, ~250 us is best */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/*
848c2ecf20Sopenharmony_ci	 * First wait for the RTC to become busy. This is when it posts its
858c2ecf20Sopenharmony_ci	 * updated seconds+msec registers to AHB side.
868c2ecf20Sopenharmony_ci	 */
878c2ecf20Sopenharmony_ci	while (tegra_rtc_check_busy(info)) {
888c2ecf20Sopenharmony_ci		if (!retries--)
898c2ecf20Sopenharmony_ci			goto retry_failed;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		udelay(1);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* now we have about 250 us to manipulate registers */
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciretry_failed:
988c2ecf20Sopenharmony_ci	dev_err(dev, "write failed: retry count exceeded\n");
998c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int tegra_rtc_read_time(struct device *dev, struct rtc_time *tm)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
1058c2ecf20Sopenharmony_ci	unsigned long flags;
1068c2ecf20Sopenharmony_ci	u32 sec;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * RTC hardware copies seconds to shadow seconds when a read of
1108c2ecf20Sopenharmony_ci	 * milliseconds occurs. use a lock to keep other threads out.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	readl(info->base + TEGRA_RTC_REG_MILLI_SECONDS);
1158c2ecf20Sopenharmony_ci	sec = readl(info->base + TEGRA_RTC_REG_SHADOW_SECONDS);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	rtc_time64_to_tm(sec, tm);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	dev_vdbg(dev, "time read as %u, %ptR\n", sec, tm);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
1298c2ecf20Sopenharmony_ci	u32 sec;
1308c2ecf20Sopenharmony_ci	int ret;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* convert tm to seconds */
1338c2ecf20Sopenharmony_ci	sec = rtc_tm_to_time64(tm);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	dev_vdbg(dev, "time set to %u, %ptR\n", sec, tm);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* seconds only written if wait succeeded */
1388c2ecf20Sopenharmony_ci	ret = tegra_rtc_wait_while_busy(dev);
1398c2ecf20Sopenharmony_ci	if (!ret)
1408c2ecf20Sopenharmony_ci		writel(sec, info->base + TEGRA_RTC_REG_SECONDS);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	dev_vdbg(dev, "time read back as %d\n",
1438c2ecf20Sopenharmony_ci		 readl(info->base + TEGRA_RTC_REG_SECONDS));
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return ret;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int tegra_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
1518c2ecf20Sopenharmony_ci	u32 sec, value;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	sec = readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (sec == 0) {
1568c2ecf20Sopenharmony_ci		/* alarm is disabled */
1578c2ecf20Sopenharmony_ci		alarm->enabled = 0;
1588c2ecf20Sopenharmony_ci	} else {
1598c2ecf20Sopenharmony_ci		/* alarm is enabled */
1608c2ecf20Sopenharmony_ci		alarm->enabled = 1;
1618c2ecf20Sopenharmony_ci		rtc_time64_to_tm(sec, &alarm->time);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	value = readl(info->base + TEGRA_RTC_REG_INTR_STATUS);
1658c2ecf20Sopenharmony_ci	alarm->pending = (value & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) != 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int tegra_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
1738c2ecf20Sopenharmony_ci	unsigned long flags;
1748c2ecf20Sopenharmony_ci	u32 status;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	tegra_rtc_wait_while_busy(dev);
1778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* read the original value, and OR in the flag */
1808c2ecf20Sopenharmony_ci	status = readl(info->base + TEGRA_RTC_REG_INTR_MASK);
1818c2ecf20Sopenharmony_ci	if (enabled)
1828c2ecf20Sopenharmony_ci		status |= TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* set it */
1838c2ecf20Sopenharmony_ci	else
1848c2ecf20Sopenharmony_ci		status &= ~TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* clear it */
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	writel(status, info->base + TEGRA_RTC_REG_INTR_MASK);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int tegra_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
1968c2ecf20Sopenharmony_ci	u32 sec;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (alarm->enabled)
1998c2ecf20Sopenharmony_ci		sec = rtc_tm_to_time64(&alarm->time);
2008c2ecf20Sopenharmony_ci	else
2018c2ecf20Sopenharmony_ci		sec = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	tegra_rtc_wait_while_busy(dev);
2048c2ecf20Sopenharmony_ci	writel(sec, info->base + TEGRA_RTC_REG_SECONDS_ALARM0);
2058c2ecf20Sopenharmony_ci	dev_vdbg(dev, "alarm read back as %d\n",
2068c2ecf20Sopenharmony_ci		 readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0));
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* if successfully written and alarm is enabled ... */
2098c2ecf20Sopenharmony_ci	if (sec) {
2108c2ecf20Sopenharmony_ci		tegra_rtc_alarm_irq_enable(dev, 1);
2118c2ecf20Sopenharmony_ci		dev_vdbg(dev, "alarm set as %u, %ptR\n", sec, &alarm->time);
2128c2ecf20Sopenharmony_ci	} else {
2138c2ecf20Sopenharmony_ci		/* disable alarm if 0 or write error */
2148c2ecf20Sopenharmony_ci		dev_vdbg(dev, "alarm disabled\n");
2158c2ecf20Sopenharmony_ci		tegra_rtc_alarm_irq_enable(dev, 0);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int tegra_rtc_proc(struct device *dev, struct seq_file *seq)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	if (!dev || !dev->driver)
2248c2ecf20Sopenharmony_ci		return 0;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	seq_printf(seq, "name\t\t: %s\n", dev_name(dev));
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return 0;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic irqreturn_t tegra_rtc_irq_handler(int irq, void *data)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct device *dev = data;
2348c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
2358c2ecf20Sopenharmony_ci	unsigned long events = 0, flags;
2368c2ecf20Sopenharmony_ci	u32 status;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	status = readl(info->base + TEGRA_RTC_REG_INTR_STATUS);
2398c2ecf20Sopenharmony_ci	if (status) {
2408c2ecf20Sopenharmony_ci		/* clear the interrupt masks and status on any IRQ */
2418c2ecf20Sopenharmony_ci		tegra_rtc_wait_while_busy(dev);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock, flags);
2448c2ecf20Sopenharmony_ci		writel(0, info->base + TEGRA_RTC_REG_INTR_MASK);
2458c2ecf20Sopenharmony_ci		writel(status, info->base + TEGRA_RTC_REG_INTR_STATUS);
2468c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock, flags);
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* check if alarm */
2508c2ecf20Sopenharmony_ci	if (status & TEGRA_RTC_INTR_STATUS_SEC_ALARM0)
2518c2ecf20Sopenharmony_ci		events |= RTC_IRQF | RTC_AF;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* check if periodic */
2548c2ecf20Sopenharmony_ci	if (status & TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM)
2558c2ecf20Sopenharmony_ci		events |= RTC_IRQF | RTC_PF;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	rtc_update_irq(info->rtc, 1, events);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic const struct rtc_class_ops tegra_rtc_ops = {
2638c2ecf20Sopenharmony_ci	.read_time = tegra_rtc_read_time,
2648c2ecf20Sopenharmony_ci	.set_time = tegra_rtc_set_time,
2658c2ecf20Sopenharmony_ci	.read_alarm = tegra_rtc_read_alarm,
2668c2ecf20Sopenharmony_ci	.set_alarm = tegra_rtc_set_alarm,
2678c2ecf20Sopenharmony_ci	.proc = tegra_rtc_proc,
2688c2ecf20Sopenharmony_ci	.alarm_irq_enable = tegra_rtc_alarm_irq_enable,
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_rtc_dt_match[] = {
2728c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra20-rtc", },
2738c2ecf20Sopenharmony_ci	{}
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_rtc_dt_match);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int tegra_rtc_probe(struct platform_device *pdev)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info;
2808c2ecf20Sopenharmony_ci	int ret;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
2838c2ecf20Sopenharmony_ci	if (!info)
2848c2ecf20Sopenharmony_ci		return -ENOMEM;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	info->base = devm_platform_ioremap_resource(pdev, 0);
2878c2ecf20Sopenharmony_ci	if (IS_ERR(info->base))
2888c2ecf20Sopenharmony_ci		return PTR_ERR(info->base);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
2918c2ecf20Sopenharmony_ci	if (ret <= 0)
2928c2ecf20Sopenharmony_ci		return ret;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	info->irq = ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	info->rtc = devm_rtc_allocate_device(&pdev->dev);
2978c2ecf20Sopenharmony_ci	if (IS_ERR(info->rtc))
2988c2ecf20Sopenharmony_ci		return PTR_ERR(info->rtc);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	info->rtc->ops = &tegra_rtc_ops;
3018c2ecf20Sopenharmony_ci	info->rtc->range_max = U32_MAX;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	info->clk = devm_clk_get(&pdev->dev, NULL);
3048c2ecf20Sopenharmony_ci	if (IS_ERR(info->clk))
3058c2ecf20Sopenharmony_ci		return PTR_ERR(info->clk);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(info->clk);
3088c2ecf20Sopenharmony_ci	if (ret < 0)
3098c2ecf20Sopenharmony_ci		return ret;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* set context info */
3128c2ecf20Sopenharmony_ci	info->pdev = pdev;
3138c2ecf20Sopenharmony_ci	spin_lock_init(&info->lock);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, info);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* clear out the hardware */
3188c2ecf20Sopenharmony_ci	writel(0, info->base + TEGRA_RTC_REG_SECONDS_ALARM0);
3198c2ecf20Sopenharmony_ci	writel(0xffffffff, info->base + TEGRA_RTC_REG_INTR_STATUS);
3208c2ecf20Sopenharmony_ci	writel(0, info->base + TEGRA_RTC_REG_INTR_MASK);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, 1);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, info->irq, tegra_rtc_irq_handler,
3258c2ecf20Sopenharmony_ci			       IRQF_TRIGGER_HIGH, dev_name(&pdev->dev),
3268c2ecf20Sopenharmony_ci			       &pdev->dev);
3278c2ecf20Sopenharmony_ci	if (ret) {
3288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request interrupt: %d\n", ret);
3298c2ecf20Sopenharmony_ci		goto disable_clk;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ret = rtc_register_device(info->rtc);
3338c2ecf20Sopenharmony_ci	if (ret)
3348c2ecf20Sopenharmony_ci		goto disable_clk;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cidisable_clk:
3418c2ecf20Sopenharmony_ci	clk_disable_unprepare(info->clk);
3428c2ecf20Sopenharmony_ci	return ret;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int tegra_rtc_remove(struct platform_device *pdev)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = platform_get_drvdata(pdev);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	clk_disable_unprepare(info->clk);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3558c2ecf20Sopenharmony_cistatic int tegra_rtc_suspend(struct device *dev)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	tegra_rtc_wait_while_busy(dev);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* only use ALARM0 as a wake source */
3628c2ecf20Sopenharmony_ci	writel(0xffffffff, info->base + TEGRA_RTC_REG_INTR_STATUS);
3638c2ecf20Sopenharmony_ci	writel(TEGRA_RTC_INTR_STATUS_SEC_ALARM0,
3648c2ecf20Sopenharmony_ci	       info->base + TEGRA_RTC_REG_INTR_MASK);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	dev_vdbg(dev, "alarm sec = %d\n",
3678c2ecf20Sopenharmony_ci		 readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0));
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	dev_vdbg(dev, "Suspend (device_may_wakeup=%d) IRQ:%d\n",
3708c2ecf20Sopenharmony_ci		 device_may_wakeup(dev), info->irq);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* leave the alarms on as a wake source */
3738c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
3748c2ecf20Sopenharmony_ci		enable_irq_wake(info->irq);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int tegra_rtc_resume(struct device *dev)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct tegra_rtc_info *info = dev_get_drvdata(dev);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	dev_vdbg(dev, "Resume (device_may_wakeup=%d)\n",
3848c2ecf20Sopenharmony_ci		 device_may_wakeup(dev));
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* alarms were left on as a wake source, turn them off */
3878c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
3888c2ecf20Sopenharmony_ci		disable_irq_wake(info->irq);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci#endif
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tegra_rtc_pm_ops, tegra_rtc_suspend, tegra_rtc_resume);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic void tegra_rtc_shutdown(struct platform_device *pdev)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	dev_vdbg(&pdev->dev, "disabling interrupts\n");
3998c2ecf20Sopenharmony_ci	tegra_rtc_alarm_irq_enable(&pdev->dev, 0);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic struct platform_driver tegra_rtc_driver = {
4038c2ecf20Sopenharmony_ci	.probe = tegra_rtc_probe,
4048c2ecf20Sopenharmony_ci	.remove = tegra_rtc_remove,
4058c2ecf20Sopenharmony_ci	.shutdown = tegra_rtc_shutdown,
4068c2ecf20Sopenharmony_ci	.driver = {
4078c2ecf20Sopenharmony_ci		.name = "tegra_rtc",
4088c2ecf20Sopenharmony_ci		.of_match_table = tegra_rtc_dt_match,
4098c2ecf20Sopenharmony_ci		.pm = &tegra_rtc_pm_ops,
4108c2ecf20Sopenharmony_ci	},
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_cimodule_platform_driver(tegra_rtc_driver);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jon Mayo <jmayo@nvidia.com>");
4158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("driver for Tegra internal RTC");
4168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
417