From 2cf2ff9f1fcdca404885bc55292b2046eabc039a Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 14 Mar 2014 10:18:19 +0000 Subject: clocksource: arm_global_timer: Only check for unusable timer on A9 The check for a usable global timer in the probe code does not enquire which CPU we are currently running on. This can cause the driver to incorrectly assume we have an unusable global timer if we are running on a CPU other than A9. Before checking the CPU revision, ensure we are running on an A9 CPU. Acked-by: Will Deacon Signed-off-by: Matthew Leach Signed-off-by: Daniel Lezcano --- drivers/clocksource/arm_global_timer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c index 0fc31d029e52..60e5a170c4d2 100644 --- a/drivers/clocksource/arm_global_timer.c +++ b/drivers/clocksource/arm_global_timer.c @@ -246,11 +246,12 @@ static void __init global_timer_of_register(struct device_node *np) int err = 0; /* - * In r2p0 the comparators for each processor with the global timer + * In A9 r2p0 the comparators for each processor with the global timer * fire when the timer value is greater than or equal to. In previous * revisions the comparators fired when the timer value was equal to. */ - if ((read_cpuid_id() & 0xf0000f) < 0x200000) { + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9 + && (read_cpuid_id() & 0xf0000f) < 0x200000) { pr_warn("global-timer: non support for this cpu version.\n"); return; } -- cgit v1.2.3 From 63cc122381bda123c2b1b0137cca09f66e5be660 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 25 Mar 2014 15:50:44 +0100 Subject: clocksource: efm32: use $vendor,$device scheme for compatible string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wolfram Sang pointed out that "efm32,$device" is non-standard. So use the common scheme and prefix device with "efm32-". The old compatible string is left in place until arch/arm/boot/dts/efm32* is fixed. Acked-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Uwe Kleine-König Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/efm32,timer.txt | 23 ---------------------- .../bindings/timer/energymicro,efm32-timer.txt | 23 ++++++++++++++++++++++ drivers/clocksource/time-efm32.c | 3 ++- 3 files changed, 25 insertions(+), 24 deletions(-) delete mode 100644 Documentation/devicetree/bindings/timer/efm32,timer.txt create mode 100644 Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt (limited to 'drivers/clocksource') diff --git a/Documentation/devicetree/bindings/timer/efm32,timer.txt b/Documentation/devicetree/bindings/timer/efm32,timer.txt deleted file mode 100644 index 97a568f696c9..000000000000 --- a/Documentation/devicetree/bindings/timer/efm32,timer.txt +++ /dev/null @@ -1,23 +0,0 @@ -* EFM32 timer hardware - -The efm32 Giant Gecko SoCs come with four 16 bit timers. Two counters can be -connected to form a 32 bit counter. Each timer has three Compare/Capture -channels and can be used as PWM or Quadrature Decoder. Available clock sources -are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin. - -Required properties: -- compatible : Should be efm32,timer -- reg : Address and length of the register set -- clocks : Should contain a reference to the HFPERCLK - -Optional properties: -- interrupts : Reference to the timer interrupt - -Example: - -timer@40010c00 { - compatible = "efm32,timer"; - reg = <0x40010c00 0x400>; - interrupts = <14>; - clocks = <&cmu clk_HFPERCLKTIMER3>; -}; diff --git a/Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt b/Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt new file mode 100644 index 000000000000..e502c11b2211 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/energymicro,efm32-timer.txt @@ -0,0 +1,23 @@ +* EFM32 timer hardware + +The efm32 Giant Gecko SoCs come with four 16 bit timers. Two counters can be +connected to form a 32 bit counter. Each timer has three Compare/Capture +channels and can be used as PWM or Quadrature Decoder. Available clock sources +are the cpu's HFPERCLK (with a 10-bit prescaler) or an external pin. + +Required properties: +- compatible : Should be "energymicro,efm32-timer" +- reg : Address and length of the register set +- clocks : Should contain a reference to the HFPERCLK + +Optional properties: +- interrupts : Reference to the timer interrupt + +Example: + +timer@40010c00 { + compatible = "energymicro,efm32-timer"; + reg = <0x40010c00 0x400>; + interrupts = <14>; + clocks = <&cmu clk_HFPERCLKTIMER3>; +}; diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c index 1a6205b7bed3..bba62f9deefb 100644 --- a/drivers/clocksource/time-efm32.c +++ b/drivers/clocksource/time-efm32.c @@ -272,4 +272,5 @@ static void __init efm32_timer_init(struct device_node *np) } } } -CLOCKSOURCE_OF_DECLARE(efm32, "efm32,timer", efm32_timer_init); +CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init); +CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init); -- cgit v1.2.3 From e50a00be5c420b4f28836dec281cdde4bed832a2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 17 Apr 2014 11:06:45 +0200 Subject: clocksource: sun5i: Add support for reset controller The Allwinner A31 that uses this timer has the timer IP asserted in reset. Add an optional reset property to the DT, and deassert the timer from reset if it's there. Signed-off-by: Maxime Ripard Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt | 4 ++++ drivers/clocksource/timer-sun5i.c | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'drivers/clocksource') diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt index 7c26154b8bbb..27cfc7d7ccd7 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt +++ b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.txt @@ -9,6 +9,9 @@ Required properties: one) - clocks: phandle to the source clock (usually the AHB clock) +Optionnal properties: +- resets: phandle to a reset controller asserting the timer + Example: timer@01c60000 { @@ -19,4 +22,5 @@ timer@01c60000 { <0 53 1>, <0 54 1>; clocks = <&ahb1_gates 19>; + resets = <&ahb1rst 19>; }; diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index deebcd6469fc..02268448dc85 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +144,7 @@ static u64 sun5i_timer_sched_read(void) static void __init sun5i_timer_init(struct device_node *node) { + struct reset_control *rstc; unsigned long rate; struct clk *clk; int ret, irq; @@ -162,6 +164,10 @@ static void __init sun5i_timer_init(struct device_node *node) clk_prepare_enable(clk); rate = clk_get_rate(clk); + rstc = of_reset_control_get(node, NULL); + if (!IS_ERR(rstc)) + reset_control_deassert(rstc); + writel(~0, timer_base + TIMER_INTVAL_LO_REG(1)); writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, timer_base + TIMER_CTL_REG(1)); -- cgit v1.2.3 From 013be5adf5cd6e6d58eb72f3bb0137a78be152c8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 13 May 2014 16:01:00 -0700 Subject: clocksource: qcom: Implement read_current_timer for udelay Setup the same timer used as the clocksource to be used as the read_current_timer implementation. This allows us to support a stable udelay implementation on MSMs where it's possible for the CPUs to scale speeds independently of one another. Signed-off-by: Stephen Boyd Signed-off-by: Daniel Lezcano --- drivers/clocksource/qcom-timer.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c index e807acf4c665..8d115db1e651 100644 --- a/drivers/clocksource/qcom-timer.c +++ b/drivers/clocksource/qcom-timer.c @@ -26,6 +26,8 @@ #include #include +#include + #define TIMER_MATCH_VAL 0x0000 #define TIMER_COUNT_VAL 0x0004 #define TIMER_ENABLE 0x0008 @@ -179,6 +181,15 @@ static u64 notrace msm_sched_clock_read(void) return msm_clocksource.read(&msm_clocksource); } +static unsigned long msm_read_current_timer(void) +{ + return msm_clocksource.read(&msm_clocksource); +} + +static struct delay_timer msm_delay_timer = { + .read_current_timer = msm_read_current_timer, +}; + static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, bool percpu) { @@ -217,6 +228,8 @@ err: if (res) pr_err("clocksource_register failed\n"); sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); + msm_delay_timer.freq = dgt_hz; + register_current_timer_delay(&msm_delay_timer); } #ifdef CONFIG_ARCH_QCOM -- cgit v1.2.3 From c54697ae180e652a96db8bc71f0c7c7baee2bf16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 16 May 2014 14:44:23 +0200 Subject: clocksource: sh_tmu: Fix channel IRQ retrieval in legacy case In the legacy platform data case each TMU platform device handles a single channel with a single IRQ for the platform device. Retrieve the IRQ using the logical channel number instead of the hardware channel number. Signed-off-by: Laurent Pinchart Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 4ba2c0fea580..ec340955e852 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -498,7 +498,7 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index, ch->base = tmu->mapbase + 8 + ch->index * 12; } - ch->irq = platform_get_irq(tmu->pdev, ch->index); + ch->irq = platform_get_irq(tmu->pdev, index); if (ch->irq < 0) { dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n", ch->index); -- cgit v1.2.3 From 4a3ae07413844733e4cb3765f67a752b846df913 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 23 Apr 2014 10:11:59 +0800 Subject: clocksource: Fix type confusion for clocksource_mmio_readX_Y The types' definations are: o cycle_t -> u64 o readl_relaxed -> u32 o readw_relaxed -> u16 So let clocksource_mmio_readX_Ys return a cast to cycle_t, though this maybe look reduntant sometimes, it make sense and they will be more readable and less confusion... This patch clarifies the functions type and fix it. Signed-off-by: Xiubo Li Cc: Daniel Lezcano Signed-off-by: Daniel Lezcano --- drivers/clocksource/mmio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c index c0e25125a55e..19a6b3f4d9cf 100644 --- a/drivers/clocksource/mmio.c +++ b/drivers/clocksource/mmio.c @@ -22,22 +22,22 @@ static inline struct clocksource_mmio *to_mmio_clksrc(struct clocksource *c) cycle_t clocksource_mmio_readl_up(struct clocksource *c) { - return readl_relaxed(to_mmio_clksrc(c)->reg); + return (cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg); } cycle_t clocksource_mmio_readl_down(struct clocksource *c) { - return ~readl_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg); } cycle_t clocksource_mmio_readw_up(struct clocksource *c) { - return readw_relaxed(to_mmio_clksrc(c)->reg); + return (cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg); } cycle_t clocksource_mmio_readw_down(struct clocksource *c) { - return ~(unsigned)readw_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg); } /** -- cgit v1.2.3 From 95c19a06ec1cf9530ebb8f2c7eeda1d7398b43f1 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 23 Apr 2014 10:12:00 +0800 Subject: clocksource: Fix clocksource_mmio_readX_down For some clocksource devices, for example, the registers are 32-bit, while the lower 16-bit is used for timer counting(And reading the upper 16-bit will return 0). For example, when the counter value is 0x00001111, and then the ~readl_relaxed(to_mmio_clksrc(c)->reg) will return the value of 0xFFFFEEEE, but it should be 0x0000EEEE. So just using the c->mask to mask the unused bits. Signed-off-by: Xiubo Li Signed-off-by: Daniel Lezcano --- drivers/clocksource/mmio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c index 19a6b3f4d9cf..1593ade2a815 100644 --- a/drivers/clocksource/mmio.c +++ b/drivers/clocksource/mmio.c @@ -27,7 +27,7 @@ cycle_t clocksource_mmio_readl_up(struct clocksource *c) cycle_t clocksource_mmio_readl_down(struct clocksource *c) { - return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readl_relaxed(to_mmio_clksrc(c)->reg) & c->mask; } cycle_t clocksource_mmio_readw_up(struct clocksource *c) @@ -37,7 +37,7 @@ cycle_t clocksource_mmio_readw_up(struct clocksource *c) cycle_t clocksource_mmio_readw_down(struct clocksource *c) { - return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg); + return ~(cycle_t)readw_relaxed(to_mmio_clksrc(c)->reg) & c->mask; } /** -- cgit v1.2.3 From 0d24d1f2495ec4d6996c70c8edec202053cf7e69 Mon Sep 17 00:00:00 2001 From: Yang Wei Date: Tue, 13 May 2014 11:10:08 +0800 Subject: clocksource: dw_apb_timer_of: Do not trace read_sched_clock We do not need to trace read_sched_clock function, so add notrace attribute for this function. Signed-off-by: Yang Wei Signed-off-by: Daniel Lezcano --- drivers/clocksource/dw_apb_timer_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 2a2ea2717f3a..d305fb089767 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -106,7 +106,7 @@ static void __init add_clocksource(struct device_node *source_timer) sched_rate = rate; } -static u64 read_sched_clock(void) +static u64 notrace read_sched_clock(void) { return ~__raw_readl(sched_io_base); } -- cgit v1.2.3 From 39dd56776e3583d08bdfaabae2171a1a70a679c9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:06 +0200 Subject: clocksource: em_sti: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/em_sti.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index 9d170834fcf3..d0a7bd66b8b9 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -318,10 +318,8 @@ static int em_sti_probe(struct platform_device *pdev) int irq; p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); - if (p == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (p == NULL) return -ENOMEM; - } p->pdev = pdev; platform_set_drvdata(pdev, p); -- cgit v1.2.3 From 0178f41d3d35b63ed25a066d90e7dda380018c06 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:06 +0200 Subject: clocksource: sh_cmt: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. [dlezcano] : refreshed against latest modifications: kmalloc -> kzalloc Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_cmt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index bc8d025ce861..dfa780396b91 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -1106,10 +1106,8 @@ static int sh_cmt_probe(struct platform_device *pdev) } cmt = kzalloc(sizeof(*cmt), GFP_KERNEL); - if (cmt == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (cmt == NULL) return -ENOMEM; - } ret = sh_cmt_setup(cmt, pdev); if (ret) { -- cgit v1.2.3 From c77a565b2966567b97d589e90a6b9ce725bb15b1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:07 +0200 Subject: clocksource: sh_mtu2: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. [dlezcano] : refreshed against latest modifications: kmalloc -> kzalloc Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_mtu2.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index f2c1c36139e1..188d4e092efc 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -533,10 +533,8 @@ static int sh_mtu2_probe(struct platform_device *pdev) } mtu = kzalloc(sizeof(*mtu), GFP_KERNEL); - if (mtu == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (mtu == NULL) return -ENOMEM; - } ret = sh_mtu2_setup(mtu, pdev); if (ret) { -- cgit v1.2.3 From 814876b0b00ae98a8568d1b989bc98ca5389b98a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 14:05:07 +0200 Subject: clocksource: sh_tmu: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. [dlezcano] : refreshed against latest modifications: kmalloc -> kzalloc Signed-off-by: Jingoo Han Signed-off-by: Daniel Lezcano --- drivers/clocksource/sh_tmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 4ba2c0fea580..a38022fa286f 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -644,10 +644,8 @@ static int sh_tmu_probe(struct platform_device *pdev) } tmu = kzalloc(sizeof(*tmu), GFP_KERNEL); - if (tmu == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + if (tmu == NULL) return -ENOMEM; - } ret = sh_tmu_setup(tmu, pdev); if (ret) { -- cgit v1.2.3 From 2529c3a330797000d699d70c9a65b8525c6652de Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 23 May 2014 10:12:04 +0200 Subject: clocksource: Add Freescale FlexTimer Module (FTM) timer support The Freescale FlexTimer Module time reference is a 16-bit counter that can be used as an unsigned or signed increase counter. CNTIN defines the starting value of the count and MOD defines the final value of the count. The value of CNTIN is loaded into the FTM counter, and the counter increments until the value of MOD is reached, at which point the counter is reloaded with the value of CNTIN. That's also when an overflow interrupt will be generated. Here using the 'evt' prefix or postfix as clock event device and the 'src' as clock source device. Signed-off-by: Xiubo Li Cc: Shawn Guo Cc: Jingchang Lu Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 5 + drivers/clocksource/Makefile | 1 + drivers/clocksource/fsl_ftm_timer.c | 367 ++++++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 drivers/clocksource/fsl_ftm_timer.c (limited to 'drivers/clocksource') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 96918e1f26a3..04377675c3fa 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -136,6 +136,11 @@ config CLKSRC_SAMSUNG_PWM for all devicetree enabled platforms. This driver will be needed only on systems that do not have the Exynos MCT available. +config FSL_FTM_TIMER + bool + help + Support for Freescale FlexTimer Module (FTM) timer. + config VF_PIT_TIMER bool help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 98cb6c51aa87..0770916a818e 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o +obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c new file mode 100644 index 000000000000..454227d4f895 --- /dev/null +++ b/drivers/clocksource/fsl_ftm_timer.c @@ -0,0 +1,367 @@ +/* + * Freescale FlexTimer Module (FTM) timer driver. + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTM_SC 0x00 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_TOIE BIT(6) +#define FTM_SC_TOF BIT(7) + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 +#define FTM_CNTIN 0x4C + +#define FTM_PS_MAX 7 + +struct ftm_clock_device { + void __iomem *clksrc_base; + void __iomem *clkevt_base; + unsigned long periodic_cyc; + unsigned long ps; + bool big_endian; +}; + +static struct ftm_clock_device *priv; + +static inline u32 ftm_readl(void __iomem *addr) +{ + if (priv->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void ftm_writel(u32 val, void __iomem *addr) +{ + if (priv->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline void ftm_counter_enable(void __iomem *base) +{ + u32 val; + + /* select and enable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= priv->ps | FTM_SC_CLK(1); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_counter_disable(void __iomem *base) +{ + u32 val; + + /* disable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_acknowledge(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOF; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_enable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val |= FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_disable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_reset_counter(void __iomem *base) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + ftm_writel(0x00, base + FTM_CNT); +} + +static u64 ftm_read_sched_clock(void) +{ + return ftm_readl(priv->clksrc_base + FTM_CNT); +} + +static int ftm_set_next_event(unsigned long delta, + struct clock_event_device *unused) +{ + /* + * The CNNIN and MOD are all double buffer registers, writing + * to the MOD register latches the value into a buffer. The MOD + * register is updated with the value of its write buffer with + * the following scenario: + * a, the counter source clock is diabled. + */ + ftm_counter_disable(priv->clkevt_base); + + /* Force the value of CNTIN to be loaded into the FTM counter */ + ftm_reset_counter(priv->clkevt_base); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the delta - 1. + */ + ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD); + + ftm_counter_enable(priv->clkevt_base); + + ftm_irq_enable(priv->clkevt_base); + + return 0; +} + +static void ftm_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ftm_set_next_event(priv->periodic_cyc, evt); + break; + case CLOCK_EVT_MODE_ONESHOT: + ftm_counter_disable(priv->clkevt_base); + break; + default: + return; + } +} + +static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + ftm_irq_acknowledge(priv->clkevt_base); + + if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) { + ftm_irq_disable(priv->clkevt_base); + ftm_counter_disable(priv->clkevt_base); + } + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct clock_event_device ftm_clockevent = { + .name = "Freescale ftm timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = ftm_set_mode, + .set_next_event = ftm_set_next_event, + .rating = 300, +}; + +static struct irqaction ftm_timer_irq = { + .name = "Freescale ftm timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = ftm_evt_interrupt, + .dev_id = &ftm_clockevent, +}; + +static int __init ftm_clockevent_init(unsigned long freq, int irq) +{ + int err; + + ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clkevt_base + FTM_MOD); + + ftm_reset_counter(priv->clkevt_base); + + err = setup_irq(irq, &ftm_timer_irq); + if (err) { + pr_err("ftm: setup irq failed: %d\n", err); + return err; + } + + ftm_clockevent.cpumask = cpumask_of(0); + ftm_clockevent.irq = irq; + + clockevents_config_and_register(&ftm_clockevent, + freq / (1 << priv->ps), + 1, 0xffff); + + ftm_counter_enable(priv->clkevt_base); + + return 0; +} + +static int __init ftm_clocksource_init(unsigned long freq) +{ + int err; + + ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clksrc_base + FTM_MOD); + + ftm_reset_counter(priv->clksrc_base); + + sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps)); + err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm", + freq / (1 << priv->ps), 300, 16, + clocksource_mmio_readl_up); + if (err) { + pr_err("ftm: init clock source mmio failed: %d\n", err); + return err; + } + + ftm_counter_enable(priv->clksrc_base); + + return 0; +} + +static int __init __ftm_clk_init(struct device_node *np, char *cnt_name, + char *ftm_name) +{ + struct clk *clk; + int err; + + clk = of_clk_get_by_name(np, cnt_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) { + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + cnt_name, err); + return err; + } + + clk = of_clk_get_by_name(np, ftm_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + ftm_name, err); + + return clk_get_rate(clk); +} + +static unsigned long __init ftm_clk_init(struct device_node *np) +{ + unsigned long freq; + + freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt"); + if (freq <= 0) + return 0; + + freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src"); + if (freq <= 0) + return 0; + + return freq; +} + +static int __init ftm_calc_closest_round_cyc(unsigned long freq) +{ + priv->ps = 0; + + /* The counter register is only using the lower 16 bits, and + * if the 'freq' value is to big here, then the periodic_cyc + * may exceed 0xFFFF. + */ + do { + priv->periodic_cyc = DIV_ROUND_CLOSEST(freq, + HZ * (1 << priv->ps++)); + } while (priv->periodic_cyc > 0xFFFF); + + if (priv->ps > FTM_PS_MAX) { + pr_err("ftm: the prescaler is %lu > %d\n", + priv->ps, FTM_PS_MAX); + return -EINVAL; + } + + return 0; +} + +static void __init ftm_timer_init(struct device_node *np) +{ + unsigned long freq; + int irq; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return; + + priv->clkevt_base = of_iomap(np, 0); + if (!priv->clkevt_base) { + pr_err("ftm: unable to map event timer registers\n"); + goto err; + } + + priv->clksrc_base = of_iomap(np, 1); + if (!priv->clksrc_base) { + pr_err("ftm: unable to map source timer registers\n"); + goto err; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("ftm: unable to get IRQ from DT, %d\n", irq); + goto err; + } + + priv->big_endian = of_property_read_bool(np, "big-endian"); + + freq = ftm_clk_init(np); + if (!freq) + goto err; + + if (ftm_calc_closest_round_cyc(freq)) + goto err; + + if (ftm_clocksource_init(freq)) + goto err; + + if (ftm_clockevent_init(freq, irq)) + goto err; + + return; + +err: + kfree(priv); +} +CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init); -- cgit v1.2.3