Commit 22627c6f authored by Sergei Shtylyov's avatar Sergei Shtylyov Committed by Daniel Lezcano

clocksource/drivers/sh_cmt: Fixup for 64-bit machines

When trying to use CMT for clockevents on R-Car gen3 SoCs, I noticed
that 'max_delta_ns' for the broadcast timer (CMT) was shown as 1000 in
/proc/timer_list. It turned out that when calculating it, the driver did
1 << 32 (causing what I think was undefined behavior) resulting in a zero
delta, later clamped to 1000 by cev_delta2ns(). The root cause turned out
to be that the driver abused *unsigned long* for the CMT register values
(which are 16/32-bit), so that the calculation of 'ch->max_match_value'
in sh_cmt_setup_channel() used the wrong branch. Using more proper 'u32'
instead fixed 'max_delta_ns' and even fixed the switching an active
clocksource to CMT (which caused the system to turn non-interactive
before).
Signed-off-by: default avatarSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
parent 0b9294fe
...@@ -70,18 +70,17 @@ struct sh_cmt_info { ...@@ -70,18 +70,17 @@ struct sh_cmt_info {
unsigned int channels_mask; unsigned int channels_mask;
unsigned long width; /* 16 or 32 bit version of hardware block */ unsigned long width; /* 16 or 32 bit version of hardware block */
unsigned long overflow_bit; u32 overflow_bit;
unsigned long clear_bits; u32 clear_bits;
/* callbacks for CMSTR and CMCSR access */ /* callbacks for CMSTR and CMCSR access */
unsigned long (*read_control)(void __iomem *base, unsigned long offs); u32 (*read_control)(void __iomem *base, unsigned long offs);
void (*write_control)(void __iomem *base, unsigned long offs, void (*write_control)(void __iomem *base, unsigned long offs,
unsigned long value); u32 value);
/* callbacks for CMCNT and CMCOR access */ /* callbacks for CMCNT and CMCOR access */
unsigned long (*read_count)(void __iomem *base, unsigned long offs); u32 (*read_count)(void __iomem *base, unsigned long offs);
void (*write_count)(void __iomem *base, unsigned long offs, void (*write_count)(void __iomem *base, unsigned long offs, u32 value);
unsigned long value);
}; };
struct sh_cmt_channel { struct sh_cmt_channel {
...@@ -95,9 +94,9 @@ struct sh_cmt_channel { ...@@ -95,9 +94,9 @@ struct sh_cmt_channel {
unsigned int timer_bit; unsigned int timer_bit;
unsigned long flags; unsigned long flags;
unsigned long match_value; u32 match_value;
unsigned long next_match_value; u32 next_match_value;
unsigned long max_match_value; u32 max_match_value;
raw_spinlock_t lock; raw_spinlock_t lock;
struct clock_event_device ced; struct clock_event_device ced;
struct clocksource cs; struct clocksource cs;
...@@ -152,24 +151,22 @@ struct sh_cmt_device { ...@@ -152,24 +151,22 @@ struct sh_cmt_device {
#define SH_CMT32_CMCSR_CKS_RCLK1 (7 << 0) #define SH_CMT32_CMCSR_CKS_RCLK1 (7 << 0)
#define SH_CMT32_CMCSR_CKS_MASK (7 << 0) #define SH_CMT32_CMCSR_CKS_MASK (7 << 0)
static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) static u32 sh_cmt_read16(void __iomem *base, unsigned long offs)
{ {
return ioread16(base + (offs << 1)); return ioread16(base + (offs << 1));
} }
static unsigned long sh_cmt_read32(void __iomem *base, unsigned long offs) static u32 sh_cmt_read32(void __iomem *base, unsigned long offs)
{ {
return ioread32(base + (offs << 2)); return ioread32(base + (offs << 2));
} }
static void sh_cmt_write16(void __iomem *base, unsigned long offs, static void sh_cmt_write16(void __iomem *base, unsigned long offs, u32 value)
unsigned long value)
{ {
iowrite16(value, base + (offs << 1)); iowrite16(value, base + (offs << 1));
} }
static void sh_cmt_write32(void __iomem *base, unsigned long offs, static void sh_cmt_write32(void __iomem *base, unsigned long offs, u32 value)
unsigned long value)
{ {
iowrite32(value, base + (offs << 2)); iowrite32(value, base + (offs << 2));
} }
...@@ -234,7 +231,7 @@ static const struct sh_cmt_info sh_cmt_info[] = { ...@@ -234,7 +231,7 @@ static const struct sh_cmt_info sh_cmt_info[] = {
#define CMCNT 1 /* channel register */ #define CMCNT 1 /* channel register */
#define CMCOR 2 /* channel register */ #define CMCOR 2 /* channel register */
static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) static inline u32 sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
{ {
if (ch->iostart) if (ch->iostart)
return ch->cmt->info->read_control(ch->iostart, 0); return ch->cmt->info->read_control(ch->iostart, 0);
...@@ -242,8 +239,7 @@ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) ...@@ -242,8 +239,7 @@ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
return ch->cmt->info->read_control(ch->cmt->mapbase, 0); return ch->cmt->info->read_control(ch->cmt->mapbase, 0);
} }
static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, u32 value)
unsigned long value)
{ {
if (ch->iostart) if (ch->iostart)
ch->cmt->info->write_control(ch->iostart, 0, value); ch->cmt->info->write_control(ch->iostart, 0, value);
...@@ -251,39 +247,35 @@ static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, ...@@ -251,39 +247,35 @@ static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
} }
static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) static inline u32 sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
{ {
return ch->cmt->info->read_control(ch->ioctrl, CMCSR); return ch->cmt->info->read_control(ch->ioctrl, CMCSR);
} }
static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, u32 value)
unsigned long value)
{ {
ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); ch->cmt->info->write_control(ch->ioctrl, CMCSR, value);
} }
static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
{ {
return ch->cmt->info->read_count(ch->ioctrl, CMCNT); return ch->cmt->info->read_count(ch->ioctrl, CMCNT);
} }
static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value)
unsigned long value)
{ {
ch->cmt->info->write_count(ch->ioctrl, CMCNT, value); ch->cmt->info->write_count(ch->ioctrl, CMCNT, value);
} }
static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, u32 value)
unsigned long value)
{ {
ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); ch->cmt->info->write_count(ch->ioctrl, CMCOR, value);
} }
static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, static u32 sh_cmt_get_counter(struct sh_cmt_channel *ch, u32 *has_wrapped)
int *has_wrapped)
{ {
unsigned long v1, v2, v3; u32 v1, v2, v3;
int o1, o2; u32 o1, o2;
o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit; o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit;
...@@ -303,7 +295,8 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, ...@@ -303,7 +295,8 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
{ {
unsigned long flags, value; unsigned long flags;
u32 value;
/* start stop register shared by multiple timer channels */ /* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&ch->cmt->lock, flags); raw_spin_lock_irqsave(&ch->cmt->lock, flags);
...@@ -410,11 +403,11 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch) ...@@ -410,11 +403,11 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch)
static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch, static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch,
int absolute) int absolute)
{ {
unsigned long new_match; u32 value = ch->next_match_value;
unsigned long value = ch->next_match_value; u32 new_match;
unsigned long delay = 0; u32 delay = 0;
unsigned long now = 0; u32 now = 0;
int has_wrapped; u32 has_wrapped;
now = sh_cmt_get_counter(ch, &has_wrapped); now = sh_cmt_get_counter(ch, &has_wrapped);
ch->flags |= FLAG_REPROGRAM; /* force reprogram */ ch->flags |= FLAG_REPROGRAM; /* force reprogram */
...@@ -611,9 +604,10 @@ static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs) ...@@ -611,9 +604,10 @@ static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs)
static u64 sh_cmt_clocksource_read(struct clocksource *cs) static u64 sh_cmt_clocksource_read(struct clocksource *cs)
{ {
struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
unsigned long flags, raw; unsigned long flags;
unsigned long value; unsigned long value;
int has_wrapped; u32 has_wrapped;
u32 raw;
raw_spin_lock_irqsave(&ch->lock, flags); raw_spin_lock_irqsave(&ch->lock, flags);
value = ch->total_cycles; value = ch->total_cycles;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment