time.c 5.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * arch/arm/mach-pxa/time.c
 *
4 5 6 7 8
 * PXA clocksource, clockevents, and OST interrupt handlers.
 * Copyright (c) 2007 by Bill Gatliff <bgat@billgatliff.com>.
 *
 * Derived from Nicolas Pitre's PXA timer handler Copyright (c) 2001
 * by MontaVista Software, Inc.  (Nico, your code rocks!)
L
Linus Torvalds 已提交
9 10 11 12 13 14 15 16 17
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
18
#include <linux/clockchips.h>
19
#include <linux/sched.h>
20
#include <linux/cnt32_to_63.h>
21

22
#include <asm/div64.h>
L
Linus Torvalds 已提交
23 24
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
25
#include <mach/regs-ost.h>
L
Linus Torvalds 已提交
26

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/*
 * This is PXA's sched_clock implementation. This has a resolution
 * of at least 308 ns and a maximum value of 208 days.
 *
 * The return value is guaranteed to be monotonic in that range as
 * long as there is always less than 582 seconds between successive
 * calls to sched_clock() which should always be the case in practice.
 */

#define OSCR2NS_SCALE_FACTOR 10

static unsigned long oscr2ns_scale;

static void __init set_oscr2ns_scale(unsigned long oscr_rate)
{
	unsigned long long v = 1000000000ULL << OSCR2NS_SCALE_FACTOR;
	do_div(v, oscr_rate);
	oscr2ns_scale = v;
	/*
	 * We want an even value to automatically clear the top bit
	 * returned by cnt32_to_63() without an additional run time
	 * instruction. So if the LSB is 1 then round it up.
	 */
	if (oscr2ns_scale & 1)
		oscr2ns_scale++;
}

unsigned long long sched_clock(void)
{
	unsigned long long v = cnt32_to_63(OSCR);
	return (v * oscr2ns_scale) >> OSCR2NS_SCALE_FACTOR;
}


61 62
#define MIN_OSCR_DELTA 16

L
Linus Torvalds 已提交
63
static irqreturn_t
64
pxa_ost0_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
65
{
66 67
	struct clock_event_device *c = dev_id;

68 69 70 71
	/* Disarm the compare/match, signal the event. */
	OIER &= ~OIER_E0;
	OSSR = OSSR_M0;
	c->event_handler(c);
L
Linus Torvalds 已提交
72 73 74 75

	return IRQ_HANDLED;
}

76 77 78
static int
pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
{
R
Russell King 已提交
79
	unsigned long flags, next, oscr;
80

R
Russell King 已提交
81
	raw_local_irq_save(flags);
82
	OIER |= OIER_E0;
R
Russell King 已提交
83 84 85 86 87 88
	next = OSCR + delta;
	OSMR0 = next;
	oscr = OSCR;
	raw_local_irq_restore(flags);

	return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
89 90 91 92 93 94 95 96 97 98 99
}

static void
pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
{
	unsigned long irqflags;

	switch (mode) {
	case CLOCK_EVT_MODE_ONESHOT:
		raw_local_irq_save(irqflags);
		OIER &= ~OIER_E0;
R
Russell King 已提交
100
		OSSR = OSSR_M0;
101 102 103 104 105 106 107 108
		raw_local_irq_restore(irqflags);
		break;

	case CLOCK_EVT_MODE_UNUSED:
	case CLOCK_EVT_MODE_SHUTDOWN:
		/* initializing, released, or preparing for suspend */
		raw_local_irq_save(irqflags);
		OIER &= ~OIER_E0;
R
Russell King 已提交
109
		OSSR = OSSR_M0;
110 111
		raw_local_irq_restore(irqflags);
		break;
112 113

	case CLOCK_EVT_MODE_RESUME:
114
	case CLOCK_EVT_MODE_PERIODIC:
115
		break;
116 117 118 119 120
	}
}

static struct clock_event_device ckevt_pxa_osmr0 = {
	.name		= "osmr0",
121
	.features	= CLOCK_EVT_FEAT_ONESHOT,
122 123 124 125
	.shift		= 32,
	.rating		= 200,
	.set_next_event	= pxa_osmr0_set_next_event,
	.set_mode	= pxa_osmr0_set_mode,
L
Linus Torvalds 已提交
126 127
};

128
static cycle_t pxa_read_oscr(void)
S
Sascha Hauer 已提交
129 130 131 132
{
	return OSCR;
}

133 134
static struct clocksource cksrc_pxa_oscr0 = {
	.name           = "oscr0",
S
Sascha Hauer 已提交
135
	.rating         = 200,
136
	.read           = pxa_read_oscr,
S
Sascha Hauer 已提交
137 138
	.mask           = CLOCKSOURCE_MASK(32),
	.shift          = 20,
139
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
S
Sascha Hauer 已提交
140 141
};

142 143 144 145 146 147 148
static struct irqaction pxa_ost0_irq = {
	.name		= "ost0",
	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
	.handler	= pxa_ost0_interrupt,
	.dev_id		= &ckevt_pxa_osmr0,
};

L
Linus Torvalds 已提交
149 150
static void __init pxa_timer_init(void)
{
151
	unsigned long clock_tick_rate = get_clock_tick_rate();
152

153 154
	OIER = 0;
	OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;
L
Linus Torvalds 已提交
155

156
	set_oscr2ns_scale(clock_tick_rate);
157

158
	ckevt_pxa_osmr0.mult =
159
		div_sc(clock_tick_rate, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);
160 161 162
	ckevt_pxa_osmr0.max_delta_ns =
		clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0);
	ckevt_pxa_osmr0.min_delta_ns =
R
Russell King 已提交
163
		clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1;
164
	ckevt_pxa_osmr0.cpumask = cpumask_of(0);
L
Linus Torvalds 已提交
165

166
	cksrc_pxa_oscr0.mult =
167
		clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift);
168

169
	setup_irq(IRQ_OST0, &pxa_ost0_irq);
170

171 172
	clocksource_register(&cksrc_pxa_oscr0);
	clockevents_register_device(&ckevt_pxa_osmr0);
173 174
}

L
Linus Torvalds 已提交
175
#ifdef CONFIG_PM
176
static unsigned long osmr[4], oier, oscr;
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184

static void pxa_timer_suspend(void)
{
	osmr[0] = OSMR0;
	osmr[1] = OSMR1;
	osmr[2] = OSMR2;
	osmr[3] = OSMR3;
	oier = OIER;
185
	oscr = OSCR;
L
Linus Torvalds 已提交
186 187 188 189
}

static void pxa_timer_resume(void)
{
190 191 192 193 194 195 196 197 198
	/*
	 * Ensure that we have at least MIN_OSCR_DELTA between match
	 * register 0 and the OSCR, to guarantee that we will receive
	 * the one-shot timer interrupt.  We adjust OSMR0 in preference
	 * to OSCR to guarantee that OSCR is monotonically incrementing.
	 */
	if (osmr[0] - oscr < MIN_OSCR_DELTA)
		osmr[0] += MIN_OSCR_DELTA;

L
Linus Torvalds 已提交
199 200 201 202 203
	OSMR0 = osmr[0];
	OSMR1 = osmr[1];
	OSMR2 = osmr[2];
	OSMR3 = osmr[3];
	OIER = oier;
204
	OSCR = oscr;
L
Linus Torvalds 已提交
205 206 207 208 209 210 211 212 213 214 215
}
#else
#define pxa_timer_suspend NULL
#define pxa_timer_resume NULL
#endif

struct sys_timer pxa_timer = {
	.init		= pxa_timer_init,
	.suspend	= pxa_timer_suspend,
	.resume		= pxa_timer_resume,
};