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