diff options
Diffstat (limited to 'drivers/rtc/rtc-at91rm9200.c')
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 135 |
1 files changed, 112 insertions, 23 deletions
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 0eab77b22340..741892632ae0 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c | |||
@@ -25,13 +25,13 @@ | |||
25 | #include <linux/rtc.h> | 25 | #include <linux/rtc.h> |
26 | #include <linux/bcd.h> | 26 | #include <linux/bcd.h> |
27 | #include <linux/interrupt.h> | 27 | #include <linux/interrupt.h> |
28 | #include <linux/spinlock.h> | ||
28 | #include <linux/ioctl.h> | 29 | #include <linux/ioctl.h> |
29 | #include <linux/completion.h> | 30 | #include <linux/completion.h> |
30 | #include <linux/io.h> | 31 | #include <linux/io.h> |
31 | #include <linux/of.h> | 32 | #include <linux/of.h> |
32 | #include <linux/of_device.h> | 33 | #include <linux/of_device.h> |
33 | 34 | #include <linux/uaccess.h> | |
34 | #include <asm/uaccess.h> | ||
35 | 35 | ||
36 | #include "rtc-at91rm9200.h" | 36 | #include "rtc-at91rm9200.h" |
37 | 37 | ||
@@ -42,10 +42,65 @@ | |||
42 | 42 | ||
43 | #define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */ | 43 | #define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */ |
44 | 44 | ||
45 | struct at91_rtc_config { | ||
46 | bool use_shadow_imr; | ||
47 | }; | ||
48 | |||
49 | static const struct at91_rtc_config *at91_rtc_config; | ||
45 | static DECLARE_COMPLETION(at91_rtc_updated); | 50 | static DECLARE_COMPLETION(at91_rtc_updated); |
46 | static unsigned int at91_alarm_year = AT91_RTC_EPOCH; | 51 | static unsigned int at91_alarm_year = AT91_RTC_EPOCH; |
47 | static void __iomem *at91_rtc_regs; | 52 | static void __iomem *at91_rtc_regs; |
48 | static int irq; | 53 | static int irq; |
54 | static DEFINE_SPINLOCK(at91_rtc_lock); | ||
55 | static u32 at91_rtc_shadow_imr; | ||
56 | |||
57 | static void at91_rtc_write_ier(u32 mask) | ||
58 | { | ||
59 | unsigned long flags; | ||
60 | |||
61 | spin_lock_irqsave(&at91_rtc_lock, flags); | ||
62 | at91_rtc_shadow_imr |= mask; | ||
63 | at91_rtc_write(AT91_RTC_IER, mask); | ||
64 | spin_unlock_irqrestore(&at91_rtc_lock, flags); | ||
65 | } | ||
66 | |||
67 | static void at91_rtc_write_idr(u32 mask) | ||
68 | { | ||
69 | unsigned long flags; | ||
70 | |||
71 | spin_lock_irqsave(&at91_rtc_lock, flags); | ||
72 | at91_rtc_write(AT91_RTC_IDR, mask); | ||
73 | /* | ||
74 | * Register read back (of any RTC-register) needed to make sure | ||
75 | * IDR-register write has reached the peripheral before updating | ||
76 | * shadow mask. | ||
77 | * | ||
78 | * Note that there is still a possibility that the mask is updated | ||
79 | * before interrupts have actually been disabled in hardware. The only | ||
80 | * way to be certain would be to poll the IMR-register, which is is | ||
81 | * the very register we are trying to emulate. The register read back | ||
82 | * is a reasonable heuristic. | ||
83 | */ | ||
84 | at91_rtc_read(AT91_RTC_SR); | ||
85 | at91_rtc_shadow_imr &= ~mask; | ||
86 | spin_unlock_irqrestore(&at91_rtc_lock, flags); | ||
87 | } | ||
88 | |||
89 | static u32 at91_rtc_read_imr(void) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | u32 mask; | ||
93 | |||
94 | if (at91_rtc_config->use_shadow_imr) { | ||
95 | spin_lock_irqsave(&at91_rtc_lock, flags); | ||
96 | mask = at91_rtc_shadow_imr; | ||
97 | spin_unlock_irqrestore(&at91_rtc_lock, flags); | ||
98 | } else { | ||
99 | mask = at91_rtc_read(AT91_RTC_IMR); | ||
100 | } | ||
101 | |||
102 | return mask; | ||
103 | } | ||
49 | 104 | ||
50 | /* | 105 | /* |
51 | * Decode time/date into rtc_time structure | 106 | * Decode time/date into rtc_time structure |
@@ -110,9 +165,9 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) | |||
110 | cr = at91_rtc_read(AT91_RTC_CR); | 165 | cr = at91_rtc_read(AT91_RTC_CR); |
111 | at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); | 166 | at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); |
112 | 167 | ||
113 | at91_rtc_write(AT91_RTC_IER, AT91_RTC_ACKUPD); | 168 | at91_rtc_write_ier(AT91_RTC_ACKUPD); |
114 | wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */ | 169 | wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */ |
115 | at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD); | 170 | at91_rtc_write_idr(AT91_RTC_ACKUPD); |
116 | 171 | ||
117 | at91_rtc_write(AT91_RTC_TIMR, | 172 | at91_rtc_write(AT91_RTC_TIMR, |
118 | bin2bcd(tm->tm_sec) << 0 | 173 | bin2bcd(tm->tm_sec) << 0 |
@@ -144,7 +199,7 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
144 | tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); | 199 | tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); |
145 | tm->tm_year = at91_alarm_year - 1900; | 200 | tm->tm_year = at91_alarm_year - 1900; |
146 | 201 | ||
147 | alrm->enabled = (at91_rtc_read(AT91_RTC_IMR) & AT91_RTC_ALARM) | 202 | alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM) |
148 | ? 1 : 0; | 203 | ? 1 : 0; |
149 | 204 | ||
150 | dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, | 205 | dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, |
@@ -169,7 +224,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
169 | tm.tm_min = alrm->time.tm_min; | 224 | tm.tm_min = alrm->time.tm_min; |
170 | tm.tm_sec = alrm->time.tm_sec; | 225 | tm.tm_sec = alrm->time.tm_sec; |
171 | 226 | ||
172 | at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); | 227 | at91_rtc_write_idr(AT91_RTC_ALARM); |
173 | at91_rtc_write(AT91_RTC_TIMALR, | 228 | at91_rtc_write(AT91_RTC_TIMALR, |
174 | bin2bcd(tm.tm_sec) << 0 | 229 | bin2bcd(tm.tm_sec) << 0 |
175 | | bin2bcd(tm.tm_min) << 8 | 230 | | bin2bcd(tm.tm_min) << 8 |
@@ -182,7 +237,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
182 | 237 | ||
183 | if (alrm->enabled) { | 238 | if (alrm->enabled) { |
184 | at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); | 239 | at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); |
185 | at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); | 240 | at91_rtc_write_ier(AT91_RTC_ALARM); |
186 | } | 241 | } |
187 | 242 | ||
188 | dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, | 243 | dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, |
@@ -198,9 +253,9 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | |||
198 | 253 | ||
199 | if (enabled) { | 254 | if (enabled) { |
200 | at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); | 255 | at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); |
201 | at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); | 256 | at91_rtc_write_ier(AT91_RTC_ALARM); |
202 | } else | 257 | } else |
203 | at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); | 258 | at91_rtc_write_idr(AT91_RTC_ALARM); |
204 | 259 | ||
205 | return 0; | 260 | return 0; |
206 | } | 261 | } |
@@ -209,7 +264,7 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | |||
209 | */ | 264 | */ |
210 | static int at91_rtc_proc(struct device *dev, struct seq_file *seq) | 265 | static int at91_rtc_proc(struct device *dev, struct seq_file *seq) |
211 | { | 266 | { |
212 | unsigned long imr = at91_rtc_read(AT91_RTC_IMR); | 267 | unsigned long imr = at91_rtc_read_imr(); |
213 | 268 | ||
214 | seq_printf(seq, "update_IRQ\t: %s\n", | 269 | seq_printf(seq, "update_IRQ\t: %s\n", |
215 | (imr & AT91_RTC_ACKUPD) ? "yes" : "no"); | 270 | (imr & AT91_RTC_ACKUPD) ? "yes" : "no"); |
@@ -229,7 +284,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) | |||
229 | unsigned int rtsr; | 284 | unsigned int rtsr; |
230 | unsigned long events = 0; | 285 | unsigned long events = 0; |
231 | 286 | ||
232 | rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read(AT91_RTC_IMR); | 287 | rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr(); |
233 | if (rtsr) { /* this interrupt is shared! Is it ours? */ | 288 | if (rtsr) { /* this interrupt is shared! Is it ours? */ |
234 | if (rtsr & AT91_RTC_ALARM) | 289 | if (rtsr & AT91_RTC_ALARM) |
235 | events |= (RTC_AF | RTC_IRQF); | 290 | events |= (RTC_AF | RTC_IRQF); |
@@ -250,6 +305,43 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) | |||
250 | return IRQ_NONE; /* not handled */ | 305 | return IRQ_NONE; /* not handled */ |
251 | } | 306 | } |
252 | 307 | ||
308 | static const struct at91_rtc_config at91rm9200_config = { | ||
309 | }; | ||
310 | |||
311 | static const struct at91_rtc_config at91sam9x5_config = { | ||
312 | .use_shadow_imr = true, | ||
313 | }; | ||
314 | |||
315 | #ifdef CONFIG_OF | ||
316 | static const struct of_device_id at91_rtc_dt_ids[] = { | ||
317 | { | ||
318 | .compatible = "atmel,at91rm9200-rtc", | ||
319 | .data = &at91rm9200_config, | ||
320 | }, { | ||
321 | .compatible = "atmel,at91sam9x5-rtc", | ||
322 | .data = &at91sam9x5_config, | ||
323 | }, { | ||
324 | /* sentinel */ | ||
325 | } | ||
326 | }; | ||
327 | MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); | ||
328 | #endif | ||
329 | |||
330 | static const struct at91_rtc_config * | ||
331 | at91_rtc_get_config(struct platform_device *pdev) | ||
332 | { | ||
333 | const struct of_device_id *match; | ||
334 | |||
335 | if (pdev->dev.of_node) { | ||
336 | match = of_match_node(at91_rtc_dt_ids, pdev->dev.of_node); | ||
337 | if (!match) | ||
338 | return NULL; | ||
339 | return (const struct at91_rtc_config *)match->data; | ||
340 | } | ||
341 | |||
342 | return &at91rm9200_config; | ||
343 | } | ||
344 | |||
253 | static const struct rtc_class_ops at91_rtc_ops = { | 345 | static const struct rtc_class_ops at91_rtc_ops = { |
254 | .read_time = at91_rtc_readtime, | 346 | .read_time = at91_rtc_readtime, |
255 | .set_time = at91_rtc_settime, | 347 | .set_time = at91_rtc_settime, |
@@ -268,6 +360,10 @@ static int __init at91_rtc_probe(struct platform_device *pdev) | |||
268 | struct resource *regs; | 360 | struct resource *regs; |
269 | int ret = 0; | 361 | int ret = 0; |
270 | 362 | ||
363 | at91_rtc_config = at91_rtc_get_config(pdev); | ||
364 | if (!at91_rtc_config) | ||
365 | return -ENODEV; | ||
366 | |||
271 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 367 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
272 | if (!regs) { | 368 | if (!regs) { |
273 | dev_err(&pdev->dev, "no mmio resource defined\n"); | 369 | dev_err(&pdev->dev, "no mmio resource defined\n"); |
@@ -290,7 +386,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) | |||
290 | at91_rtc_write(AT91_RTC_MR, 0); /* 24 hour mode */ | 386 | at91_rtc_write(AT91_RTC_MR, 0); /* 24 hour mode */ |
291 | 387 | ||
292 | /* Disable all interrupts */ | 388 | /* Disable all interrupts */ |
293 | at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | | 389 | at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM | |
294 | AT91_RTC_SECEV | AT91_RTC_TIMEV | | 390 | AT91_RTC_SECEV | AT91_RTC_TIMEV | |
295 | AT91_RTC_CALEV); | 391 | AT91_RTC_CALEV); |
296 | 392 | ||
@@ -335,14 +431,13 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) | |||
335 | struct rtc_device *rtc = platform_get_drvdata(pdev); | 431 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
336 | 432 | ||
337 | /* Disable all interrupts */ | 433 | /* Disable all interrupts */ |
338 | at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | | 434 | at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM | |
339 | AT91_RTC_SECEV | AT91_RTC_TIMEV | | 435 | AT91_RTC_SECEV | AT91_RTC_TIMEV | |
340 | AT91_RTC_CALEV); | 436 | AT91_RTC_CALEV); |
341 | free_irq(irq, pdev); | 437 | free_irq(irq, pdev); |
342 | 438 | ||
343 | rtc_device_unregister(rtc); | 439 | rtc_device_unregister(rtc); |
344 | iounmap(at91_rtc_regs); | 440 | iounmap(at91_rtc_regs); |
345 | platform_set_drvdata(pdev, NULL); | ||
346 | 441 | ||
347 | return 0; | 442 | return 0; |
348 | } | 443 | } |
@@ -358,13 +453,13 @@ static int at91_rtc_suspend(struct device *dev) | |||
358 | /* this IRQ is shared with DBGU and other hardware which isn't | 453 | /* this IRQ is shared with DBGU and other hardware which isn't |
359 | * necessarily doing PM like we are... | 454 | * necessarily doing PM like we are... |
360 | */ | 455 | */ |
361 | at91_rtc_imr = at91_rtc_read(AT91_RTC_IMR) | 456 | at91_rtc_imr = at91_rtc_read_imr() |
362 | & (AT91_RTC_ALARM|AT91_RTC_SECEV); | 457 | & (AT91_RTC_ALARM|AT91_RTC_SECEV); |
363 | if (at91_rtc_imr) { | 458 | if (at91_rtc_imr) { |
364 | if (device_may_wakeup(dev)) | 459 | if (device_may_wakeup(dev)) |
365 | enable_irq_wake(irq); | 460 | enable_irq_wake(irq); |
366 | else | 461 | else |
367 | at91_rtc_write(AT91_RTC_IDR, at91_rtc_imr); | 462 | at91_rtc_write_idr(at91_rtc_imr); |
368 | } | 463 | } |
369 | return 0; | 464 | return 0; |
370 | } | 465 | } |
@@ -375,7 +470,7 @@ static int at91_rtc_resume(struct device *dev) | |||
375 | if (device_may_wakeup(dev)) | 470 | if (device_may_wakeup(dev)) |
376 | disable_irq_wake(irq); | 471 | disable_irq_wake(irq); |
377 | else | 472 | else |
378 | at91_rtc_write(AT91_RTC_IER, at91_rtc_imr); | 473 | at91_rtc_write_ier(at91_rtc_imr); |
379 | } | 474 | } |
380 | return 0; | 475 | return 0; |
381 | } | 476 | } |
@@ -383,12 +478,6 @@ static int at91_rtc_resume(struct device *dev) | |||
383 | 478 | ||
384 | static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); | 479 | static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); |
385 | 480 | ||
386 | static const struct of_device_id at91_rtc_dt_ids[] = { | ||
387 | { .compatible = "atmel,at91rm9200-rtc" }, | ||
388 | { /* sentinel */ } | ||
389 | }; | ||
390 | MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); | ||
391 | |||
392 | static struct platform_driver at91_rtc_driver = { | 481 | static struct platform_driver at91_rtc_driver = { |
393 | .remove = __exit_p(at91_rtc_remove), | 482 | .remove = __exit_p(at91_rtc_remove), |
394 | .driver = { | 483 | .driver = { |