Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers/rtc/rtc-at91rm9200.c: use a variable for storing IMR

On some revisions of AT91 SoCs, the RTC IMR register is not working.
Instead of elaborating a workaround for that specific SoC or IP version,
we simply use a software variable to store the Interrupt Mask Register
and modify it for each enabling/disabling of an interrupt. The overhead
of this is negligible anyway.

The interrupt mask register (IMR) for the RTC is broken on the AT91SAM9x5
sub-family of SoCs (good overview of the members here:
http://www.eewiki.net/display/linuxonarm/AT91SAM9x5 ). The "user visible
effect" is the RTC doesn't work.

That sub-family is less than two years old and only has devicetree (DT)
support and came online circa lk 3.7 . The dust is yet to settle on the
DT stuff at least for AT91 SoCs (translation: lots of stuff is still
broken, so much that it is hard to know where to start).

The fix in the patch is pretty simple: just shadow the silicon IMR
register with a variable in the driver. Some older SoCs (pre-DT) use the
the rtc-at91rm9200 driver (e.g. obviously the AT91RM9200) and they should
not be impacted by the change. There shouldn't be a large volume of
interrupts associated with a RTC.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Reported-by: Douglas Gilbert <dgilbert@interlog.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Ludovic Desroches <ludovic.desroches@atmel.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Nicolas Ferre and committed by
Linus Torvalds
0ef1594c e66b0587

+31 -20
+31 -19
drivers/rtc/rtc-at91rm9200.c
··· 44 44 static unsigned int at91_alarm_year = AT91_RTC_EPOCH; 45 45 static void __iomem *at91_rtc_regs; 46 46 static int irq; 47 + static u32 at91_rtc_imr; 47 48 48 49 /* 49 50 * Decode time/date into rtc_time structure ··· 109 108 cr = at91_rtc_read(AT91_RTC_CR); 110 109 at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); 111 110 111 + at91_rtc_imr |= AT91_RTC_ACKUPD; 112 112 at91_rtc_write(AT91_RTC_IER, AT91_RTC_ACKUPD); 113 113 wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */ 114 114 at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD); 115 + at91_rtc_imr &= ~AT91_RTC_ACKUPD; 115 116 116 117 at91_rtc_write(AT91_RTC_TIMR, 117 118 bin2bcd(tm->tm_sec) << 0 ··· 145 142 tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); 146 143 tm->tm_year = at91_alarm_year - 1900; 147 144 148 - alrm->enabled = (at91_rtc_read(AT91_RTC_IMR) & AT91_RTC_ALARM) 145 + alrm->enabled = (at91_rtc_imr & AT91_RTC_ALARM) 149 146 ? 1 : 0; 150 147 151 148 dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, ··· 171 168 tm.tm_sec = alrm->time.tm_sec; 172 169 173 170 at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); 171 + at91_rtc_imr &= ~AT91_RTC_ALARM; 174 172 at91_rtc_write(AT91_RTC_TIMALR, 175 173 bin2bcd(tm.tm_sec) << 0 176 174 | bin2bcd(tm.tm_min) << 8 ··· 184 180 185 181 if (alrm->enabled) { 186 182 at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); 183 + at91_rtc_imr |= AT91_RTC_ALARM; 187 184 at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); 188 185 } 189 186 ··· 201 196 202 197 if (enabled) { 203 198 at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); 199 + at91_rtc_imr |= AT91_RTC_ALARM; 204 200 at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); 205 - } else 201 + } else { 206 202 at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); 203 + at91_rtc_imr &= ~AT91_RTC_ALARM; 204 + } 207 205 208 206 return 0; 209 207 } ··· 215 207 */ 216 208 static int at91_rtc_proc(struct device *dev, struct seq_file *seq) 217 209 { 218 - unsigned long imr = at91_rtc_read(AT91_RTC_IMR); 219 - 220 210 seq_printf(seq, "update_IRQ\t: %s\n", 221 - (imr & AT91_RTC_ACKUPD) ? "yes" : "no"); 211 + (at91_rtc_imr & AT91_RTC_ACKUPD) ? "yes" : "no"); 222 212 seq_printf(seq, "periodic_IRQ\t: %s\n", 223 - (imr & AT91_RTC_SECEV) ? "yes" : "no"); 213 + (at91_rtc_imr & AT91_RTC_SECEV) ? "yes" : "no"); 224 214 225 215 return 0; 226 216 } ··· 233 227 unsigned int rtsr; 234 228 unsigned long events = 0; 235 229 236 - rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read(AT91_RTC_IMR); 230 + rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_imr; 237 231 if (rtsr) { /* this interrupt is shared! Is it ours? */ 238 232 if (rtsr & AT91_RTC_ALARM) 239 233 events |= (RTC_AF | RTC_IRQF); ··· 297 291 at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | 298 292 AT91_RTC_SECEV | AT91_RTC_TIMEV | 299 293 AT91_RTC_CALEV); 294 + at91_rtc_imr = 0; 300 295 301 296 ret = request_irq(irq, at91_rtc_interrupt, 302 297 IRQF_SHARED, ··· 336 329 at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | 337 330 AT91_RTC_SECEV | AT91_RTC_TIMEV | 338 331 AT91_RTC_CALEV); 332 + at91_rtc_imr = 0; 339 333 free_irq(irq, pdev); 340 334 341 335 rtc_device_unregister(rtc); ··· 349 341 350 342 /* AT91RM9200 RTC Power management control */ 351 343 352 - static u32 at91_rtc_imr; 344 + static u32 at91_rtc_bkpimr; 345 + 353 346 354 347 static int at91_rtc_suspend(struct device *dev) 355 348 { 356 349 /* this IRQ is shared with DBGU and other hardware which isn't 357 350 * necessarily doing PM like we are... 358 351 */ 359 - at91_rtc_imr = at91_rtc_read(AT91_RTC_IMR) 360 - & (AT91_RTC_ALARM|AT91_RTC_SECEV); 361 - if (at91_rtc_imr) { 362 - if (device_may_wakeup(dev)) 352 + at91_rtc_bkpimr = at91_rtc_imr & (AT91_RTC_ALARM|AT91_RTC_SECEV); 353 + if (at91_rtc_bkpimr) { 354 + if (device_may_wakeup(dev)) { 363 355 enable_irq_wake(irq); 364 - else 365 - at91_rtc_write(AT91_RTC_IDR, at91_rtc_imr); 366 - } 356 + } else { 357 + at91_rtc_write(AT91_RTC_IDR, at91_rtc_bkpimr); 358 + at91_rtc_imr &= ~at91_rtc_bkpimr; 359 + } 360 + } 367 361 return 0; 368 362 } 369 363 370 364 static int at91_rtc_resume(struct device *dev) 371 365 { 372 - if (at91_rtc_imr) { 373 - if (device_may_wakeup(dev)) 366 + if (at91_rtc_bkpimr) { 367 + if (device_may_wakeup(dev)) { 374 368 disable_irq_wake(irq); 375 - else 376 - at91_rtc_write(AT91_RTC_IER, at91_rtc_imr); 369 + } else { 370 + at91_rtc_imr |= at91_rtc_bkpimr; 371 + at91_rtc_write(AT91_RTC_IER, at91_rtc_bkpimr); 372 + } 377 373 } 378 374 return 0; 379 375 }
-1
drivers/rtc/rtc-at91rm9200.h
··· 64 64 #define AT91_RTC_SCCR 0x1c /* Status Clear Command Register */ 65 65 #define AT91_RTC_IER 0x20 /* Interrupt Enable Register */ 66 66 #define AT91_RTC_IDR 0x24 /* Interrupt Disable Register */ 67 - #define AT91_RTC_IMR 0x28 /* Interrupt Mask Register */ 68 67 69 68 #define AT91_RTC_VER 0x2c /* Valid Entry Register */ 70 69 #define AT91_RTC_NVTIM (1 << 0) /* Non valid Time */