diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2015-06-02 10:57:47 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-06-02 10:57:47 -0400 |
commit | 09cbbf0c169dd81487682622500c81a9012cbeef (patch) | |
tree | 0beb190e21c9ba3ad2f2dbc3ee072e1c8623ad27 | |
parent | be3ef76e9d9b97962c70bd6351787d29071ae481 (diff) | |
parent | d4688bdc6335e9faaf3f0173f96932cd520cee1a (diff) |
Merge branch 'clockevents/4.2' of http://git.linaro.org/people/daniel.lezcano/linux into timers/core
Pull clockevents/clocksource changes from Daniel Lezcano:
- Removed dead code in the files related to mach-msm for qcom (Stephen Boyd)
- Cleaned up code for exynos_mct (Krzysztof Kozlowski)
- Added the new timer lpc3220 (Joachim Eastwood)
- Added the new timer STM32 and ARM system timer (Maxime Coquelin)
-rw-r--r-- | Documentation/devicetree/bindings/arm/armv7m_systick.txt | 26 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt | 26 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/timer/st,stm32-timer.txt | 22 | ||||
-rw-r--r-- | drivers/clocksource/Kconfig | 17 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 3 | ||||
-rw-r--r-- | drivers/clocksource/armv7m_systick.c | 79 | ||||
-rw-r--r-- | drivers/clocksource/exynos_mct.c | 22 | ||||
-rw-r--r-- | drivers/clocksource/qcom-timer.c | 59 | ||||
-rw-r--r-- | drivers/clocksource/time-lpc32xx.c | 272 | ||||
-rw-r--r-- | drivers/clocksource/timer-stm32.c | 184 |
10 files changed, 632 insertions, 78 deletions
diff --git a/Documentation/devicetree/bindings/arm/armv7m_systick.txt b/Documentation/devicetree/bindings/arm/armv7m_systick.txt new file mode 100644 index 000000000000..7cf4a24601eb --- /dev/null +++ b/Documentation/devicetree/bindings/arm/armv7m_systick.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | * ARMv7M System Timer | ||
2 | |||
3 | ARMv7-M includes a system timer, known as SysTick. Current driver only | ||
4 | implements the clocksource feature. | ||
5 | |||
6 | Required properties: | ||
7 | - compatible : Should be "arm,armv7m-systick" | ||
8 | - reg : The address range of the timer | ||
9 | |||
10 | Required clocking property, have to be one of: | ||
11 | - clocks : The input clock of the timer | ||
12 | - clock-frequency : The rate in HZ in input of the ARM SysTick | ||
13 | |||
14 | Examples: | ||
15 | |||
16 | systick: timer@e000e010 { | ||
17 | compatible = "arm,armv7m-systick"; | ||
18 | reg = <0xe000e010 0x10>; | ||
19 | clocks = <&clk_systick>; | ||
20 | }; | ||
21 | |||
22 | systick: timer@e000e010 { | ||
23 | compatible = "arm,armv7m-systick"; | ||
24 | reg = <0xe000e010 0x10>; | ||
25 | clock-frequency = <90000000>; | ||
26 | }; | ||
diff --git a/Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt b/Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt new file mode 100644 index 000000000000..51b05a0e70d1 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | * NXP LPC3220 timer | ||
2 | |||
3 | The NXP LPC3220 timer is used on a wide range of NXP SoCs. This | ||
4 | includes LPC32xx, LPC178x, LPC18xx and LPC43xx parts. | ||
5 | |||
6 | Required properties: | ||
7 | - compatible: | ||
8 | Should be "nxp,lpc3220-timer". | ||
9 | - reg: | ||
10 | Address and length of the register set. | ||
11 | - interrupts: | ||
12 | Reference to the timer interrupt | ||
13 | - clocks: | ||
14 | Should contain a reference to timer clock. | ||
15 | - clock-names: | ||
16 | Should contain "timerclk". | ||
17 | |||
18 | Example: | ||
19 | |||
20 | timer1: timer@40085000 { | ||
21 | compatible = "nxp,lpc3220-timer"; | ||
22 | reg = <0x40085000 0x1000>; | ||
23 | interrupts = <13>; | ||
24 | clocks = <&ccu1 CLK_CPU_TIMER1>; | ||
25 | clock-names = "timerclk"; | ||
26 | }; | ||
diff --git a/Documentation/devicetree/bindings/timer/st,stm32-timer.txt b/Documentation/devicetree/bindings/timer/st,stm32-timer.txt new file mode 100644 index 000000000000..8ef28e70d6e8 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/st,stm32-timer.txt | |||
@@ -0,0 +1,22 @@ | |||
1 | . STMicroelectronics STM32 timer | ||
2 | |||
3 | The STM32 MCUs family has several general-purpose 16 and 32 bits timers. | ||
4 | |||
5 | Required properties: | ||
6 | - compatible : Should be "st,stm32-timer" | ||
7 | - reg : Address and length of the register set | ||
8 | - clocks : Reference on the timer input clock | ||
9 | - interrupts : Reference to the timer interrupt | ||
10 | |||
11 | Optional properties: | ||
12 | - resets: Reference to a reset controller asserting the timer | ||
13 | |||
14 | Example: | ||
15 | |||
16 | timer5: timer@40000c00 { | ||
17 | compatible = "st,stm32-timer"; | ||
18 | reg = <0x40000c00 0x400>; | ||
19 | interrupts = <50>; | ||
20 | resets = <&rrc 259>; | ||
21 | clocks = <&clk_pmtr1>; | ||
22 | }; | ||
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 51d7865fdddb..bec25b39ecf2 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -106,6 +106,16 @@ config CLKSRC_EFM32 | |||
106 | Support to use the timers of EFM32 SoCs as clock source and clock | 106 | Support to use the timers of EFM32 SoCs as clock source and clock |
107 | event device. | 107 | event device. |
108 | 108 | ||
109 | config CLKSRC_LPC32XX | ||
110 | bool | ||
111 | select CLKSRC_MMIO | ||
112 | select CLKSRC_OF | ||
113 | |||
114 | config CLKSRC_STM32 | ||
115 | bool "Clocksource for STM32 SoCs" if COMPILE_TEST | ||
116 | depends on OF | ||
117 | select CLKSRC_MMIO | ||
118 | |||
109 | config ARM_ARCH_TIMER | 119 | config ARM_ARCH_TIMER |
110 | bool | 120 | bool |
111 | select CLKSRC_OF if OF | 121 | select CLKSRC_OF if OF |
@@ -139,6 +149,13 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK | |||
139 | help | 149 | help |
140 | Use ARM global timer clock source as sched_clock | 150 | Use ARM global timer clock source as sched_clock |
141 | 151 | ||
152 | config ARMV7M_SYSTICK | ||
153 | bool | ||
154 | select CLKSRC_OF if OF | ||
155 | select CLKSRC_MMIO | ||
156 | help | ||
157 | This options enables support for the ARMv7M system timer unit | ||
158 | |||
142 | config ATMEL_PIT | 159 | config ATMEL_PIT |
143 | select CLKSRC_OF if OF | 160 | select CLKSRC_OF if OF |
144 | def_bool SOC_AT91SAM9 || SOC_SAMA5 | 161 | def_bool SOC_AT91SAM9 || SOC_SAMA5 |
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 5b85f6adb258..1831a588b988 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
@@ -36,7 +36,9 @@ obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o | |||
36 | obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm_kona_timer.o | 36 | obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm_kona_timer.o |
37 | obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o | 37 | obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o |
38 | obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o | 38 | obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o |
39 | obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o | ||
39 | obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o | 40 | obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o |
41 | obj-$(CONFIG_CLKSRC_LPC32XX) += time-lpc32xx.o | ||
40 | obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o | 42 | obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o |
41 | obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o | 43 | obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o |
42 | obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o | 44 | obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o |
@@ -45,6 +47,7 @@ obj-$(CONFIG_MTK_TIMER) += mtk_timer.o | |||
45 | 47 | ||
46 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o | 48 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o |
47 | obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o | 49 | obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o |
50 | obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o | ||
48 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o | 51 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o |
49 | obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o | 52 | obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o |
50 | obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o | 53 | obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o |
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c new file mode 100644 index 000000000000..addfd2c64f54 --- /dev/null +++ b/drivers/clocksource/armv7m_systick.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Copyright (C) Maxime Coquelin 2015 | ||
3 | * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com> | ||
4 | * License terms: GNU General Public License (GPL), version 2 | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/clocksource.h> | ||
9 | #include <linux/clockchips.h> | ||
10 | #include <linux/of.h> | ||
11 | #include <linux/of_address.h> | ||
12 | #include <linux/clk.h> | ||
13 | #include <linux/bitops.h> | ||
14 | |||
15 | #define SYST_CSR 0x00 | ||
16 | #define SYST_RVR 0x04 | ||
17 | #define SYST_CVR 0x08 | ||
18 | #define SYST_CALIB 0x0c | ||
19 | |||
20 | #define SYST_CSR_ENABLE BIT(0) | ||
21 | |||
22 | #define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF | ||
23 | |||
24 | static void __init system_timer_of_register(struct device_node *np) | ||
25 | { | ||
26 | struct clk *clk = NULL; | ||
27 | void __iomem *base; | ||
28 | u32 rate; | ||
29 | int ret; | ||
30 | |||
31 | base = of_iomap(np, 0); | ||
32 | if (!base) { | ||
33 | pr_warn("system-timer: invalid base address\n"); | ||
34 | return; | ||
35 | } | ||
36 | |||
37 | ret = of_property_read_u32(np, "clock-frequency", &rate); | ||
38 | if (ret) { | ||
39 | clk = of_clk_get(np, 0); | ||
40 | if (IS_ERR(clk)) | ||
41 | goto out_unmap; | ||
42 | |||
43 | ret = clk_prepare_enable(clk); | ||
44 | if (ret) | ||
45 | goto out_clk_put; | ||
46 | |||
47 | rate = clk_get_rate(clk); | ||
48 | if (!rate) | ||
49 | goto out_clk_disable; | ||
50 | } | ||
51 | |||
52 | writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR); | ||
53 | writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR); | ||
54 | |||
55 | ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate, | ||
56 | 200, 24, clocksource_mmio_readl_down); | ||
57 | if (ret) { | ||
58 | pr_err("failed to init clocksource (%d)\n", ret); | ||
59 | if (clk) | ||
60 | goto out_clk_disable; | ||
61 | else | ||
62 | goto out_unmap; | ||
63 | } | ||
64 | |||
65 | pr_info("ARM System timer initialized as clocksource\n"); | ||
66 | |||
67 | return; | ||
68 | |||
69 | out_clk_disable: | ||
70 | clk_disable_unprepare(clk); | ||
71 | out_clk_put: | ||
72 | clk_put(clk); | ||
73 | out_unmap: | ||
74 | iounmap(base); | ||
75 | pr_warn("ARM System timer register failed (%d)\n", ret); | ||
76 | } | ||
77 | |||
78 | CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick", | ||
79 | system_timer_of_register); | ||
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 83564c9cfdbe..935b05936dbd 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c | |||
@@ -209,7 +209,7 @@ static void exynos4_frc_resume(struct clocksource *cs) | |||
209 | exynos4_mct_frc_start(); | 209 | exynos4_mct_frc_start(); |
210 | } | 210 | } |
211 | 211 | ||
212 | struct clocksource mct_frc = { | 212 | static struct clocksource mct_frc = { |
213 | .name = "mct-frc", | 213 | .name = "mct-frc", |
214 | .rating = 400, | 214 | .rating = 400, |
215 | .read = exynos4_frc_read, | 215 | .read = exynos4_frc_read, |
@@ -413,7 +413,7 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode, | |||
413 | } | 413 | } |
414 | } | 414 | } |
415 | 415 | ||
416 | static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) | 416 | static void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) |
417 | { | 417 | { |
418 | struct clock_event_device *evt = &mevt->evt; | 418 | struct clock_event_device *evt = &mevt->evt; |
419 | 419 | ||
@@ -426,12 +426,8 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) | |||
426 | exynos4_mct_tick_stop(mevt); | 426 | exynos4_mct_tick_stop(mevt); |
427 | 427 | ||
428 | /* Clear the MCT tick interrupt */ | 428 | /* Clear the MCT tick interrupt */ |
429 | if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) { | 429 | if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) |
430 | exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); | 430 | exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); |
431 | return 1; | ||
432 | } else { | ||
433 | return 0; | ||
434 | } | ||
435 | } | 431 | } |
436 | 432 | ||
437 | static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) | 433 | static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) |
@@ -564,18 +560,6 @@ out_irq: | |||
564 | free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick); | 560 | free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick); |
565 | } | 561 | } |
566 | 562 | ||
567 | void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1) | ||
568 | { | ||
569 | mct_irqs[MCT_G0_IRQ] = irq_g0; | ||
570 | mct_irqs[MCT_L0_IRQ] = irq_l0; | ||
571 | mct_irqs[MCT_L1_IRQ] = irq_l1; | ||
572 | mct_int_type = MCT_INT_SPI; | ||
573 | |||
574 | exynos4_timer_resources(NULL, base); | ||
575 | exynos4_clocksource_init(); | ||
576 | exynos4_clockevent_init(); | ||
577 | } | ||
578 | |||
579 | static void __init mct_init_dt(struct device_node *np, unsigned int int_type) | 563 | static void __init mct_init_dt(struct device_node *np, unsigned int int_type) |
580 | { | 564 | { |
581 | u32 nr_irqs, i; | 565 | u32 nr_irqs, i; |
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c index 098c542e5c53..cba2d015564c 100644 --- a/drivers/clocksource/qcom-timer.c +++ b/drivers/clocksource/qcom-timer.c | |||
@@ -40,8 +40,6 @@ | |||
40 | 40 | ||
41 | #define GPT_HZ 32768 | 41 | #define GPT_HZ 32768 |
42 | 42 | ||
43 | #define MSM_DGT_SHIFT 5 | ||
44 | |||
45 | static void __iomem *event_base; | 43 | static void __iomem *event_base; |
46 | static void __iomem *sts_base; | 44 | static void __iomem *sts_base; |
47 | 45 | ||
@@ -232,7 +230,6 @@ err: | |||
232 | register_current_timer_delay(&msm_delay_timer); | 230 | register_current_timer_delay(&msm_delay_timer); |
233 | } | 231 | } |
234 | 232 | ||
235 | #ifdef CONFIG_ARCH_QCOM | ||
236 | static void __init msm_dt_timer_init(struct device_node *np) | 233 | static void __init msm_dt_timer_init(struct device_node *np) |
237 | { | 234 | { |
238 | u32 freq; | 235 | u32 freq; |
@@ -285,59 +282,3 @@ static void __init msm_dt_timer_init(struct device_node *np) | |||
285 | } | 282 | } |
286 | CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); | 283 | CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); |
287 | CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); | 284 | CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); |
288 | #else | ||
289 | |||
290 | static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source, | ||
291 | u32 sts) | ||
292 | { | ||
293 | void __iomem *base; | ||
294 | |||
295 | base = ioremap(addr, SZ_256); | ||
296 | if (!base) { | ||
297 | pr_err("Failed to map timer base\n"); | ||
298 | return -ENOMEM; | ||
299 | } | ||
300 | event_base = base + event; | ||
301 | source_base = base + source; | ||
302 | if (sts) | ||
303 | sts_base = base + sts; | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs) | ||
309 | { | ||
310 | /* | ||
311 | * Shift timer count down by a constant due to unreliable lower bits | ||
312 | * on some targets. | ||
313 | */ | ||
314 | return msm_read_timer_count(cs) >> MSM_DGT_SHIFT; | ||
315 | } | ||
316 | |||
317 | void __init msm7x01_timer_init(void) | ||
318 | { | ||
319 | struct clocksource *cs = &msm_clocksource; | ||
320 | |||
321 | if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0)) | ||
322 | return; | ||
323 | cs->read = msm_read_timer_count_shift; | ||
324 | cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); | ||
325 | /* 600 KHz */ | ||
326 | msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7, | ||
327 | false); | ||
328 | } | ||
329 | |||
330 | void __init msm7x30_timer_init(void) | ||
331 | { | ||
332 | if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80)) | ||
333 | return; | ||
334 | msm_timer_init(24576000 / 4, 32, 1, false); | ||
335 | } | ||
336 | |||
337 | void __init qsd8x50_timer_init(void) | ||
338 | { | ||
339 | if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34)) | ||
340 | return; | ||
341 | msm_timer_init(19200000 / 4, 32, 7, false); | ||
342 | } | ||
343 | #endif | ||
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c new file mode 100644 index 000000000000..a1c06a2bc77c --- /dev/null +++ b/drivers/clocksource/time-lpc32xx.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * Clocksource driver for NXP LPC32xx/18xx/43xx timer | ||
3 | * | ||
4 | * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> | ||
5 | * | ||
6 | * Based on: | ||
7 | * time-efm32 Copyright (C) 2013 Pengutronix | ||
8 | * mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors | ||
9 | * | ||
10 | * This file is licensed under the terms of the GNU General Public | ||
11 | * License version 2. This program is licensed "as is" without any | ||
12 | * warranty of any kind, whether express or implied. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #define pr_fmt(fmt) "%s: " fmt, __func__ | ||
17 | |||
18 | #include <linux/clk.h> | ||
19 | #include <linux/clockchips.h> | ||
20 | #include <linux/clocksource.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/irq.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_address.h> | ||
26 | #include <linux/of_irq.h> | ||
27 | #include <linux/sched_clock.h> | ||
28 | |||
29 | #define LPC32XX_TIMER_IR 0x000 | ||
30 | #define LPC32XX_TIMER_IR_MR0INT BIT(0) | ||
31 | #define LPC32XX_TIMER_TCR 0x004 | ||
32 | #define LPC32XX_TIMER_TCR_CEN BIT(0) | ||
33 | #define LPC32XX_TIMER_TCR_CRST BIT(1) | ||
34 | #define LPC32XX_TIMER_TC 0x008 | ||
35 | #define LPC32XX_TIMER_PR 0x00c | ||
36 | #define LPC32XX_TIMER_MCR 0x014 | ||
37 | #define LPC32XX_TIMER_MCR_MR0I BIT(0) | ||
38 | #define LPC32XX_TIMER_MCR_MR0R BIT(1) | ||
39 | #define LPC32XX_TIMER_MCR_MR0S BIT(2) | ||
40 | #define LPC32XX_TIMER_MR0 0x018 | ||
41 | #define LPC32XX_TIMER_CTCR 0x070 | ||
42 | |||
43 | struct lpc32xx_clock_event_ddata { | ||
44 | struct clock_event_device evtdev; | ||
45 | void __iomem *base; | ||
46 | }; | ||
47 | |||
48 | /* Needed for the sched clock */ | ||
49 | static void __iomem *clocksource_timer_counter; | ||
50 | |||
51 | static u64 notrace lpc32xx_read_sched_clock(void) | ||
52 | { | ||
53 | return readl(clocksource_timer_counter); | ||
54 | } | ||
55 | |||
56 | static int lpc32xx_clkevt_next_event(unsigned long delta, | ||
57 | struct clock_event_device *evtdev) | ||
58 | { | ||
59 | struct lpc32xx_clock_event_ddata *ddata = | ||
60 | container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev); | ||
61 | |||
62 | /* | ||
63 | * Place timer in reset and program the delta in the prescale | ||
64 | * register (PR). When the prescale counter matches the value | ||
65 | * in PR the counter register is incremented and the compare | ||
66 | * match will trigger. After setup the timer is released from | ||
67 | * reset and enabled. | ||
68 | */ | ||
69 | writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR); | ||
70 | writel_relaxed(delta, ddata->base + LPC32XX_TIMER_PR); | ||
71 | writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int lpc32xx_clkevt_shutdown(struct clock_event_device *evtdev) | ||
77 | { | ||
78 | struct lpc32xx_clock_event_ddata *ddata = | ||
79 | container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev); | ||
80 | |||
81 | /* Disable the timer */ | ||
82 | writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR); | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int lpc32xx_clkevt_oneshot(struct clock_event_device *evtdev) | ||
88 | { | ||
89 | /* | ||
90 | * When using oneshot, we must also disable the timer | ||
91 | * to wait for the first call to set_next_event(). | ||
92 | */ | ||
93 | return lpc32xx_clkevt_shutdown(evtdev); | ||
94 | } | ||
95 | |||
96 | static irqreturn_t lpc32xx_clock_event_handler(int irq, void *dev_id) | ||
97 | { | ||
98 | struct lpc32xx_clock_event_ddata *ddata = dev_id; | ||
99 | |||
100 | /* Clear match on channel 0 */ | ||
101 | writel_relaxed(LPC32XX_TIMER_IR_MR0INT, ddata->base + LPC32XX_TIMER_IR); | ||
102 | |||
103 | ddata->evtdev.event_handler(&ddata->evtdev); | ||
104 | |||
105 | return IRQ_HANDLED; | ||
106 | } | ||
107 | |||
108 | static struct lpc32xx_clock_event_ddata lpc32xx_clk_event_ddata = { | ||
109 | .evtdev = { | ||
110 | .name = "lpc3220 clockevent", | ||
111 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
112 | .rating = 300, | ||
113 | .set_next_event = lpc32xx_clkevt_next_event, | ||
114 | .set_state_shutdown = lpc32xx_clkevt_shutdown, | ||
115 | .set_state_oneshot = lpc32xx_clkevt_oneshot, | ||
116 | }, | ||
117 | }; | ||
118 | |||
119 | static int __init lpc32xx_clocksource_init(struct device_node *np) | ||
120 | { | ||
121 | void __iomem *base; | ||
122 | unsigned long rate; | ||
123 | struct clk *clk; | ||
124 | int ret; | ||
125 | |||
126 | clk = of_clk_get_by_name(np, "timerclk"); | ||
127 | if (IS_ERR(clk)) { | ||
128 | pr_err("clock get failed (%lu)\n", PTR_ERR(clk)); | ||
129 | return PTR_ERR(clk); | ||
130 | } | ||
131 | |||
132 | ret = clk_prepare_enable(clk); | ||
133 | if (ret) { | ||
134 | pr_err("clock enable failed (%d)\n", ret); | ||
135 | goto err_clk_enable; | ||
136 | } | ||
137 | |||
138 | base = of_iomap(np, 0); | ||
139 | if (!base) { | ||
140 | pr_err("unable to map registers\n"); | ||
141 | ret = -EADDRNOTAVAIL; | ||
142 | goto err_iomap; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Disable and reset timer then set it to free running timer | ||
147 | * mode (CTCR) with no prescaler (PR) or match operations (MCR). | ||
148 | * After setup the timer is released from reset and enabled. | ||
149 | */ | ||
150 | writel_relaxed(LPC32XX_TIMER_TCR_CRST, base + LPC32XX_TIMER_TCR); | ||
151 | writel_relaxed(0, base + LPC32XX_TIMER_PR); | ||
152 | writel_relaxed(0, base + LPC32XX_TIMER_MCR); | ||
153 | writel_relaxed(0, base + LPC32XX_TIMER_CTCR); | ||
154 | writel_relaxed(LPC32XX_TIMER_TCR_CEN, base + LPC32XX_TIMER_TCR); | ||
155 | |||
156 | rate = clk_get_rate(clk); | ||
157 | ret = clocksource_mmio_init(base + LPC32XX_TIMER_TC, "lpc3220 timer", | ||
158 | rate, 300, 32, clocksource_mmio_readl_up); | ||
159 | if (ret) { | ||
160 | pr_err("failed to init clocksource (%d)\n", ret); | ||
161 | goto err_clocksource_init; | ||
162 | } | ||
163 | |||
164 | clocksource_timer_counter = base + LPC32XX_TIMER_TC; | ||
165 | sched_clock_register(lpc32xx_read_sched_clock, 32, rate); | ||
166 | |||
167 | return 0; | ||
168 | |||
169 | err_clocksource_init: | ||
170 | iounmap(base); | ||
171 | err_iomap: | ||
172 | clk_disable_unprepare(clk); | ||
173 | err_clk_enable: | ||
174 | clk_put(clk); | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | static int __init lpc32xx_clockevent_init(struct device_node *np) | ||
179 | { | ||
180 | void __iomem *base; | ||
181 | unsigned long rate; | ||
182 | struct clk *clk; | ||
183 | int ret, irq; | ||
184 | |||
185 | clk = of_clk_get_by_name(np, "timerclk"); | ||
186 | if (IS_ERR(clk)) { | ||
187 | pr_err("clock get failed (%lu)\n", PTR_ERR(clk)); | ||
188 | return PTR_ERR(clk); | ||
189 | } | ||
190 | |||
191 | ret = clk_prepare_enable(clk); | ||
192 | if (ret) { | ||
193 | pr_err("clock enable failed (%d)\n", ret); | ||
194 | goto err_clk_enable; | ||
195 | } | ||
196 | |||
197 | base = of_iomap(np, 0); | ||
198 | if (!base) { | ||
199 | pr_err("unable to map registers\n"); | ||
200 | ret = -EADDRNOTAVAIL; | ||
201 | goto err_iomap; | ||
202 | } | ||
203 | |||
204 | irq = irq_of_parse_and_map(np, 0); | ||
205 | if (!irq) { | ||
206 | pr_err("get irq failed\n"); | ||
207 | ret = -ENOENT; | ||
208 | goto err_irq; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Disable timer and clear any pending interrupt (IR) on match | ||
213 | * channel 0 (MR0). Configure a compare match value of 1 on MR0 | ||
214 | * and enable interrupt, reset on match and stop on match (MCR). | ||
215 | */ | ||
216 | writel_relaxed(0, base + LPC32XX_TIMER_TCR); | ||
217 | writel_relaxed(0, base + LPC32XX_TIMER_CTCR); | ||
218 | writel_relaxed(LPC32XX_TIMER_IR_MR0INT, base + LPC32XX_TIMER_IR); | ||
219 | writel_relaxed(1, base + LPC32XX_TIMER_MR0); | ||
220 | writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R | | ||
221 | LPC32XX_TIMER_MCR_MR0S, base + LPC32XX_TIMER_MCR); | ||
222 | |||
223 | rate = clk_get_rate(clk); | ||
224 | lpc32xx_clk_event_ddata.base = base; | ||
225 | clockevents_config_and_register(&lpc32xx_clk_event_ddata.evtdev, | ||
226 | rate, 1, -1); | ||
227 | |||
228 | ret = request_irq(irq, lpc32xx_clock_event_handler, | ||
229 | IRQF_TIMER | IRQF_IRQPOLL, "lpc3220 clockevent", | ||
230 | &lpc32xx_clk_event_ddata); | ||
231 | if (ret) { | ||
232 | pr_err("request irq failed\n"); | ||
233 | goto err_irq; | ||
234 | } | ||
235 | |||
236 | return 0; | ||
237 | |||
238 | err_irq: | ||
239 | iounmap(base); | ||
240 | err_iomap: | ||
241 | clk_disable_unprepare(clk); | ||
242 | err_clk_enable: | ||
243 | clk_put(clk); | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * This function asserts that we have exactly one clocksource and one | ||
249 | * clock_event_device in the end. | ||
250 | */ | ||
251 | static void __init lpc32xx_timer_init(struct device_node *np) | ||
252 | { | ||
253 | static int has_clocksource, has_clockevent; | ||
254 | int ret; | ||
255 | |||
256 | if (!has_clocksource) { | ||
257 | ret = lpc32xx_clocksource_init(np); | ||
258 | if (!ret) { | ||
259 | has_clocksource = 1; | ||
260 | return; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | if (!has_clockevent) { | ||
265 | ret = lpc32xx_clockevent_init(np); | ||
266 | if (!ret) { | ||
267 | has_clockevent = 1; | ||
268 | return; | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init); | ||
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c new file mode 100644 index 000000000000..a97e8b50701c --- /dev/null +++ b/drivers/clocksource/timer-stm32.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * Copyright (C) Maxime Coquelin 2015 | ||
3 | * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com> | ||
4 | * License terms: GNU General Public License (GPL), version 2 | ||
5 | * | ||
6 | * Inspired by time-efm32.c from Uwe Kleine-Koenig | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/clocksource.h> | ||
11 | #include <linux/clockchips.h> | ||
12 | #include <linux/irq.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/of_irq.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/reset.h> | ||
19 | |||
20 | #define TIM_CR1 0x00 | ||
21 | #define TIM_DIER 0x0c | ||
22 | #define TIM_SR 0x10 | ||
23 | #define TIM_EGR 0x14 | ||
24 | #define TIM_PSC 0x28 | ||
25 | #define TIM_ARR 0x2c | ||
26 | |||
27 | #define TIM_CR1_CEN BIT(0) | ||
28 | #define TIM_CR1_OPM BIT(3) | ||
29 | #define TIM_CR1_ARPE BIT(7) | ||
30 | |||
31 | #define TIM_DIER_UIE BIT(0) | ||
32 | |||
33 | #define TIM_SR_UIF BIT(0) | ||
34 | |||
35 | #define TIM_EGR_UG BIT(0) | ||
36 | |||
37 | struct stm32_clock_event_ddata { | ||
38 | struct clock_event_device evtdev; | ||
39 | unsigned periodic_top; | ||
40 | void __iomem *base; | ||
41 | }; | ||
42 | |||
43 | static void stm32_clock_event_set_mode(enum clock_event_mode mode, | ||
44 | struct clock_event_device *evtdev) | ||
45 | { | ||
46 | struct stm32_clock_event_ddata *data = | ||
47 | container_of(evtdev, struct stm32_clock_event_ddata, evtdev); | ||
48 | void *base = data->base; | ||
49 | |||
50 | switch (mode) { | ||
51 | case CLOCK_EVT_MODE_PERIODIC: | ||
52 | writel_relaxed(data->periodic_top, base + TIM_ARR); | ||
53 | writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1); | ||
54 | break; | ||
55 | |||
56 | case CLOCK_EVT_MODE_ONESHOT: | ||
57 | default: | ||
58 | writel_relaxed(0, base + TIM_CR1); | ||
59 | break; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | static int stm32_clock_event_set_next_event(unsigned long evt, | ||
64 | struct clock_event_device *evtdev) | ||
65 | { | ||
66 | struct stm32_clock_event_ddata *data = | ||
67 | container_of(evtdev, struct stm32_clock_event_ddata, evtdev); | ||
68 | |||
69 | writel_relaxed(evt, data->base + TIM_ARR); | ||
70 | writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN, | ||
71 | data->base + TIM_CR1); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id) | ||
77 | { | ||
78 | struct stm32_clock_event_ddata *data = dev_id; | ||
79 | |||
80 | writel_relaxed(0, data->base + TIM_SR); | ||
81 | |||
82 | data->evtdev.event_handler(&data->evtdev); | ||
83 | |||
84 | return IRQ_HANDLED; | ||
85 | } | ||
86 | |||
87 | static struct stm32_clock_event_ddata clock_event_ddata = { | ||
88 | .evtdev = { | ||
89 | .name = "stm32 clockevent", | ||
90 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, | ||
91 | .set_mode = stm32_clock_event_set_mode, | ||
92 | .set_next_event = stm32_clock_event_set_next_event, | ||
93 | .rating = 200, | ||
94 | }, | ||
95 | }; | ||
96 | |||
97 | static void __init stm32_clockevent_init(struct device_node *np) | ||
98 | { | ||
99 | struct stm32_clock_event_ddata *data = &clock_event_ddata; | ||
100 | struct clk *clk; | ||
101 | struct reset_control *rstc; | ||
102 | unsigned long rate, max_delta; | ||
103 | int irq, ret, bits, prescaler = 1; | ||
104 | |||
105 | clk = of_clk_get(np, 0); | ||
106 | if (IS_ERR(clk)) { | ||
107 | ret = PTR_ERR(clk); | ||
108 | pr_err("failed to get clock for clockevent (%d)\n", ret); | ||
109 | goto err_clk_get; | ||
110 | } | ||
111 | |||
112 | ret = clk_prepare_enable(clk); | ||
113 | if (ret) { | ||
114 | pr_err("failed to enable timer clock for clockevent (%d)\n", | ||
115 | ret); | ||
116 | goto err_clk_enable; | ||
117 | } | ||
118 | |||
119 | rate = clk_get_rate(clk); | ||
120 | |||
121 | rstc = of_reset_control_get(np, NULL); | ||
122 | if (!IS_ERR(rstc)) { | ||
123 | reset_control_assert(rstc); | ||
124 | reset_control_deassert(rstc); | ||
125 | } | ||
126 | |||
127 | data->base = of_iomap(np, 0); | ||
128 | if (!data->base) { | ||
129 | pr_err("failed to map registers for clockevent\n"); | ||
130 | goto err_iomap; | ||
131 | } | ||
132 | |||
133 | irq = irq_of_parse_and_map(np, 0); | ||
134 | if (!irq) { | ||
135 | pr_err("%s: failed to get irq.\n", np->full_name); | ||
136 | goto err_get_irq; | ||
137 | } | ||
138 | |||
139 | /* Detect whether the timer is 16 or 32 bits */ | ||
140 | writel_relaxed(~0U, data->base + TIM_ARR); | ||
141 | max_delta = readl_relaxed(data->base + TIM_ARR); | ||
142 | if (max_delta == ~0U) { | ||
143 | prescaler = 1; | ||
144 | bits = 32; | ||
145 | } else { | ||
146 | prescaler = 1024; | ||
147 | bits = 16; | ||
148 | } | ||
149 | writel_relaxed(0, data->base + TIM_ARR); | ||
150 | |||
151 | writel_relaxed(prescaler - 1, data->base + TIM_PSC); | ||
152 | writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR); | ||
153 | writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER); | ||
154 | writel_relaxed(0, data->base + TIM_SR); | ||
155 | |||
156 | data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ); | ||
157 | |||
158 | clockevents_config_and_register(&data->evtdev, | ||
159 | DIV_ROUND_CLOSEST(rate, prescaler), | ||
160 | 0x1, max_delta); | ||
161 | |||
162 | ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER, | ||
163 | "stm32 clockevent", data); | ||
164 | if (ret) { | ||
165 | pr_err("%s: failed to request irq.\n", np->full_name); | ||
166 | goto err_get_irq; | ||
167 | } | ||
168 | |||
169 | pr_info("%s: STM32 clockevent driver initialized (%d bits)\n", | ||
170 | np->full_name, bits); | ||
171 | |||
172 | return; | ||
173 | |||
174 | err_get_irq: | ||
175 | iounmap(data->base); | ||
176 | err_iomap: | ||
177 | clk_disable_unprepare(clk); | ||
178 | err_clk_enable: | ||
179 | clk_put(clk); | ||
180 | err_clk_get: | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init); | ||