summaryrefslogtreecommitdiffstats
path: root/arch/m68k
diff options
context:
space:
mode:
authorFinn Thain <fthain@telegraphics.com.au>2018-11-30 19:53:10 -0500
committerGeert Uytterhoeven <geert@linux-m68k.org>2019-03-25 05:22:25 -0400
commit19999a8b8782d7f887353753c3c7cb5fca2f3784 (patch)
treebd52382ef3503e75a2875918185073aada40335b /arch/m68k
parent3d744eee38f11cafb0fc332c3081ab1cd07a89f7 (diff)
m68k: mvme16x: Handle timer counter overflow
Reading the timer counter races with timer overflow (and the corresponding interrupt). This is resolved by reading the overflow register and taking this value into account. The interrupt handler must clear the overflow register when it eventually executes. Suggested-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Finn Thain <fthain@telegraphics.com.au> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Diffstat (limited to 'arch/m68k')
-rw-r--r--arch/m68k/mvme16x/config.c45
1 files changed, 32 insertions, 13 deletions
diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c
index 2c109ee2a1a5..9bc2da69f80c 100644
--- a/arch/m68k/mvme16x/config.c
+++ b/arch/m68k/mvme16x/config.c
@@ -115,11 +115,11 @@ static void __init mvme16x_init_IRQ (void)
115 m68k_setup_user_interrupt(VEC_USER, 192); 115 m68k_setup_user_interrupt(VEC_USER, 192);
116} 116}
117 117
118#define pcc2chip ((volatile u_char *)0xfff42000) 118#define PCC2CHIP (0xfff42000)
119#define PccSCCMICR 0x1d 119#define PCCSCCMICR (PCC2CHIP + 0x1d)
120#define PccSCCTICR 0x1e 120#define PCCSCCTICR (PCC2CHIP + 0x1e)
121#define PccSCCRICR 0x1f 121#define PCCSCCRICR (PCC2CHIP + 0x1f)
122#define PccTPIACKR 0x25 122#define PCCTPIACKR (PCC2CHIP + 0x25)
123 123
124#ifdef CONFIG_EARLY_PRINTK 124#ifdef CONFIG_EARLY_PRINTK
125 125
@@ -227,10 +227,10 @@ void mvme16x_cons_write(struct console *co, const char *str, unsigned count)
227 base_addr[CyIER] = CyTxMpty; 227 base_addr[CyIER] = CyTxMpty;
228 228
229 while (1) { 229 while (1) {
230 if (pcc2chip[PccSCCTICR] & 0x20) 230 if (in_8(PCCSCCTICR) & 0x20)
231 { 231 {
232 /* We have a Tx int. Acknowledge it */ 232 /* We have a Tx int. Acknowledge it */
233 sink = pcc2chip[PccTPIACKR]; 233 sink = in_8(PCCTPIACKR);
234 if ((base_addr[CyLICR] >> 2) == port) { 234 if ((base_addr[CyLICR] >> 2) == port) {
235 if (i == count) { 235 if (i == count) {
236 /* Last char of string is now output */ 236 /* Last char of string is now output */
@@ -359,13 +359,26 @@ static u32 clk_total;
359#define PCC_TIMER_CLOCK_FREQ 1000000 359#define PCC_TIMER_CLOCK_FREQ 1000000
360#define PCC_TIMER_CYCLES (PCC_TIMER_CLOCK_FREQ / HZ) 360#define PCC_TIMER_CYCLES (PCC_TIMER_CLOCK_FREQ / HZ)
361 361
362#define PCCTCMP1 (PCC2CHIP + 0x04)
363#define PCCTCNT1 (PCC2CHIP + 0x08)
364#define PCCTOVR1 (PCC2CHIP + 0x17)
365#define PCCTIC1 (PCC2CHIP + 0x1b)
366
367#define PCCTOVR1_TIC_EN 0x01
368#define PCCTOVR1_COC_EN 0x02
369#define PCCTOVR1_OVR_CLR 0x04
370
371#define PCCTIC1_INT_CLR 0x08
372#define PCCTIC1_INT_EN 0x10
373
362static irqreturn_t mvme16x_timer_int (int irq, void *dev_id) 374static irqreturn_t mvme16x_timer_int (int irq, void *dev_id)
363{ 375{
364 irq_handler_t timer_routine = dev_id; 376 irq_handler_t timer_routine = dev_id;
365 unsigned long flags; 377 unsigned long flags;
366 378
367 local_irq_save(flags); 379 local_irq_save(flags);
368 *(volatile unsigned char *)0xfff4201b |= 8; 380 out_8(PCCTIC1, in_8(PCCTIC1) | PCCTIC1_INT_CLR);
381 out_8(PCCTOVR1, PCCTOVR1_OVR_CLR);
369 clk_total += PCC_TIMER_CYCLES; 382 clk_total += PCC_TIMER_CYCLES;
370 timer_routine(0, NULL); 383 timer_routine(0, NULL);
371 local_irq_restore(flags); 384 local_irq_restore(flags);
@@ -379,10 +392,10 @@ void mvme16x_sched_init (irq_handler_t timer_routine)
379 int irq; 392 int irq;
380 393
381 /* Using PCCchip2 or MC2 chip tick timer 1 */ 394 /* Using PCCchip2 or MC2 chip tick timer 1 */
382 *(volatile unsigned long *)0xfff42008 = 0; 395 out_be32(PCCTCNT1, 0);
383 *(volatile unsigned long *)0xfff42004 = PCC_TIMER_CYCLES; 396 out_be32(PCCTCMP1, PCC_TIMER_CYCLES);
384 *(volatile unsigned char *)0xfff42017 |= 3; 397 out_8(PCCTOVR1, in_8(PCCTOVR1) | PCCTOVR1_TIC_EN | PCCTOVR1_COC_EN);
385 *(volatile unsigned char *)0xfff4201b = 0x16; 398 out_8(PCCTIC1, PCCTIC1_INT_EN | 6);
386 if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, IRQF_TIMER, "timer", 399 if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, IRQF_TIMER, "timer",
387 timer_routine)) 400 timer_routine))
388 panic ("Couldn't register timer int"); 401 panic ("Couldn't register timer int");
@@ -401,10 +414,16 @@ void mvme16x_sched_init (irq_handler_t timer_routine)
401static u64 mvme16x_read_clk(struct clocksource *cs) 414static u64 mvme16x_read_clk(struct clocksource *cs)
402{ 415{
403 unsigned long flags; 416 unsigned long flags;
417 u8 overflow, tmp;
404 u32 ticks; 418 u32 ticks;
405 419
406 local_irq_save(flags); 420 local_irq_save(flags);
407 ticks = *(volatile u32 *)0xfff42008; 421 tmp = in_8(PCCTOVR1) >> 4;
422 ticks = in_be32(PCCTCNT1);
423 overflow = in_8(PCCTOVR1) >> 4;
424 if (overflow != tmp)
425 ticks = in_be32(PCCTCNT1);
426 ticks += overflow * PCC_TIMER_CYCLES;
408 ticks += clk_total; 427 ticks += clk_total;
409 local_irq_restore(flags); 428 local_irq_restore(flags);
410 429