aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 17:58:40 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 17:58:40 -0500
commitb274776c54c320763bc12eb035c0e244f76ccb43 (patch)
treec75b70d0824a7ae029229b19d61884039abf2127 /drivers/clocksource
parentb24174b0cbbe383c5bb6097aeb24480b8fd2d338 (diff)
parent3b1209e7994c4d31ff9932a7f566ae1c96b3c443 (diff)
Merge tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC cleanups from Arnd Bergmann: "A large number of cleanups, all over the platforms. This is dominated largely by the Samsung platforms (s3c, s5p, exynos) and a few of the others moving code out of arch/arm into more appropriate subsystems. The clocksource and irqchip drivers are now abstracted to the point where platforms that are already cleaned up do not need to even specify the driver they use, it can all get configured from the device tree as we do for normal device drivers. The clocksource changes basically touch every single platform in the process. We further clean up the use of platform specific header files here, with the goal of turning more of the platforms over to being "multiplatform" enabled, which implies that they cannot expose their headers to architecture independent code any more. It is expected that no functional changes are part of the cleanup. The overall reduction in total code lines is mostly the result of removing broken and obsolete code." * tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (133 commits) ARM: mvebu: correct gated clock documentation ARM: kirkwood: add missing include for nsa310 ARM: exynos: move exynos4210-combiner to drivers/irqchip mfd: db8500-prcmu: update resource passing drivers/db8500-cpufreq: delete dangling include ARM: at91: remove NEOCORE 926 board sunxi: Cleanup the reset code and add meaningful registers defines ARM: S3C24XX: header mach/regs-mem.h local ARM: S3C24XX: header mach/regs-power.h local ARM: S3C24XX: header mach/regs-s3c2412-mem.h local ARM: S3C24XX: Remove plat-s3c24xx directory in arch/arm/ ARM: S3C24XX: transform s3c2443 subirqs into new structure ARM: S3C24XX: modify s3c2443 irq init to initialize all irqs ARM: S3C24XX: move s3c2443 irq code to irq.c ARM: S3C24XX: transform s3c2416 irqs into new structure ARM: S3C24XX: modify s3c2416 irq init to initialize all irqs ARM: S3C24XX: move s3c2416 irq init to common irq code ARM: S3C24XX: Modify s3c_irq_wake to use the hwirq property ARM: S3C24XX: Move irq syscore-ops to irq-pm clocksource: always define CLOCKSOURCE_OF_DECLARE ...
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig6
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/bcm2835_timer.c9
-rw-r--r--drivers/clocksource/clksrc-of.c35
-rw-r--r--drivers/clocksource/cs5535-clockevt.c11
-rw-r--r--drivers/clocksource/dw_apb_timer_of.c6
-rw-r--r--drivers/clocksource/nomadik-mtu.c33
-rw-r--r--drivers/clocksource/sunxi_timer.c17
-rw-r--r--drivers/clocksource/tcb_clksrc.c7
-rw-r--r--drivers/clocksource/tegra20_timer.c281
-rw-r--r--drivers/clocksource/vt8500_timer.c180
11 files changed, 535 insertions, 53 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 7fdcbd3f4da5..7d978c1bd528 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -1,3 +1,6 @@
1config CLKSRC_OF
2 bool
3
1config CLKSRC_I8253 4config CLKSRC_I8253
2 bool 5 bool
3 6
@@ -25,6 +28,9 @@ config ARMADA_370_XP_TIMER
25config SUNXI_TIMER 28config SUNXI_TIMER
26 bool 29 bool
27 30
31config VT8500_TIMER
32 bool
33
28config CLKSRC_NOMADIK_MTU 34config CLKSRC_NOMADIK_MTU
29 bool 35 bool
30 depends on (ARCH_NOMADIK || ARCH_U8500) 36 depends on (ARCH_NOMADIK || ARCH_U8500)
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index f93453d01673..596c45c2f192 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -1,3 +1,4 @@
1obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o
1obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o 2obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
2obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o 3obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
3obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o 4obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
@@ -16,5 +17,7 @@ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
16obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o 17obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
17obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o 18obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
18obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o 19obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o
20obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
21obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
19 22
20obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o 23obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o
diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
index bc19f12c20ce..50c68fef944b 100644
--- a/drivers/clocksource/bcm2835_timer.c
+++ b/drivers/clocksource/bcm2835_timer.c
@@ -16,7 +16,6 @@
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */ 17 */
18 18
19#include <linux/bcm2835_timer.h>
20#include <linux/bitops.h> 19#include <linux/bitops.h>
21#include <linux/clockchips.h> 20#include <linux/clockchips.h>
22#include <linux/clocksource.h> 21#include <linux/clocksource.h>
@@ -101,7 +100,7 @@ static struct of_device_id bcm2835_time_match[] __initconst = {
101 {} 100 {}
102}; 101};
103 102
104static void __init bcm2835_time_init(void) 103static void __init bcm2835_timer_init(void)
105{ 104{
106 struct device_node *node; 105 struct device_node *node;
107 void __iomem *base; 106 void __iomem *base;
@@ -155,7 +154,5 @@ static void __init bcm2835_time_init(void)
155 154
156 pr_info("bcm2835: system timer (irq = %d)\n", irq); 155 pr_info("bcm2835: system timer (irq = %d)\n", irq);
157} 156}
158 157CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
159struct sys_timer bcm2835_timer = { 158 bcm2835_timer_init);
160 .init = bcm2835_time_init,
161};
diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c
new file mode 100644
index 000000000000..bdabdaa8d00f
--- /dev/null
+++ b/drivers/clocksource/clksrc-of.c
@@ -0,0 +1,35 @@
1/*
2 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <linux/init.h>
18#include <linux/of.h>
19
20extern struct of_device_id __clksrc_of_table[];
21
22static const struct of_device_id __clksrc_of_table_sentinel
23 __used __section(__clksrc_of_table_end);
24
25void __init clocksource_of_init(void)
26{
27 struct device_node *np;
28 const struct of_device_id *match;
29 void (*init_func)(void);
30
31 for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
32 init_func = match->data;
33 init_func();
34 }
35}
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
index d9279385304d..ea210482dd20 100644
--- a/drivers/clocksource/cs5535-clockevt.c
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -100,7 +100,6 @@ static struct clock_event_device cs5535_clockevent = {
100 .set_mode = mfgpt_set_mode, 100 .set_mode = mfgpt_set_mode,
101 .set_next_event = mfgpt_next_event, 101 .set_next_event = mfgpt_next_event,
102 .rating = 250, 102 .rating = 250,
103 .shift = 32
104}; 103};
105 104
106static irqreturn_t mfgpt_tick(int irq, void *dev_id) 105static irqreturn_t mfgpt_tick(int irq, void *dev_id)
@@ -169,17 +168,11 @@ static int __init cs5535_mfgpt_init(void)
169 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val); 168 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
170 169
171 /* Set up the clock event */ 170 /* Set up the clock event */
172 cs5535_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC,
173 cs5535_clockevent.shift);
174 cs5535_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
175 &cs5535_clockevent);
176 cs5535_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
177 &cs5535_clockevent);
178
179 printk(KERN_INFO DRV_NAME 171 printk(KERN_INFO DRV_NAME
180 ": Registering MFGPT timer as a clock event, using IRQ %d\n", 172 ": Registering MFGPT timer as a clock event, using IRQ %d\n",
181 timer_irq); 173 timer_irq);
182 clockevents_register_device(&cs5535_clockevent); 174 clockevents_config_and_register(&cs5535_clockevent, MFGPT_HZ,
175 0xF, 0xFFFE);
183 176
184 return 0; 177 return 0;
185 178
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index f7dba5b79b44..ab09ed3742ee 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -107,7 +107,7 @@ static const struct of_device_id osctimer_ids[] __initconst = {
107 {}, 107 {},
108}; 108};
109 109
110static void __init timer_init(void) 110void __init dw_apb_timer_init(void)
111{ 111{
112 struct device_node *event_timer, *source_timer; 112 struct device_node *event_timer, *source_timer;
113 113
@@ -125,7 +125,3 @@ static void __init timer_init(void)
125 125
126 init_sched_clock(); 126 init_sched_clock();
127} 127}
128
129struct sys_timer dw_apb_timer = {
130 .init = timer_init,
131};
diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
index 8914c3c1c88b..025afc6dd324 100644
--- a/drivers/clocksource/nomadik-mtu.c
+++ b/drivers/clocksource/nomadik-mtu.c
@@ -134,12 +134,32 @@ static void nmdk_clkevt_mode(enum clock_event_mode mode,
134 } 134 }
135} 135}
136 136
137void nmdk_clksrc_reset(void)
138{
139 /* Disable */
140 writel(0, mtu_base + MTU_CR(0));
141
142 /* ClockSource: configure load and background-load, and fire it up */
143 writel(nmdk_cycle, mtu_base + MTU_LR(0));
144 writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
145
146 writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
147 mtu_base + MTU_CR(0));
148}
149
150static void nmdk_clkevt_resume(struct clock_event_device *cedev)
151{
152 nmdk_clkevt_reset();
153 nmdk_clksrc_reset();
154}
155
137static struct clock_event_device nmdk_clkevt = { 156static struct clock_event_device nmdk_clkevt = {
138 .name = "mtu_1", 157 .name = "mtu_1",
139 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, 158 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
140 .rating = 200, 159 .rating = 200,
141 .set_mode = nmdk_clkevt_mode, 160 .set_mode = nmdk_clkevt_mode,
142 .set_next_event = nmdk_clkevt_next, 161 .set_next_event = nmdk_clkevt_next,
162 .resume = nmdk_clkevt_resume,
143}; 163};
144 164
145/* 165/*
@@ -161,19 +181,6 @@ static struct irqaction nmdk_timer_irq = {
161 .dev_id = &nmdk_clkevt, 181 .dev_id = &nmdk_clkevt,
162}; 182};
163 183
164void nmdk_clksrc_reset(void)
165{
166 /* Disable */
167 writel(0, mtu_base + MTU_CR(0));
168
169 /* ClockSource: configure load and background-load, and fire it up */
170 writel(nmdk_cycle, mtu_base + MTU_LR(0));
171 writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
172
173 writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
174 mtu_base + MTU_CR(0));
175}
176
177void __init nmdk_timer_init(void __iomem *base, int irq) 184void __init nmdk_timer_init(void __iomem *base, int irq)
178{ 185{
179 unsigned long rate; 186 unsigned long rate;
diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c
index 93d09d0e009f..4086b9167159 100644
--- a/drivers/clocksource/sunxi_timer.c
+++ b/drivers/clocksource/sunxi_timer.c
@@ -74,7 +74,6 @@ static int sunxi_clkevt_next_event(unsigned long evt,
74 74
75static struct clock_event_device sunxi_clockevent = { 75static struct clock_event_device sunxi_clockevent = {
76 .name = "sunxi_tick", 76 .name = "sunxi_tick",
77 .shift = 32,
78 .rating = 300, 77 .rating = 300,
79 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 78 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
80 .set_mode = sunxi_clkevt_mode, 79 .set_mode = sunxi_clkevt_mode,
@@ -104,7 +103,7 @@ static struct of_device_id sunxi_timer_dt_ids[] = {
104 { } 103 { }
105}; 104};
106 105
107static void __init sunxi_timer_init(void) 106void __init sunxi_timer_init(void)
108{ 107{
109 struct device_node *node; 108 struct device_node *node;
110 unsigned long rate = 0; 109 unsigned long rate = 0;
@@ -154,18 +153,8 @@ static void __init sunxi_timer_init(void)
154 val = readl(timer_base + TIMER_CTL_REG); 153 val = readl(timer_base + TIMER_CTL_REG);
155 writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); 154 writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG);
156 155
157 sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL,
158 NSEC_PER_SEC,
159 sunxi_clockevent.shift);
160 sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff,
161 &sunxi_clockevent);
162 sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1,
163 &sunxi_clockevent);
164 sunxi_clockevent.cpumask = cpumask_of(0); 156 sunxi_clockevent.cpumask = cpumask_of(0);
165 157
166 clockevents_register_device(&sunxi_clockevent); 158 clockevents_config_and_register(&sunxi_clockevent, rate / TIMER_SCAL,
159 0x1, 0xff);
167} 160}
168
169struct sys_timer sunxi_timer = {
170 .init = sunxi_timer_init,
171};
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index 32cb929b8eb6..8a6187225dd0 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -157,7 +157,6 @@ static struct tc_clkevt_device clkevt = {
157 .name = "tc_clkevt", 157 .name = "tc_clkevt",
158 .features = CLOCK_EVT_FEAT_PERIODIC 158 .features = CLOCK_EVT_FEAT_PERIODIC
159 | CLOCK_EVT_FEAT_ONESHOT, 159 | CLOCK_EVT_FEAT_ONESHOT,
160 .shift = 32,
161 /* Should be lower than at91rm9200's system timer */ 160 /* Should be lower than at91rm9200's system timer */
162 .rating = 125, 161 .rating = 125,
163 .set_next_event = tc_next_event, 162 .set_next_event = tc_next_event,
@@ -196,13 +195,9 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
196 195
197 timer_clock = clk32k_divisor_idx; 196 timer_clock = clk32k_divisor_idx;
198 197
199 clkevt.clkevt.mult = div_sc(32768, NSEC_PER_SEC, clkevt.clkevt.shift);
200 clkevt.clkevt.max_delta_ns
201 = clockevent_delta2ns(0xffff, &clkevt.clkevt);
202 clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1;
203 clkevt.clkevt.cpumask = cpumask_of(0); 198 clkevt.clkevt.cpumask = cpumask_of(0);
204 199
205 clockevents_register_device(&clkevt.clkevt); 200 clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff);
206 201
207 setup_irq(irq, &tc_irqaction); 202 setup_irq(irq, &tc_irqaction);
208} 203}
diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
new file mode 100644
index 000000000000..0bde03feb095
--- /dev/null
+++ b/drivers/clocksource/tegra20_timer.c
@@ -0,0 +1,281 @@
1/*
2 * Copyright (C) 2010 Google, Inc.
3 *
4 * Author:
5 * Colin Cross <ccross@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/init.h>
19#include <linux/err.h>
20#include <linux/time.h>
21#include <linux/interrupt.h>
22#include <linux/irq.h>
23#include <linux/clockchips.h>
24#include <linux/clocksource.h>
25#include <linux/clk.h>
26#include <linux/io.h>
27#include <linux/of_address.h>
28#include <linux/of_irq.h>
29
30#include <asm/mach/time.h>
31#include <asm/smp_twd.h>
32#include <asm/sched_clock.h>
33
34#define RTC_SECONDS 0x08
35#define RTC_SHADOW_SECONDS 0x0c
36#define RTC_MILLISECONDS 0x10
37
38#define TIMERUS_CNTR_1US 0x10
39#define TIMERUS_USEC_CFG 0x14
40#define TIMERUS_CNTR_FREEZE 0x4c
41
42#define TIMER1_BASE 0x0
43#define TIMER2_BASE 0x8
44#define TIMER3_BASE 0x50
45#define TIMER4_BASE 0x58
46
47#define TIMER_PTV 0x0
48#define TIMER_PCR 0x4
49
50static void __iomem *timer_reg_base;
51static void __iomem *rtc_base;
52
53static struct timespec persistent_ts;
54static u64 persistent_ms, last_persistent_ms;
55
56#define timer_writel(value, reg) \
57 __raw_writel(value, timer_reg_base + (reg))
58#define timer_readl(reg) \
59 __raw_readl(timer_reg_base + (reg))
60
61static int tegra_timer_set_next_event(unsigned long cycles,
62 struct clock_event_device *evt)
63{
64 u32 reg;
65
66 reg = 0x80000000 | ((cycles > 1) ? (cycles-1) : 0);
67 timer_writel(reg, TIMER3_BASE + TIMER_PTV);
68
69 return 0;
70}
71
72static void tegra_timer_set_mode(enum clock_event_mode mode,
73 struct clock_event_device *evt)
74{
75 u32 reg;
76
77 timer_writel(0, TIMER3_BASE + TIMER_PTV);
78
79 switch (mode) {
80 case CLOCK_EVT_MODE_PERIODIC:
81 reg = 0xC0000000 | ((1000000/HZ)-1);
82 timer_writel(reg, TIMER3_BASE + TIMER_PTV);
83 break;
84 case CLOCK_EVT_MODE_ONESHOT:
85 break;
86 case CLOCK_EVT_MODE_UNUSED:
87 case CLOCK_EVT_MODE_SHUTDOWN:
88 case CLOCK_EVT_MODE_RESUME:
89 break;
90 }
91}
92
93static struct clock_event_device tegra_clockevent = {
94 .name = "timer0",
95 .rating = 300,
96 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
97 .set_next_event = tegra_timer_set_next_event,
98 .set_mode = tegra_timer_set_mode,
99};
100
101static u32 notrace tegra_read_sched_clock(void)
102{
103 return timer_readl(TIMERUS_CNTR_1US);
104}
105
106/*
107 * tegra_rtc_read - Reads the Tegra RTC registers
108 * Care must be taken that this funciton is not called while the
109 * tegra_rtc driver could be executing to avoid race conditions
110 * on the RTC shadow register
111 */
112static u64 tegra_rtc_read_ms(void)
113{
114 u32 ms = readl(rtc_base + RTC_MILLISECONDS);
115 u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
116 return (u64)s * MSEC_PER_SEC + ms;
117}
118
119/*
120 * tegra_read_persistent_clock - Return time from a persistent clock.
121 *
122 * Reads the time from a source which isn't disabled during PM, the
123 * 32k sync timer. Convert the cycles elapsed since last read into
124 * nsecs and adds to a monotonically increasing timespec.
125 * Care must be taken that this funciton is not called while the
126 * tegra_rtc driver could be executing to avoid race conditions
127 * on the RTC shadow register
128 */
129static void tegra_read_persistent_clock(struct timespec *ts)
130{
131 u64 delta;
132 struct timespec *tsp = &persistent_ts;
133
134 last_persistent_ms = persistent_ms;
135 persistent_ms = tegra_rtc_read_ms();
136 delta = persistent_ms - last_persistent_ms;
137
138 timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
139 *ts = *tsp;
140}
141
142static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
143{
144 struct clock_event_device *evt = (struct clock_event_device *)dev_id;
145 timer_writel(1<<30, TIMER3_BASE + TIMER_PCR);
146 evt->event_handler(evt);
147 return IRQ_HANDLED;
148}
149
150static struct irqaction tegra_timer_irq = {
151 .name = "timer0",
152 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH,
153 .handler = tegra_timer_interrupt,
154 .dev_id = &tegra_clockevent,
155};
156
157static const struct of_device_id timer_match[] __initconst = {
158 { .compatible = "nvidia,tegra20-timer" },
159 {}
160};
161
162static const struct of_device_id rtc_match[] __initconst = {
163 { .compatible = "nvidia,tegra20-rtc" },
164 {}
165};
166
167static void __init tegra20_init_timer(void)
168{
169 struct device_node *np;
170 struct clk *clk;
171 unsigned long rate;
172 int ret;
173
174 np = of_find_matching_node(NULL, timer_match);
175 if (!np) {
176 pr_err("Failed to find timer DT node\n");
177 BUG();
178 }
179
180 timer_reg_base = of_iomap(np, 0);
181 if (!timer_reg_base) {
182 pr_err("Can't map timer registers\n");
183 BUG();
184 }
185
186 tegra_timer_irq.irq = irq_of_parse_and_map(np, 2);
187 if (tegra_timer_irq.irq <= 0) {
188 pr_err("Failed to map timer IRQ\n");
189 BUG();
190 }
191
192 clk = clk_get_sys("timer", NULL);
193 if (IS_ERR(clk)) {
194 pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n");
195 rate = 12000000;
196 } else {
197 clk_prepare_enable(clk);
198 rate = clk_get_rate(clk);
199 }
200
201 of_node_put(np);
202
203 np = of_find_matching_node(NULL, rtc_match);
204 if (!np) {
205 pr_err("Failed to find RTC DT node\n");
206 BUG();
207 }
208
209 rtc_base = of_iomap(np, 0);
210 if (!rtc_base) {
211 pr_err("Can't map RTC registers");
212 BUG();
213 }
214
215 /*
216 * rtc registers are used by read_persistent_clock, keep the rtc clock
217 * enabled
218 */
219 clk = clk_get_sys("rtc-tegra", NULL);
220 if (IS_ERR(clk))
221 pr_warn("Unable to get rtc-tegra clock\n");
222 else
223 clk_prepare_enable(clk);
224
225 of_node_put(np);
226
227 switch (rate) {
228 case 12000000:
229 timer_writel(0x000b, TIMERUS_USEC_CFG);
230 break;
231 case 13000000:
232 timer_writel(0x000c, TIMERUS_USEC_CFG);
233 break;
234 case 19200000:
235 timer_writel(0x045f, TIMERUS_USEC_CFG);
236 break;
237 case 26000000:
238 timer_writel(0x0019, TIMERUS_USEC_CFG);
239 break;
240 default:
241 WARN(1, "Unknown clock rate");
242 }
243
244 setup_sched_clock(tegra_read_sched_clock, 32, 1000000);
245
246 if (clocksource_mmio_init(timer_reg_base + TIMERUS_CNTR_1US,
247 "timer_us", 1000000, 300, 32, clocksource_mmio_readl_up)) {
248 pr_err("Failed to register clocksource\n");
249 BUG();
250 }
251
252 ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq);
253 if (ret) {
254 pr_err("Failed to register timer IRQ: %d\n", ret);
255 BUG();
256 }
257
258 tegra_clockevent.cpumask = cpu_all_mask;
259 tegra_clockevent.irq = tegra_timer_irq.irq;
260 clockevents_config_and_register(&tegra_clockevent, 1000000,
261 0x1, 0x1fffffff);
262#ifdef CONFIG_HAVE_ARM_TWD
263 twd_local_timer_of_register();
264#endif
265 register_persistent_clock(NULL, tegra_read_persistent_clock);
266}
267CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer);
268
269#ifdef CONFIG_PM
270static u32 usec_config;
271
272void tegra_timer_suspend(void)
273{
274 usec_config = timer_readl(TIMERUS_USEC_CFG);
275}
276
277void tegra_timer_resume(void)
278{
279 timer_writel(usec_config, TIMERUS_USEC_CFG);
280}
281#endif
diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c
new file mode 100644
index 000000000000..8efc86b5b5dd
--- /dev/null
+++ b/drivers/clocksource/vt8500_timer.c
@@ -0,0 +1,180 @@
1/*
2 * arch/arm/mach-vt8500/timer.c
3 *
4 * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
5 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22/*
23 * This file is copied and modified from the original timer.c provided by
24 * Alexey Charkov. Minor changes have been made for Device Tree Support.
25 */
26
27#include <linux/io.h>
28#include <linux/irq.h>
29#include <linux/interrupt.h>
30#include <linux/clocksource.h>
31#include <linux/clockchips.h>
32#include <linux/delay.h>
33#include <asm/mach/time.h>
34
35#include <linux/of.h>
36#include <linux/of_address.h>
37#include <linux/of_irq.h>
38
39#define VT8500_TIMER_OFFSET 0x0100
40#define VT8500_TIMER_HZ 3000000
41#define TIMER_MATCH_VAL 0x0000
42#define TIMER_COUNT_VAL 0x0010
43#define TIMER_STATUS_VAL 0x0014
44#define TIMER_IER_VAL 0x001c /* interrupt enable */
45#define TIMER_CTRL_VAL 0x0020
46#define TIMER_AS_VAL 0x0024 /* access status */
47#define TIMER_COUNT_R_ACTIVE (1 << 5) /* not ready for read */
48#define TIMER_COUNT_W_ACTIVE (1 << 4) /* not ready for write */
49#define TIMER_MATCH_W_ACTIVE (1 << 0) /* not ready for write */
50
51#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
52
53static void __iomem *regbase;
54
55static cycle_t vt8500_timer_read(struct clocksource *cs)
56{
57 int loops = msecs_to_loops(10);
58 writel(3, regbase + TIMER_CTRL_VAL);
59 while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
60 && --loops)
61 cpu_relax();
62 return readl(regbase + TIMER_COUNT_VAL);
63}
64
65static struct clocksource clocksource = {
66 .name = "vt8500_timer",
67 .rating = 200,
68 .read = vt8500_timer_read,
69 .mask = CLOCKSOURCE_MASK(32),
70 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
71};
72
73static int vt8500_timer_set_next_event(unsigned long cycles,
74 struct clock_event_device *evt)
75{
76 int loops = msecs_to_loops(10);
77 cycle_t alarm = clocksource.read(&clocksource) + cycles;
78 while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
79 && --loops)
80 cpu_relax();
81 writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
82
83 if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
84 return -ETIME;
85
86 writel(1, regbase + TIMER_IER_VAL);
87
88 return 0;
89}
90
91static void vt8500_timer_set_mode(enum clock_event_mode mode,
92 struct clock_event_device *evt)
93{
94 switch (mode) {
95 case CLOCK_EVT_MODE_RESUME:
96 case CLOCK_EVT_MODE_PERIODIC:
97 break;
98 case CLOCK_EVT_MODE_ONESHOT:
99 case CLOCK_EVT_MODE_UNUSED:
100 case CLOCK_EVT_MODE_SHUTDOWN:
101 writel(readl(regbase + TIMER_CTRL_VAL) | 1,
102 regbase + TIMER_CTRL_VAL);
103 writel(0, regbase + TIMER_IER_VAL);
104 break;
105 }
106}
107
108static struct clock_event_device clockevent = {
109 .name = "vt8500_timer",
110 .features = CLOCK_EVT_FEAT_ONESHOT,
111 .rating = 200,
112 .set_next_event = vt8500_timer_set_next_event,
113 .set_mode = vt8500_timer_set_mode,
114};
115
116static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
117{
118 struct clock_event_device *evt = dev_id;
119 writel(0xf, regbase + TIMER_STATUS_VAL);
120 evt->event_handler(evt);
121
122 return IRQ_HANDLED;
123}
124
125static struct irqaction irq = {
126 .name = "vt8500_timer",
127 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
128 .handler = vt8500_timer_interrupt,
129 .dev_id = &clockevent,
130};
131
132static struct of_device_id vt8500_timer_ids[] = {
133 { .compatible = "via,vt8500-timer" },
134 { }
135};
136
137static void __init vt8500_timer_init(void)
138{
139 struct device_node *np;
140 int timer_irq;
141
142 np = of_find_matching_node(NULL, vt8500_timer_ids);
143 if (!np) {
144 pr_err("%s: Timer description missing from Device Tree\n",
145 __func__);
146 return;
147 }
148 regbase = of_iomap(np, 0);
149 if (!regbase) {
150 pr_err("%s: Missing iobase description in Device Tree\n",
151 __func__);
152 of_node_put(np);
153 return;
154 }
155 timer_irq = irq_of_parse_and_map(np, 0);
156 if (!timer_irq) {
157 pr_err("%s: Missing irq description in Device Tree\n",
158 __func__);
159 of_node_put(np);
160 return;
161 }
162
163 writel(1, regbase + TIMER_CTRL_VAL);
164 writel(0xf, regbase + TIMER_STATUS_VAL);
165 writel(~0, regbase + TIMER_MATCH_VAL);
166
167 if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
168 pr_err("%s: vt8500_timer_init: clocksource_register failed for %s\n",
169 __func__, clocksource.name);
170
171 clockevent.cpumask = cpumask_of(0);
172
173 if (setup_irq(timer_irq, &irq))
174 pr_err("%s: setup_irq failed for %s\n", __func__,
175 clockevent.name);
176 clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
177 4, 0xf0000000);
178}
179
180CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init)