time.c 5.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * arch/arm/plat-orion/time.c
 *
 * Marvell Orion SoC timer handling.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 *
 * Timer 0 is used as free-running clocksource, while timer 1 is
 * used as clock_event_device.
 */

#include <linux/kernel.h>
15 16 17
#include <linux/sched.h>
#include <linux/cnt32_to_63.h>
#include <linux/timer.h>
18 19 20 21
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/mach/time.h>
22
#include <mach/bridge-regs.h>
23
#include <mach/hardware.h>
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

/*
 * Number of timer ticks per jiffy.
 */
static u32 ticks_per_jiffy;


/*
 * Timer block registers.
 */
#define TIMER_CTRL		(TIMER_VIRT_BASE + 0x0000)
#define  TIMER0_EN		0x0001
#define  TIMER0_RELOAD_EN	0x0002
#define  TIMER1_EN		0x0004
#define  TIMER1_RELOAD_EN	0x0008
#define TIMER0_RELOAD		(TIMER_VIRT_BASE + 0x0010)
#define TIMER0_VAL		(TIMER_VIRT_BASE + 0x0014)
#define TIMER1_RELOAD		(TIMER_VIRT_BASE + 0x0018)
#define TIMER1_VAL		(TIMER_VIRT_BASE + 0x001c)


45 46 47
/*
 * Orion's sched_clock implementation. It has a resolution of
 * at least 7.5ns (133MHz TCLK) and a maximum value of 834 days.
48 49 50 51 52
 *
 * Because the hardware timer period is quite short (21 secs if
 * 200MHz TCLK) and because cnt32_to_63() needs to be called at
 * least once per half period to work properly, a kernel timer is
 * set up to ensure this requirement is always met.
53 54 55 56 57
 */
#define TCLK2NS_SCALE_FACTOR 8

static unsigned long tclk2ns_scale;

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
unsigned long long sched_clock(void)
{
	unsigned long long v = cnt32_to_63(0xffffffff - readl(TIMER0_VAL));
	return (v * tclk2ns_scale) >> TCLK2NS_SCALE_FACTOR;
}

static struct timer_list cnt32_to_63_keepwarm_timer;

static void cnt32_to_63_keepwarm(unsigned long data)
{
	mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
	(void) sched_clock();
}

static void __init setup_sched_clock(unsigned long tclk)
73
{
74 75 76 77
	unsigned long long v;
	unsigned long data;

	v = NSEC_PER_SEC;
78 79 80 81 82 83 84 85 86 87 88 89
	v <<= TCLK2NS_SCALE_FACTOR;
	v += tclk/2;
	do_div(v, tclk);
	/*
	 * 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 (v & 1)
		v++;
	tclk2ns_scale = v;

90 91 92
	data = (0xffffffffUL / tclk / 2 - 2) * HZ;
	setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, data);
	mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
93 94
}

95 96 97
/*
 * Clocksource handling.
 */
98
static cycle_t orion_clksrc_read(struct clocksource *cs)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
{
	return 0xffffffff - readl(TIMER0_VAL);
}

static struct clocksource orion_clksrc = {
	.name		= "orion_clocksource",
	.rating		= 300,
	.read		= orion_clksrc_read,
	.mask		= CLOCKSOURCE_MASK(32),
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
};



/*
 * Clockevent handling.
 */
static int
orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
{
	unsigned long flags;
	u32 u;

	if (delta == 0)
		return -ETIME;

	local_irq_save(flags);

	/*
	 * Clear and enable clockevent timer interrupt.
	 */
130
	writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);
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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

	u = readl(BRIDGE_MASK);
	u |= BRIDGE_INT_TIMER1;
	writel(u, BRIDGE_MASK);

	/*
	 * Setup new clockevent timer value.
	 */
	writel(delta, TIMER1_VAL);

	/*
	 * Enable the timer.
	 */
	u = readl(TIMER_CTRL);
	u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
	writel(u, TIMER_CTRL);

	local_irq_restore(flags);

	return 0;
}

static void
orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
{
	unsigned long flags;
	u32 u;

	local_irq_save(flags);
	if (mode == CLOCK_EVT_MODE_PERIODIC) {
		/*
		 * Setup timer to fire at 1/HZ intervals.
		 */
		writel(ticks_per_jiffy - 1, TIMER1_RELOAD);
		writel(ticks_per_jiffy - 1, TIMER1_VAL);

		/*
		 * Enable timer interrupt.
		 */
		u = readl(BRIDGE_MASK);
		writel(u | BRIDGE_INT_TIMER1, BRIDGE_MASK);

		/*
		 * Enable timer.
		 */
		u = readl(TIMER_CTRL);
		writel(u | TIMER1_EN | TIMER1_RELOAD_EN, TIMER_CTRL);
	} else {
		/*
		 * Disable timer.
		 */
		u = readl(TIMER_CTRL);
		writel(u & ~TIMER1_EN, TIMER_CTRL);

		/*
		 * Disable timer interrupt.
		 */
		u = readl(BRIDGE_MASK);
		writel(u & ~BRIDGE_INT_TIMER1, BRIDGE_MASK);

		/*
		 * ACK pending timer interrupt.
		 */
194
		writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

	}
	local_irq_restore(flags);
}

static struct clock_event_device orion_clkevt = {
	.name		= "orion_tick",
	.features	= CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
	.shift		= 32,
	.rating		= 300,
	.set_next_event	= orion_clkevt_next_event,
	.set_mode	= orion_clkevt_mode,
};

static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
{
	/*
	 * ACK timer interrupt and call event handler.
	 */
214
	writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
	orion_clkevt.event_handler(&orion_clkevt);

	return IRQ_HANDLED;
}

static struct irqaction orion_timer_irq = {
	.name		= "orion_tick",
	.flags		= IRQF_DISABLED | IRQF_TIMER,
	.handler	= orion_timer_interrupt
};

void __init orion_time_init(unsigned int irq, unsigned int tclk)
{
	u32 u;

	ticks_per_jiffy = (tclk + HZ/2) / HZ;

232
	/*
233
	 * Set scale and timer for sched_clock
234
	 */
235
	setup_sched_clock(tclk);
236 237 238 239 240 241 242 243 244 245 246

	/*
	 * Setup free-running clocksource timer (interrupts
	 * disabled.)
	 */
	writel(0xffffffff, TIMER0_VAL);
	writel(0xffffffff, TIMER0_RELOAD);
	u = readl(BRIDGE_MASK);
	writel(u & ~BRIDGE_INT_TIMER0, BRIDGE_MASK);
	u = readl(TIMER_CTRL);
	writel(u | TIMER0_EN | TIMER0_RELOAD_EN, TIMER_CTRL);
247
	clocksource_register_hz(&orion_clksrc, tclk);
248 249 250 251 252 253 254 255

	/*
	 * Setup clockevent timer (interrupt-driven.)
	 */
	setup_irq(irq, &orion_timer_irq);
	orion_clkevt.mult = div_sc(tclk, NSEC_PER_SEC, orion_clkevt.shift);
	orion_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe, &orion_clkevt);
	orion_clkevt.min_delta_ns = clockevent_delta2ns(1, &orion_clkevt);
256
	orion_clkevt.cpumask = cpumask_of(0);
257 258
	clockevents_register_device(&orion_clkevt);
}