18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/init.h>
68c2ecf20Sopenharmony_ci#include <linux/io.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h>
128c2ecf20Sopenharmony_ci#include <linux/rtc.h>
138c2ecf20Sopenharmony_ci#include <linux/clk.h>
148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define SNVS_LPREGISTER_OFFSET	0x34
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* These register offsets are relative to LP (Low Power) range */
208c2ecf20Sopenharmony_ci#define SNVS_LPCR		0x04
218c2ecf20Sopenharmony_ci#define SNVS_LPSR		0x18
228c2ecf20Sopenharmony_ci#define SNVS_LPSRTCMR		0x1c
238c2ecf20Sopenharmony_ci#define SNVS_LPSRTCLR		0x20
248c2ecf20Sopenharmony_ci#define SNVS_LPTAR		0x24
258c2ecf20Sopenharmony_ci#define SNVS_LPPGDR		0x30
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SNVS_LPCR_SRTC_ENV	(1 << 0)
288c2ecf20Sopenharmony_ci#define SNVS_LPCR_LPTA_EN	(1 << 1)
298c2ecf20Sopenharmony_ci#define SNVS_LPCR_LPWUI_EN	(1 << 3)
308c2ecf20Sopenharmony_ci#define SNVS_LPSR_LPTA		(1 << 0)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define SNVS_LPPGDR_INIT	0x41736166
338c2ecf20Sopenharmony_ci#define CNTR_TO_SECS_SH		15
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* The maximum RTC clock cycles that are allowed to pass between two
368c2ecf20Sopenharmony_ci * consecutive clock counter register reads. If the values are corrupted a
378c2ecf20Sopenharmony_ci * bigger difference is expected. The RTC frequency is 32kHz. With 320 cycles
388c2ecf20Sopenharmony_ci * we end at 10ms which should be enough for most cases. If it once takes
398c2ecf20Sopenharmony_ci * longer than expected we do a retry.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci#define MAX_RTC_READ_DIFF_CYCLES	320
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct snvs_rtc_data {
448c2ecf20Sopenharmony_ci	struct rtc_device *rtc;
458c2ecf20Sopenharmony_ci	struct regmap *regmap;
468c2ecf20Sopenharmony_ci	int offset;
478c2ecf20Sopenharmony_ci	int irq;
488c2ecf20Sopenharmony_ci	struct clk *clk;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Read 64 bit timer register, which could be in inconsistent state */
528c2ecf20Sopenharmony_cistatic u64 rtc_read_lpsrt(struct snvs_rtc_data *data)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	u32 msb, lsb;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &msb);
578c2ecf20Sopenharmony_ci	regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &lsb);
588c2ecf20Sopenharmony_ci	return (u64)msb << 32 | lsb;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Read the secure real time counter, taking care to deal with the cases of the
628c2ecf20Sopenharmony_ci * counter updating while being read.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	u64 read1, read2;
678c2ecf20Sopenharmony_ci	s64 diff;
688c2ecf20Sopenharmony_ci	unsigned int timeout = 100;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* As expected, the registers might update between the read of the LSB
718c2ecf20Sopenharmony_ci	 * reg and the MSB reg.  It's also possible that one register might be
728c2ecf20Sopenharmony_ci	 * in partially modified state as well.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	read1 = rtc_read_lpsrt(data);
758c2ecf20Sopenharmony_ci	do {
768c2ecf20Sopenharmony_ci		read2 = read1;
778c2ecf20Sopenharmony_ci		read1 = rtc_read_lpsrt(data);
788c2ecf20Sopenharmony_ci		diff = read1 - read2;
798c2ecf20Sopenharmony_ci	} while (((diff < 0) || (diff > MAX_RTC_READ_DIFF_CYCLES)) && --timeout);
808c2ecf20Sopenharmony_ci	if (!timeout)
818c2ecf20Sopenharmony_ci		dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n");
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Convert 47-bit counter to 32-bit raw second count */
848c2ecf20Sopenharmony_ci	return (u32) (read1 >> CNTR_TO_SECS_SH);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Just read the lsb from the counter, dealing with inconsistent state */
888c2ecf20Sopenharmony_cistatic int rtc_read_lp_counter_lsb(struct snvs_rtc_data *data, u32 *lsb)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	u32 count1, count2;
918c2ecf20Sopenharmony_ci	s32 diff;
928c2ecf20Sopenharmony_ci	unsigned int timeout = 100;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
958c2ecf20Sopenharmony_ci	do {
968c2ecf20Sopenharmony_ci		count2 = count1;
978c2ecf20Sopenharmony_ci		regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
988c2ecf20Sopenharmony_ci		diff = count1 - count2;
998c2ecf20Sopenharmony_ci	} while (((diff < 0) || (diff > MAX_RTC_READ_DIFF_CYCLES)) && --timeout);
1008c2ecf20Sopenharmony_ci	if (!timeout) {
1018c2ecf20Sopenharmony_ci		dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n");
1028c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	*lsb = count1;
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int rtc_write_sync_lp(struct snvs_rtc_data *data)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	u32 count1, count2;
1128c2ecf20Sopenharmony_ci	u32 elapsed;
1138c2ecf20Sopenharmony_ci	unsigned int timeout = 1000;
1148c2ecf20Sopenharmony_ci	int ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	ret = rtc_read_lp_counter_lsb(data, &count1);
1178c2ecf20Sopenharmony_ci	if (ret)
1188c2ecf20Sopenharmony_ci		return ret;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Wait for 3 CKIL cycles, about 61.0-91.5 µs */
1218c2ecf20Sopenharmony_ci	do {
1228c2ecf20Sopenharmony_ci		ret = rtc_read_lp_counter_lsb(data, &count2);
1238c2ecf20Sopenharmony_ci		if (ret)
1248c2ecf20Sopenharmony_ci			return ret;
1258c2ecf20Sopenharmony_ci		elapsed = count2 - count1; /* wrap around _is_ handled! */
1268c2ecf20Sopenharmony_ci	} while (elapsed < 3 && --timeout);
1278c2ecf20Sopenharmony_ci	if (!timeout) {
1288c2ecf20Sopenharmony_ci		dev_err(&data->rtc->dev, "Timeout waiting for LPSRT Counter to change\n");
1298c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	int timeout = 1000;
1378c2ecf20Sopenharmony_ci	u32 lpcr;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV,
1408c2ecf20Sopenharmony_ci			   enable ? SNVS_LPCR_SRTC_ENV : 0);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	while (--timeout) {
1438c2ecf20Sopenharmony_ci		regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (enable) {
1468c2ecf20Sopenharmony_ci			if (lpcr & SNVS_LPCR_SRTC_ENV)
1478c2ecf20Sopenharmony_ci				break;
1488c2ecf20Sopenharmony_ci		} else {
1498c2ecf20Sopenharmony_ci			if (!(lpcr & SNVS_LPCR_SRTC_ENV))
1508c2ecf20Sopenharmony_ci				break;
1518c2ecf20Sopenharmony_ci		}
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (!timeout)
1558c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
1638c2ecf20Sopenharmony_ci	unsigned long time;
1648c2ecf20Sopenharmony_ci	int ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (data->clk) {
1678c2ecf20Sopenharmony_ci		ret = clk_enable(data->clk);
1688c2ecf20Sopenharmony_ci		if (ret)
1698c2ecf20Sopenharmony_ci			return ret;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	time = rtc_read_lp_counter(data);
1738c2ecf20Sopenharmony_ci	rtc_time64_to_tm(time, tm);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (data->clk)
1768c2ecf20Sopenharmony_ci		clk_disable(data->clk);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
1848c2ecf20Sopenharmony_ci	unsigned long time = rtc_tm_to_time64(tm);
1858c2ecf20Sopenharmony_ci	int ret;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (data->clk) {
1888c2ecf20Sopenharmony_ci		ret = clk_enable(data->clk);
1898c2ecf20Sopenharmony_ci		if (ret)
1908c2ecf20Sopenharmony_ci			return ret;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Disable RTC first */
1948c2ecf20Sopenharmony_ci	ret = snvs_rtc_enable(data, false);
1958c2ecf20Sopenharmony_ci	if (ret)
1968c2ecf20Sopenharmony_ci		return ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
1998c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
2008c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Enable RTC again */
2038c2ecf20Sopenharmony_ci	ret = snvs_rtc_enable(data, true);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (data->clk)
2068c2ecf20Sopenharmony_ci		clk_disable(data->clk);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return ret;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
2148c2ecf20Sopenharmony_ci	u32 lptar, lpsr;
2158c2ecf20Sopenharmony_ci	int ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (data->clk) {
2188c2ecf20Sopenharmony_ci		ret = clk_enable(data->clk);
2198c2ecf20Sopenharmony_ci		if (ret)
2208c2ecf20Sopenharmony_ci			return ret;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);
2248c2ecf20Sopenharmony_ci	rtc_time64_to_tm(lptar, &alrm->time);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
2278c2ecf20Sopenharmony_ci	alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (data->clk)
2308c2ecf20Sopenharmony_ci		clk_disable(data->clk);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
2388c2ecf20Sopenharmony_ci	int ret;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (data->clk) {
2418c2ecf20Sopenharmony_ci		ret = clk_enable(data->clk);
2428c2ecf20Sopenharmony_ci		if (ret)
2438c2ecf20Sopenharmony_ci			return ret;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR,
2478c2ecf20Sopenharmony_ci			   (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN),
2488c2ecf20Sopenharmony_ci			   enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ret = rtc_write_sync_lp(data);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (data->clk)
2538c2ecf20Sopenharmony_ci		clk_disable(data->clk);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return ret;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
2618c2ecf20Sopenharmony_ci	unsigned long time = rtc_tm_to_time64(&alrm->time);
2628c2ecf20Sopenharmony_ci	int ret;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (data->clk) {
2658c2ecf20Sopenharmony_ci		ret = clk_enable(data->clk);
2668c2ecf20Sopenharmony_ci		if (ret)
2678c2ecf20Sopenharmony_ci			return ret;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
2718c2ecf20Sopenharmony_ci	ret = rtc_write_sync_lp(data);
2728c2ecf20Sopenharmony_ci	if (ret)
2738c2ecf20Sopenharmony_ci		return ret;
2748c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Clear alarm interrupt status bit */
2778c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (data->clk)
2808c2ecf20Sopenharmony_ci		clk_disable(data->clk);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic const struct rtc_class_ops snvs_rtc_ops = {
2868c2ecf20Sopenharmony_ci	.read_time = snvs_rtc_read_time,
2878c2ecf20Sopenharmony_ci	.set_time = snvs_rtc_set_time,
2888c2ecf20Sopenharmony_ci	.read_alarm = snvs_rtc_read_alarm,
2898c2ecf20Sopenharmony_ci	.set_alarm = snvs_rtc_set_alarm,
2908c2ecf20Sopenharmony_ci	.alarm_irq_enable = snvs_rtc_alarm_irq_enable,
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct device *dev = dev_id;
2968c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
2978c2ecf20Sopenharmony_ci	u32 lpsr;
2988c2ecf20Sopenharmony_ci	u32 events = 0;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (data->clk)
3018c2ecf20Sopenharmony_ci		clk_enable(data->clk);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (lpsr & SNVS_LPSR_LPTA) {
3068c2ecf20Sopenharmony_ci		events |= (RTC_AF | RTC_IRQF);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		/* RTC alarm should be one-shot */
3098c2ecf20Sopenharmony_ci		snvs_rtc_alarm_irq_enable(dev, 0);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		rtc_update_irq(data->rtc, 1, events);
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* clear interrupt status */
3158c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (data->clk)
3188c2ecf20Sopenharmony_ci		clk_disable(data->clk);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return events ? IRQ_HANDLED : IRQ_NONE;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic const struct regmap_config snvs_rtc_config = {
3248c2ecf20Sopenharmony_ci	.reg_bits = 32,
3258c2ecf20Sopenharmony_ci	.val_bits = 32,
3268c2ecf20Sopenharmony_ci	.reg_stride = 4,
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void snvs_rtc_action(void *data)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	if (data)
3328c2ecf20Sopenharmony_ci		clk_disable_unprepare(data);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int snvs_rtc_probe(struct platform_device *pdev)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data;
3388c2ecf20Sopenharmony_ci	int ret;
3398c2ecf20Sopenharmony_ci	void __iomem *mmio;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
3428c2ecf20Sopenharmony_ci	if (!data)
3438c2ecf20Sopenharmony_ci		return -ENOMEM;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	data->rtc = devm_rtc_allocate_device(&pdev->dev);
3468c2ecf20Sopenharmony_ci	if (IS_ERR(data->rtc))
3478c2ecf20Sopenharmony_ci		return PTR_ERR(data->rtc);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (IS_ERR(data->regmap)) {
3528c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		mmio = devm_platform_ioremap_resource(pdev, 0);
3558c2ecf20Sopenharmony_ci		if (IS_ERR(mmio))
3568c2ecf20Sopenharmony_ci			return PTR_ERR(mmio);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);
3598c2ecf20Sopenharmony_ci	} else {
3608c2ecf20Sopenharmony_ci		data->offset = SNVS_LPREGISTER_OFFSET;
3618c2ecf20Sopenharmony_ci		of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (IS_ERR(data->regmap)) {
3658c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Can't find snvs syscon\n");
3668c2ecf20Sopenharmony_ci		return -ENODEV;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	data->irq = platform_get_irq(pdev, 0);
3708c2ecf20Sopenharmony_ci	if (data->irq < 0)
3718c2ecf20Sopenharmony_ci		return data->irq;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	data->clk = devm_clk_get(&pdev->dev, "snvs-rtc");
3748c2ecf20Sopenharmony_ci	if (IS_ERR(data->clk)) {
3758c2ecf20Sopenharmony_ci		data->clk = NULL;
3768c2ecf20Sopenharmony_ci	} else {
3778c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(data->clk);
3788c2ecf20Sopenharmony_ci		if (ret) {
3798c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
3808c2ecf20Sopenharmony_ci				"Could not prepare or enable the snvs clock\n");
3818c2ecf20Sopenharmony_ci			return ret;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ret = devm_add_action_or_reset(&pdev->dev, snvs_rtc_action, data->clk);
3868c2ecf20Sopenharmony_ci	if (ret)
3878c2ecf20Sopenharmony_ci		return ret;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* Initialize glitch detect */
3928c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* Clear interrupt status */
3958c2ecf20Sopenharmony_ci	regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* Enable RTC */
3988c2ecf20Sopenharmony_ci	ret = snvs_rtc_enable(data, true);
3998c2ecf20Sopenharmony_ci	if (ret) {
4008c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable rtc %d\n", ret);
4018c2ecf20Sopenharmony_ci		return ret;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, true);
4058c2ecf20Sopenharmony_ci	ret = dev_pm_set_wake_irq(&pdev->dev, data->irq);
4068c2ecf20Sopenharmony_ci	if (ret)
4078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable irq wake\n");
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
4108c2ecf20Sopenharmony_ci			       IRQF_SHARED, "rtc alarm", &pdev->dev);
4118c2ecf20Sopenharmony_ci	if (ret) {
4128c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request irq %d: %d\n",
4138c2ecf20Sopenharmony_ci			data->irq, ret);
4148c2ecf20Sopenharmony_ci		return ret;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	data->rtc->ops = &snvs_rtc_ops;
4188c2ecf20Sopenharmony_ci	data->rtc->range_max = U32_MAX;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return rtc_register_device(data->rtc);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int __maybe_unused snvs_rtc_suspend_noirq(struct device *dev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (data->clk)
4288c2ecf20Sopenharmony_ci		clk_disable(data->clk);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic int __maybe_unused snvs_rtc_resume_noirq(struct device *dev)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct snvs_rtc_data *data = dev_get_drvdata(dev);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (data->clk)
4388c2ecf20Sopenharmony_ci		return clk_enable(data->clk);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic const struct dev_pm_ops snvs_rtc_pm_ops = {
4448c2ecf20Sopenharmony_ci	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(snvs_rtc_suspend_noirq, snvs_rtc_resume_noirq)
4458c2ecf20Sopenharmony_ci};
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic const struct of_device_id snvs_dt_ids[] = {
4488c2ecf20Sopenharmony_ci	{ .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
4498c2ecf20Sopenharmony_ci	{ /* sentinel */ }
4508c2ecf20Sopenharmony_ci};
4518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, snvs_dt_ids);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic struct platform_driver snvs_rtc_driver = {
4548c2ecf20Sopenharmony_ci	.driver = {
4558c2ecf20Sopenharmony_ci		.name	= "snvs_rtc",
4568c2ecf20Sopenharmony_ci		.pm	= &snvs_rtc_pm_ops,
4578c2ecf20Sopenharmony_ci		.of_match_table = snvs_dt_ids,
4588c2ecf20Sopenharmony_ci	},
4598c2ecf20Sopenharmony_ci	.probe		= snvs_rtc_probe,
4608c2ecf20Sopenharmony_ci};
4618c2ecf20Sopenharmony_cimodule_platform_driver(snvs_rtc_driver);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
4648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale SNVS RTC Driver");
4658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
466