Skip to content

Commit 3c1efe2

Browse files
h2phongKelsey Skunberg
authored andcommitted
clocksource/drivers/sh_cmt: Fix wrong setting if don't request IRQ for clock source channel
BugLink: https://bugs.launchpad.net/bugs/1946024 [ Upstream commit be83c3b ] 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: bfa76bb ("clocksource: sh_cmt: Request IRQ for clock event device only") Fixes: 37e7742 ("clocksource/drivers/sh_cmt: Fix clocksource width for 32-bit machines") Signed-off-by: Phong Hoang <[email protected]> Signed-off-by: Niklas Söderlund <[email protected]> Signed-off-by: Daniel Lezcano <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Kamal Mostafa <[email protected]> Signed-off-by: Kelsey Skunberg <[email protected]>
1 parent 8fd0478 commit 3c1efe2

File tree

1 file changed

+18
-12
lines changed

1 file changed

+18
-12
lines changed

drivers/clocksource/sh_cmt.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ static int sh_cmt_start(struct sh_cmt_channel *ch, unsigned long flag)
568568
ch->flags |= flag;
569569

570570
/* setup timeout if no clockevent */
571-
if ((flag == FLAG_CLOCKSOURCE) && (!(ch->flags & FLAG_CLOCKEVENT)))
571+
if (ch->cmt->num_channels == 1 &&
572+
flag == FLAG_CLOCKSOURCE && (!(ch->flags & FLAG_CLOCKEVENT)))
572573
__sh_cmt_set_next(ch, ch->max_match_value);
573574
out:
574575
raw_spin_unlock_irqrestore(&ch->lock, flags);
@@ -604,20 +605,25 @@ static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs)
604605
static u64 sh_cmt_clocksource_read(struct clocksource *cs)
605606
{
606607
struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
607-
unsigned long flags;
608608
u32 has_wrapped;
609-
u64 value;
610-
u32 raw;
611609

612-
raw_spin_lock_irqsave(&ch->lock, flags);
613-
value = ch->total_cycles;
614-
raw = sh_cmt_get_counter(ch, &has_wrapped);
610+
if (ch->cmt->num_channels == 1) {
611+
unsigned long flags;
612+
u64 value;
613+
u32 raw;
615614

616-
if (unlikely(has_wrapped))
617-
raw += ch->match_value + 1;
618-
raw_spin_unlock_irqrestore(&ch->lock, flags);
615+
raw_spin_lock_irqsave(&ch->lock, flags);
616+
value = ch->total_cycles;
617+
raw = sh_cmt_get_counter(ch, &has_wrapped);
618+
619+
if (unlikely(has_wrapped))
620+
raw += ch->match_value + 1;
621+
raw_spin_unlock_irqrestore(&ch->lock, flags);
622+
623+
return value + raw;
624+
}
619625

620-
return value + raw;
626+
return sh_cmt_get_counter(ch, &has_wrapped);
621627
}
622628

623629
static int sh_cmt_clocksource_enable(struct clocksource *cs)
@@ -680,7 +686,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch,
680686
cs->disable = sh_cmt_clocksource_disable;
681687
cs->suspend = sh_cmt_clocksource_suspend;
682688
cs->resume = sh_cmt_clocksource_resume;
683-
cs->mask = CLOCKSOURCE_MASK(sizeof(u64) * 8);
689+
cs->mask = CLOCKSOURCE_MASK(ch->cmt->info->width);
684690
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
685691

686692
dev_info(&ch->cmt->pdev->dev, "ch%u: used as clock source\n",

0 commit comments

Comments
 (0)