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