18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Real Time Clock (RTC) Driver for i.MX53
48c2ecf20Sopenharmony_ci * Copyright (c) 2004-2011 Freescale Semiconductor, Inc.
58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Beckhoff Automation GmbH & Co. KG
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h>
148c2ecf20Sopenharmony_ci#include <linux/rtc.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define SRTC_LPPDR_INIT       0x41736166	/* init for glitch detect */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define SRTC_LPCR_EN_LP       BIT(3)	/* lp enable */
198c2ecf20Sopenharmony_ci#define SRTC_LPCR_WAE         BIT(4)	/* lp wakeup alarm enable */
208c2ecf20Sopenharmony_ci#define SRTC_LPCR_ALP         BIT(7)	/* lp alarm flag */
218c2ecf20Sopenharmony_ci#define SRTC_LPCR_NSA         BIT(11)	/* lp non secure access */
228c2ecf20Sopenharmony_ci#define SRTC_LPCR_NVE         BIT(14)	/* lp non valid state exit bit */
238c2ecf20Sopenharmony_ci#define SRTC_LPCR_IE          BIT(15)	/* lp init state exit bit */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define SRTC_LPSR_ALP         BIT(3)	/* lp alarm flag */
268c2ecf20Sopenharmony_ci#define SRTC_LPSR_NVES        BIT(14)	/* lp non-valid state exit status */
278c2ecf20Sopenharmony_ci#define SRTC_LPSR_IES         BIT(15)	/* lp init state exit status */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define SRTC_LPSCMR	0x00	/* LP Secure Counter MSB Reg */
308c2ecf20Sopenharmony_ci#define SRTC_LPSCLR	0x04	/* LP Secure Counter LSB Reg */
318c2ecf20Sopenharmony_ci#define SRTC_LPSAR	0x08	/* LP Secure Alarm Reg */
328c2ecf20Sopenharmony_ci#define SRTC_LPCR	0x10	/* LP Control Reg */
338c2ecf20Sopenharmony_ci#define SRTC_LPSR	0x14	/* LP Status Reg */
348c2ecf20Sopenharmony_ci#define SRTC_LPPDR	0x18	/* LP Power Supply Glitch Detector Reg */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* max. number of retries to read registers, 120 was max during test */
378c2ecf20Sopenharmony_ci#define REG_READ_TIMEOUT 2000
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct mxc_rtc_data {
408c2ecf20Sopenharmony_ci	struct rtc_device *rtc;
418c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
428c2ecf20Sopenharmony_ci	struct clk *clk;
438c2ecf20Sopenharmony_ci	spinlock_t lock; /* protects register access */
448c2ecf20Sopenharmony_ci	int irq;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * This function does write synchronization for writes to the lp srtc block.
498c2ecf20Sopenharmony_ci * To take care of the asynchronous CKIL clock, all writes from the IP domain
508c2ecf20Sopenharmony_ci * will be synchronized to the CKIL domain.
518c2ecf20Sopenharmony_ci * The caller should hold the pdata->lock
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic void mxc_rtc_sync_lp_locked(struct device *dev, void __iomem *ioaddr)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned int i;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Wait for 3 CKIL cycles */
588c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
598c2ecf20Sopenharmony_ci		const u32 count = readl(ioaddr + SRTC_LPSCLR);
608c2ecf20Sopenharmony_ci		unsigned int timeout = REG_READ_TIMEOUT;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		while ((readl(ioaddr + SRTC_LPSCLR)) == count) {
638c2ecf20Sopenharmony_ci			if (!--timeout) {
648c2ecf20Sopenharmony_ci				dev_err_once(dev, "SRTC_LPSCLR stuck! Check your hw.\n");
658c2ecf20Sopenharmony_ci				return;
668c2ecf20Sopenharmony_ci			}
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* This function is the RTC interrupt service routine. */
728c2ecf20Sopenharmony_cistatic irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct device *dev = dev_id;
758c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
768c2ecf20Sopenharmony_ci	void __iomem *ioaddr = pdata->ioaddr;
778c2ecf20Sopenharmony_ci	unsigned long flags;
788c2ecf20Sopenharmony_ci	u32 lp_status;
798c2ecf20Sopenharmony_ci	u32 lp_cr;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pdata->lock, flags);
828c2ecf20Sopenharmony_ci	if (clk_enable(pdata->clk)) {
838c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pdata->lock, flags);
848c2ecf20Sopenharmony_ci		return IRQ_NONE;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	lp_status = readl(ioaddr + SRTC_LPSR);
888c2ecf20Sopenharmony_ci	lp_cr = readl(ioaddr + SRTC_LPCR);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* update irq data & counter */
918c2ecf20Sopenharmony_ci	if (lp_status & SRTC_LPSR_ALP) {
928c2ecf20Sopenharmony_ci		if (lp_cr & SRTC_LPCR_ALP)
938c2ecf20Sopenharmony_ci			rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		/* disable further lp alarm interrupts */
968c2ecf20Sopenharmony_ci		lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Update interrupt enables */
1008c2ecf20Sopenharmony_ci	writel(lp_cr, ioaddr + SRTC_LPCR);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* clear interrupt status */
1038c2ecf20Sopenharmony_ci	writel(lp_status, ioaddr + SRTC_LPSR);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	mxc_rtc_sync_lp_locked(dev, ioaddr);
1068c2ecf20Sopenharmony_ci	clk_disable(pdata->clk);
1078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pdata->lock, flags);
1088c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci * Enable clk and aquire spinlock
1138c2ecf20Sopenharmony_ci * @return  0 if successful; non-zero otherwise.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic int mxc_rtc_lock(struct mxc_rtc_data *const pdata)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	spin_lock_irq(&pdata->lock);
1208c2ecf20Sopenharmony_ci	ret = clk_enable(pdata->clk);
1218c2ecf20Sopenharmony_ci	if (ret) {
1228c2ecf20Sopenharmony_ci		spin_unlock_irq(&pdata->lock);
1238c2ecf20Sopenharmony_ci		return ret;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int mxc_rtc_unlock(struct mxc_rtc_data *const pdata)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	clk_disable(pdata->clk);
1318c2ecf20Sopenharmony_ci	spin_unlock_irq(&pdata->lock);
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/*
1368c2ecf20Sopenharmony_ci * This function reads the current RTC time into tm in Gregorian date.
1378c2ecf20Sopenharmony_ci *
1388c2ecf20Sopenharmony_ci * @param  tm           contains the RTC time value upon return
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * @return  0 if successful; non-zero otherwise.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_cistatic int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
1458c2ecf20Sopenharmony_ci	const int clk_failed = clk_enable(pdata->clk);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!clk_failed) {
1488c2ecf20Sopenharmony_ci		const time64_t now = readl(pdata->ioaddr + SRTC_LPSCMR);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		rtc_time64_to_tm(now, tm);
1518c2ecf20Sopenharmony_ci		clk_disable(pdata->clk);
1528c2ecf20Sopenharmony_ci		return 0;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	return clk_failed;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * This function sets the internal RTC time based on tm in Gregorian date.
1598c2ecf20Sopenharmony_ci *
1608c2ecf20Sopenharmony_ci * @param  tm           the time value to be set in the RTC
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * @return  0 if successful; non-zero otherwise.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_cistatic int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
1678c2ecf20Sopenharmony_ci	time64_t time = rtc_tm_to_time64(tm);
1688c2ecf20Sopenharmony_ci	int ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ret = mxc_rtc_lock(pdata);
1718c2ecf20Sopenharmony_ci	if (ret)
1728c2ecf20Sopenharmony_ci		return ret;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	writel(time, pdata->ioaddr + SRTC_LPSCMR);
1758c2ecf20Sopenharmony_ci	mxc_rtc_sync_lp_locked(dev, pdata->ioaddr);
1768c2ecf20Sopenharmony_ci	return mxc_rtc_unlock(pdata);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/*
1808c2ecf20Sopenharmony_ci * This function reads the current alarm value into the passed in \b alrm
1818c2ecf20Sopenharmony_ci * argument. It updates the \b alrm's pending field value based on the whether
1828c2ecf20Sopenharmony_ci * an alarm interrupt occurs or not.
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * @param  alrm         contains the RTC alarm value upon return
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * @return  0 if successful; non-zero otherwise.
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_cistatic int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
1918c2ecf20Sopenharmony_ci	void __iomem *ioaddr = pdata->ioaddr;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = mxc_rtc_lock(pdata);
1958c2ecf20Sopenharmony_ci	if (ret)
1968c2ecf20Sopenharmony_ci		return ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rtc_time64_to_tm(readl(ioaddr + SRTC_LPSAR), &alrm->time);
1998c2ecf20Sopenharmony_ci	alrm->pending = !!(readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_ALP);
2008c2ecf20Sopenharmony_ci	return mxc_rtc_unlock(pdata);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * Enable/Disable alarm interrupt
2058c2ecf20Sopenharmony_ci * The caller should hold the pdata->lock
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic void mxc_rtc_alarm_irq_enable_locked(struct mxc_rtc_data *pdata,
2088c2ecf20Sopenharmony_ci					    unsigned int enable)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	u32 lp_cr = readl(pdata->ioaddr + SRTC_LPCR);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (enable)
2138c2ecf20Sopenharmony_ci		lp_cr |= (SRTC_LPCR_ALP | SRTC_LPCR_WAE);
2148c2ecf20Sopenharmony_ci	else
2158c2ecf20Sopenharmony_ci		lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	writel(lp_cr, pdata->ioaddr + SRTC_LPCR);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
2238c2ecf20Sopenharmony_ci	int ret = mxc_rtc_lock(pdata);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (ret)
2268c2ecf20Sopenharmony_ci		return ret;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	mxc_rtc_alarm_irq_enable_locked(pdata, enable);
2298c2ecf20Sopenharmony_ci	return mxc_rtc_unlock(pdata);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/*
2338c2ecf20Sopenharmony_ci * This function sets the RTC alarm based on passed in alrm.
2348c2ecf20Sopenharmony_ci *
2358c2ecf20Sopenharmony_ci * @param  alrm         the alarm value to be set in the RTC
2368c2ecf20Sopenharmony_ci *
2378c2ecf20Sopenharmony_ci * @return  0 if successful; non-zero otherwise.
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_cistatic int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	const time64_t time = rtc_tm_to_time64(&alrm->time);
2428c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
2438c2ecf20Sopenharmony_ci	int ret = mxc_rtc_lock(pdata);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (ret)
2468c2ecf20Sopenharmony_ci		return ret;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	writel((u32)time, pdata->ioaddr + SRTC_LPSAR);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* clear alarm interrupt status bit */
2518c2ecf20Sopenharmony_ci	writel(SRTC_LPSR_ALP, pdata->ioaddr + SRTC_LPSR);
2528c2ecf20Sopenharmony_ci	mxc_rtc_sync_lp_locked(dev, pdata->ioaddr);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	mxc_rtc_alarm_irq_enable_locked(pdata, alrm->enabled);
2558c2ecf20Sopenharmony_ci	mxc_rtc_sync_lp_locked(dev, pdata->ioaddr);
2568c2ecf20Sopenharmony_ci	mxc_rtc_unlock(pdata);
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic const struct rtc_class_ops mxc_rtc_ops = {
2618c2ecf20Sopenharmony_ci	.read_time = mxc_rtc_read_time,
2628c2ecf20Sopenharmony_ci	.set_time = mxc_rtc_set_time,
2638c2ecf20Sopenharmony_ci	.read_alarm = mxc_rtc_read_alarm,
2648c2ecf20Sopenharmony_ci	.set_alarm = mxc_rtc_set_alarm,
2658c2ecf20Sopenharmony_ci	.alarm_irq_enable = mxc_rtc_alarm_irq_enable,
2668c2ecf20Sopenharmony_ci};
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int mxc_rtc_wait_for_flag(void __iomem *ioaddr, int flag)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	unsigned int timeout = REG_READ_TIMEOUT;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	while (!(readl(ioaddr) & flag)) {
2738c2ecf20Sopenharmony_ci		if (!--timeout)
2748c2ecf20Sopenharmony_ci			return -EBUSY;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int mxc_rtc_probe(struct platform_device *pdev)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata;
2828c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
2838c2ecf20Sopenharmony_ci	int ret = 0;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
2868c2ecf20Sopenharmony_ci	if (!pdata)
2878c2ecf20Sopenharmony_ci		return -ENOMEM;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0);
2908c2ecf20Sopenharmony_ci	if (IS_ERR(pdata->ioaddr))
2918c2ecf20Sopenharmony_ci		return PTR_ERR(pdata->ioaddr);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	ioaddr = pdata->ioaddr;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	pdata->clk = devm_clk_get(&pdev->dev, NULL);
2968c2ecf20Sopenharmony_ci	if (IS_ERR(pdata->clk)) {
2978c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to get rtc clock!\n");
2988c2ecf20Sopenharmony_ci		return PTR_ERR(pdata->clk);
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	spin_lock_init(&pdata->lock);
3028c2ecf20Sopenharmony_ci	pdata->irq = platform_get_irq(pdev, 0);
3038c2ecf20Sopenharmony_ci	if (pdata->irq < 0)
3048c2ecf20Sopenharmony_ci		return pdata->irq;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, 1);
3078c2ecf20Sopenharmony_ci	ret = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
3088c2ecf20Sopenharmony_ci	if (ret)
3098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable irq wake\n");
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(pdata->clk);
3128c2ecf20Sopenharmony_ci	if (ret)
3138c2ecf20Sopenharmony_ci		return ret;
3148c2ecf20Sopenharmony_ci	/* initialize glitch detect */
3158c2ecf20Sopenharmony_ci	writel(SRTC_LPPDR_INIT, ioaddr + SRTC_LPPDR);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* clear lp interrupt status */
3188c2ecf20Sopenharmony_ci	writel(0xFFFFFFFF, ioaddr + SRTC_LPSR);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* move out of init state */
3218c2ecf20Sopenharmony_ci	writel((SRTC_LPCR_IE | SRTC_LPCR_NSA), ioaddr + SRTC_LPCR);
3228c2ecf20Sopenharmony_ci	ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_IES);
3238c2ecf20Sopenharmony_ci	if (ret) {
3248c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_IES\n");
3258c2ecf20Sopenharmony_ci		clk_disable_unprepare(pdata->clk);
3268c2ecf20Sopenharmony_ci		return ret;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* move out of non-valid state */
3308c2ecf20Sopenharmony_ci	writel((SRTC_LPCR_IE | SRTC_LPCR_NVE | SRTC_LPCR_NSA |
3318c2ecf20Sopenharmony_ci		SRTC_LPCR_EN_LP), ioaddr + SRTC_LPCR);
3328c2ecf20Sopenharmony_ci	ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_NVES);
3338c2ecf20Sopenharmony_ci	if (ret) {
3348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_NVES\n");
3358c2ecf20Sopenharmony_ci		clk_disable_unprepare(pdata->clk);
3368c2ecf20Sopenharmony_ci		return ret;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
3408c2ecf20Sopenharmony_ci	if (IS_ERR(pdata->rtc)) {
3418c2ecf20Sopenharmony_ci		clk_disable_unprepare(pdata->clk);
3428c2ecf20Sopenharmony_ci		return PTR_ERR(pdata->rtc);
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	pdata->rtc->ops = &mxc_rtc_ops;
3468c2ecf20Sopenharmony_ci	pdata->rtc->range_max = U32_MAX;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	clk_disable(pdata->clk);
3498c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pdata);
3508c2ecf20Sopenharmony_ci	ret =
3518c2ecf20Sopenharmony_ci	    devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt, 0,
3528c2ecf20Sopenharmony_ci			     pdev->name, &pdev->dev);
3538c2ecf20Sopenharmony_ci	if (ret < 0) {
3548c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "interrupt not available.\n");
3558c2ecf20Sopenharmony_ci		clk_unprepare(pdata->clk);
3568c2ecf20Sopenharmony_ci		return ret;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ret = rtc_register_device(pdata->rtc);
3608c2ecf20Sopenharmony_ci	if (ret < 0)
3618c2ecf20Sopenharmony_ci		clk_unprepare(pdata->clk);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return ret;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int mxc_rtc_remove(struct platform_device *pdev)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct mxc_rtc_data *pdata = platform_get_drvdata(pdev);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	clk_disable_unprepare(pdata->clk);
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic const struct of_device_id mxc_ids[] = {
3758c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx53-rtc", },
3768c2ecf20Sopenharmony_ci	{}
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxc_ids);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic struct platform_driver mxc_rtc_driver = {
3818c2ecf20Sopenharmony_ci	.driver = {
3828c2ecf20Sopenharmony_ci		.name = "mxc_rtc_v2",
3838c2ecf20Sopenharmony_ci		.of_match_table = mxc_ids,
3848c2ecf20Sopenharmony_ci	},
3858c2ecf20Sopenharmony_ci	.probe = mxc_rtc_probe,
3868c2ecf20Sopenharmony_ci	.remove = mxc_rtc_remove,
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cimodule_platform_driver(mxc_rtc_driver);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
3928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Real Time Clock (RTC) Driver for i.MX53");
3938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
394