18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PIC32 RTC driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Joshua Henderson <joshua.henderson@microchip.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/rtc.h> 178c2ecf20Sopenharmony_ci#include <linux/bcd.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/mach-pic32/pic32.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PIC32_RTCCON 0x00 228c2ecf20Sopenharmony_ci#define PIC32_RTCCON_ON BIT(15) 238c2ecf20Sopenharmony_ci#define PIC32_RTCCON_SIDL BIT(13) 248c2ecf20Sopenharmony_ci#define PIC32_RTCCON_RTCCLKSEL (3 << 9) 258c2ecf20Sopenharmony_ci#define PIC32_RTCCON_RTCCLKON BIT(6) 268c2ecf20Sopenharmony_ci#define PIC32_RTCCON_RTCWREN BIT(3) 278c2ecf20Sopenharmony_ci#define PIC32_RTCCON_RTCSYNC BIT(2) 288c2ecf20Sopenharmony_ci#define PIC32_RTCCON_HALFSEC BIT(1) 298c2ecf20Sopenharmony_ci#define PIC32_RTCCON_RTCOE BIT(0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PIC32_RTCALRM 0x10 328c2ecf20Sopenharmony_ci#define PIC32_RTCALRM_ALRMEN BIT(15) 338c2ecf20Sopenharmony_ci#define PIC32_RTCALRM_CHIME BIT(14) 348c2ecf20Sopenharmony_ci#define PIC32_RTCALRM_PIV BIT(13) 358c2ecf20Sopenharmony_ci#define PIC32_RTCALRM_ALARMSYNC BIT(12) 368c2ecf20Sopenharmony_ci#define PIC32_RTCALRM_AMASK 0x0F00 378c2ecf20Sopenharmony_ci#define PIC32_RTCALRM_ARPT 0xFF 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define PIC32_RTCHOUR 0x23 408c2ecf20Sopenharmony_ci#define PIC32_RTCMIN 0x22 418c2ecf20Sopenharmony_ci#define PIC32_RTCSEC 0x21 428c2ecf20Sopenharmony_ci#define PIC32_RTCYEAR 0x33 438c2ecf20Sopenharmony_ci#define PIC32_RTCMON 0x32 448c2ecf20Sopenharmony_ci#define PIC32_RTCDAY 0x31 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define PIC32_ALRMTIME 0x40 478c2ecf20Sopenharmony_ci#define PIC32_ALRMDATE 0x50 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define PIC32_ALRMHOUR 0x43 508c2ecf20Sopenharmony_ci#define PIC32_ALRMMIN 0x42 518c2ecf20Sopenharmony_ci#define PIC32_ALRMSEC 0x41 528c2ecf20Sopenharmony_ci#define PIC32_ALRMYEAR 0x53 538c2ecf20Sopenharmony_ci#define PIC32_ALRMMON 0x52 548c2ecf20Sopenharmony_ci#define PIC32_ALRMDAY 0x51 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct pic32_rtc_dev { 578c2ecf20Sopenharmony_ci struct rtc_device *rtc; 588c2ecf20Sopenharmony_ci void __iomem *reg_base; 598c2ecf20Sopenharmony_ci struct clk *clk; 608c2ecf20Sopenharmony_ci spinlock_t alarm_lock; 618c2ecf20Sopenharmony_ci int alarm_irq; 628c2ecf20Sopenharmony_ci bool alarm_clk_enabled; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata, 668c2ecf20Sopenharmony_ci bool enable) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci unsigned long flags; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci spin_lock_irqsave(&pdata->alarm_lock, flags); 718c2ecf20Sopenharmony_ci if (enable) { 728c2ecf20Sopenharmony_ci if (!pdata->alarm_clk_enabled) { 738c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 748c2ecf20Sopenharmony_ci pdata->alarm_clk_enabled = true; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci } else { 778c2ecf20Sopenharmony_ci if (pdata->alarm_clk_enabled) { 788c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 798c2ecf20Sopenharmony_ci pdata->alarm_clk_enabled = false; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pdata->alarm_lock, flags); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic irqreturn_t pic32_rtc_alarmirq(int irq, void *id) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 908c2ecf20Sopenharmony_ci rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); 918c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci pic32_rtc_alarm_clk_enable(pdata, false); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int pic32_rtc_setaie(struct device *dev, unsigned int enabled) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 1018c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci writel(PIC32_RTCALRM_ALRMEN, 1068c2ecf20Sopenharmony_ci base + (enabled ? PIC32_SET(PIC32_RTCALRM) : 1078c2ecf20Sopenharmony_ci PIC32_CLR(PIC32_RTCALRM))); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci pic32_rtc_alarm_clk_enable(pdata, enabled); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int pic32_rtc_setfreq(struct device *dev, int freq) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 1198c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM)); 1248c2ecf20Sopenharmony_ci writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM)); 1258c2ecf20Sopenharmony_ci writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM)); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 1358c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 1368c2ecf20Sopenharmony_ci unsigned int tries = 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci do { 1418c2ecf20Sopenharmony_ci rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR); 1428c2ecf20Sopenharmony_ci rtc_tm->tm_min = readb(base + PIC32_RTCMIN); 1438c2ecf20Sopenharmony_ci rtc_tm->tm_mon = readb(base + PIC32_RTCMON); 1448c2ecf20Sopenharmony_ci rtc_tm->tm_mday = readb(base + PIC32_RTCDAY); 1458c2ecf20Sopenharmony_ci rtc_tm->tm_year = readb(base + PIC32_RTCYEAR); 1468c2ecf20Sopenharmony_ci rtc_tm->tm_sec = readb(base + PIC32_RTCSEC); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * The only way to work out whether the system was mid-update 1508c2ecf20Sopenharmony_ci * when we read it is to check the second counter, and if it 1518c2ecf20Sopenharmony_ci * is zero, then we re-try the entire read. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci tries += 1; 1548c2ecf20Sopenharmony_ci } while (rtc_tm->tm_sec == 0 && tries < 2); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); 1578c2ecf20Sopenharmony_ci rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); 1588c2ecf20Sopenharmony_ci rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); 1598c2ecf20Sopenharmony_ci rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); 1608c2ecf20Sopenharmony_ci rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1; 1618c2ecf20Sopenharmony_ci rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci rtc_tm->tm_year += 100; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci dev_dbg(dev, "read time %ptR\n", rtc_tm); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int pic32_rtc_settime(struct device *dev, struct rtc_time *tm) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 1748c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dev_dbg(dev, "set time %ptR\n", tm); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 1798c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC); 1808c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN); 1818c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR); 1828c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY); 1838c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON); 1848c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR); 1858c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 1938c2ecf20Sopenharmony_ci struct rtc_time *alm_tm = &alrm->time; 1948c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 1958c2ecf20Sopenharmony_ci unsigned int alm_en; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 1988c2ecf20Sopenharmony_ci alm_tm->tm_sec = readb(base + PIC32_ALRMSEC); 1998c2ecf20Sopenharmony_ci alm_tm->tm_min = readb(base + PIC32_ALRMMIN); 2008c2ecf20Sopenharmony_ci alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR); 2018c2ecf20Sopenharmony_ci alm_tm->tm_mon = readb(base + PIC32_ALRMMON); 2028c2ecf20Sopenharmony_ci alm_tm->tm_mday = readb(base + PIC32_ALRMDAY); 2038c2ecf20Sopenharmony_ci alm_tm->tm_year = readb(base + PIC32_ALRMYEAR); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci alm_en = readb(base + PIC32_RTCALRM); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci dev_dbg(dev, "getalarm: %d, %ptR\n", alm_en, alm_tm); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); 2128c2ecf20Sopenharmony_ci alm_tm->tm_min = bcd2bin(alm_tm->tm_min); 2138c2ecf20Sopenharmony_ci alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); 2148c2ecf20Sopenharmony_ci alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); 2158c2ecf20Sopenharmony_ci alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1; 2168c2ecf20Sopenharmony_ci alm_tm->tm_year = bcd2bin(alm_tm->tm_year); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 2258c2ecf20Sopenharmony_ci struct rtc_time *tm = &alrm->time; 2268c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 2298c2ecf20Sopenharmony_ci dev_dbg(dev, "setalarm: %d, %ptR\n", alrm->enabled, tm); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci writel(0x00, base + PIC32_ALRMTIME); 2328c2ecf20Sopenharmony_ci writel(0x00, base + PIC32_ALRMDATE); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pic32_rtc_setaie(dev, alrm->enabled); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int pic32_rtc_proc(struct device *dev, struct seq_file *seq) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 2438c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 2448c2ecf20Sopenharmony_ci unsigned int repeat; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci repeat = readw(base + PIC32_RTCALRM); 2498c2ecf20Sopenharmony_ci repeat &= PIC32_RTCALRM_ARPT; 2508c2ecf20Sopenharmony_ci seq_printf(seq, "periodic_IRQ\t: %s\n", repeat ? "yes" : "no"); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic const struct rtc_class_ops pic32_rtcops = { 2578c2ecf20Sopenharmony_ci .read_time = pic32_rtc_gettime, 2588c2ecf20Sopenharmony_ci .set_time = pic32_rtc_settime, 2598c2ecf20Sopenharmony_ci .read_alarm = pic32_rtc_getalarm, 2608c2ecf20Sopenharmony_ci .set_alarm = pic32_rtc_setalarm, 2618c2ecf20Sopenharmony_ci .proc = pic32_rtc_proc, 2628c2ecf20Sopenharmony_ci .alarm_irq_enable = pic32_rtc_setaie, 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci void __iomem *base = pdata->reg_base; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (!base) 2708c2ecf20Sopenharmony_ci return; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci clk_enable(pdata->clk); 2738c2ecf20Sopenharmony_ci if (!en) { 2748c2ecf20Sopenharmony_ci writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON)); 2758c2ecf20Sopenharmony_ci } else { 2768c2ecf20Sopenharmony_ci pic32_syskey_unlock(); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON)); 2798c2ecf20Sopenharmony_ci writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON)); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON)) 2828c2ecf20Sopenharmony_ci writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON)); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int pic32_rtc_remove(struct platform_device *pdev) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci pic32_rtc_setaie(&pdev->dev, 0); 2928c2ecf20Sopenharmony_ci clk_unprepare(pdata->clk); 2938c2ecf20Sopenharmony_ci pdata->clk = NULL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int pic32_rtc_probe(struct platform_device *pdev) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct pic32_rtc_dev *pdata; 3018c2ecf20Sopenharmony_ci int ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 3048c2ecf20Sopenharmony_ci if (!pdata) 3058c2ecf20Sopenharmony_ci return -ENOMEM; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pdata); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci pdata->alarm_irq = platform_get_irq(pdev, 0); 3108c2ecf20Sopenharmony_ci if (pdata->alarm_irq < 0) 3118c2ecf20Sopenharmony_ci return pdata->alarm_irq; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci pdata->reg_base = devm_platform_ioremap_resource(pdev, 0); 3148c2ecf20Sopenharmony_ci if (IS_ERR(pdata->reg_base)) 3158c2ecf20Sopenharmony_ci return PTR_ERR(pdata->reg_base); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pdata->clk = devm_clk_get(&pdev->dev, NULL); 3188c2ecf20Sopenharmony_ci if (IS_ERR(pdata->clk)) { 3198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to find rtc clock source\n"); 3208c2ecf20Sopenharmony_ci ret = PTR_ERR(pdata->clk); 3218c2ecf20Sopenharmony_ci pdata->clk = NULL; 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci spin_lock_init(&pdata->alarm_lock); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci pdata->rtc = devm_rtc_allocate_device(&pdev->dev); 3288c2ecf20Sopenharmony_ci if (IS_ERR(pdata->rtc)) 3298c2ecf20Sopenharmony_ci return PTR_ERR(pdata->rtc); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci clk_prepare_enable(pdata->clk); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci pic32_rtc_enable(pdata, 1); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci pdata->rtc->ops = &pic32_rtcops; 3388c2ecf20Sopenharmony_ci pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 3398c2ecf20Sopenharmony_ci pdata->rtc->range_max = RTC_TIMESTAMP_END_2099; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = rtc_register_device(pdata->rtc); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci goto err_nortc; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci pdata->rtc->max_user_freq = 128; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci pic32_rtc_setfreq(&pdev->dev, 1); 3488c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, pdata->alarm_irq, 3498c2ecf20Sopenharmony_ci pic32_rtc_alarmirq, 0, 3508c2ecf20Sopenharmony_ci dev_name(&pdev->dev), pdata); 3518c2ecf20Sopenharmony_ci if (ret) { 3528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3538c2ecf20Sopenharmony_ci "IRQ %d error %d\n", pdata->alarm_irq, ret); 3548c2ecf20Sopenharmony_ci goto err_nortc; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci clk_disable(pdata->clk); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cierr_nortc: 3628c2ecf20Sopenharmony_ci pic32_rtc_enable(pdata, 0); 3638c2ecf20Sopenharmony_ci clk_disable_unprepare(pdata->clk); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct of_device_id pic32_rtc_dt_ids[] = { 3698c2ecf20Sopenharmony_ci { .compatible = "microchip,pic32mzda-rtc" }, 3708c2ecf20Sopenharmony_ci { /* sentinel */ } 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic struct platform_driver pic32_rtc_driver = { 3758c2ecf20Sopenharmony_ci .probe = pic32_rtc_probe, 3768c2ecf20Sopenharmony_ci .remove = pic32_rtc_remove, 3778c2ecf20Sopenharmony_ci .driver = { 3788c2ecf20Sopenharmony_ci .name = "pic32-rtc", 3798c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(pic32_rtc_dt_ids), 3808c2ecf20Sopenharmony_ci }, 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_cimodule_platform_driver(pic32_rtc_driver); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip PIC32 RTC Driver"); 3858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 3868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 387