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

clocksource/drivers/sh_cmt: Fix wrong setting if don't request IRQ for clock source channel

If CMT instance has at least two channels, one channel will be used
as a clock source and another one used as a clock event device.
In that case, IRQ is not requested for clock source channel so
sh_cmt_clock_event_program_verify() might work incorrectly.
Besides, when a channel is only used for clock source, don't need to
re-set the next match_value since it should be maximum timeout as
it still is.

On the other hand, due to no IRQ, total_cycles is not counted up
when reaches compare match time (timer counter resets to zero),
so sh_cmt_clocksource_read() returns unexpected value.
Therefore, use 64-bit clocksoure's mask for 32-bit or 16-bit variants
will also lead to wrong delta calculation. Hence, this mask should
correspond to timer counter width, and above function just returns
the raw value of timer counter register.

Fixes: bfa76bb12f23 ("clocksource: sh_cmt: Request IRQ for clock event device only")
Fixes: 37e7742c55ba ("clocksource/drivers/sh_cmt: Fix clocksource width for 32-bit machines")
Signed-off-by: Phong Hoang <phong.hoang.wz@renesas.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20210422123443.73334-1-niklas.soderlund+renesas@ragnatech.se

authored by

Phong Hoang and committed by
Daniel Lezcano
be83c3b6 faa186ad

+18 -12
+18 -12
drivers/clocksource/sh_cmt.c
··· 579 579 ch->flags |= flag; 580 580 581 581 /* setup timeout if no clockevent */ 582 - if ((flag == FLAG_CLOCKSOURCE) && (!(ch->flags & FLAG_CLOCKEVENT))) 582 + if (ch->cmt->num_channels == 1 && 583 + flag == FLAG_CLOCKSOURCE && (!(ch->flags & FLAG_CLOCKEVENT))) 583 584 __sh_cmt_set_next(ch, ch->max_match_value); 584 585 out: 585 586 raw_spin_unlock_irqrestore(&ch->lock, flags); ··· 622 621 static u64 sh_cmt_clocksource_read(struct clocksource *cs) 623 622 { 624 623 struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); 625 - unsigned long flags; 626 624 u32 has_wrapped; 627 - u64 value; 628 - u32 raw; 629 625 630 - raw_spin_lock_irqsave(&ch->lock, flags); 631 - value = ch->total_cycles; 632 - raw = sh_cmt_get_counter(ch, &has_wrapped); 626 + if (ch->cmt->num_channels == 1) { 627 + unsigned long flags; 628 + u64 value; 629 + u32 raw; 633 630 634 - if (unlikely(has_wrapped)) 635 - raw += ch->match_value + 1; 636 - raw_spin_unlock_irqrestore(&ch->lock, flags); 631 + raw_spin_lock_irqsave(&ch->lock, flags); 632 + value = ch->total_cycles; 633 + raw = sh_cmt_get_counter(ch, &has_wrapped); 637 634 638 - return value + raw; 635 + if (unlikely(has_wrapped)) 636 + raw += ch->match_value + 1; 637 + raw_spin_unlock_irqrestore(&ch->lock, flags); 638 + 639 + return value + raw; 640 + } 641 + 642 + return sh_cmt_get_counter(ch, &has_wrapped); 639 643 } 640 644 641 645 static int sh_cmt_clocksource_enable(struct clocksource *cs) ··· 703 697 cs->disable = sh_cmt_clocksource_disable; 704 698 cs->suspend = sh_cmt_clocksource_suspend; 705 699 cs->resume = sh_cmt_clocksource_resume; 706 - cs->mask = CLOCKSOURCE_MASK(sizeof(u64) * 8); 700 + cs->mask = CLOCKSOURCE_MASK(ch->cmt->info->width); 707 701 cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; 708 702 709 703 dev_info(&ch->cmt->pdev->dev, "ch%u: used as clock source\n",