18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Spreadtrum Communications Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * SPDX-License-Identifier: GPL-2.0 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/bitops.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci#include <linux/rtc.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_CNT_VALUE 0x0 178c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_CNT_VALUE 0x4 188c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_CNT_VALUE 0x8 198c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_CNT_VALUE 0xc 208c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_CNT_UPD 0x10 218c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_CNT_UPD 0x14 228c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_CNT_UPD 0x18 238c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_CNT_UPD 0x1c 248c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_ALM_UPD 0x20 258c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_ALM_UPD 0x24 268c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_ALM_UPD 0x28 278c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_ALM_UPD 0x2c 288c2ecf20Sopenharmony_ci#define SPRD_RTC_INT_EN 0x30 298c2ecf20Sopenharmony_ci#define SPRD_RTC_INT_RAW_STS 0x34 308c2ecf20Sopenharmony_ci#define SPRD_RTC_INT_CLR 0x38 318c2ecf20Sopenharmony_ci#define SPRD_RTC_INT_MASK_STS 0x3C 328c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_ALM_VALUE 0x40 338c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_ALM_VALUE 0x44 348c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_ALM_VALUE 0x48 358c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_ALM_VALUE 0x4c 368c2ecf20Sopenharmony_ci#define SPRD_RTC_SPG_VALUE 0x50 378c2ecf20Sopenharmony_ci#define SPRD_RTC_SPG_UPD 0x54 388c2ecf20Sopenharmony_ci#define SPRD_RTC_PWR_CTRL 0x58 398c2ecf20Sopenharmony_ci#define SPRD_RTC_PWR_STS 0x5c 408c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_AUXALM_UPD 0x60 418c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_AUXALM_UPD 0x64 428c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_AUXALM_UPD 0x68 438c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_AUXALM_UPD 0x6c 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* BIT & MASK definition for SPRD_RTC_INT_* registers */ 468c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_EN BIT(0) 478c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_EN BIT(1) 488c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_EN BIT(2) 498c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_EN BIT(3) 508c2ecf20Sopenharmony_ci#define SPRD_RTC_ALARM_EN BIT(4) 518c2ecf20Sopenharmony_ci#define SPRD_RTC_HRS_FORMAT_EN BIT(5) 528c2ecf20Sopenharmony_ci#define SPRD_RTC_AUXALM_EN BIT(6) 538c2ecf20Sopenharmony_ci#define SPRD_RTC_SPG_UPD_EN BIT(7) 548c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_UPD_EN BIT(8) 558c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_UPD_EN BIT(9) 568c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_UPD_EN BIT(10) 578c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_UPD_EN BIT(11) 588c2ecf20Sopenharmony_ci#define SPRD_RTC_ALMSEC_UPD_EN BIT(12) 598c2ecf20Sopenharmony_ci#define SPRD_RTC_ALMMIN_UPD_EN BIT(13) 608c2ecf20Sopenharmony_ci#define SPRD_RTC_ALMHOUR_UPD_EN BIT(14) 618c2ecf20Sopenharmony_ci#define SPRD_RTC_ALMDAY_UPD_EN BIT(15) 628c2ecf20Sopenharmony_ci#define SPRD_RTC_INT_MASK GENMASK(15, 0) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define SPRD_RTC_TIME_INT_MASK \ 658c2ecf20Sopenharmony_ci (SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN | \ 668c2ecf20Sopenharmony_ci SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define SPRD_RTC_ALMTIME_INT_MASK \ 698c2ecf20Sopenharmony_ci (SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN | \ 708c2ecf20Sopenharmony_ci SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define SPRD_RTC_ALM_INT_MASK \ 738c2ecf20Sopenharmony_ci (SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN | \ 748c2ecf20Sopenharmony_ci SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN | \ 758c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* second/minute/hour/day values mask definition */ 788c2ecf20Sopenharmony_ci#define SPRD_RTC_SEC_MASK GENMASK(5, 0) 798c2ecf20Sopenharmony_ci#define SPRD_RTC_MIN_MASK GENMASK(5, 0) 808c2ecf20Sopenharmony_ci#define SPRD_RTC_HOUR_MASK GENMASK(4, 0) 818c2ecf20Sopenharmony_ci#define SPRD_RTC_DAY_MASK GENMASK(15, 0) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* alarm lock definition for SPRD_RTC_SPG_UPD register */ 848c2ecf20Sopenharmony_ci#define SPRD_RTC_ALMLOCK_MASK GENMASK(7, 0) 858c2ecf20Sopenharmony_ci#define SPRD_RTC_ALM_UNLOCK 0xa5 868c2ecf20Sopenharmony_ci#define SPRD_RTC_ALM_LOCK (~SPRD_RTC_ALM_UNLOCK & \ 878c2ecf20Sopenharmony_ci SPRD_RTC_ALMLOCK_MASK) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* SPG values definition for SPRD_RTC_SPG_UPD register */ 908c2ecf20Sopenharmony_ci#define SPRD_RTC_POWEROFF_ALM_FLAG BIT(8) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* power control/status definition */ 938c2ecf20Sopenharmony_ci#define SPRD_RTC_POWER_RESET_VALUE 0x96 948c2ecf20Sopenharmony_ci#define SPRD_RTC_POWER_STS_CLEAR GENMASK(7, 0) 958c2ecf20Sopenharmony_ci#define SPRD_RTC_POWER_STS_SHIFT 8 968c2ecf20Sopenharmony_ci#define SPRD_RTC_POWER_STS_VALID \ 978c2ecf20Sopenharmony_ci (~SPRD_RTC_POWER_RESET_VALUE << SPRD_RTC_POWER_STS_SHIFT) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* timeout of synchronizing time and alarm registers (us) */ 1008c2ecf20Sopenharmony_ci#define SPRD_RTC_POLL_TIMEOUT 200000 1018c2ecf20Sopenharmony_ci#define SPRD_RTC_POLL_DELAY_US 20000 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistruct sprd_rtc { 1048c2ecf20Sopenharmony_ci struct rtc_device *rtc; 1058c2ecf20Sopenharmony_ci struct regmap *regmap; 1068c2ecf20Sopenharmony_ci struct device *dev; 1078c2ecf20Sopenharmony_ci u32 base; 1088c2ecf20Sopenharmony_ci int irq; 1098c2ecf20Sopenharmony_ci bool valid; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * The Spreadtrum RTC controller has 3 groups registers, including time, normal 1148c2ecf20Sopenharmony_ci * alarm and auxiliary alarm. The time group registers are used to set RTC time, 1158c2ecf20Sopenharmony_ci * the normal alarm registers are used to set normal alarm, and the auxiliary 1168c2ecf20Sopenharmony_ci * alarm registers are used to set auxiliary alarm. Both alarm event and 1178c2ecf20Sopenharmony_ci * auxiliary alarm event can wake up system from deep sleep, but only alarm 1188c2ecf20Sopenharmony_ci * event can power up system from power down status. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cienum sprd_rtc_reg_types { 1218c2ecf20Sopenharmony_ci SPRD_RTC_TIME, 1228c2ecf20Sopenharmony_ci SPRD_RTC_ALARM, 1238c2ecf20Sopenharmony_ci SPRD_RTC_AUX_ALARM, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, 1298c2ecf20Sopenharmony_ci SPRD_RTC_ALM_INT_MASK); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int ret; 1358c2ecf20Sopenharmony_ci u32 val; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); 1388c2ecf20Sopenharmony_ci if (ret) 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci val &= ~SPRD_RTC_ALMLOCK_MASK; 1428c2ecf20Sopenharmony_ci if (lock) 1438c2ecf20Sopenharmony_ci val |= SPRD_RTC_ALM_LOCK; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val); 1488c2ecf20Sopenharmony_ci if (ret) 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* wait until the SPG value is updated successfully */ 1528c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(rtc->regmap, 1538c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_RAW_STS, val, 1548c2ecf20Sopenharmony_ci (val & SPRD_RTC_SPG_UPD_EN), 1558c2ecf20Sopenharmony_ci SPRD_RTC_POLL_DELAY_US, 1568c2ecf20Sopenharmony_ci SPRD_RTC_POLL_TIMEOUT); 1578c2ecf20Sopenharmony_ci if (ret) { 1588c2ecf20Sopenharmony_ci dev_err(rtc->dev, "failed to update SPG value:%d\n", ret); 1598c2ecf20Sopenharmony_ci return ret; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, 1638c2ecf20Sopenharmony_ci SPRD_RTC_SPG_UPD_EN); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, 1678c2ecf20Sopenharmony_ci time64_t *secs) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci u32 sec_reg, min_reg, hour_reg, day_reg; 1708c2ecf20Sopenharmony_ci u32 val, sec, min, hour, day; 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci switch (type) { 1748c2ecf20Sopenharmony_ci case SPRD_RTC_TIME: 1758c2ecf20Sopenharmony_ci sec_reg = SPRD_RTC_SEC_CNT_VALUE; 1768c2ecf20Sopenharmony_ci min_reg = SPRD_RTC_MIN_CNT_VALUE; 1778c2ecf20Sopenharmony_ci hour_reg = SPRD_RTC_HOUR_CNT_VALUE; 1788c2ecf20Sopenharmony_ci day_reg = SPRD_RTC_DAY_CNT_VALUE; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci case SPRD_RTC_ALARM: 1818c2ecf20Sopenharmony_ci sec_reg = SPRD_RTC_SEC_ALM_VALUE; 1828c2ecf20Sopenharmony_ci min_reg = SPRD_RTC_MIN_ALM_VALUE; 1838c2ecf20Sopenharmony_ci hour_reg = SPRD_RTC_HOUR_ALM_VALUE; 1848c2ecf20Sopenharmony_ci day_reg = SPRD_RTC_DAY_ALM_VALUE; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case SPRD_RTC_AUX_ALARM: 1878c2ecf20Sopenharmony_ci sec_reg = SPRD_RTC_SEC_AUXALM_UPD; 1888c2ecf20Sopenharmony_ci min_reg = SPRD_RTC_MIN_AUXALM_UPD; 1898c2ecf20Sopenharmony_ci hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; 1908c2ecf20Sopenharmony_ci day_reg = SPRD_RTC_DAY_AUXALM_UPD; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci default: 1938c2ecf20Sopenharmony_ci return -EINVAL; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val); 1978c2ecf20Sopenharmony_ci if (ret) 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci sec = val & SPRD_RTC_SEC_MASK; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val); 2038c2ecf20Sopenharmony_ci if (ret) 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci min = val & SPRD_RTC_MIN_MASK; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val); 2098c2ecf20Sopenharmony_ci if (ret) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci hour = val & SPRD_RTC_HOUR_MASK; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val); 2158c2ecf20Sopenharmony_ci if (ret) 2168c2ecf20Sopenharmony_ci return ret; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci day = val & SPRD_RTC_DAY_MASK; 2198c2ecf20Sopenharmony_ci *secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec; 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, 2248c2ecf20Sopenharmony_ci time64_t secs) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask; 2278c2ecf20Sopenharmony_ci u32 sec, min, hour, day, val; 2288c2ecf20Sopenharmony_ci int ret, rem; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* convert seconds to RTC time format */ 2318c2ecf20Sopenharmony_ci day = div_s64_rem(secs, 86400, &rem); 2328c2ecf20Sopenharmony_ci hour = rem / 3600; 2338c2ecf20Sopenharmony_ci rem -= hour * 3600; 2348c2ecf20Sopenharmony_ci min = rem / 60; 2358c2ecf20Sopenharmony_ci sec = rem - min * 60; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci switch (type) { 2388c2ecf20Sopenharmony_ci case SPRD_RTC_TIME: 2398c2ecf20Sopenharmony_ci sec_reg = SPRD_RTC_SEC_CNT_UPD; 2408c2ecf20Sopenharmony_ci min_reg = SPRD_RTC_MIN_CNT_UPD; 2418c2ecf20Sopenharmony_ci hour_reg = SPRD_RTC_HOUR_CNT_UPD; 2428c2ecf20Sopenharmony_ci day_reg = SPRD_RTC_DAY_CNT_UPD; 2438c2ecf20Sopenharmony_ci sts_mask = SPRD_RTC_TIME_INT_MASK; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case SPRD_RTC_ALARM: 2468c2ecf20Sopenharmony_ci sec_reg = SPRD_RTC_SEC_ALM_UPD; 2478c2ecf20Sopenharmony_ci min_reg = SPRD_RTC_MIN_ALM_UPD; 2488c2ecf20Sopenharmony_ci hour_reg = SPRD_RTC_HOUR_ALM_UPD; 2498c2ecf20Sopenharmony_ci day_reg = SPRD_RTC_DAY_ALM_UPD; 2508c2ecf20Sopenharmony_ci sts_mask = SPRD_RTC_ALMTIME_INT_MASK; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case SPRD_RTC_AUX_ALARM: 2538c2ecf20Sopenharmony_ci sec_reg = SPRD_RTC_SEC_AUXALM_UPD; 2548c2ecf20Sopenharmony_ci min_reg = SPRD_RTC_MIN_AUXALM_UPD; 2558c2ecf20Sopenharmony_ci hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; 2568c2ecf20Sopenharmony_ci day_reg = SPRD_RTC_DAY_AUXALM_UPD; 2578c2ecf20Sopenharmony_ci sts_mask = 0; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci default: 2608c2ecf20Sopenharmony_ci return -EINVAL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec); 2648c2ecf20Sopenharmony_ci if (ret) 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + min_reg, min); 2688c2ecf20Sopenharmony_ci if (ret) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour); 2728c2ecf20Sopenharmony_ci if (ret) 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + day_reg, day); 2768c2ecf20Sopenharmony_ci if (ret) 2778c2ecf20Sopenharmony_ci return ret; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (type == SPRD_RTC_AUX_ALARM) 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * Since the time and normal alarm registers are put in always-power-on 2848c2ecf20Sopenharmony_ci * region supplied by VDDRTC, then these registers changing time will 2858c2ecf20Sopenharmony_ci * be very long, about 125ms. Thus here we should wait until all 2868c2ecf20Sopenharmony_ci * values are updated successfully. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(rtc->regmap, 2898c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_RAW_STS, val, 2908c2ecf20Sopenharmony_ci ((val & sts_mask) == sts_mask), 2918c2ecf20Sopenharmony_ci SPRD_RTC_POLL_DELAY_US, 2928c2ecf20Sopenharmony_ci SPRD_RTC_POLL_TIMEOUT); 2938c2ecf20Sopenharmony_ci if (ret < 0) { 2948c2ecf20Sopenharmony_ci dev_err(rtc->dev, "set time/alarm values timeout\n"); 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, 2998c2ecf20Sopenharmony_ci sts_mask); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int sprd_rtc_read_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 3058c2ecf20Sopenharmony_ci time64_t secs; 3068c2ecf20Sopenharmony_ci u32 val; 3078c2ecf20Sopenharmony_ci int ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = sprd_rtc_get_secs(rtc, SPRD_RTC_AUX_ALARM, &secs); 3108c2ecf20Sopenharmony_ci if (ret) 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, &alrm->time); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); 3168c2ecf20Sopenharmony_ci if (ret) 3178c2ecf20Sopenharmony_ci return ret; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci alrm->enabled = !!(val & SPRD_RTC_AUXALM_EN); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); 3228c2ecf20Sopenharmony_ci if (ret) 3238c2ecf20Sopenharmony_ci return ret; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci alrm->pending = !!(val & SPRD_RTC_AUXALM_EN); 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 3328c2ecf20Sopenharmony_ci time64_t secs = rtc_tm_to_time64(&alrm->time); 3338c2ecf20Sopenharmony_ci int ret; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* clear the auxiliary alarm interrupt status */ 3368c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, 3378c2ecf20Sopenharmony_ci SPRD_RTC_AUXALM_EN); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (alrm->enabled) { 3468c2ecf20Sopenharmony_ci ret = regmap_update_bits(rtc->regmap, 3478c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_EN, 3488c2ecf20Sopenharmony_ci SPRD_RTC_AUXALM_EN, 3498c2ecf20Sopenharmony_ci SPRD_RTC_AUXALM_EN); 3508c2ecf20Sopenharmony_ci } else { 3518c2ecf20Sopenharmony_ci ret = regmap_update_bits(rtc->regmap, 3528c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_EN, 3538c2ecf20Sopenharmony_ci SPRD_RTC_AUXALM_EN, 0); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 3628c2ecf20Sopenharmony_ci time64_t secs; 3638c2ecf20Sopenharmony_ci int ret; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (!rtc->valid) { 3668c2ecf20Sopenharmony_ci dev_warn(dev, "RTC values are invalid\n"); 3678c2ecf20Sopenharmony_ci return -EINVAL; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs); 3718c2ecf20Sopenharmony_ci if (ret) 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, tm); 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 3818c2ecf20Sopenharmony_ci time64_t secs = rtc_tm_to_time64(tm); 3828c2ecf20Sopenharmony_ci int ret; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ret = sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs); 3858c2ecf20Sopenharmony_ci if (ret) 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (!rtc->valid) { 3898c2ecf20Sopenharmony_ci /* Clear RTC power status firstly */ 3908c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_PWR_CTRL, 3918c2ecf20Sopenharmony_ci SPRD_RTC_POWER_STS_CLEAR); 3928c2ecf20Sopenharmony_ci if (ret) 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * Set RTC power status to indicate now RTC has valid time 3978c2ecf20Sopenharmony_ci * values. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_PWR_CTRL, 4008c2ecf20Sopenharmony_ci SPRD_RTC_POWER_STS_VALID); 4018c2ecf20Sopenharmony_ci if (ret) 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci rtc->valid = true; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 4138c2ecf20Sopenharmony_ci time64_t secs; 4148c2ecf20Sopenharmony_ci int ret; 4158c2ecf20Sopenharmony_ci u32 val; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* 4188c2ecf20Sopenharmony_ci * Before RTC device is registered, it will check to see if there is an 4198c2ecf20Sopenharmony_ci * alarm already set in RTC hardware, and we always read the normal 4208c2ecf20Sopenharmony_ci * alarm at this time. 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Or if aie_timer is enabled, we should get the normal alarm time. 4238c2ecf20Sopenharmony_ci * Otherwise we should get auxiliary alarm time. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci if (rtc->rtc && rtc->rtc->registered && rtc->rtc->aie_timer.enabled == 0) 4268c2ecf20Sopenharmony_ci return sprd_rtc_read_aux_alarm(dev, alrm); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs); 4298c2ecf20Sopenharmony_ci if (ret) 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci rtc_time64_to_tm(secs, &alrm->time); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); 4358c2ecf20Sopenharmony_ci if (ret) 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci alrm->enabled = !!(val & SPRD_RTC_ALARM_EN); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); 4418c2ecf20Sopenharmony_ci if (ret) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci alrm->pending = !!(val & SPRD_RTC_ALARM_EN); 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 4518c2ecf20Sopenharmony_ci time64_t secs = rtc_tm_to_time64(&alrm->time); 4528c2ecf20Sopenharmony_ci struct rtc_time aie_time = 4538c2ecf20Sopenharmony_ci rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires); 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * We have 2 groups alarms: normal alarm and auxiliary alarm. Since 4588c2ecf20Sopenharmony_ci * both normal alarm event and auxiliary alarm event can wake up system 4598c2ecf20Sopenharmony_ci * from deep sleep, but only alarm event can power up system from power 4608c2ecf20Sopenharmony_ci * down status. Moreover we do not need to poll about 125ms when 4618c2ecf20Sopenharmony_ci * updating auxiliary alarm registers. Thus we usually set auxiliary 4628c2ecf20Sopenharmony_ci * alarm when wake up system from deep sleep, and for other scenarios, 4638c2ecf20Sopenharmony_ci * we should set normal alarm with polling status. 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * So here we check if the alarm time is set by aie_timer, if yes, we 4668c2ecf20Sopenharmony_ci * should set normal alarm, if not, we should set auxiliary alarm which 4678c2ecf20Sopenharmony_ci * means it is just a wake event. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_ci if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time)) 4708c2ecf20Sopenharmony_ci return sprd_rtc_set_aux_alarm(dev, alrm); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* clear the alarm interrupt status firstly */ 4738c2ecf20Sopenharmony_ci ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, 4748c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN); 4758c2ecf20Sopenharmony_ci if (ret) 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs); 4798c2ecf20Sopenharmony_ci if (ret) 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (alrm->enabled) { 4838c2ecf20Sopenharmony_ci ret = regmap_update_bits(rtc->regmap, 4848c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_EN, 4858c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN, 4868c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN); 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* unlock the alarm to enable the alarm function. */ 4918c2ecf20Sopenharmony_ci ret = sprd_rtc_lock_alarm(rtc, false); 4928c2ecf20Sopenharmony_ci } else { 4938c2ecf20Sopenharmony_ci regmap_update_bits(rtc->regmap, 4948c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_EN, 4958c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN, 0); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * Lock the alarm function in case fake alarm event will power 4998c2ecf20Sopenharmony_ci * up systems. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci ret = sprd_rtc_lock_alarm(rtc, true); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_get_drvdata(dev); 5108c2ecf20Sopenharmony_ci int ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (enabled) { 5138c2ecf20Sopenharmony_ci ret = regmap_update_bits(rtc->regmap, 5148c2ecf20Sopenharmony_ci rtc->base + SPRD_RTC_INT_EN, 5158c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 5168c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN); 5178c2ecf20Sopenharmony_ci if (ret) 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ret = sprd_rtc_lock_alarm(rtc, false); 5218c2ecf20Sopenharmony_ci } else { 5228c2ecf20Sopenharmony_ci regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, 5238c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci ret = sprd_rtc_lock_alarm(rtc, true); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return ret; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic const struct rtc_class_ops sprd_rtc_ops = { 5328c2ecf20Sopenharmony_ci .read_time = sprd_rtc_read_time, 5338c2ecf20Sopenharmony_ci .set_time = sprd_rtc_set_time, 5348c2ecf20Sopenharmony_ci .read_alarm = sprd_rtc_read_alarm, 5358c2ecf20Sopenharmony_ci .set_alarm = sprd_rtc_set_alarm, 5368c2ecf20Sopenharmony_ci .alarm_irq_enable = sprd_rtc_alarm_irq_enable, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic irqreturn_t sprd_rtc_handler(int irq, void *dev_id) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct sprd_rtc *rtc = dev_id; 5428c2ecf20Sopenharmony_ci int ret; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ret = sprd_rtc_clear_alarm_ints(rtc); 5458c2ecf20Sopenharmony_ci if (ret) 5468c2ecf20Sopenharmony_ci return IRQ_RETVAL(ret); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF); 5498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int sprd_rtc_check_power_down(struct sprd_rtc *rtc) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci u32 val; 5558c2ecf20Sopenharmony_ci int ret; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_PWR_STS, &val); 5588c2ecf20Sopenharmony_ci if (ret) 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * If the RTC power status value is SPRD_RTC_POWER_RESET_VALUE, which 5638c2ecf20Sopenharmony_ci * means the RTC has been powered down, so the RTC time values are 5648c2ecf20Sopenharmony_ci * invalid. 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci rtc->valid = val == SPRD_RTC_POWER_RESET_VALUE ? false : true; 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int sprd_rtc_check_alarm_int(struct sprd_rtc *rtc) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci u32 val; 5738c2ecf20Sopenharmony_ci int ret; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); 5768c2ecf20Sopenharmony_ci if (ret) 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* 5808c2ecf20Sopenharmony_ci * The SPRD_RTC_INT_EN register is not put in always-power-on region 5818c2ecf20Sopenharmony_ci * supplied by VDDRTC, so we should check if we need enable the alarm 5828c2ecf20Sopenharmony_ci * interrupt when system booting. 5838c2ecf20Sopenharmony_ci * 5848c2ecf20Sopenharmony_ci * If we have set SPRD_RTC_POWEROFF_ALM_FLAG which is saved in 5858c2ecf20Sopenharmony_ci * always-power-on region, that means we have set one alarm last time, 5868c2ecf20Sopenharmony_ci * so we should enable the alarm interrupt to help RTC core to see if 5878c2ecf20Sopenharmony_ci * there is an alarm already set in RTC hardware. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci if (!(val & SPRD_RTC_POWEROFF_ALM_FLAG)) 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, 5938c2ecf20Sopenharmony_ci SPRD_RTC_ALARM_EN, SPRD_RTC_ALARM_EN); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int sprd_rtc_probe(struct platform_device *pdev) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 5998c2ecf20Sopenharmony_ci struct sprd_rtc *rtc; 6008c2ecf20Sopenharmony_ci int ret; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 6038c2ecf20Sopenharmony_ci if (!rtc) 6048c2ecf20Sopenharmony_ci return -ENOMEM; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); 6078c2ecf20Sopenharmony_ci if (!rtc->regmap) 6088c2ecf20Sopenharmony_ci return -ENODEV; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "reg", &rtc->base); 6118c2ecf20Sopenharmony_ci if (ret) { 6128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get RTC base address\n"); 6138c2ecf20Sopenharmony_ci return ret; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci rtc->irq = platform_get_irq(pdev, 0); 6178c2ecf20Sopenharmony_ci if (rtc->irq < 0) 6188c2ecf20Sopenharmony_ci return rtc->irq; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci rtc->rtc = devm_rtc_allocate_device(&pdev->dev); 6218c2ecf20Sopenharmony_ci if (IS_ERR(rtc->rtc)) 6228c2ecf20Sopenharmony_ci return PTR_ERR(rtc->rtc); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci rtc->dev = &pdev->dev; 6258c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rtc); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* check if we need set the alarm interrupt */ 6288c2ecf20Sopenharmony_ci ret = sprd_rtc_check_alarm_int(rtc); 6298c2ecf20Sopenharmony_ci if (ret) { 6308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to check RTC alarm interrupt\n"); 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* check if RTC time values are valid */ 6358c2ecf20Sopenharmony_ci ret = sprd_rtc_check_power_down(rtc); 6368c2ecf20Sopenharmony_ci if (ret) { 6378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to check RTC time values\n"); 6388c2ecf20Sopenharmony_ci return ret; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, 6428c2ecf20Sopenharmony_ci sprd_rtc_handler, 6438c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_EARLY_RESUME, 6448c2ecf20Sopenharmony_ci pdev->name, rtc); 6458c2ecf20Sopenharmony_ci if (ret < 0) { 6468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to request RTC irq\n"); 6478c2ecf20Sopenharmony_ci return ret; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci rtc->rtc->ops = &sprd_rtc_ops; 6538c2ecf20Sopenharmony_ci rtc->rtc->range_min = 0; 6548c2ecf20Sopenharmony_ci rtc->rtc->range_max = 5662310399LL; 6558c2ecf20Sopenharmony_ci ret = rtc_register_device(rtc->rtc); 6568c2ecf20Sopenharmony_ci if (ret) { 6578c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 0); 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic const struct of_device_id sprd_rtc_of_match[] = { 6658c2ecf20Sopenharmony_ci { .compatible = "sprd,sc2731-rtc", }, 6668c2ecf20Sopenharmony_ci { }, 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sprd_rtc_of_match); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic struct platform_driver sprd_rtc_driver = { 6718c2ecf20Sopenharmony_ci .driver = { 6728c2ecf20Sopenharmony_ci .name = "sprd-rtc", 6738c2ecf20Sopenharmony_ci .of_match_table = sprd_rtc_of_match, 6748c2ecf20Sopenharmony_ci }, 6758c2ecf20Sopenharmony_ci .probe = sprd_rtc_probe, 6768c2ecf20Sopenharmony_ci}; 6778c2ecf20Sopenharmony_cimodule_platform_driver(sprd_rtc_driver); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum RTC Device Driver"); 6818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>"); 682