timer-gp.c 5.0 KB
Newer Older
1 2 3 4 5
/*
 * linux/arch/arm/mach-omap2/timer-gp.c
 *
 * OMAP2 GP timer support.
 *
6 7 8 9 10
 * Update to use new clocksource/clockevent layers
 * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
 * Copyright (C) 2007 MontaVista Software, Inc.
 *
 * Original driver:
11 12
 * Copyright (C) 2005 Nokia Corporation
 * Author: Paul Mundt <paul.mundt@nokia.com>
13
 *         Juha Yrjölä <juha.yrjola@nokia.com>
14
 * OMAP Dual-mode timer framework support by Timo Teras
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Some parts based off of TI's 24xx code:
 *
 *   Copyright (C) 2004 Texas Instruments, Inc.
 *
 * Roughly modelled after the OMAP1 MPU timer code.
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/err.h>
30
#include <linux/clk.h>
31
#include <linux/delay.h>
32
#include <linux/irq.h>
33 34
#include <linux/clocksource.h>
#include <linux/clockchips.h>
35

36
#include <asm/mach/time.h>
37
#include <mach/dmtimer.h>
38

39
static struct omap_dm_timer *gptimer;
40
static struct clock_event_device clockevent_gpt;
41

42
static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
43
{
44 45 46 47
	struct omap_dm_timer *gpt = (struct omap_dm_timer *)dev_id;
	struct clock_event_device *evt = &clockevent_gpt;

	omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_OVERFLOW);
48

49
	evt->event_handler(evt);
50 51 52 53 54
	return IRQ_HANDLED;
}

static struct irqaction omap2_gp_timer_irq = {
	.name		= "gp timer",
B
Bernhard Walle 已提交
55
	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
56 57 58
	.handler	= omap2_gp_timer_interrupt,
};

59 60
static int omap2_gp_timer_set_next_event(unsigned long cycles,
					 struct clock_event_device *evt)
61
{
62
	omap_dm_timer_set_load_start(gptimer, 0, 0xffffffff - cycles);
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

	return 0;
}

static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
				    struct clock_event_device *evt)
{
	u32 period;

	omap_dm_timer_stop(gptimer);

	switch (mode) {
	case CLOCK_EVT_MODE_PERIODIC:
		period = clk_get_rate(omap_dm_timer_get_fclk(gptimer)) / HZ;
		period -= 1;

79
		omap_dm_timer_set_load_start(gptimer, 1, 0xffffffff - period);
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
		break;
	case CLOCK_EVT_MODE_ONESHOT:
		break;
	case CLOCK_EVT_MODE_UNUSED:
	case CLOCK_EVT_MODE_SHUTDOWN:
	case CLOCK_EVT_MODE_RESUME:
		break;
	}
}

static struct clock_event_device clockevent_gpt = {
	.name		= "gp timer",
	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
	.shift		= 32,
	.set_next_event	= omap2_gp_timer_set_next_event,
	.set_mode	= omap2_gp_timer_set_mode,
};

static void __init omap2_gp_clockevent_init(void)
{
	u32 tick_rate;
101

102
	gptimer = omap_dm_timer_request_specific(1);
103
	BUG_ON(gptimer == NULL);
104

105 106 107
#if defined(CONFIG_OMAP_32K_TIMER)
	omap_dm_timer_set_source(gptimer, OMAP_TIMER_SRC_32_KHZ);
#else
108
	omap_dm_timer_set_source(gptimer, OMAP_TIMER_SRC_SYS_CLK);
109 110
#endif
	tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer));
111

112
	omap2_gp_timer_irq.dev_id = (void *)gptimer;
113
	setup_irq(omap_dm_timer_get_irq(gptimer), &omap2_gp_timer_irq);
114 115 116 117 118 119 120 121 122
	omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW);

	clockevent_gpt.mult = div_sc(tick_rate, NSEC_PER_SEC,
				     clockevent_gpt.shift);
	clockevent_gpt.max_delta_ns =
		clockevent_delta2ns(0xffffffff, &clockevent_gpt);
	clockevent_gpt.min_delta_ns =
		clockevent_delta2ns(1, &clockevent_gpt);

123
	clockevent_gpt.cpumask = cpumask_of(0);
124 125 126 127 128 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 170 171 172
	clockevents_register_device(&clockevent_gpt);
}

#ifdef CONFIG_OMAP_32K_TIMER
/* 
 * When 32k-timer is enabled, don't use GPTimer for clocksource
 * instead, just leave default clocksource which uses the 32k
 * sync counter.  See clocksource setup in see plat-omap/common.c. 
 */

static inline void __init omap2_gp_clocksource_init(void) {}
#else
/*
 * clocksource
 */
static struct omap_dm_timer *gpt_clocksource;
static cycle_t clocksource_read_cycles(void)
{
	return (cycle_t)omap_dm_timer_read_counter(gpt_clocksource);
}

static struct clocksource clocksource_gpt = {
	.name		= "gp timer",
	.rating		= 300,
	.read		= clocksource_read_cycles,
	.mask		= CLOCKSOURCE_MASK(32),
	.shift		= 24,
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
};

/* Setup free-running counter for clocksource */
static void __init omap2_gp_clocksource_init(void)
{
	static struct omap_dm_timer *gpt;
	u32 tick_rate, tick_period;
	static char err1[] __initdata = KERN_ERR
		"%s: failed to request dm-timer\n";
	static char err2[] __initdata = KERN_ERR
		"%s: can't register clocksource!\n";

	gpt = omap_dm_timer_request();
	if (!gpt)
		printk(err1, clocksource_gpt.name);
	gpt_clocksource = gpt;

	omap_dm_timer_set_source(gpt, OMAP_TIMER_SRC_SYS_CLK);
	tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gpt));
	tick_period = (tick_rate / HZ) - 1;

173
	omap_dm_timer_set_load_start(gpt, 1, 0);
174 175 176 177 178 179 180 181 182 183 184 185 186 187

	clocksource_gpt.mult =
		clocksource_khz2mult(tick_rate/1000, clocksource_gpt.shift);
	if (clocksource_register(&clocksource_gpt))
		printk(err2, clocksource_gpt.name);
}
#endif

static void __init omap2_gp_timer_init(void)
{
	omap_dm_timer_init();

	omap2_gp_clockevent_init();
	omap2_gp_clocksource_init();
188 189 190 191 192
}

struct sys_timer omap_timer = {
	.init	= omap2_gp_timer_init,
};