Skip to content

Commit

Permalink
msm: timer: SMP timer support for msm
Browse files Browse the repository at this point in the history
The msm provides timer hardware that is private to each core. Each
timer has separate counter and match registers, so we create separate
clock_event_devices for each core. For the global clocksource, use
cpu 0's counter.

Signed-off-by: Jeff Ohlstein <johlstei@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
  • Loading branch information
Jeff Ohlstein authored and David Brown committed Jan 7, 2011
1 parent 7b18144 commit 94790ec
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 24 deletions.
6 changes: 5 additions & 1 deletion arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@

#define MSM_TMR_BASE IOMEM(0xF0200000)
#define MSM_TMR_PHYS 0x02000000
#define MSM_TMR_SIZE (SZ_1M)
#define MSM_TMR_SIZE SZ_4K

#define MSM_TMR0_BASE IOMEM(0xF0201000)
#define MSM_TMR0_PHYS 0x02040000
#define MSM_TMR0_SIZE SZ_4K

#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4)
#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24)
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-msm/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ static struct map_desc msm8x60_io_desc[] __initdata = {
MSM_DEVICE(QGIC_DIST),
MSM_DEVICE(QGIC_CPU),
MSM_DEVICE(TMR),
MSM_DEVICE(TMR0),
MSM_DEVICE(ACC),
MSM_DEVICE(GCC),
};
Expand Down
125 changes: 102 additions & 23 deletions arch/arm/mach-msm/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ enum {

#define GPT_HZ 32768

enum timer_location {
LOCAL_TIMER = 0,
GLOBAL_TIMER = 1,
};

#ifdef MSM_TMR0_BASE
#define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE)
#else
#define MSM_TMR_GLOBAL 0
#endif

#define MSM_GLOBAL_TIMER MSM_CLOCK_DGT

#if defined(CONFIG_ARCH_QSD8X50)
#define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
#define MSM_DGT_SHIFT (0)
Expand All @@ -65,49 +78,67 @@ struct msm_clock {
void __iomem *regbase;
uint32_t freq;
uint32_t shift;
void __iomem *global_counter;
void __iomem *local_counter;
};

enum {
MSM_CLOCK_GPT,
MSM_CLOCK_DGT,
NR_TIMERS,
};


static struct msm_clock msm_clocks[];
static struct clock_event_device *local_clock_event;

static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
if (smp_processor_id() != 0)
evt = local_clock_event;
if (evt->event_handler == NULL)
return IRQ_HANDLED;
evt->event_handler(evt);
return IRQ_HANDLED;
}

static cycle_t msm_gpt_read(struct clocksource *cs)
static cycle_t msm_read_timer_count(struct clocksource *cs)
{
return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);

return readl(clk->global_counter);
}

static cycle_t msm_dgt_read(struct clocksource *cs)
static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt)
{
return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT;
#ifdef CONFIG_SMP
int i;
for (i = 0; i < NR_TIMERS; i++)
if (evt == &(msm_clocks[i].clockevent))
return &msm_clocks[i];
return &msm_clocks[MSM_GLOBAL_TIMER];
#else
return container_of(evt, struct msm_clock, clockevent);
#endif
}

static int msm_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL);
struct msm_clock *clock = clockevent_to_clock(evt);
uint32_t now = readl(clock->local_counter);
uint32_t alarm = now + (cycles << clock->shift);
int late;

writel(alarm, clock->regbase + TIMER_MATCH_VAL);
now = readl(clock->regbase + TIMER_COUNT_VAL);
late = now - alarm;
if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
"alarm already expired, now %x, alarm %x, late %d\n",
cycles, clock->clockevent.name, now, alarm, late);
return -ETIME;
}
return 0;
}

static void msm_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
struct msm_clock *clock = clockevent_to_clock(evt);

switch (mode) {
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_PERIODIC:
Expand All @@ -123,7 +154,7 @@ static void msm_timer_set_mode(enum clock_event_mode mode,
}

static struct msm_clock msm_clocks[] = {
{
[MSM_CLOCK_GPT] = {
.clockevent = {
.name = "gp_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
Expand All @@ -135,7 +166,7 @@ static struct msm_clock msm_clocks[] = {
.clocksource = {
.name = "gp_timer",
.rating = 200,
.read = msm_gpt_read,
.read = msm_read_timer_count,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
},
Expand All @@ -147,9 +178,12 @@ static struct msm_clock msm_clocks[] = {
.irq = INT_GP_TIMER_EXP
},
.regbase = MSM_GPT_BASE,
.freq = GPT_HZ
.freq = GPT_HZ,
.local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL,
.global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL +
MSM_TMR_GLOBAL,
},
{
[MSM_CLOCK_DGT] = {
.clockevent = {
.name = "dg_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
Expand All @@ -161,7 +195,7 @@ static struct msm_clock msm_clocks[] = {
.clocksource = {
.name = "dg_timer",
.rating = 300,
.read = msm_dgt_read,
.read = msm_read_timer_count,
.mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
},
Expand All @@ -174,7 +208,10 @@ static struct msm_clock msm_clocks[] = {
},
.regbase = MSM_DGT_BASE,
.freq = DGT_HZ >> MSM_DGT_SHIFT,
.shift = MSM_DGT_SHIFT
.shift = MSM_DGT_SHIFT,
.local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL,
.global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL +
MSM_TMR_GLOBAL,
}
};

Expand All @@ -183,7 +220,7 @@ static void __init msm_timer_init(void)
int i;
int res;

#ifdef CONFIG_ARCH_MSM8X60
#ifdef CONFIG_ARCH_MSM_SCORPIONMP
writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
#endif

Expand Down Expand Up @@ -217,6 +254,48 @@ static void __init msm_timer_init(void)
}
}

#ifdef CONFIG_SMP
void __cpuinit local_timer_setup(struct clock_event_device *evt)
{
struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];

/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return;

writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);

if (!local_clock_event) {
writel(0, clock->regbase + TIMER_ENABLE);
writel(0, clock->regbase + TIMER_CLEAR);
writel(~0, clock->regbase + TIMER_MATCH_VAL);
}
evt->irq = clock->irq.irq;
evt->name = "local_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT;
evt->rating = clock->clockevent.rating;
evt->set_mode = msm_timer_set_mode;
evt->set_next_event = msm_timer_set_next_event;
evt->shift = clock->clockevent.shift;
evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns =
clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
evt->min_delta_ns = clockevent_delta2ns(4, evt);

local_clock_event = evt;

gic_enable_ppi(clock->irq.irq);

clockevents_register_device(evt);
}

inline int local_timer_ack(void)
{
return 1;
}

#endif

struct sys_timer msm_timer = {
.init = msm_timer_init
};

0 comments on commit 94790ec

Please sign in to comment.