18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for MediaTek SoC based RTC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/rtc.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MTK_RTC_DEV KBUILD_MODNAME 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define MTK_RTC_PWRCHK1 0x4 198c2ecf20Sopenharmony_ci#define RTC_PWRCHK1_MAGIC 0xc6 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define MTK_RTC_PWRCHK2 0x8 228c2ecf20Sopenharmony_ci#define RTC_PWRCHK2_MAGIC 0x9a 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MTK_RTC_KEY 0xc 258c2ecf20Sopenharmony_ci#define RTC_KEY_MAGIC 0x59 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MTK_RTC_PROT1 0x10 288c2ecf20Sopenharmony_ci#define RTC_PROT1_MAGIC 0xa3 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define MTK_RTC_PROT2 0x14 318c2ecf20Sopenharmony_ci#define RTC_PROT2_MAGIC 0x57 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MTK_RTC_PROT3 0x18 348c2ecf20Sopenharmony_ci#define RTC_PROT3_MAGIC 0x67 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MTK_RTC_PROT4 0x1c 378c2ecf20Sopenharmony_ci#define RTC_PROT4_MAGIC 0xd2 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MTK_RTC_CTL 0x20 408c2ecf20Sopenharmony_ci#define RTC_RC_STOP BIT(0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MTK_RTC_DEBNCE 0x2c 438c2ecf20Sopenharmony_ci#define RTC_DEBNCE_MASK GENMASK(2, 0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MTK_RTC_INT 0x30 468c2ecf20Sopenharmony_ci#define RTC_INT_AL_STA BIT(4) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Ranges from 0x40 to 0x78 provide RTC time setup for year, month, 508c2ecf20Sopenharmony_ci * day of month, day of week, hour, minute and second. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define MTK_RTC_TREG(_t, _f) (0x40 + (0x4 * (_f)) + ((_t) * 0x20)) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define MTK_RTC_AL_CTL 0x7c 558c2ecf20Sopenharmony_ci#define RTC_AL_EN BIT(0) 568c2ecf20Sopenharmony_ci#define RTC_AL_ALL GENMASK(7, 0) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * The offset is used in the translation for the year between in struct 608c2ecf20Sopenharmony_ci * rtc_time and in hardware register MTK_RTC_TREG(x,MTK_YEA) 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define MTK_RTC_TM_YR_OFFSET 100 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * The lowest value for the valid tm_year. RTC hardware would take incorrectly 668c2ecf20Sopenharmony_ci * tm_year 100 as not a leap year and thus it is also required being excluded 678c2ecf20Sopenharmony_ci * from the valid options. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#define MTK_RTC_TM_YR_L (MTK_RTC_TM_YR_OFFSET + 1) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * The most year the RTC can hold is 99 and the next to 99 in year register 738c2ecf20Sopenharmony_ci * would be wraparound to 0, for MT7622. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#define MTK_RTC_HW_YR_LIMIT 99 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* The highest value for the valid tm_year */ 788c2ecf20Sopenharmony_ci#define MTK_RTC_TM_YR_H (MTK_RTC_TM_YR_OFFSET + MTK_RTC_HW_YR_LIMIT) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Simple macro helps to check whether the hardware supports the tm_year */ 818c2ecf20Sopenharmony_ci#define MTK_RTC_TM_YR_VALID(_y) ((_y) >= MTK_RTC_TM_YR_L && \ 828c2ecf20Sopenharmony_ci (_y) <= MTK_RTC_TM_YR_H) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Types of the function the RTC provides are time counter and alarm. */ 858c2ecf20Sopenharmony_cienum { 868c2ecf20Sopenharmony_ci MTK_TC, 878c2ecf20Sopenharmony_ci MTK_AL, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Indexes are used for the pointer to relevant registers in MTK_RTC_TREG */ 918c2ecf20Sopenharmony_cienum { 928c2ecf20Sopenharmony_ci MTK_YEA, 938c2ecf20Sopenharmony_ci MTK_MON, 948c2ecf20Sopenharmony_ci MTK_DOM, 958c2ecf20Sopenharmony_ci MTK_DOW, 968c2ecf20Sopenharmony_ci MTK_HOU, 978c2ecf20Sopenharmony_ci MTK_MIN, 988c2ecf20Sopenharmony_ci MTK_SEC 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistruct mtk_rtc { 1028c2ecf20Sopenharmony_ci struct rtc_device *rtc; 1038c2ecf20Sopenharmony_ci void __iomem *base; 1048c2ecf20Sopenharmony_ci int irq; 1058c2ecf20Sopenharmony_ci struct clk *clk; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void mtk_w32(struct mtk_rtc *rtc, u32 reg, u32 val) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci writel_relaxed(val, rtc->base + reg); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic u32 mtk_r32(struct mtk_rtc *rtc, u32 reg) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci return readl_relaxed(rtc->base + reg); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void mtk_rmw(struct mtk_rtc *rtc, u32 reg, u32 mask, u32 set) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u32 val; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci val = mtk_r32(rtc, reg); 1238c2ecf20Sopenharmony_ci val &= ~mask; 1248c2ecf20Sopenharmony_ci val |= set; 1258c2ecf20Sopenharmony_ci mtk_w32(rtc, reg, val); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void mtk_set(struct mtk_rtc *rtc, u32 reg, u32 val) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci mtk_rmw(rtc, reg, 0, val); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void mtk_clr(struct mtk_rtc *rtc, u32 reg, u32 val) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci mtk_rmw(rtc, reg, val, 0); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void mtk_rtc_hw_init(struct mtk_rtc *hw) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci /* The setup of the init sequence is for allowing RTC got to work */ 1418c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_PWRCHK1, RTC_PWRCHK1_MAGIC); 1428c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_PWRCHK2, RTC_PWRCHK2_MAGIC); 1438c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_KEY, RTC_KEY_MAGIC); 1448c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_PROT1, RTC_PROT1_MAGIC); 1458c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_PROT2, RTC_PROT2_MAGIC); 1468c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_PROT3, RTC_PROT3_MAGIC); 1478c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_PROT4, RTC_PROT4_MAGIC); 1488c2ecf20Sopenharmony_ci mtk_rmw(hw, MTK_RTC_DEBNCE, RTC_DEBNCE_MASK, 0); 1498c2ecf20Sopenharmony_ci mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void mtk_rtc_get_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, 1538c2ecf20Sopenharmony_ci int time_alarm) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci u32 year, mon, mday, wday, hour, min, sec; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Read again until the field of the second is not changed which 1598c2ecf20Sopenharmony_ci * ensures all fields in the consistent state. Note that MTK_SEC must 1608c2ecf20Sopenharmony_ci * be read first. In this way, it guarantees the others remain not 1618c2ecf20Sopenharmony_ci * changed when the results for two MTK_SEC consecutive reads are same. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci do { 1648c2ecf20Sopenharmony_ci sec = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC)); 1658c2ecf20Sopenharmony_ci min = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN)); 1668c2ecf20Sopenharmony_ci hour = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU)); 1678c2ecf20Sopenharmony_ci wday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW)); 1688c2ecf20Sopenharmony_ci mday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM)); 1698c2ecf20Sopenharmony_ci mon = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MON)); 1708c2ecf20Sopenharmony_ci year = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA)); 1718c2ecf20Sopenharmony_ci } while (sec != mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC))); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci tm->tm_sec = sec; 1748c2ecf20Sopenharmony_ci tm->tm_min = min; 1758c2ecf20Sopenharmony_ci tm->tm_hour = hour; 1768c2ecf20Sopenharmony_ci tm->tm_wday = wday; 1778c2ecf20Sopenharmony_ci tm->tm_mday = mday; 1788c2ecf20Sopenharmony_ci tm->tm_mon = mon - 1; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Rebase to the absolute year which userspace queries */ 1818c2ecf20Sopenharmony_ci tm->tm_year = year + MTK_RTC_TM_YR_OFFSET; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void mtk_rtc_set_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, 1858c2ecf20Sopenharmony_ci int time_alarm) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci u32 year; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Rebase to the relative year which RTC hardware requires */ 1908c2ecf20Sopenharmony_ci year = tm->tm_year - MTK_RTC_TM_YR_OFFSET; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA), year); 1938c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MON), tm->tm_mon + 1); 1948c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW), tm->tm_wday); 1958c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM), tm->tm_mday); 1968c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU), tm->tm_hour); 1978c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN), tm->tm_min); 1988c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC), tm->tm_sec); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic irqreturn_t mtk_rtc_alarmirq(int irq, void *id) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct mtk_rtc *hw = (struct mtk_rtc *)id; 2048c2ecf20Sopenharmony_ci u32 irq_sta; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci irq_sta = mtk_r32(hw, MTK_RTC_INT); 2078c2ecf20Sopenharmony_ci if (irq_sta & RTC_INT_AL_STA) { 2088c2ecf20Sopenharmony_ci /* Stop alarm also implicitly disables the alarm interrupt */ 2098c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_AL_CTL, 0); 2108c2ecf20Sopenharmony_ci rtc_update_irq(hw->rtc, 1, RTC_IRQF | RTC_AF); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Ack alarm interrupt status */ 2138c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_INT, RTC_INT_AL_STA); 2148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return IRQ_NONE; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct mtk_rtc *hw = dev_get_drvdata(dev); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int mtk_rtc_settime(struct device *dev, struct rtc_time *tm) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct mtk_rtc *hw = dev_get_drvdata(dev); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!MTK_RTC_TM_YR_VALID(tm->tm_year)) 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* Stop time counter before setting a new one*/ 2378c2ecf20Sopenharmony_ci mtk_set(hw, MTK_RTC_CTL, RTC_RC_STOP); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci mtk_rtc_set_alarm_or_time(hw, tm, MTK_TC); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Restart the time counter */ 2428c2ecf20Sopenharmony_ci mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int mtk_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct mtk_rtc *hw = dev_get_drvdata(dev); 2508c2ecf20Sopenharmony_ci struct rtc_time *alrm_tm = &wkalrm->time; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci mtk_rtc_get_alarm_or_time(hw, alrm_tm, MTK_AL); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci wkalrm->enabled = !!(mtk_r32(hw, MTK_RTC_AL_CTL) & RTC_AL_EN); 2558c2ecf20Sopenharmony_ci wkalrm->pending = !!(mtk_r32(hw, MTK_RTC_INT) & RTC_INT_AL_STA); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int mtk_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct mtk_rtc *hw = dev_get_drvdata(dev); 2638c2ecf20Sopenharmony_ci struct rtc_time *alrm_tm = &wkalrm->time; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!MTK_RTC_TM_YR_VALID(alrm_tm->tm_year)) 2668c2ecf20Sopenharmony_ci return -EINVAL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * Stop the alarm also implicitly including disables interrupt before 2708c2ecf20Sopenharmony_ci * setting a new one. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci mtk_clr(hw, MTK_RTC_AL_CTL, RTC_AL_EN); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Avoid contention between mtk_rtc_setalarm and IRQ handler so that 2768c2ecf20Sopenharmony_ci * disabling the interrupt and awaiting for pending IRQ handler to 2778c2ecf20Sopenharmony_ci * complete. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci synchronize_irq(hw->irq); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mtk_rtc_set_alarm_or_time(hw, alrm_tm, MTK_AL); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Restart the alarm with the new setup */ 2848c2ecf20Sopenharmony_ci mtk_w32(hw, MTK_RTC_AL_CTL, RTC_AL_ALL); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct rtc_class_ops mtk_rtc_ops = { 2908c2ecf20Sopenharmony_ci .read_time = mtk_rtc_gettime, 2918c2ecf20Sopenharmony_ci .set_time = mtk_rtc_settime, 2928c2ecf20Sopenharmony_ci .read_alarm = mtk_rtc_getalarm, 2938c2ecf20Sopenharmony_ci .set_alarm = mtk_rtc_setalarm, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_rtc_match[] = { 2978c2ecf20Sopenharmony_ci { .compatible = "mediatek,mt7622-rtc" }, 2988c2ecf20Sopenharmony_ci { .compatible = "mediatek,soc-rtc" }, 2998c2ecf20Sopenharmony_ci {}, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_rtc_match); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int mtk_rtc_probe(struct platform_device *pdev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct mtk_rtc *hw; 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL); 3098c2ecf20Sopenharmony_ci if (!hw) 3108c2ecf20Sopenharmony_ci return -ENOMEM; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, hw); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci hw->base = devm_platform_ioremap_resource(pdev, 0); 3158c2ecf20Sopenharmony_ci if (IS_ERR(hw->base)) 3168c2ecf20Sopenharmony_ci return PTR_ERR(hw->base); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci hw->clk = devm_clk_get(&pdev->dev, "rtc"); 3198c2ecf20Sopenharmony_ci if (IS_ERR(hw->clk)) { 3208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No clock\n"); 3218c2ecf20Sopenharmony_ci return PTR_ERR(hw->clk); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ret = clk_prepare_enable(hw->clk); 3258c2ecf20Sopenharmony_ci if (ret) 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci hw->irq = platform_get_irq(pdev, 0); 3298c2ecf20Sopenharmony_ci if (hw->irq < 0) { 3308c2ecf20Sopenharmony_ci ret = hw->irq; 3318c2ecf20Sopenharmony_ci goto err; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, hw->irq, mtk_rtc_alarmirq, 3358c2ecf20Sopenharmony_ci 0, dev_name(&pdev->dev), hw); 3368c2ecf20Sopenharmony_ci if (ret) { 3378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't request IRQ\n"); 3388c2ecf20Sopenharmony_ci goto err; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci mtk_rtc_hw_init(hw); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci hw->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, 3468c2ecf20Sopenharmony_ci &mtk_rtc_ops, THIS_MODULE); 3478c2ecf20Sopenharmony_ci if (IS_ERR(hw->rtc)) { 3488c2ecf20Sopenharmony_ci ret = PTR_ERR(hw->rtc); 3498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to register device\n"); 3508c2ecf20Sopenharmony_ci goto err; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_cierr: 3558c2ecf20Sopenharmony_ci clk_disable_unprepare(hw->clk); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int mtk_rtc_remove(struct platform_device *pdev) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct mtk_rtc *hw = platform_get_drvdata(pdev); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci clk_disable_unprepare(hw->clk); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3708c2ecf20Sopenharmony_cistatic int mtk_rtc_suspend(struct device *dev) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct mtk_rtc *hw = dev_get_drvdata(dev); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 3758c2ecf20Sopenharmony_ci enable_irq_wake(hw->irq); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int mtk_rtc_resume(struct device *dev) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct mtk_rtc *hw = dev_get_drvdata(dev); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 3858c2ecf20Sopenharmony_ci disable_irq_wake(hw->irq); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci#define MTK_RTC_PM_OPS (&mtk_rtc_pm_ops) 3938c2ecf20Sopenharmony_ci#else /* CONFIG_PM */ 3948c2ecf20Sopenharmony_ci#define MTK_RTC_PM_OPS NULL 3958c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic struct platform_driver mtk_rtc_driver = { 3988c2ecf20Sopenharmony_ci .probe = mtk_rtc_probe, 3998c2ecf20Sopenharmony_ci .remove = mtk_rtc_remove, 4008c2ecf20Sopenharmony_ci .driver = { 4018c2ecf20Sopenharmony_ci .name = MTK_RTC_DEV, 4028c2ecf20Sopenharmony_ci .of_match_table = mtk_rtc_match, 4038c2ecf20Sopenharmony_ci .pm = MTK_RTC_PM_OPS, 4048c2ecf20Sopenharmony_ci }, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cimodule_platform_driver(mtk_rtc_driver); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MediaTek SoC based RTC Driver"); 4108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 4118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 412