time.c 6.4 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

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

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


L
Linus Torvalds 已提交
61
static irqreturn_t
62
pxa_ost0_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
63 64
{
	int next_match;
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
	struct clock_event_device *c = dev_id;

	if (c->mode == CLOCK_EVT_MODE_ONESHOT) {
		/* Disarm the compare/match, signal the event. */
		OIER &= ~OIER_E0;
		c->event_handler(c);
	} else if (c->mode == CLOCK_EVT_MODE_PERIODIC) {
		/* Call the event handler as many times as necessary
		 * to recover missed events, if any (if we update
		 * OSMR0 and OSCR0 is still ahead of us, we've missed
		 * the event).  As we're dealing with that, re-arm the
		 * compare/match for the next event.
		 *
		 * HACK ALERT:
		 *
		 * There's a latency between the instruction that
		 * writes to OSMR0 and the actual commit to the
		 * physical hardware, because the CPU doesn't (have
		 * to) run at bus speed, there's a write buffer
		 * between the CPU and the bus, etc. etc.  So if the
		 * target OSCR0 is "very close", to the OSMR0 load
		 * value, the update to OSMR0 might not get to the
		 * hardware in time and we'll miss that interrupt.
		 *
		 * To be safe, if the new OSMR0 is "very close" to the
		 * target OSCR0 value, we call the event_handler as
		 * though the event actually happened.  According to
		 * Nico's comment in the previous version of this
		 * code, experience has shown that 6 OSCR ticks is
		 * "very close" but he went with 8.  We will use 16,
		 * based on the results of testing on PXA270.
		 *
		 * To be doubly sure, we also tell clkevt via
		 * clockevents_register_device() not to ask for
		 * anything that might put us "very close".
L
Linus Torvalds 已提交
100
	 */
101
#define MIN_OSCR_DELTA 16
L
Linus Torvalds 已提交
102
	do {
103
			OSSR = OSSR_M0;
L
Linus Torvalds 已提交
104
		next_match = (OSMR0 += LATCH);
105 106 107 108
			c->event_handler(c);
		} while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA)
			 && (c->mode == CLOCK_EVT_MODE_PERIODIC));
	}
L
Linus Torvalds 已提交
109 110 111 112

	return IRQ_HANDLED;
}

113 114 115 116 117 118 119 120 121 122 123 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
static int
pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
{
	unsigned long irqflags;

	raw_local_irq_save(irqflags);
	OSMR0 = OSCR + delta;
	OSSR = OSSR_M0;
	OIER |= OIER_E0;
	raw_local_irq_restore(irqflags);
	return 0;
}

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_PERIODIC:
		raw_local_irq_save(irqflags);
		OSMR0 = OSCR + LATCH;
		OSSR = OSSR_M0;
		OIER |= OIER_E0;
		raw_local_irq_restore(irqflags);
		break;

	case CLOCK_EVT_MODE_ONESHOT:
		raw_local_irq_save(irqflags);
		OIER &= ~OIER_E0;
		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;
		raw_local_irq_restore(irqflags);
		break;
	}
}

static struct clock_event_device ckevt_pxa_osmr0 = {
	.name		= "osmr0",
	.features	= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
	.shift		= 32,
	.rating		= 200,
	.cpumask	= CPU_MASK_CPU0,
	.set_next_event	= pxa_osmr0_set_next_event,
	.set_mode	= pxa_osmr0_set_mode,
L
Linus Torvalds 已提交
164 165
};

166
static cycle_t pxa_read_oscr(void)
S
Sascha Hauer 已提交
167 168 169 170
{
	return OSCR;
}

171 172
static struct clocksource cksrc_pxa_oscr0 = {
	.name           = "oscr0",
S
Sascha Hauer 已提交
173
	.rating         = 200,
174
	.read           = pxa_read_oscr,
S
Sascha Hauer 已提交
175 176
	.mask           = CLOCKSOURCE_MASK(32),
	.shift          = 20,
177
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
S
Sascha Hauer 已提交
178 179
};

180 181 182 183 184 185 186
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 已提交
187 188
static void __init pxa_timer_init(void)
{
189 190
	OIER = 0;
	OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;
L
Linus Torvalds 已提交
191

192 193
	set_oscr2ns_scale(CLOCK_TICK_RATE);

194 195 196 197 198 199
	ckevt_pxa_osmr0.mult =
		div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);
	ckevt_pxa_osmr0.max_delta_ns =
		clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0);
	ckevt_pxa_osmr0.min_delta_ns =
		clockevent_delta2ns(MIN_OSCR_DELTA, &ckevt_pxa_osmr0) + 1;
L
Linus Torvalds 已提交
200

201 202
	cksrc_pxa_oscr0.mult =
		clocksource_hz2mult(CLOCK_TICK_RATE, cksrc_pxa_oscr0.shift);
203

204
	setup_irq(IRQ_OST0, &pxa_ost0_irq);
205

206 207
	clocksource_register(&cksrc_pxa_oscr0);
	clockevents_register_device(&ckevt_pxa_osmr0);
208 209
}

L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
#ifdef CONFIG_PM
static unsigned long osmr[4], oier;

static void pxa_timer_suspend(void)
{
	osmr[0] = OSMR0;
	osmr[1] = OSMR1;
	osmr[2] = OSMR2;
	osmr[3] = OSMR3;
	oier = OIER;
}

static void pxa_timer_resume(void)
{
	OSMR0 = osmr[0];
	OSMR1 = osmr[1];
	OSMR2 = osmr[2];
	OSMR3 = osmr[3];
	OIER = oier;

	/*
231 232 233 234
	 * OSCR0 is the system timer, which has to increase
	 * monotonically until it rolls over in hardware.  The value
	 * (OSMR0 - LATCH) is OSCR0 at the most recent system tick,
	 * which is a handy value to restore to OSCR0.
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247
	 */
	OSCR = OSMR0 - LATCH;
}
#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,
};