aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-mmp/time.c
diff options
context:
space:
mode:
authorEric Miao <eric.miao@marvell.com>2009-01-20 01:15:18 -0500
committerEric Miao <eric.miao@marvell.com>2009-03-22 22:11:34 -0400
commit49cbe78637eb0503f45fc9b556ec08918a616534 (patch)
tree96de29959c5ef512d8f1e0bea7eae2245b7cc3f9 /arch/arm/mach-mmp/time.c
parentf8dec04d33b94a4cfa9358fd9666c01480bb164d (diff)
[ARM] pxa: add base support for Marvell's PXA168 processor line
"""The MarvellĀ® PXA168 processor is the first in a family of application processors targeted at mass market opportunities in computing and consumer devices. It balances high computing and multimedia performance with low power consumption to support extended battery life, and includes a wealth of integrated peripherals to reduce overall BOM cost .... """ See http://www.marvell.com/featured/pxa168.jsp for more information. 1. Marvell Mohawk core is a hybrid of xscale3 and its own ARM core, there are many enhancements like instructions for flushing the whole D-cache, and so on 2. Clock reuses Russell's common clkdev, and added the basic support for UART1/2. 3. Devices are a bit different from the 'mach-pxa' way, the platform devices are now dynamically allocated only when necessary (i.e. when pxa_register_device() is called). Description for each device are stored in an array of 'struct pxa_device_desc'. Now that: a. this array of device description is marked with __initdata and can be freed up system is fully up b. which means board code has to add all needed devices early in his initializing function c. platform specific data can now be marked as __initdata since they are allocated and copied by platform_device_add_data() 4. only the basic UART1/2/3 are added, more devices will come later. Signed-off-by: Jason Chagas <chagas@marvell.com> Signed-off-by: Eric Miao <eric.miao@marvell.com>
Diffstat (limited to 'arch/arm/mach-mmp/time.c')
-rw-r--r--arch/arm/mach-mmp/time.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c
new file mode 100644
index 000000000000..b03a6eda7419
--- /dev/null
+++ b/arch/arm/mach-mmp/time.c
@@ -0,0 +1,199 @@
1/*
2 * linux/arch/arm/mach-mmp/time.c
3 *
4 * Support for clocksource and clockevents
5 *
6 * Copyright (C) 2008 Marvell International Ltd.
7 * All rights reserved.
8 *
9 * 2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
10 * 2008-10-08: Bin Yang <bin.yang@marvell.com>
11 *
12 * The timers module actually includes three timers, each timer with upto
13 * three match comparators. Timer #0 is used here in free-running mode as
14 * the clock source, and match comparator #1 used as clock event device.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License version 2 as
18 * published by the Free Software Foundation.
19 */
20
21#include <linux/init.h>
22#include <linux/kernel.h>
23#include <linux/interrupt.h>
24#include <linux/clockchips.h>
25
26#include <linux/io.h>
27#include <linux/irq.h>
28#include <linux/sched.h>
29#include <linux/cnt32_to_63.h>
30
31#include <mach/addr-map.h>
32#include <mach/regs-timers.h>
33#include <mach/irqs.h>
34
35#include "clock.h"
36
37#define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE
38
39#define MAX_DELTA (0xfffffffe)
40#define MIN_DELTA (16)
41
42#define TCR2NS_SCALE_FACTOR 10
43
44static unsigned long tcr2ns_scale;
45
46static void __init set_tcr2ns_scale(unsigned long tcr_rate)
47{
48 unsigned long long v = 1000000000ULL << TCR2NS_SCALE_FACTOR;
49 do_div(v, tcr_rate);
50 tcr2ns_scale = v;
51 /*
52 * We want an even value to automatically clear the top bit
53 * returned by cnt32_to_63() without an additional run time
54 * instruction. So if the LSB is 1 then round it up.
55 */
56 if (tcr2ns_scale & 1)
57 tcr2ns_scale++;
58}
59
60/*
61 * FIXME: the timer needs some delay to stablize the counter capture
62 */
63static inline uint32_t timer_read(void)
64{
65 int delay = 100;
66
67 __raw_writel(1, TIMERS_VIRT_BASE + TMR_CVWR(0));
68
69 while (delay--)
70 cpu_relax();
71
72 return __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(0));
73}
74
75unsigned long long sched_clock(void)
76{
77 unsigned long long v = cnt32_to_63(timer_read());
78 return (v * tcr2ns_scale) >> TCR2NS_SCALE_FACTOR;
79}
80
81static irqreturn_t timer_interrupt(int irq, void *dev_id)
82{
83 struct clock_event_device *c = dev_id;
84
85 /* disable and clear pending interrupt status */
86 __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(0));
87 __raw_writel(0x1, TIMERS_VIRT_BASE + TMR_ICR(0));
88 c->event_handler(c);
89 return IRQ_HANDLED;
90}
91
92static int timer_set_next_event(unsigned long delta,
93 struct clock_event_device *dev)
94{
95 unsigned long flags, next;
96
97 local_irq_save(flags);
98
99 /* clear pending interrupt status and enable */
100 __raw_writel(0x01, TIMERS_VIRT_BASE + TMR_ICR(0));
101 __raw_writel(0x01, TIMERS_VIRT_BASE + TMR_IER(0));
102
103 next = timer_read() + delta;
104 __raw_writel(next, TIMERS_VIRT_BASE + TMR_TN_MM(0, 0));
105
106 local_irq_restore(flags);
107 return 0;
108}
109
110static void timer_set_mode(enum clock_event_mode mode,
111 struct clock_event_device *dev)
112{
113 unsigned long flags;
114
115 local_irq_save(flags);
116 switch (mode) {
117 case CLOCK_EVT_MODE_ONESHOT:
118 case CLOCK_EVT_MODE_UNUSED:
119 case CLOCK_EVT_MODE_SHUTDOWN:
120 /* disable the matching interrupt */
121 __raw_writel(0x00, TIMERS_VIRT_BASE + TMR_IER(0));
122 break;
123 case CLOCK_EVT_MODE_RESUME:
124 case CLOCK_EVT_MODE_PERIODIC:
125 break;
126 }
127 local_irq_restore(flags);
128}
129
130static struct clock_event_device ckevt = {
131 .name = "clockevent",
132 .features = CLOCK_EVT_FEAT_ONESHOT,
133 .shift = 32,
134 .rating = 200,
135 .set_next_event = timer_set_next_event,
136 .set_mode = timer_set_mode,
137};
138
139static cycle_t clksrc_read(void)
140{
141 return timer_read();
142}
143
144static struct clocksource cksrc = {
145 .name = "clocksource",
146 .shift = 20,
147 .rating = 200,
148 .read = clksrc_read,
149 .mask = CLOCKSOURCE_MASK(32),
150 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
151};
152
153static void __init timer_config(void)
154{
155 uint32_t ccr = __raw_readl(TIMERS_VIRT_BASE + TMR_CCR);
156 uint32_t cer = __raw_readl(TIMERS_VIRT_BASE + TMR_CER);
157 uint32_t cmr = __raw_readl(TIMERS_VIRT_BASE + TMR_CMR);
158
159 __raw_writel(cer & ~0x1, TIMERS_VIRT_BASE + TMR_CER); /* disable */
160
161 ccr &= TMR_CCR_CS_0(0x3);
162 __raw_writel(ccr, TIMERS_VIRT_BASE + TMR_CCR);
163
164 /* free-running mode */
165 __raw_writel(cmr | 0x01, TIMERS_VIRT_BASE + TMR_CMR);
166
167 __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_PLCR(0)); /* free-running */
168 __raw_writel(0x7, TIMERS_VIRT_BASE + TMR_ICR(0)); /* clear status */
169 __raw_writel(0x0, TIMERS_VIRT_BASE + TMR_IER(0));
170
171 /* enable timer counter */
172 __raw_writel(cer | 0x01, TIMERS_VIRT_BASE + TMR_CER);
173}
174
175static struct irqaction timer_irq = {
176 .name = "timer",
177 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
178 .handler = timer_interrupt,
179 .dev_id = &ckevt,
180};
181
182void __init timer_init(int irq)
183{
184 timer_config();
185
186 set_tcr2ns_scale(CLOCK_TICK_RATE);
187
188 ckevt.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt.shift);
189 ckevt.max_delta_ns = clockevent_delta2ns(MAX_DELTA, &ckevt);
190 ckevt.min_delta_ns = clockevent_delta2ns(MIN_DELTA, &ckevt);
191 ckevt.cpumask = cpumask_of(0);
192
193 cksrc.mult = clocksource_hz2mult(CLOCK_TICK_RATE, cksrc.shift);
194
195 setup_irq(irq, &timer_irq);
196
197 clocksource_register(&cksrc);
198 clockevents_register_device(&ckevt);
199}