diff options
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Kconfig | 6 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 3 | ||||
-rw-r--r-- | drivers/clocksource/bcm2835_timer.c | 9 | ||||
-rw-r--r-- | drivers/clocksource/clksrc-of.c | 35 | ||||
-rw-r--r-- | drivers/clocksource/cs5535-clockevt.c | 11 | ||||
-rw-r--r-- | drivers/clocksource/dw_apb_timer_of.c | 6 | ||||
-rw-r--r-- | drivers/clocksource/nomadik-mtu.c | 33 | ||||
-rw-r--r-- | drivers/clocksource/sunxi_timer.c | 17 | ||||
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 7 | ||||
-rw-r--r-- | drivers/clocksource/tegra20_timer.c | 281 | ||||
-rw-r--r-- | drivers/clocksource/vt8500_timer.c | 180 |
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 @@ | |||
1 | config CLKSRC_OF | ||
2 | bool | ||
3 | |||
1 | config CLKSRC_I8253 | 4 | config CLKSRC_I8253 |
2 | bool | 5 | bool |
3 | 6 | ||
@@ -25,6 +28,9 @@ config ARMADA_370_XP_TIMER | |||
25 | config SUNXI_TIMER | 28 | config SUNXI_TIMER |
26 | bool | 29 | bool |
27 | 30 | ||
31 | config VT8500_TIMER | ||
32 | bool | ||
33 | |||
28 | config CLKSRC_NOMADIK_MTU | 34 | config 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 @@ | |||
1 | obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o | ||
1 | obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o | 2 | obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o |
2 | obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o | 3 | obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o |
3 | obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o | 4 | obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o |
@@ -16,5 +17,7 @@ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o | |||
16 | obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o | 17 | obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o |
17 | obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o | 18 | obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o |
18 | obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o | 19 | obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o |
20 | obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o | ||
21 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o | ||
19 | 22 | ||
20 | obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o | 23 | obj-$(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 | ||
104 | static void __init bcm2835_time_init(void) | 103 | static 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 | 157 | CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer", | |
159 | struct 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 | |||
20 | extern struct of_device_id __clksrc_of_table[]; | ||
21 | |||
22 | static const struct of_device_id __clksrc_of_table_sentinel | ||
23 | __used __section(__clksrc_of_table_end); | ||
24 | |||
25 | void __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 | ||
106 | static irqreturn_t mfgpt_tick(int irq, void *dev_id) | 105 | static 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 | ||
110 | static void __init timer_init(void) | 110 | void __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 | |||
129 | struct 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 | ||
137 | void 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 | |||
150 | static void nmdk_clkevt_resume(struct clock_event_device *cedev) | ||
151 | { | ||
152 | nmdk_clkevt_reset(); | ||
153 | nmdk_clksrc_reset(); | ||
154 | } | ||
155 | |||
137 | static struct clock_event_device nmdk_clkevt = { | 156 | static 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 | ||
164 | void 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 | |||
177 | void __init nmdk_timer_init(void __iomem *base, int irq) | 184 | void __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 | ||
75 | static struct clock_event_device sunxi_clockevent = { | 75 | static 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 | ||
107 | static void __init sunxi_timer_init(void) | 106 | void __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 | |||
169 | struct 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 | |||
50 | static void __iomem *timer_reg_base; | ||
51 | static void __iomem *rtc_base; | ||
52 | |||
53 | static struct timespec persistent_ts; | ||
54 | static 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 | |||
61 | static 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 | |||
72 | static 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 | |||
93 | static 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 | |||
101 | static 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 | */ | ||
112 | static 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 | */ | ||
129 | static 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 | |||
142 | static 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 | |||
150 | static 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 | |||
157 | static const struct of_device_id timer_match[] __initconst = { | ||
158 | { .compatible = "nvidia,tegra20-timer" }, | ||
159 | {} | ||
160 | }; | ||
161 | |||
162 | static const struct of_device_id rtc_match[] __initconst = { | ||
163 | { .compatible = "nvidia,tegra20-rtc" }, | ||
164 | {} | ||
165 | }; | ||
166 | |||
167 | static 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 | } | ||
267 | CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer); | ||
268 | |||
269 | #ifdef CONFIG_PM | ||
270 | static u32 usec_config; | ||
271 | |||
272 | void tegra_timer_suspend(void) | ||
273 | { | ||
274 | usec_config = timer_readl(TIMERUS_USEC_CFG); | ||
275 | } | ||
276 | |||
277 | void 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 | |||
53 | static void __iomem *regbase; | ||
54 | |||
55 | static 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 | |||
65 | static 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 | |||
73 | static 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 | |||
91 | static 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 | |||
108 | static 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 | |||
116 | static 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 | |||
125 | static 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 | |||
132 | static struct of_device_id vt8500_timer_ids[] = { | ||
133 | { .compatible = "via,vt8500-timer" }, | ||
134 | { } | ||
135 | }; | ||
136 | |||
137 | static 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 | |||
180 | CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init) | ||