Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94790ec2 authored by Jeff Ohlstein's avatar Jeff Ohlstein Committed by David Brown
Browse files

msm: timer: SMP timer support for msm



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: default avatarJeff Ohlstein <johlstei@codeaurora.org>
Signed-off-by: default avatarDavid Brown <davidb@codeaurora.org>
parent 7b181446
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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),
};
+102 −23
Original line number Diff line number Diff line
@@ -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)
@@ -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:
@@ -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,
@@ -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,
		},
@@ -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,
@@ -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,
		},
@@ -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,
	}
};

@@ -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

@@ -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
};