xref: /linux/drivers/rtc/rtc-snvs.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
15874c7f1SFabio Estevam // SPDX-License-Identifier: GPL-2.0+
25874c7f1SFabio Estevam //
35874c7f1SFabio Estevam // Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
4179a502fSShawn Guo 
5179a502fSShawn Guo #include <linux/init.h>
6179a502fSShawn Guo #include <linux/io.h>
7179a502fSShawn Guo #include <linux/kernel.h>
8179a502fSShawn Guo #include <linux/module.h>
9179a502fSShawn Guo #include <linux/of.h>
10179a502fSShawn Guo #include <linux/platform_device.h>
11e7afddb2SAnson Huang #include <linux/pm_wakeirq.h>
12179a502fSShawn Guo #include <linux/rtc.h>
137f899399SSanchayan Maity #include <linux/clk.h>
14d482893bSFrank Li #include <linux/mfd/syscon.h>
15d482893bSFrank Li #include <linux/regmap.h>
16d482893bSFrank Li 
17d482893bSFrank Li #define SNVS_LPREGISTER_OFFSET	0x34
18179a502fSShawn Guo 
19179a502fSShawn Guo /* These register offsets are relative to LP (Low Power) range */
20179a502fSShawn Guo #define SNVS_LPCR		0x04
21179a502fSShawn Guo #define SNVS_LPSR		0x18
22179a502fSShawn Guo #define SNVS_LPSRTCMR		0x1c
23179a502fSShawn Guo #define SNVS_LPSRTCLR		0x20
24179a502fSShawn Guo #define SNVS_LPTAR		0x24
25179a502fSShawn Guo #define SNVS_LPPGDR		0x30
26179a502fSShawn Guo 
27179a502fSShawn Guo #define SNVS_LPCR_SRTC_ENV	(1 << 0)
28179a502fSShawn Guo #define SNVS_LPCR_LPTA_EN	(1 << 1)
29179a502fSShawn Guo #define SNVS_LPCR_LPWUI_EN	(1 << 3)
30179a502fSShawn Guo #define SNVS_LPSR_LPTA		(1 << 0)
31179a502fSShawn Guo 
32179a502fSShawn Guo #define SNVS_LPPGDR_INIT	0x41736166
33179a502fSShawn Guo #define CNTR_TO_SECS_SH		15
34179a502fSShawn Guo 
35*0462681eSStefan Eichenberger /* The maximum RTC clock cycles that are allowed to pass between two
36*0462681eSStefan Eichenberger  * consecutive clock counter register reads. If the values are corrupted a
37*0462681eSStefan Eichenberger  * bigger difference is expected. The RTC frequency is 32kHz. With 320 cycles
38*0462681eSStefan Eichenberger  * we end at 10ms which should be enough for most cases. If it once takes
39*0462681eSStefan Eichenberger  * longer than expected we do a retry.
40*0462681eSStefan Eichenberger  */
41*0462681eSStefan Eichenberger #define MAX_RTC_READ_DIFF_CYCLES	320
42*0462681eSStefan Eichenberger 
43179a502fSShawn Guo struct snvs_rtc_data {
44179a502fSShawn Guo 	struct rtc_device *rtc;
45d482893bSFrank Li 	struct regmap *regmap;
46d482893bSFrank Li 	int offset;
47179a502fSShawn Guo 	int irq;
487f899399SSanchayan Maity 	struct clk *clk;
49179a502fSShawn Guo };
50179a502fSShawn Guo 
51cd7f3a24STrent Piepho /* Read 64 bit timer register, which could be in inconsistent state */
rtc_read_lpsrt(struct snvs_rtc_data * data)52cd7f3a24STrent Piepho static u64 rtc_read_lpsrt(struct snvs_rtc_data *data)
53cd7f3a24STrent Piepho {
54cd7f3a24STrent Piepho 	u32 msb, lsb;
55cd7f3a24STrent Piepho 
56cd7f3a24STrent Piepho 	regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &msb);
57cd7f3a24STrent Piepho 	regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &lsb);
58cd7f3a24STrent Piepho 	return (u64)msb << 32 | lsb;
59cd7f3a24STrent Piepho }
60cd7f3a24STrent Piepho 
61cd7f3a24STrent Piepho /* Read the secure real time counter, taking care to deal with the cases of the
62cd7f3a24STrent Piepho  * counter updating while being read.
63cd7f3a24STrent Piepho  */
rtc_read_lp_counter(struct snvs_rtc_data * data)64d482893bSFrank Li static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
65179a502fSShawn Guo {
66179a502fSShawn Guo 	u64 read1, read2;
67*0462681eSStefan Eichenberger 	s64 diff;
68cd7f3a24STrent Piepho 	unsigned int timeout = 100;
69179a502fSShawn Guo 
70cd7f3a24STrent Piepho 	/* As expected, the registers might update between the read of the LSB
71cd7f3a24STrent Piepho 	 * reg and the MSB reg.  It's also possible that one register might be
72cd7f3a24STrent Piepho 	 * in partially modified state as well.
73cd7f3a24STrent Piepho 	 */
74cd7f3a24STrent Piepho 	read1 = rtc_read_lpsrt(data);
75179a502fSShawn Guo 	do {
76cd7f3a24STrent Piepho 		read2 = read1;
77cd7f3a24STrent Piepho 		read1 = rtc_read_lpsrt(data);
78*0462681eSStefan Eichenberger 		diff = read1 - read2;
79*0462681eSStefan Eichenberger 	} while (((diff < 0) || (diff > MAX_RTC_READ_DIFF_CYCLES)) && --timeout);
80cd7f3a24STrent Piepho 	if (!timeout)
81cd7f3a24STrent Piepho 		dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n");
82179a502fSShawn Guo 
83179a502fSShawn Guo 	/* Convert 47-bit counter to 32-bit raw second count */
84179a502fSShawn Guo 	return (u32) (read1 >> CNTR_TO_SECS_SH);
85179a502fSShawn Guo }
86179a502fSShawn Guo 
87cd7f3a24STrent Piepho /* Just read the lsb from the counter, dealing with inconsistent state */
rtc_read_lp_counter_lsb(struct snvs_rtc_data * data,u32 * lsb)88cd7f3a24STrent Piepho static int rtc_read_lp_counter_lsb(struct snvs_rtc_data *data, u32 *lsb)
89179a502fSShawn Guo {
90cd7f3a24STrent Piepho 	u32 count1, count2;
91*0462681eSStefan Eichenberger 	s32 diff;
92cd7f3a24STrent Piepho 	unsigned int timeout = 100;
93179a502fSShawn Guo 
94d482893bSFrank Li 	regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
95179a502fSShawn Guo 	do {
96cd7f3a24STrent Piepho 		count2 = count1;
97cd7f3a24STrent Piepho 		regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
98*0462681eSStefan Eichenberger 		diff = count1 - count2;
99*0462681eSStefan Eichenberger 	} while (((diff < 0) || (diff > MAX_RTC_READ_DIFF_CYCLES)) && --timeout);
100cd7f3a24STrent Piepho 	if (!timeout) {
101cd7f3a24STrent Piepho 		dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n");
102cd7f3a24STrent Piepho 		return -ETIMEDOUT;
103179a502fSShawn Guo 	}
104cd7f3a24STrent Piepho 
105cd7f3a24STrent Piepho 	*lsb = count1;
106cd7f3a24STrent Piepho 	return 0;
107cd7f3a24STrent Piepho }
108cd7f3a24STrent Piepho 
rtc_write_sync_lp(struct snvs_rtc_data * data)109cd7f3a24STrent Piepho static int rtc_write_sync_lp(struct snvs_rtc_data *data)
110cd7f3a24STrent Piepho {
111cd7f3a24STrent Piepho 	u32 count1, count2;
112cd7f3a24STrent Piepho 	u32 elapsed;
113cd7f3a24STrent Piepho 	unsigned int timeout = 1000;
114cd7f3a24STrent Piepho 	int ret;
115cd7f3a24STrent Piepho 
116cd7f3a24STrent Piepho 	ret = rtc_read_lp_counter_lsb(data, &count1);
117cd7f3a24STrent Piepho 	if (ret)
118cd7f3a24STrent Piepho 		return ret;
119cd7f3a24STrent Piepho 
120cd7f3a24STrent Piepho 	/* Wait for 3 CKIL cycles, about 61.0-91.5 µs */
121cd7f3a24STrent Piepho 	do {
122cd7f3a24STrent Piepho 		ret = rtc_read_lp_counter_lsb(data, &count2);
123cd7f3a24STrent Piepho 		if (ret)
124cd7f3a24STrent Piepho 			return ret;
125cd7f3a24STrent Piepho 		elapsed = count2 - count1; /* wrap around _is_ handled! */
126cd7f3a24STrent Piepho 	} while (elapsed < 3 && --timeout);
127cd7f3a24STrent Piepho 	if (!timeout) {
128cd7f3a24STrent Piepho 		dev_err(&data->rtc->dev, "Timeout waiting for LPSRT Counter to change\n");
129cd7f3a24STrent Piepho 		return -ETIMEDOUT;
130cd7f3a24STrent Piepho 	}
131cd7f3a24STrent Piepho 	return 0;
132179a502fSShawn Guo }
133179a502fSShawn Guo 
snvs_rtc_enable(struct snvs_rtc_data * data,bool enable)134179a502fSShawn Guo static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
135179a502fSShawn Guo {
136179a502fSShawn Guo 	int timeout = 1000;
137179a502fSShawn Guo 	u32 lpcr;
138179a502fSShawn Guo 
139d482893bSFrank Li 	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV,
140d482893bSFrank Li 			   enable ? SNVS_LPCR_SRTC_ENV : 0);
141179a502fSShawn Guo 
142179a502fSShawn Guo 	while (--timeout) {
143d482893bSFrank Li 		regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr);
144179a502fSShawn Guo 
145179a502fSShawn Guo 		if (enable) {
146179a502fSShawn Guo 			if (lpcr & SNVS_LPCR_SRTC_ENV)
147179a502fSShawn Guo 				break;
148179a502fSShawn Guo 		} else {
149179a502fSShawn Guo 			if (!(lpcr & SNVS_LPCR_SRTC_ENV))
150179a502fSShawn Guo 				break;
151179a502fSShawn Guo 		}
152179a502fSShawn Guo 	}
153179a502fSShawn Guo 
154179a502fSShawn Guo 	if (!timeout)
155179a502fSShawn Guo 		return -ETIMEDOUT;
156179a502fSShawn Guo 
157179a502fSShawn Guo 	return 0;
158179a502fSShawn Guo }
159179a502fSShawn Guo 
snvs_rtc_read_time(struct device * dev,struct rtc_time * tm)160179a502fSShawn Guo static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
161179a502fSShawn Guo {
162179a502fSShawn Guo 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
1634b957bdeSAnson Huang 	unsigned long time;
1644b957bdeSAnson Huang 	int ret;
165179a502fSShawn Guo 
1664b957bdeSAnson Huang 	ret = clk_enable(data->clk);
1674b957bdeSAnson Huang 	if (ret)
1684b957bdeSAnson Huang 		return ret;
1694b957bdeSAnson Huang 
1704b957bdeSAnson Huang 	time = rtc_read_lp_counter(data);
171c59a9fc7SAlexandre Belloni 	rtc_time64_to_tm(time, tm);
172179a502fSShawn Guo 
1734b957bdeSAnson Huang 	clk_disable(data->clk);
1744b957bdeSAnson Huang 
175179a502fSShawn Guo 	return 0;
176179a502fSShawn Guo }
177179a502fSShawn Guo 
snvs_rtc_set_time(struct device * dev,struct rtc_time * tm)178179a502fSShawn Guo static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
179179a502fSShawn Guo {
180179a502fSShawn Guo 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
181c59a9fc7SAlexandre Belloni 	unsigned long time = rtc_tm_to_time64(tm);
1821485991cSBryan O'Donoghue 	int ret;
183179a502fSShawn Guo 
1844b957bdeSAnson Huang 	ret = clk_enable(data->clk);
1854b957bdeSAnson Huang 	if (ret)
1864b957bdeSAnson Huang 		return ret;
1874b957bdeSAnson Huang 
188179a502fSShawn Guo 	/* Disable RTC first */
1891485991cSBryan O'Donoghue 	ret = snvs_rtc_enable(data, false);
1901485991cSBryan O'Donoghue 	if (ret)
1911485991cSBryan O'Donoghue 		return ret;
192179a502fSShawn Guo 
193179a502fSShawn Guo 	/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
194d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
195d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
196179a502fSShawn Guo 
197179a502fSShawn Guo 	/* Enable RTC again */
1981485991cSBryan O'Donoghue 	ret = snvs_rtc_enable(data, true);
199179a502fSShawn Guo 
2004b957bdeSAnson Huang 	clk_disable(data->clk);
2014b957bdeSAnson Huang 
2021485991cSBryan O'Donoghue 	return ret;
203179a502fSShawn Guo }
204179a502fSShawn Guo 
snvs_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)205179a502fSShawn Guo static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
206179a502fSShawn Guo {
207179a502fSShawn Guo 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
208179a502fSShawn Guo 	u32 lptar, lpsr;
2094b957bdeSAnson Huang 	int ret;
2104b957bdeSAnson Huang 
2114b957bdeSAnson Huang 	ret = clk_enable(data->clk);
2124b957bdeSAnson Huang 	if (ret)
2134b957bdeSAnson Huang 		return ret;
214179a502fSShawn Guo 
215d482893bSFrank Li 	regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);
216c59a9fc7SAlexandre Belloni 	rtc_time64_to_tm(lptar, &alrm->time);
217179a502fSShawn Guo 
218d482893bSFrank Li 	regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
219179a502fSShawn Guo 	alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
220179a502fSShawn Guo 
2214b957bdeSAnson Huang 	clk_disable(data->clk);
2224b957bdeSAnson Huang 
223179a502fSShawn Guo 	return 0;
224179a502fSShawn Guo }
225179a502fSShawn Guo 
snvs_rtc_alarm_irq_enable(struct device * dev,unsigned int enable)226179a502fSShawn Guo static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
227179a502fSShawn Guo {
228179a502fSShawn Guo 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
2294b957bdeSAnson Huang 	int ret;
2304b957bdeSAnson Huang 
2314b957bdeSAnson Huang 	ret = clk_enable(data->clk);
2324b957bdeSAnson Huang 	if (ret)
2334b957bdeSAnson Huang 		return ret;
234179a502fSShawn Guo 
235d482893bSFrank Li 	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR,
236d482893bSFrank Li 			   (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN),
237d482893bSFrank Li 			   enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0);
238179a502fSShawn Guo 
2394b957bdeSAnson Huang 	ret = rtc_write_sync_lp(data);
2404b957bdeSAnson Huang 
2414b957bdeSAnson Huang 	clk_disable(data->clk);
2424b957bdeSAnson Huang 
2434b957bdeSAnson Huang 	return ret;
244179a502fSShawn Guo }
245179a502fSShawn Guo 
snvs_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)246179a502fSShawn Guo static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
247179a502fSShawn Guo {
248179a502fSShawn Guo 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
249c59a9fc7SAlexandre Belloni 	unsigned long time = rtc_tm_to_time64(&alrm->time);
250cd7f3a24STrent Piepho 	int ret;
251179a502fSShawn Guo 
2524b957bdeSAnson Huang 	ret = clk_enable(data->clk);
2534b957bdeSAnson Huang 	if (ret)
2544b957bdeSAnson Huang 		return ret;
2554b957bdeSAnson Huang 
256d482893bSFrank Li 	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
257cd7f3a24STrent Piepho 	ret = rtc_write_sync_lp(data);
258cd7f3a24STrent Piepho 	if (ret)
259cd7f3a24STrent Piepho 		return ret;
260d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
261179a502fSShawn Guo 
262179a502fSShawn Guo 	/* Clear alarm interrupt status bit */
263d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA);
264179a502fSShawn Guo 
2654b957bdeSAnson Huang 	clk_disable(data->clk);
2664b957bdeSAnson Huang 
267179a502fSShawn Guo 	return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
268179a502fSShawn Guo }
269179a502fSShawn Guo 
270179a502fSShawn Guo static const struct rtc_class_ops snvs_rtc_ops = {
271179a502fSShawn Guo 	.read_time = snvs_rtc_read_time,
272179a502fSShawn Guo 	.set_time = snvs_rtc_set_time,
273179a502fSShawn Guo 	.read_alarm = snvs_rtc_read_alarm,
274179a502fSShawn Guo 	.set_alarm = snvs_rtc_set_alarm,
275179a502fSShawn Guo 	.alarm_irq_enable = snvs_rtc_alarm_irq_enable,
276179a502fSShawn Guo };
277179a502fSShawn Guo 
snvs_rtc_irq_handler(int irq,void * dev_id)278179a502fSShawn Guo static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
279179a502fSShawn Guo {
280179a502fSShawn Guo 	struct device *dev = dev_id;
281179a502fSShawn Guo 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
282179a502fSShawn Guo 	u32 lpsr;
283179a502fSShawn Guo 	u32 events = 0;
284179a502fSShawn Guo 
285edb190cbSAnson Huang 	clk_enable(data->clk);
286edb190cbSAnson Huang 
287d482893bSFrank Li 	regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
288179a502fSShawn Guo 
289179a502fSShawn Guo 	if (lpsr & SNVS_LPSR_LPTA) {
290179a502fSShawn Guo 		events |= (RTC_AF | RTC_IRQF);
291179a502fSShawn Guo 
292179a502fSShawn Guo 		/* RTC alarm should be one-shot */
293179a502fSShawn Guo 		snvs_rtc_alarm_irq_enable(dev, 0);
294179a502fSShawn Guo 
295179a502fSShawn Guo 		rtc_update_irq(data->rtc, 1, events);
296179a502fSShawn Guo 	}
297179a502fSShawn Guo 
298179a502fSShawn Guo 	/* clear interrupt status */
299d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
300179a502fSShawn Guo 
301edb190cbSAnson Huang 	clk_disable(data->clk);
302edb190cbSAnson Huang 
303179a502fSShawn Guo 	return events ? IRQ_HANDLED : IRQ_NONE;
304179a502fSShawn Guo }
305179a502fSShawn Guo 
306d482893bSFrank Li static const struct regmap_config snvs_rtc_config = {
307d482893bSFrank Li 	.reg_bits = 32,
308d482893bSFrank Li 	.val_bits = 32,
309d482893bSFrank Li 	.reg_stride = 4,
310d482893bSFrank Li };
311d482893bSFrank Li 
snvs_rtc_action(void * data)3127863bd07SAnson Huang static void snvs_rtc_action(void *data)
3137863bd07SAnson Huang {
3147863bd07SAnson Huang 	clk_disable_unprepare(data);
3157863bd07SAnson Huang }
3167863bd07SAnson Huang 
snvs_rtc_probe(struct platform_device * pdev)3175a167f45SGreg Kroah-Hartman static int snvs_rtc_probe(struct platform_device *pdev)
318179a502fSShawn Guo {
319179a502fSShawn Guo 	struct snvs_rtc_data *data;
320179a502fSShawn Guo 	int ret;
321d482893bSFrank Li 	void __iomem *mmio;
322179a502fSShawn Guo 
323179a502fSShawn Guo 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
324179a502fSShawn Guo 	if (!data)
325179a502fSShawn Guo 		return -ENOMEM;
326179a502fSShawn Guo 
3276fd4fe9bSAnson Huang 	data->rtc = devm_rtc_allocate_device(&pdev->dev);
3286fd4fe9bSAnson Huang 	if (IS_ERR(data->rtc))
3296fd4fe9bSAnson Huang 		return PTR_ERR(data->rtc);
3306fd4fe9bSAnson Huang 
331d482893bSFrank Li 	data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
332d482893bSFrank Li 
333d482893bSFrank Li 	if (IS_ERR(data->regmap)) {
334d482893bSFrank Li 		dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
335d482893bSFrank Li 
3360c46b07cSAnson Huang 		mmio = devm_platform_ioremap_resource(pdev, 0);
337d482893bSFrank Li 		if (IS_ERR(mmio))
338d482893bSFrank Li 			return PTR_ERR(mmio);
339d482893bSFrank Li 
340d482893bSFrank Li 		data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);
341d482893bSFrank Li 	} else {
342d482893bSFrank Li 		data->offset = SNVS_LPREGISTER_OFFSET;
343d482893bSFrank Li 		of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
344d482893bSFrank Li 	}
345d482893bSFrank Li 
34675892900SPan Bian 	if (IS_ERR(data->regmap)) {
347d482893bSFrank Li 		dev_err(&pdev->dev, "Can't find snvs syscon\n");
348d482893bSFrank Li 		return -ENODEV;
349d482893bSFrank Li 	}
350179a502fSShawn Guo 
351179a502fSShawn Guo 	data->irq = platform_get_irq(pdev, 0);
352179a502fSShawn Guo 	if (data->irq < 0)
353179a502fSShawn Guo 		return data->irq;
354179a502fSShawn Guo 
3557f899399SSanchayan Maity 	data->clk = devm_clk_get(&pdev->dev, "snvs-rtc");
3567f899399SSanchayan Maity 	if (IS_ERR(data->clk)) {
3577f899399SSanchayan Maity 		data->clk = NULL;
3587f899399SSanchayan Maity 	} else {
3597f899399SSanchayan Maity 		ret = clk_prepare_enable(data->clk);
3607f899399SSanchayan Maity 		if (ret) {
3617f899399SSanchayan Maity 			dev_err(&pdev->dev,
3627f899399SSanchayan Maity 				"Could not prepare or enable the snvs clock\n");
3637f899399SSanchayan Maity 			return ret;
3647f899399SSanchayan Maity 		}
3657f899399SSanchayan Maity 	}
3667f899399SSanchayan Maity 
3677863bd07SAnson Huang 	ret = devm_add_action_or_reset(&pdev->dev, snvs_rtc_action, data->clk);
3687863bd07SAnson Huang 	if (ret)
3697863bd07SAnson Huang 		return ret;
3707863bd07SAnson Huang 
371179a502fSShawn Guo 	platform_set_drvdata(pdev, data);
372179a502fSShawn Guo 
373179a502fSShawn Guo 	/* Initialize glitch detect */
374d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);
375179a502fSShawn Guo 
376179a502fSShawn Guo 	/* Clear interrupt status */
377d482893bSFrank Li 	regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
378179a502fSShawn Guo 
379179a502fSShawn Guo 	/* Enable RTC */
3801485991cSBryan O'Donoghue 	ret = snvs_rtc_enable(data, true);
3811485991cSBryan O'Donoghue 	if (ret) {
3821485991cSBryan O'Donoghue 		dev_err(&pdev->dev, "failed to enable rtc %d\n", ret);
3837863bd07SAnson Huang 		return ret;
3841485991cSBryan O'Donoghue 	}
385179a502fSShawn Guo 
386179a502fSShawn Guo 	device_init_wakeup(&pdev->dev, true);
387e7afddb2SAnson Huang 	ret = dev_pm_set_wake_irq(&pdev->dev, data->irq);
388e7afddb2SAnson Huang 	if (ret)
389e7afddb2SAnson Huang 		dev_err(&pdev->dev, "failed to enable irq wake\n");
390179a502fSShawn Guo 
391179a502fSShawn Guo 	ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
392179a502fSShawn Guo 			       IRQF_SHARED, "rtc alarm", &pdev->dev);
393179a502fSShawn Guo 	if (ret) {
394179a502fSShawn Guo 		dev_err(&pdev->dev, "failed to request irq %d: %d\n",
395179a502fSShawn Guo 			data->irq, ret);
3967863bd07SAnson Huang 		return ret;
397179a502fSShawn Guo 	}
398179a502fSShawn Guo 
3996fd4fe9bSAnson Huang 	data->rtc->ops = &snvs_rtc_ops;
40079610340SAlexandre Belloni 	data->rtc->range_max = U32_MAX;
401179a502fSShawn Guo 
402fdcfd854SBartosz Golaszewski 	return devm_rtc_register_device(data->rtc);
403179a502fSShawn Guo }
404179a502fSShawn Guo 
snvs_rtc_suspend_noirq(struct device * dev)405dacb6a40SAnson Huang static int __maybe_unused snvs_rtc_suspend_noirq(struct device *dev)
406119434f4SStefan Agner {
407119434f4SStefan Agner 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
408119434f4SStefan Agner 
40920af6770SAnson Huang 	clk_disable(data->clk);
4107f899399SSanchayan Maity 
411179a502fSShawn Guo 	return 0;
412179a502fSShawn Guo }
413179a502fSShawn Guo 
snvs_rtc_resume_noirq(struct device * dev)414dacb6a40SAnson Huang static int __maybe_unused snvs_rtc_resume_noirq(struct device *dev)
415119434f4SStefan Agner {
416119434f4SStefan Agner 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
417119434f4SStefan Agner 
418119434f4SStefan Agner 	if (data->clk)
41920af6770SAnson Huang 		return clk_enable(data->clk);
420119434f4SStefan Agner 
421179a502fSShawn Guo 	return 0;
422179a502fSShawn Guo }
423179a502fSShawn Guo 
4247654e9d4SSanchayan Maity static const struct dev_pm_ops snvs_rtc_pm_ops = {
425dacb6a40SAnson Huang 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(snvs_rtc_suspend_noirq, snvs_rtc_resume_noirq)
4267654e9d4SSanchayan Maity };
427179a502fSShawn Guo 
4285a167f45SGreg Kroah-Hartman static const struct of_device_id snvs_dt_ids[] = {
429179a502fSShawn Guo 	{ .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
430179a502fSShawn Guo 	{ /* sentinel */ }
431179a502fSShawn Guo };
432179a502fSShawn Guo MODULE_DEVICE_TABLE(of, snvs_dt_ids);
433179a502fSShawn Guo 
434179a502fSShawn Guo static struct platform_driver snvs_rtc_driver = {
435179a502fSShawn Guo 	.driver = {
436179a502fSShawn Guo 		.name	= "snvs_rtc",
437dacb6a40SAnson Huang 		.pm	= &snvs_rtc_pm_ops,
438c39b3717SSachin Kamat 		.of_match_table = snvs_dt_ids,
439179a502fSShawn Guo 	},
440179a502fSShawn Guo 	.probe		= snvs_rtc_probe,
441179a502fSShawn Guo };
442179a502fSShawn Guo module_platform_driver(snvs_rtc_driver);
443179a502fSShawn Guo 
444179a502fSShawn Guo MODULE_AUTHOR("Freescale Semiconductor, Inc.");
445179a502fSShawn Guo MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
446179a502fSShawn Guo MODULE_LICENSE("GPL");
447