timer.c 6.9 KB
Newer Older
1
/*
2 3
 *
 * Copyright (C) 2007 Google, Inc.
4
 * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

17 18
#include <linux/clocksource.h>
#include <linux/clockchips.h>
19 20 21
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
22
#include <linux/io.h>
23 24

#include <asm/mach/time.h>
S
Stephen Boyd 已提交
25
#include <asm/hardware/gic.h>
26
#include <asm/localtimer.h>
S
Stephen Boyd 已提交
27
#include <asm/sched_clock.h>
S
Stephen Boyd 已提交
28

29
#include <mach/msm_iomap.h>
30
#include <mach/cpu.h>
31
#include <mach/board.h>
32 33 34 35

#define TIMER_MATCH_VAL         0x0000
#define TIMER_COUNT_VAL         0x0004
#define TIMER_ENABLE            0x0008
36 37
#define TIMER_ENABLE_CLR_ON_MATCH_EN    BIT(1)
#define TIMER_ENABLE_EN                 BIT(0)
38
#define TIMER_CLEAR             0x000C
J
Jeff Ohlstein 已提交
39
#define DGT_CLK_CTL             0x0034
40
#define DGT_CLK_CTL_DIV_4	0x3
41 42

#define GPT_HZ 32768
J
Jeff Ohlstein 已提交
43

44
#define MSM_DGT_SHIFT 5
45

46
static void __iomem *event_base;
47

48 49
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
{
50
	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
51 52
	/* Stop the timer tick */
	if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
53
		u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
54
		ctrl &= ~TIMER_ENABLE_EN;
55
		writel_relaxed(ctrl, event_base + TIMER_ENABLE);
56
	}
57 58 59 60 61 62 63
	evt->event_handler(evt);
	return IRQ_HANDLED;
}

static int msm_timer_set_next_event(unsigned long cycles,
				    struct clock_event_device *evt)
{
64
	u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
65

66 67 68
	writel_relaxed(0, event_base + TIMER_CLEAR);
	writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
	writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
69 70 71 72 73 74
	return 0;
}

static void msm_timer_set_mode(enum clock_event_mode mode,
			      struct clock_event_device *evt)
{
75 76
	u32 ctrl;

77
	ctrl = readl_relaxed(event_base + TIMER_ENABLE);
78
	ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
79

80 81 82 83 84
	switch (mode) {
	case CLOCK_EVT_MODE_RESUME:
	case CLOCK_EVT_MODE_PERIODIC:
		break;
	case CLOCK_EVT_MODE_ONESHOT:
85
		/* Timer is enabled in set_next_event */
86 87 88 89 90
		break;
	case CLOCK_EVT_MODE_UNUSED:
	case CLOCK_EVT_MODE_SHUTDOWN:
		break;
	}
91
	writel_relaxed(ctrl, event_base + TIMER_ENABLE);
92 93
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
static struct clock_event_device msm_clockevent = {
	.name		= "gp_timer",
	.features	= CLOCK_EVT_FEAT_ONESHOT,
	.rating		= 200,
	.set_next_event	= msm_timer_set_next_event,
	.set_mode	= msm_timer_set_mode,
};

static union {
	struct clock_event_device *evt;
	struct clock_event_device __percpu **percpu_evt;
} msm_evt;

static void __iomem *source_base;

S
Stephen Boyd 已提交
109
static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
110 111 112 113
{
	return readl_relaxed(source_base + TIMER_COUNT_VAL);
}

S
Stephen Boyd 已提交
114
static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
115 116 117 118 119
{
	/*
	 * Shift timer count down by a constant due to unreliable lower bits
	 * on some targets.
	 */
120
	return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
121 122 123 124 125 126
}

static struct clocksource msm_clocksource = {
	.name	= "dg_timer",
	.rating	= 300,
	.read	= msm_read_timer_count,
127
	.mask	= CLOCKSOURCE_MASK(32),
128
	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
129 130
};

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
#ifdef CONFIG_LOCAL_TIMERS
static int __cpuinit msm_local_timer_setup(struct clock_event_device *evt)
{
	/* Use existing clock_event for cpu 0 */
	if (!smp_processor_id())
		return 0;

	writel_relaxed(0, event_base + TIMER_ENABLE);
	writel_relaxed(0, event_base + TIMER_CLEAR);
	writel_relaxed(~0, event_base + TIMER_MATCH_VAL);
	evt->irq = msm_clockevent.irq;
	evt->name = "local_timer";
	evt->features = msm_clockevent.features;
	evt->rating = msm_clockevent.rating;
	evt->set_mode = msm_timer_set_mode;
	evt->set_next_event = msm_timer_set_next_event;
	evt->shift = msm_clockevent.shift;
	evt->mult = div_sc(GPT_HZ, NSEC_PER_SEC, evt->shift);
	evt->max_delta_ns = clockevent_delta2ns(0xf0000000, evt);
	evt->min_delta_ns = clockevent_delta2ns(4, evt);

	*__this_cpu_ptr(msm_evt.percpu_evt) = evt;
	clockevents_register_device(evt);
	enable_percpu_irq(evt->irq, 0);
	return 0;
}

static void msm_local_timer_stop(struct clock_event_device *evt)
{
	evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
	disable_percpu_irq(evt->irq);
}

static struct local_timer_ops msm_local_timer_ops __cpuinitdata = {
	.setup	= msm_local_timer_setup,
	.stop	= msm_local_timer_stop,
};
#endif /* CONFIG_LOCAL_TIMERS */

S
Stephen Boyd 已提交
170 171 172 173 174
static notrace u32 msm_sched_clock_read(void)
{
	return msm_clocksource.read(&msm_clocksource);
}

175 176
static void __init msm_timer_init(void)
{
177 178
	struct clock_event_device *ce = &msm_clockevent;
	struct clocksource *cs = &msm_clocksource;
179
	int res;
180
	u32 dgt_hz;
181

182
	if (cpu_is_msm7x01()) {
183 184
		event_base = MSM_CSR_BASE;
		source_base = MSM_CSR_BASE + 0x10;
185 186 187
		dgt_hz = 19200000 >> MSM_DGT_SHIFT; /* 600 KHz */
		cs->read = msm_read_timer_count_shift;
		cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
188
	} else if (cpu_is_msm7x30()) {
189 190
		event_base = MSM_CSR_BASE + 0x04;
		source_base = MSM_CSR_BASE + 0x24;
191
		dgt_hz = 24576000 / 4;
192
	} else if (cpu_is_qsd8x50()) {
193 194
		event_base = MSM_CSR_BASE;
		source_base = MSM_CSR_BASE + 0x10;
195
		dgt_hz = 19200000 / 4;
196
	} else if (cpu_is_msm8x60() || cpu_is_msm8960()) {
197 198 199
		event_base = MSM_TMR_BASE + 0x04;
		/* Use CPU0's timer as the global clock source. */
		source_base = MSM_TMR0_BASE + 0x24;
200 201
		dgt_hz = 27000000 / 4;
		writel_relaxed(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
202 203
	} else
		BUG();
204

205 206 207
	writel_relaxed(0, event_base + TIMER_ENABLE);
	writel_relaxed(0, event_base + TIMER_CLEAR);
	writel_relaxed(~0, event_base + TIMER_MATCH_VAL);
208 209
	ce->cpumask = cpumask_of(0);

210
	ce->irq = INT_GP_TIMER_EXP;
211
	clockevents_config_and_register(ce, GPT_HZ, 4, 0xffffffff);
212
	if (cpu_is_msm8x60() || cpu_is_msm8960()) {
213 214
		msm_evt.percpu_evt = alloc_percpu(struct clock_event_device *);
		if (!msm_evt.percpu_evt) {
215 216
			pr_err("memory allocation failed for %s\n", ce->name);
			goto err;
217
		}
218
		*__this_cpu_ptr(msm_evt.percpu_evt) = ce;
219
		res = request_percpu_irq(ce->irq, msm_timer_interrupt,
220
					 ce->name, msm_evt.percpu_evt);
221
		if (!res) {
222
			enable_percpu_irq(ce->irq, 0);
223 224 225 226
#ifdef CONFIG_LOCAL_TIMERS
			local_timer_register(&msm_local_timer_ops);
#endif
		}
227
	} else {
228
		msm_evt.evt = ce;
229 230
		res = request_irq(ce->irq, msm_timer_interrupt,
				  IRQF_TIMER | IRQF_NOBALANCING |
231
				  IRQF_TRIGGER_RISING, ce->name, &msm_evt.evt);
232
	}
233 234 235 236

	if (res)
		pr_err("request_irq failed for %s\n", ce->name);
err:
237
	writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
238
	res = clocksource_register_hz(cs, dgt_hz);
239
	if (res)
240
		pr_err("clocksource_register failed\n");
S
Stephen Boyd 已提交
241 242
	setup_sched_clock(msm_sched_clock_read,
			cpu_is_msm7x01() ? 32 - MSM_DGT_SHIFT : 32, dgt_hz);
243 244 245 246 247
}

struct sys_timer msm_timer = {
	.init = msm_timer_init
};