xref: /openbmc/linux/drivers/rtc/rtc-st-lpc.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b5b2bdfcSLee Jones /*
3b5b2bdfcSLee Jones  * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
4b5b2bdfcSLee Jones  *
5b5b2bdfcSLee Jones  * Copyright (C) 2014 STMicroelectronics Limited
6b5b2bdfcSLee Jones  *
7b5b2bdfcSLee Jones  * Author: David Paris <david.paris@st.com> for STMicroelectronics
8b5b2bdfcSLee Jones  *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
9b5b2bdfcSLee Jones  *
10b5b2bdfcSLee Jones  * Based on the original driver written by Stuart Menefy.
11b5b2bdfcSLee Jones  */
12b5b2bdfcSLee Jones 
13b5b2bdfcSLee Jones #include <linux/clk.h>
14b5b2bdfcSLee Jones #include <linux/delay.h>
15b5b2bdfcSLee Jones #include <linux/init.h>
16b5b2bdfcSLee Jones #include <linux/io.h>
17b5b2bdfcSLee Jones #include <linux/irq.h>
18b5b2bdfcSLee Jones #include <linux/kernel.h>
19b5b2bdfcSLee Jones #include <linux/module.h>
20b5b2bdfcSLee Jones #include <linux/of.h>
21b5b2bdfcSLee Jones #include <linux/of_irq.h>
22b5b2bdfcSLee Jones #include <linux/platform_device.h>
23b5b2bdfcSLee Jones #include <linux/rtc.h>
24b5b2bdfcSLee Jones 
25b5b2bdfcSLee Jones #include <dt-bindings/mfd/st-lpc.h>
26b5b2bdfcSLee Jones 
27b5b2bdfcSLee Jones /* Low Power Timer */
28b5b2bdfcSLee Jones #define LPC_LPT_LSB_OFF		0x400
29b5b2bdfcSLee Jones #define LPC_LPT_MSB_OFF		0x404
30b5b2bdfcSLee Jones #define LPC_LPT_START_OFF	0x408
31b5b2bdfcSLee Jones 
32b5b2bdfcSLee Jones /* Low Power Alarm */
33b5b2bdfcSLee Jones #define LPC_LPA_LSB_OFF		0x410
34b5b2bdfcSLee Jones #define LPC_LPA_MSB_OFF		0x414
35b5b2bdfcSLee Jones #define LPC_LPA_START_OFF	0x418
36b5b2bdfcSLee Jones 
37b5b2bdfcSLee Jones /* LPC as WDT */
38b5b2bdfcSLee Jones #define LPC_WDT_OFF		0x510
39b5b2bdfcSLee Jones #define LPC_WDT_FLAG_OFF	0x514
40b5b2bdfcSLee Jones 
41b5b2bdfcSLee Jones struct st_rtc {
42b5b2bdfcSLee Jones 	struct rtc_device *rtc_dev;
43b5b2bdfcSLee Jones 	struct rtc_wkalrm alarm;
44b5b2bdfcSLee Jones 	struct clk *clk;
45b5b2bdfcSLee Jones 	unsigned long clkrate;
46b5b2bdfcSLee Jones 	void __iomem *ioaddr;
47b5b2bdfcSLee Jones 	bool irq_enabled:1;
48b5b2bdfcSLee Jones 	spinlock_t lock;
49b5b2bdfcSLee Jones 	short irq;
50b5b2bdfcSLee Jones };
51b5b2bdfcSLee Jones 
st_rtc_set_hw_alarm(struct st_rtc * rtc,unsigned long msb,unsigned long lsb)52b5b2bdfcSLee Jones static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
53b5b2bdfcSLee Jones 				unsigned long msb, unsigned long  lsb)
54b5b2bdfcSLee Jones {
55b5b2bdfcSLee Jones 	unsigned long flags;
56b5b2bdfcSLee Jones 
57b5b2bdfcSLee Jones 	spin_lock_irqsave(&rtc->lock, flags);
58b5b2bdfcSLee Jones 
59b5b2bdfcSLee Jones 	writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
60b5b2bdfcSLee Jones 
61b5b2bdfcSLee Jones 	writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
62b5b2bdfcSLee Jones 	writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
63b5b2bdfcSLee Jones 	writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
64b5b2bdfcSLee Jones 
65b5b2bdfcSLee Jones 	writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
66b5b2bdfcSLee Jones 
67b5b2bdfcSLee Jones 	spin_unlock_irqrestore(&rtc->lock, flags);
68b5b2bdfcSLee Jones }
69b5b2bdfcSLee Jones 
st_rtc_handler(int this_irq,void * data)70b5b2bdfcSLee Jones static irqreturn_t st_rtc_handler(int this_irq, void *data)
71b5b2bdfcSLee Jones {
72b5b2bdfcSLee Jones 	struct st_rtc *rtc = (struct st_rtc *)data;
73b5b2bdfcSLee Jones 
74b5b2bdfcSLee Jones 	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
75b5b2bdfcSLee Jones 
76b5b2bdfcSLee Jones 	return IRQ_HANDLED;
77b5b2bdfcSLee Jones }
78b5b2bdfcSLee Jones 
st_rtc_read_time(struct device * dev,struct rtc_time * tm)79b5b2bdfcSLee Jones static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
80b5b2bdfcSLee Jones {
81b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
82b5b2bdfcSLee Jones 	unsigned long lpt_lsb, lpt_msb;
83b5b2bdfcSLee Jones 	unsigned long long lpt;
84b5b2bdfcSLee Jones 	unsigned long flags;
85b5b2bdfcSLee Jones 
86b5b2bdfcSLee Jones 	spin_lock_irqsave(&rtc->lock, flags);
87b5b2bdfcSLee Jones 
88b5b2bdfcSLee Jones 	do {
89b5b2bdfcSLee Jones 		lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF);
90b5b2bdfcSLee Jones 		lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF);
91b5b2bdfcSLee Jones 	} while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
92b5b2bdfcSLee Jones 
93b5b2bdfcSLee Jones 	spin_unlock_irqrestore(&rtc->lock, flags);
94b5b2bdfcSLee Jones 
95b5b2bdfcSLee Jones 	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
96b5b2bdfcSLee Jones 	do_div(lpt, rtc->clkrate);
97f4b82d39SBenjamin Gaignard 	rtc_time64_to_tm(lpt, tm);
98b5b2bdfcSLee Jones 
99b5b2bdfcSLee Jones 	return 0;
100b5b2bdfcSLee Jones }
101b5b2bdfcSLee Jones 
st_rtc_set_time(struct device * dev,struct rtc_time * tm)102b5b2bdfcSLee Jones static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
103b5b2bdfcSLee Jones {
104b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
105f4b82d39SBenjamin Gaignard 	unsigned long long lpt, secs;
106f4b82d39SBenjamin Gaignard 	unsigned long flags;
107b5b2bdfcSLee Jones 
108f4b82d39SBenjamin Gaignard 	secs = rtc_tm_to_time64(tm);
109b5b2bdfcSLee Jones 
110b5b2bdfcSLee Jones 	lpt = (unsigned long long)secs * rtc->clkrate;
111b5b2bdfcSLee Jones 
112b5b2bdfcSLee Jones 	spin_lock_irqsave(&rtc->lock, flags);
113b5b2bdfcSLee Jones 
114b5b2bdfcSLee Jones 	writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
115b5b2bdfcSLee Jones 	writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
116b5b2bdfcSLee Jones 	writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF);
117b5b2bdfcSLee Jones 
118b5b2bdfcSLee Jones 	spin_unlock_irqrestore(&rtc->lock, flags);
119b5b2bdfcSLee Jones 
120b5b2bdfcSLee Jones 	return 0;
121b5b2bdfcSLee Jones }
122b5b2bdfcSLee Jones 
st_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * wkalrm)123b5b2bdfcSLee Jones static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
124b5b2bdfcSLee Jones {
125b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
126b5b2bdfcSLee Jones 	unsigned long flags;
127b5b2bdfcSLee Jones 
128b5b2bdfcSLee Jones 	spin_lock_irqsave(&rtc->lock, flags);
129b5b2bdfcSLee Jones 
130b5b2bdfcSLee Jones 	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
131b5b2bdfcSLee Jones 
132b5b2bdfcSLee Jones 	spin_unlock_irqrestore(&rtc->lock, flags);
133b5b2bdfcSLee Jones 
134b5b2bdfcSLee Jones 	return 0;
135b5b2bdfcSLee Jones }
136b5b2bdfcSLee Jones 
st_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)137b5b2bdfcSLee Jones static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
138b5b2bdfcSLee Jones {
139b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
140b5b2bdfcSLee Jones 
141b5b2bdfcSLee Jones 	if (enabled && !rtc->irq_enabled) {
142b5b2bdfcSLee Jones 		enable_irq(rtc->irq);
143b5b2bdfcSLee Jones 		rtc->irq_enabled = true;
144b5b2bdfcSLee Jones 	} else if (!enabled && rtc->irq_enabled) {
145b5b2bdfcSLee Jones 		disable_irq(rtc->irq);
146b5b2bdfcSLee Jones 		rtc->irq_enabled = false;
147b5b2bdfcSLee Jones 	}
148b5b2bdfcSLee Jones 
149b5b2bdfcSLee Jones 	return 0;
150b5b2bdfcSLee Jones }
151b5b2bdfcSLee Jones 
st_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * t)152b5b2bdfcSLee Jones static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
153b5b2bdfcSLee Jones {
154b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
155b5b2bdfcSLee Jones 	struct rtc_time now;
156f4b82d39SBenjamin Gaignard 	unsigned long long now_secs;
157f4b82d39SBenjamin Gaignard 	unsigned long long alarm_secs;
158b5b2bdfcSLee Jones 	unsigned long long lpa;
159b5b2bdfcSLee Jones 
160b5b2bdfcSLee Jones 	st_rtc_read_time(dev, &now);
161f4b82d39SBenjamin Gaignard 	now_secs = rtc_tm_to_time64(&now);
162f4b82d39SBenjamin Gaignard 	alarm_secs = rtc_tm_to_time64(&t->time);
163b5b2bdfcSLee Jones 
164b5b2bdfcSLee Jones 	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
165b5b2bdfcSLee Jones 
166b5b2bdfcSLee Jones 	/* Now many secs to fire */
167b5b2bdfcSLee Jones 	alarm_secs -= now_secs;
168b5b2bdfcSLee Jones 	lpa = (unsigned long long)alarm_secs * rtc->clkrate;
169b5b2bdfcSLee Jones 
170b5b2bdfcSLee Jones 	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
171b5b2bdfcSLee Jones 	st_rtc_alarm_irq_enable(dev, t->enabled);
172b5b2bdfcSLee Jones 
173b5b2bdfcSLee Jones 	return 0;
174b5b2bdfcSLee Jones }
175b5b2bdfcSLee Jones 
176d0a3b650SRikard Falkeborn static const struct rtc_class_ops st_rtc_ops = {
177b5b2bdfcSLee Jones 	.read_time		= st_rtc_read_time,
178b5b2bdfcSLee Jones 	.set_time		= st_rtc_set_time,
179b5b2bdfcSLee Jones 	.read_alarm		= st_rtc_read_alarm,
180b5b2bdfcSLee Jones 	.set_alarm		= st_rtc_set_alarm,
181b5b2bdfcSLee Jones 	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
182b5b2bdfcSLee Jones };
183b5b2bdfcSLee Jones 
st_rtc_probe(struct platform_device * pdev)184b5b2bdfcSLee Jones static int st_rtc_probe(struct platform_device *pdev)
185b5b2bdfcSLee Jones {
186b5b2bdfcSLee Jones 	struct device_node *np = pdev->dev.of_node;
187b5b2bdfcSLee Jones 	struct st_rtc *rtc;
188b5b2bdfcSLee Jones 	uint32_t mode;
189b5b2bdfcSLee Jones 	int ret = 0;
190b5b2bdfcSLee Jones 
191b5b2bdfcSLee Jones 	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
192b5b2bdfcSLee Jones 	if (ret) {
193b5b2bdfcSLee Jones 		dev_err(&pdev->dev, "An LPC mode must be provided\n");
194b5b2bdfcSLee Jones 		return -EINVAL;
195b5b2bdfcSLee Jones 	}
196b5b2bdfcSLee Jones 
197742e4097SLee Jones 	/* LPC can either run as a Clocksource or in RTC or WDT mode */
198b5b2bdfcSLee Jones 	if (mode != ST_LPC_MODE_RTC)
199b5b2bdfcSLee Jones 		return -ENODEV;
200b5b2bdfcSLee Jones 
201b5b2bdfcSLee Jones 	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
202b5b2bdfcSLee Jones 	if (!rtc)
203b5b2bdfcSLee Jones 		return -ENOMEM;
204b5b2bdfcSLee Jones 
205d482510fSAlexandre Belloni 	rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
206d482510fSAlexandre Belloni 	if (IS_ERR(rtc->rtc_dev))
207d482510fSAlexandre Belloni 		return PTR_ERR(rtc->rtc_dev);
208d482510fSAlexandre Belloni 
209b5b2bdfcSLee Jones 	spin_lock_init(&rtc->lock);
210b5b2bdfcSLee Jones 
21109ef18bcSYueHaibing 	rtc->ioaddr = devm_platform_ioremap_resource(pdev, 0);
212b5b2bdfcSLee Jones 	if (IS_ERR(rtc->ioaddr))
213b5b2bdfcSLee Jones 		return PTR_ERR(rtc->ioaddr);
214b5b2bdfcSLee Jones 
215b5b2bdfcSLee Jones 	rtc->irq = irq_of_parse_and_map(np, 0);
216b5b2bdfcSLee Jones 	if (!rtc->irq) {
217b5b2bdfcSLee Jones 		dev_err(&pdev->dev, "IRQ missing or invalid\n");
218b5b2bdfcSLee Jones 		return -EINVAL;
219b5b2bdfcSLee Jones 	}
220b5b2bdfcSLee Jones 
221*96fd3dfbSJinjie Ruan 	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler,
222*96fd3dfbSJinjie Ruan 			       IRQF_NO_AUTOEN, pdev->name, rtc);
223b5b2bdfcSLee Jones 	if (ret) {
224b5b2bdfcSLee Jones 		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
225b5b2bdfcSLee Jones 		return ret;
226b5b2bdfcSLee Jones 	}
227b5b2bdfcSLee Jones 
228b5b2bdfcSLee Jones 	enable_irq_wake(rtc->irq);
229b5b2bdfcSLee Jones 
2300e6f36ccSChristophe JAILLET 	rtc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
2310e6f36ccSChristophe JAILLET 	if (IS_ERR(rtc->clk))
2320e6f36ccSChristophe JAILLET 		return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk),
2330e6f36ccSChristophe JAILLET 				     "Unable to request clock\n");
234b5b2bdfcSLee Jones 
235b5b2bdfcSLee Jones 	rtc->clkrate = clk_get_rate(rtc->clk);
236b5b2bdfcSLee Jones 	if (!rtc->clkrate) {
237b5b2bdfcSLee Jones 		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
238b5b2bdfcSLee Jones 		return -EINVAL;
239b5b2bdfcSLee Jones 	}
240b5b2bdfcSLee Jones 
241b5b2bdfcSLee Jones 	device_set_wakeup_capable(&pdev->dev, 1);
242b5b2bdfcSLee Jones 
243b5b2bdfcSLee Jones 	platform_set_drvdata(pdev, rtc);
244b5b2bdfcSLee Jones 
245d482510fSAlexandre Belloni 	rtc->rtc_dev->ops = &st_rtc_ops;
246b64c984aSAlexandre Belloni 	rtc->rtc_dev->range_max = U64_MAX;
247b64c984aSAlexandre Belloni 	do_div(rtc->rtc_dev->range_max, rtc->clkrate);
248d482510fSAlexandre Belloni 
249fdcfd854SBartosz Golaszewski 	ret = devm_rtc_register_device(rtc->rtc_dev);
2500e6f36ccSChristophe JAILLET 	if (ret)
251d482510fSAlexandre Belloni 		return ret;
252b5b2bdfcSLee Jones 
253b5b2bdfcSLee Jones 	return 0;
254b5b2bdfcSLee Jones }
255b5b2bdfcSLee Jones 
256b5b2bdfcSLee Jones #ifdef CONFIG_PM_SLEEP
st_rtc_suspend(struct device * dev)257b5b2bdfcSLee Jones static int st_rtc_suspend(struct device *dev)
258b5b2bdfcSLee Jones {
259b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
260b5b2bdfcSLee Jones 
261b5b2bdfcSLee Jones 	if (device_may_wakeup(dev))
262b5b2bdfcSLee Jones 		return 0;
263b5b2bdfcSLee Jones 
264b5b2bdfcSLee Jones 	writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
265b5b2bdfcSLee Jones 	writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF);
266b5b2bdfcSLee Jones 	writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
267b5b2bdfcSLee Jones 
268b5b2bdfcSLee Jones 	return 0;
269b5b2bdfcSLee Jones }
270b5b2bdfcSLee Jones 
st_rtc_resume(struct device * dev)271b5b2bdfcSLee Jones static int st_rtc_resume(struct device *dev)
272b5b2bdfcSLee Jones {
273b5b2bdfcSLee Jones 	struct st_rtc *rtc = dev_get_drvdata(dev);
274b5b2bdfcSLee Jones 
275b5b2bdfcSLee Jones 	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
276b5b2bdfcSLee Jones 
277b5b2bdfcSLee Jones 	/*
278b5b2bdfcSLee Jones 	 * clean 'rtc->alarm' to allow a new
279b5b2bdfcSLee Jones 	 * .set_alarm to the upper RTC layer
280b5b2bdfcSLee Jones 	 */
281b5b2bdfcSLee Jones 	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
282b5b2bdfcSLee Jones 
283b5b2bdfcSLee Jones 	writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
284b5b2bdfcSLee Jones 	writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
285b5b2bdfcSLee Jones 	writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
286b5b2bdfcSLee Jones 	writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
287b5b2bdfcSLee Jones 	writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
288b5b2bdfcSLee Jones 
289b5b2bdfcSLee Jones 	return 0;
290b5b2bdfcSLee Jones }
291b5b2bdfcSLee Jones #endif
292b5b2bdfcSLee Jones 
293b5b2bdfcSLee Jones static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
294b5b2bdfcSLee Jones 
295b5b2bdfcSLee Jones static const struct of_device_id st_rtc_match[] = {
296b5b2bdfcSLee Jones 	{ .compatible = "st,stih407-lpc" },
297b5b2bdfcSLee Jones 	{}
298b5b2bdfcSLee Jones };
299b5b2bdfcSLee Jones MODULE_DEVICE_TABLE(of, st_rtc_match);
300b5b2bdfcSLee Jones 
301b5b2bdfcSLee Jones static struct platform_driver st_rtc_platform_driver = {
302b5b2bdfcSLee Jones 	.driver = {
303b5b2bdfcSLee Jones 		.name = "st-lpc-rtc",
304b5b2bdfcSLee Jones 		.pm = &st_rtc_pm_ops,
305b5b2bdfcSLee Jones 		.of_match_table = st_rtc_match,
306b5b2bdfcSLee Jones 	},
307b5b2bdfcSLee Jones 	.probe = st_rtc_probe,
308b5b2bdfcSLee Jones };
309b5b2bdfcSLee Jones 
310b5b2bdfcSLee Jones module_platform_driver(st_rtc_platform_driver);
311b5b2bdfcSLee Jones 
312b5b2bdfcSLee Jones MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
313b5b2bdfcSLee Jones MODULE_AUTHOR("David Paris <david.paris@st.com>");
314b5b2bdfcSLee Jones MODULE_LICENSE("GPL");
315