aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorShawn Guo <shawn.guo@linaro.org>2012-10-04 20:13:49 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-05 14:05:01 -0400
commit179a502f8c4605c502f798e18f66ae096fe42402 (patch)
treec0cf9d17799cf41b9d80a51338bfcb6f6c88036d /drivers/rtc
parent7418a1198991beb5bf6656b2ac0bcbde378c5539 (diff)
rtc: snvs: add Freescale rtc-snvs driver
Add an RTC driver for Freescale Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC. Signed-off-by: Shawn Guo <shawn.guo@linaro.org> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Kim Phillips <kim.phillips@freescale.com> Cc: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-snvs.c350
3 files changed, 362 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index d4681c97f7cc..f87bfea6718c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1109,4 +1109,15 @@ config RTC_DRV_MXC
1109 This driver can also be built as a module, if so, the module 1109 This driver can also be built as a module, if so, the module
1110 will be called "rtc-mxc". 1110 will be called "rtc-mxc".
1111 1111
1112config RTC_DRV_SNVS
1113 tristate "Freescale SNVS RTC support"
1114 depends on HAS_IOMEM
1115 depends on OF
1116 help
1117 If you say yes here you get support for the Freescale SNVS
1118 Low Power (LP) RTC module.
1119
1120 This driver can also be built as a module, if so, the module
1121 will be called "rtc-snvs".
1122
1112endif # RTC_CLASS 1123endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 7ecdb2983dde..6056f71033db 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
97obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o 97obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
98obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o 98obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
99obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o 99obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
100obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
100obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o 101obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
101obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o 102obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
102obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o 103obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
new file mode 100644
index 000000000000..3c0da333f465
--- /dev/null
+++ b/drivers/rtc/rtc-snvs.c
@@ -0,0 +1,350 @@
1/*
2 * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
3 *
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
7 *
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
10 */
11
12#include <linux/init.h>
13#include <linux/io.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/of_device.h>
18#include <linux/platform_device.h>
19#include <linux/rtc.h>
20
21/* These register offsets are relative to LP (Low Power) range */
22#define SNVS_LPCR 0x04
23#define SNVS_LPSR 0x18
24#define SNVS_LPSRTCMR 0x1c
25#define SNVS_LPSRTCLR 0x20
26#define SNVS_LPTAR 0x24
27#define SNVS_LPPGDR 0x30
28
29#define SNVS_LPCR_SRTC_ENV (1 << 0)
30#define SNVS_LPCR_LPTA_EN (1 << 1)
31#define SNVS_LPCR_LPWUI_EN (1 << 3)
32#define SNVS_LPSR_LPTA (1 << 0)
33
34#define SNVS_LPPGDR_INIT 0x41736166
35#define CNTR_TO_SECS_SH 15
36
37struct snvs_rtc_data {
38 struct rtc_device *rtc;
39 void __iomem *ioaddr;
40 int irq;
41 spinlock_t lock;
42};
43
44static u32 rtc_read_lp_counter(void __iomem *ioaddr)
45{
46 u64 read1, read2;
47
48 do {
49 read1 = readl(ioaddr + SNVS_LPSRTCMR);
50 read1 <<= 32;
51 read1 |= readl(ioaddr + SNVS_LPSRTCLR);
52
53 read2 = readl(ioaddr + SNVS_LPSRTCMR);
54 read2 <<= 32;
55 read2 |= readl(ioaddr + SNVS_LPSRTCLR);
56 } while (read1 != read2);
57
58 /* Convert 47-bit counter to 32-bit raw second count */
59 return (u32) (read1 >> CNTR_TO_SECS_SH);
60}
61
62static void rtc_write_sync_lp(void __iomem *ioaddr)
63{
64 u32 count1, count2, count3;
65 int i;
66
67 /* Wait for 3 CKIL cycles */
68 for (i = 0; i < 3; i++) {
69 do {
70 count1 = readl(ioaddr + SNVS_LPSRTCLR);
71 count2 = readl(ioaddr + SNVS_LPSRTCLR);
72 } while (count1 != count2);
73
74 /* Now wait until counter value changes */
75 do {
76 do {
77 count2 = readl(ioaddr + SNVS_LPSRTCLR);
78 count3 = readl(ioaddr + SNVS_LPSRTCLR);
79 } while (count2 != count3);
80 } while (count3 == count1);
81 }
82}
83
84static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
85{
86 unsigned long flags;
87 int timeout = 1000;
88 u32 lpcr;
89
90 spin_lock_irqsave(&data->lock, flags);
91
92 lpcr = readl(data->ioaddr + SNVS_LPCR);
93 if (enable)
94 lpcr |= SNVS_LPCR_SRTC_ENV;
95 else
96 lpcr &= ~SNVS_LPCR_SRTC_ENV;
97 writel(lpcr, data->ioaddr + SNVS_LPCR);
98
99 spin_unlock_irqrestore(&data->lock, flags);
100
101 while (--timeout) {
102 lpcr = readl(data->ioaddr + SNVS_LPCR);
103
104 if (enable) {
105 if (lpcr & SNVS_LPCR_SRTC_ENV)
106 break;
107 } else {
108 if (!(lpcr & SNVS_LPCR_SRTC_ENV))
109 break;
110 }
111 }
112
113 if (!timeout)
114 return -ETIMEDOUT;
115
116 return 0;
117}
118
119static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
120{
121 struct snvs_rtc_data *data = dev_get_drvdata(dev);
122 unsigned long time = rtc_read_lp_counter(data->ioaddr);
123
124 rtc_time_to_tm(time, tm);
125
126 return 0;
127}
128
129static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
130{
131 struct snvs_rtc_data *data = dev_get_drvdata(dev);
132 unsigned long time;
133
134 rtc_tm_to_time(tm, &time);
135
136 /* Disable RTC first */
137 snvs_rtc_enable(data, false);
138
139 /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
140 writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
141 writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
142
143 /* Enable RTC again */
144 snvs_rtc_enable(data, true);
145
146 return 0;
147}
148
149static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
150{
151 struct snvs_rtc_data *data = dev_get_drvdata(dev);
152 u32 lptar, lpsr;
153
154 lptar = readl(data->ioaddr + SNVS_LPTAR);
155 rtc_time_to_tm(lptar, &alrm->time);
156
157 lpsr = readl(data->ioaddr + SNVS_LPSR);
158 alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
159
160 return 0;
161}
162
163static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
164{
165 struct snvs_rtc_data *data = dev_get_drvdata(dev);
166 u32 lpcr;
167 unsigned long flags;
168
169 spin_lock_irqsave(&data->lock, flags);
170
171 lpcr = readl(data->ioaddr + SNVS_LPCR);
172 if (enable)
173 lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
174 else
175 lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
176 writel(lpcr, data->ioaddr + SNVS_LPCR);
177
178 spin_unlock_irqrestore(&data->lock, flags);
179
180 rtc_write_sync_lp(data->ioaddr);
181
182 return 0;
183}
184
185static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
186{
187 struct snvs_rtc_data *data = dev_get_drvdata(dev);
188 struct rtc_time *alrm_tm = &alrm->time;
189 unsigned long time;
190 unsigned long flags;
191 u32 lpcr;
192
193 rtc_tm_to_time(alrm_tm, &time);
194
195 spin_lock_irqsave(&data->lock, flags);
196
197 /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
198 lpcr = readl(data->ioaddr + SNVS_LPCR);
199 lpcr &= ~SNVS_LPCR_LPTA_EN;
200 writel(lpcr, data->ioaddr + SNVS_LPCR);
201
202 spin_unlock_irqrestore(&data->lock, flags);
203
204 writel(time, data->ioaddr + SNVS_LPTAR);
205
206 /* Clear alarm interrupt status bit */
207 writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
208
209 return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
210}
211
212static const struct rtc_class_ops snvs_rtc_ops = {
213 .read_time = snvs_rtc_read_time,
214 .set_time = snvs_rtc_set_time,
215 .read_alarm = snvs_rtc_read_alarm,
216 .set_alarm = snvs_rtc_set_alarm,
217 .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
218};
219
220static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
221{
222 struct device *dev = dev_id;
223 struct snvs_rtc_data *data = dev_get_drvdata(dev);
224 u32 lpsr;
225 u32 events = 0;
226
227 lpsr = readl(data->ioaddr + SNVS_LPSR);
228
229 if (lpsr & SNVS_LPSR_LPTA) {
230 events |= (RTC_AF | RTC_IRQF);
231
232 /* RTC alarm should be one-shot */
233 snvs_rtc_alarm_irq_enable(dev, 0);
234
235 rtc_update_irq(data->rtc, 1, events);
236 }
237
238 /* clear interrupt status */
239 writel(lpsr, data->ioaddr + SNVS_LPSR);
240
241 return events ? IRQ_HANDLED : IRQ_NONE;
242}
243
244static int __devinit snvs_rtc_probe(struct platform_device *pdev)
245{
246 struct snvs_rtc_data *data;
247 struct resource *res;
248 int ret;
249
250 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
251 if (!data)
252 return -ENOMEM;
253
254 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
255 data->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
256 if (!data->ioaddr)
257 return -EADDRNOTAVAIL;
258
259 data->irq = platform_get_irq(pdev, 0);
260 if (data->irq < 0)
261 return data->irq;
262
263 platform_set_drvdata(pdev, data);
264
265 spin_lock_init(&data->lock);
266
267 /* Initialize glitch detect */
268 writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
269
270 /* Clear interrupt status */
271 writel(0xffffffff, data->ioaddr + SNVS_LPSR);
272
273 /* Enable RTC */
274 snvs_rtc_enable(data, true);
275
276 device_init_wakeup(&pdev->dev, true);
277
278 ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
279 IRQF_SHARED, "rtc alarm", &pdev->dev);
280 if (ret) {
281 dev_err(&pdev->dev, "failed to request irq %d: %d\n",
282 data->irq, ret);
283 return ret;
284 }
285
286 data->rtc = rtc_device_register(pdev->name, &pdev->dev,
287 &snvs_rtc_ops, THIS_MODULE);
288 if (IS_ERR(data->rtc)) {
289 ret = PTR_ERR(data->rtc);
290 dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
291 return ret;
292 }
293
294 return 0;
295}
296
297static int __devexit snvs_rtc_remove(struct platform_device *pdev)
298{
299 struct snvs_rtc_data *data = platform_get_drvdata(pdev);
300
301 rtc_device_unregister(data->rtc);
302
303 return 0;
304}
305
306#ifdef CONFIG_PM_SLEEP
307static int snvs_rtc_suspend(struct device *dev)
308{
309 struct snvs_rtc_data *data = dev_get_drvdata(dev);
310
311 if (device_may_wakeup(dev))
312 enable_irq_wake(data->irq);
313
314 return 0;
315}
316
317static int snvs_rtc_resume(struct device *dev)
318{
319 struct snvs_rtc_data *data = dev_get_drvdata(dev);
320
321 if (device_may_wakeup(dev))
322 disable_irq_wake(data->irq);
323
324 return 0;
325}
326#endif
327
328static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume);
329
330static const struct of_device_id __devinitconst snvs_dt_ids[] = {
331 { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
332 { /* sentinel */ }
333};
334MODULE_DEVICE_TABLE(of, snvs_dt_ids);
335
336static struct platform_driver snvs_rtc_driver = {
337 .driver = {
338 .name = "snvs_rtc",
339 .owner = THIS_MODULE,
340 .pm = &snvs_rtc_pm_ops,
341 .of_match_table = snvs_dt_ids,
342 },
343 .probe = snvs_rtc_probe,
344 .remove = __devexit_p(snvs_rtc_remove),
345};
346module_platform_driver(snvs_rtc_driver);
347
348MODULE_AUTHOR("Freescale Semiconductor, Inc.");
349MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
350MODULE_LICENSE("GPL");