aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-at91rm9200.c
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2013-06-12 17:04:56 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-06-12 19:29:46 -0400
commite9f08bbe3f97829975d2b59091ef557101c83f61 (patch)
treeb31705b2e71ba74c55a29d447386c41de50d9a91 /drivers/rtc/rtc-at91rm9200.c
parente304fcd075a0e97d0e538dd4408b95406b505f85 (diff)
rtc-at91rm9200: add shadow interrupt mask
Add shadow interrupt-mask register which can be used on SoCs where the actual hardware register is broken. Note that some care needs to be taken to make sure the shadow mask corresponds to the actual hardware state. The added overhead is not an issue for the non-broken SoCs due to the relatively infrequent interrupt-mask updates. We do, however, only use the shadow mask value as a fall-back when it actually needed as there is still a theoretical possibility that the mask is incorrect (see the code for details). Signed-off-by: Johan Hovold <jhovold@gmail.com> Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Douglas Gilbert <dgilbert@interlog.com> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Ludovic Desroches <ludovic.desroches@atmel.com> Cc: Robert Nelson <Robert.Nelson@digikey.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-at91rm9200.c')
-rw-r--r--drivers/rtc/rtc-at91rm9200.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index 9592d08f3b11..811a102092d4 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -25,6 +25,7 @@
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>
@@ -43,6 +44,7 @@
43#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */ 44#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */
44 45
45struct at91_rtc_config { 46struct at91_rtc_config {
47 bool use_shadow_imr;
46}; 48};
47 49
48static const struct at91_rtc_config *at91_rtc_config; 50static const struct at91_rtc_config *at91_rtc_config;
@@ -50,20 +52,55 @@ static DECLARE_COMPLETION(at91_rtc_updated);
50static unsigned int at91_alarm_year = AT91_RTC_EPOCH; 52static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
51static void __iomem *at91_rtc_regs; 53static void __iomem *at91_rtc_regs;
52static int irq; 54static int irq;
55static DEFINE_SPINLOCK(at91_rtc_lock);
56static u32 at91_rtc_shadow_imr;
53 57
54static void at91_rtc_write_ier(u32 mask) 58static void at91_rtc_write_ier(u32 mask)
55{ 59{
60 unsigned long flags;
61
62 spin_lock_irqsave(&at91_rtc_lock, flags);
63 at91_rtc_shadow_imr |= mask;
56 at91_rtc_write(AT91_RTC_IER, mask); 64 at91_rtc_write(AT91_RTC_IER, mask);
65 spin_unlock_irqrestore(&at91_rtc_lock, flags);
57} 66}
58 67
59static void at91_rtc_write_idr(u32 mask) 68static void at91_rtc_write_idr(u32 mask)
60{ 69{
70 unsigned long flags;
71
72 spin_lock_irqsave(&at91_rtc_lock, flags);
61 at91_rtc_write(AT91_RTC_IDR, mask); 73 at91_rtc_write(AT91_RTC_IDR, mask);
74 /*
75 * Register read back (of any RTC-register) needed to make sure
76 * IDR-register write has reached the peripheral before updating
77 * shadow mask.
78 *
79 * Note that there is still a possibility that the mask is updated
80 * before interrupts have actually been disabled in hardware. The only
81 * way to be certain would be to poll the IMR-register, which is is
82 * the very register we are trying to emulate. The register read back
83 * is a reasonable heuristic.
84 */
85 at91_rtc_read(AT91_RTC_SR);
86 at91_rtc_shadow_imr &= ~mask;
87 spin_unlock_irqrestore(&at91_rtc_lock, flags);
62} 88}
63 89
64static u32 at91_rtc_read_imr(void) 90static u32 at91_rtc_read_imr(void)
65{ 91{
66 return at91_rtc_read(AT91_RTC_IMR); 92 unsigned long flags;
93 u32 mask;
94
95 if (at91_rtc_config->use_shadow_imr) {
96 spin_lock_irqsave(&at91_rtc_lock, flags);
97 mask = at91_rtc_shadow_imr;
98 spin_unlock_irqrestore(&at91_rtc_lock, flags);
99 } else {
100 mask = at91_rtc_read(AT91_RTC_IMR);
101 }
102
103 return mask;
67} 104}
68 105
69/* 106/*