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