[v3,04/15] clocksource: Add ARM System timer driver
diff mbox

Message ID 1426197361-19290-5-git-send-email-maxime.coquelin@st.com
State New
Headers show

Commit Message

Maxime Coquelin March 12, 2015, 9:55 p.m. UTC
From: Maxime Coquelin <mcoquelin.stm32@gmail.com>

This patch adds clocksource support for ARMv7-M's System timer,
also known as SysTick.

Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
---
 drivers/clocksource/Kconfig          |  7 ++++
 drivers/clocksource/Makefile         |  1 +
 drivers/clocksource/armv7m_systick.c | 78 ++++++++++++++++++++++++++++++++++++
 3 files changed, 86 insertions(+)
 create mode 100644 drivers/clocksource/armv7m_systick.c

Comments

Daniel Lezcano March 26, 2015, 9:50 a.m. UTC | #1
On 03/12/2015 10:55 PM, Maxime Coquelin wrote:
> From: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>
> This patch adds clocksource support for ARMv7-M's System timer,
> also known as SysTick.
>
> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>

Hi Maxime,

the driver looks good. Three comments below.

   -- Daniel

> ---
>   drivers/clocksource/Kconfig          |  7 ++++
>   drivers/clocksource/Makefile         |  1 +
>   drivers/clocksource/armv7m_systick.c | 78 ++++++++++++++++++++++++++++++++++++
>   3 files changed, 86 insertions(+)
>   create mode 100644 drivers/clocksource/armv7m_systick.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 1c2506f..b82e58b 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -134,6 +134,13 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
>   	help
>   	 Use ARM global timer clock source as sched_clock
>
> +config ARMV7M_SYSTICK
> +	bool
> +	select CLKSRC_OF if OF
> +	select CLKSRC_MMIO
> +	help
> +	  This options enables support for the ARMv7M system timer unit
> +
>   config ATMEL_PIT
>   	select CLKSRC_OF if OF
>   	def_bool SOC_AT91SAM9 || SOC_SAMA5
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 752d5c7..1c9a643 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_MTK_TIMER)		+= mtk_timer.o
>
>   obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o
>   obj-$(CONFIG_ARM_GLOBAL_TIMER)		+= arm_global_timer.o
> +obj-$(CONFIG_ARMV7M_SYSTICK)		+= armv7m_systick.o
>   obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
>   obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST)	+= dummy_timer.o
>   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 0000000..23d8249
> --- /dev/null
> +++ b/drivers/clocksource/armv7m_systick.c
> @@ -0,0 +1,78 @@
> +/*
> + * Copyright (C) Maxime Coquelin 2015
> + * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/clocksource.h>
> +#include <linux/clockchips.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/clk.h>
> +#include <linux/bitops.h>
> +
> +#define SYST_CSR	0x00
> +#define SYST_RVR	0x04
> +#define SYST_CVR	0x08
> +#define SYST_CALIB	0x0c
> +
> +#define SYST_CSR_ENABLE BIT(0)
> +
> +#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
> +
> +static void __init system_timer_of_register(struct device_node *np)
> +{
> +	struct clk *clk;
> +	void __iomem *base;
> +	u32 rate = 0;
> +	int ret;
> +
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		pr_warn("system-timer: invalid base address\n");
> +		return;
> +	}
> +
> +	clk = of_clk_get(np, 0);
> +	if (!IS_ERR(clk)) {
> +		ret = clk_prepare_enable(clk);
> +		if (ret) {
> +			clk_put(clk);
> +			goto out_unmap;
> +		}
> +
> +		rate = clk_get_rate(clk);
> +	}
> +
> +	/* If no clock found, try to get clock-frequency property */
> +	if (!rate) {
> +		ret = of_property_read_u32(np, "clock-frequency", &rate);
> +		if (ret)
> +			goto out_unmap;

Shouldn't be 'goto out_clk_disable' ?

> +	}
> +
> +	writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
> +	writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
> +
> +	ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
> +			200, 24, clocksource_mmio_readl_down);
> +	if (ret) {
> +		pr_err("failed to init clocksource (%d)\n", ret);
> +		goto out_clk_disable;
> +	}
> +
> +	pr_info("ARM System timer initialized as clocksource\n");
> +
> +	return;
> +
> +out_clk_disable:
> +	if (!IS_ERR(clk))

Why do you need this check ?

It isn't missing a clk_put ?

> +		clk_disable_unprepare(clk);
> +out_unmap:
> +	iounmap(base);
> +	WARN(ret, "ARM System timer register failed (%d)\n", ret);
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
> +			system_timer_of_register);
>
Maxime Coquelin March 26, 2015, 8:19 p.m. UTC | #2
Hi Daniel,

  Thanks for the review. Please find my answers below.

2015-03-26 10:50 GMT+01:00 Daniel Lezcano <daniel.lezcano@linaro.org>:
> On 03/12/2015 10:55 PM, Maxime Coquelin wrote:
>>
>> From: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>>
>> This patch adds clocksource support for ARMv7-M's System timer,
>> also known as SysTick.
>>
>> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>
>
> Hi Maxime,
>
> the driver looks good. Three comments below.
>
>   -- Daniel
>
>
>> ---
>>   drivers/clocksource/Kconfig          |  7 ++++
>>   drivers/clocksource/Makefile         |  1 +
>>   drivers/clocksource/armv7m_systick.c | 78
>> ++++++++++++++++++++++++++++++++++++
>>   3 files changed, 86 insertions(+)
>>   create mode 100644 drivers/clocksource/armv7m_systick.c
>>
>> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
>> index 1c2506f..b82e58b 100644
>> --- a/drivers/clocksource/Kconfig
>> +++ b/drivers/clocksource/Kconfig
>> @@ -134,6 +134,13 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
>>         help
>>          Use ARM global timer clock source as sched_clock
>>
>> +config ARMV7M_SYSTICK
>> +       bool
>> +       select CLKSRC_OF if OF
>> +       select CLKSRC_MMIO
>> +       help
>> +         This options enables support for the ARMv7M system timer unit
>> +
>>   config ATMEL_PIT
>>         select CLKSRC_OF if OF
>>         def_bool SOC_AT91SAM9 || SOC_SAMA5
>> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
>> index 752d5c7..1c9a643 100644
>> --- a/drivers/clocksource/Makefile
>> +++ b/drivers/clocksource/Makefile
>> @@ -44,6 +44,7 @@ obj-$(CONFIG_MTK_TIMER)               += mtk_timer.o
>>
>>   obj-$(CONFIG_ARM_ARCH_TIMER)          += arm_arch_timer.o
>>   obj-$(CONFIG_ARM_GLOBAL_TIMER)                += arm_global_timer.o
>> +obj-$(CONFIG_ARMV7M_SYSTICK)           += armv7m_systick.o
>>   obj-$(CONFIG_CLKSRC_METAG_GENERIC)    += metag_generic.o
>>   obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
>>   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 0000000..23d8249
>> --- /dev/null
>> +++ b/drivers/clocksource/armv7m_systick.c
>> @@ -0,0 +1,78 @@
>> +/*
>> + * Copyright (C) Maxime Coquelin 2015
>> + * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/clocksource.h>
>> +#include <linux/clockchips.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/clk.h>
>> +#include <linux/bitops.h>
>> +
>> +#define SYST_CSR       0x00
>> +#define SYST_RVR       0x04
>> +#define SYST_CVR       0x08
>> +#define SYST_CALIB     0x0c
>> +
>> +#define SYST_CSR_ENABLE BIT(0)
>> +
>> +#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
>> +
>> +static void __init system_timer_of_register(struct device_node *np)
>> +{
>> +       struct clk *clk;
>> +       void __iomem *base;
>> +       u32 rate = 0;
>> +       int ret;
>> +
>> +       base = of_iomap(np, 0);
>> +       if (!base) {
>> +               pr_warn("system-timer: invalid base address\n");
>> +               return;
>> +       }
>> +
>> +       clk = of_clk_get(np, 0);
>> +       if (!IS_ERR(clk)) {
>> +               ret = clk_prepare_enable(clk);
>> +               if (ret) {
>> +                       clk_put(clk);
>> +                       goto out_unmap;
>> +               }
>> +
>> +               rate = clk_get_rate(clk);
>> +       }
>> +
>> +       /* If no clock found, try to get clock-frequency property */
>> +       if (!rate) {
>> +               ret = of_property_read_u32(np, "clock-frequency", &rate);
>> +               if (ret)
>> +                       goto out_unmap;
>
>
> Shouldn't be 'goto out_clk_disable' ?

No, because I assumed !rate means we failed to get the clock.
Actually, clk_get_rate could return 0, so relying on rate value is not safe.

I propose to get clock-frequency property if IS_ERR(clk).

Is it fine for you?

>
>> +       }
>> +
>> +       writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
>> +       writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
>> +
>> +       ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer",
>> rate,
>> +                       200, 24, clocksource_mmio_readl_down);
>> +       if (ret) {
>> +               pr_err("failed to init clocksource (%d)\n", ret);
>> +               goto out_clk_disable;
>> +       }
>> +
>> +       pr_info("ARM System timer initialized as clocksource\n");
>> +
>> +       return;
>> +
>> +out_clk_disable:
>> +       if (!IS_ERR(clk))
>
>
> Why do you need this check ?

To handle the case were no clock was found, but a clk-frequency value
was provided.

>
> It isn't missing a clk_put ?

Right, thanks for spotting this.

I wonder if it makes sense to implement the error path.
If we fail to initialize the clocksource, the system will be unusable.

Maybe I should just perform a BUG_ON() in the error cases, as most of
the other clocksource drivers do.
What is your view?

Regards,
Maxime

>
>> +               clk_disable_unprepare(clk);
>> +out_unmap:
>> +       iounmap(base);
>> +       WARN(ret, "ARM System timer register failed (%d)\n", ret);
>> +}
>> +
>> +CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
>> +                       system_timer_of_register);
>>
>
>
> --
>  <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
>
> Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
> <http://twitter.com/#!/linaroorg> Twitter |
> <http://www.linaro.org/linaro-blog/> Blog
>
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Daniel Lezcano March 27, 2015, 8:36 a.m. UTC | #3
On 03/26/2015 09:19 PM, Maxime Coquelin wrote:
> Hi Daniel,
>
>    Thanks for the review. Please find my answers below.
>
> 2015-03-26 10:50 GMT+01:00 Daniel Lezcano <daniel.lezcano@linaro.org>:
>> On 03/12/2015 10:55 PM, Maxime Coquelin wrote:
>>>
>>> From: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>>>
>>> This patch adds clocksource support for ARMv7-M's System timer,
>>> also known as SysTick.
>>>
>>> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>>
>>
>> Hi Maxime,
>>
>> the driver looks good. Three comments below.
>>
>>    -- Daniel
>>
>>

[ ... ]

>>> +static void __init system_timer_of_register(struct device_node *np)
>>> +{
>>> +       struct clk *clk;
>>> +       void __iomem *base;
>>> +       u32 rate = 0;
>>> +       int ret;
>>> +
>>> +       base = of_iomap(np, 0);
>>> +       if (!base) {
>>> +               pr_warn("system-timer: invalid base address\n");
>>> +               return;
>>> +       }
>>> +
>>> +       clk = of_clk_get(np, 0);
>>> +       if (!IS_ERR(clk)) {
>>> +               ret = clk_prepare_enable(clk);
>>> +               if (ret) {
>>> +                       clk_put(clk);
>>> +                       goto out_unmap;
>>> +               }
>>> +
>>> +               rate = clk_get_rate(clk);
>>> +       }
>>> +
>>> +       /* If no clock found, try to get clock-frequency property */
>>> +       if (!rate) {
>>> +               ret = of_property_read_u32(np, "clock-frequency", &rate);
>>> +               if (ret)
>>> +                       goto out_unmap;
>>
>>
>> Shouldn't be 'goto out_clk_disable' ?
>
> No, because I assumed !rate means we failed to get the clock.
> Actually, clk_get_rate could return 0, so relying on rate value is not safe.
>
> I propose to get clock-frequency property if IS_ERR(clk).
>
> Is it fine for you?

Why not invert the conditions ? If the 'clock-frequency' is specified in 
the DT then it overrides the clk_get_rate(). So the resulting code will be:

ret = of_property_read_u32(np, "clock-frequency", &rate);
if (ret) {
	clk = of_clk_get(np, 0);
	if (IS_ERR(clk))
		goto out_unmap;

	ret = clk_prepare_enable(clk);
	if (ret)
		goto out_clk_put;

	rate = clk_get_rate(clk);
	if (!rate)
		goto out_clk_unprepare;
}



>>> +       }
>>> +
>>> +       writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
>>> +       writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
>>> +
>>> +       ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer",
>>> rate,
>>> +                       200, 24, clocksource_mmio_readl_down);
>>> +       if (ret) {
>>> +               pr_err("failed to init clocksource (%d)\n", ret);
>>> +               goto out_clk_disable;
>>> +       }
>>> +
>>> +       pr_info("ARM System timer initialized as clocksource\n");
>>> +
>>> +       return;
>>> +
>>> +out_clk_disable:
>>> +       if (!IS_ERR(clk))
>>
>>
>> Why do you need this check ?
>
> To handle the case were no clock was found, but a clk-frequency value
> was provided.
>
>>
>> It isn't missing a clk_put ?
>
> Right, thanks for spotting this.
>
> I wonder if it makes sense to implement the error path.
> If we fail to initialize the clocksource, the system will be unusable.
>
> Maybe I should just perform a BUG_ON() in the error cases, as most of
> the other clocksource drivers do.
> What is your view?

I prefer to not BUG_ON in the init functions because it already happen 
that drivers were bugging at init time and when a driver was reused on 
another platform with several timers available, the board was not able 
to boot because one timer was not used, hence not defined in the DT. I 
don't know if that could be the case for this platform but I prefer to 
keep thing going smoothly and return from init even if that lead to a 
kernel hang. Of course, the errors must be displayed (pr_warn, pr_err, 
pr_notice, etc ...).

>>
>>> +               clk_disable_unprepare(clk);
>>> +out_unmap:
>>> +       iounmap(base);
>>> +       WARN(ret, "ARM System timer register failed (%d)\n", ret);

pr_warn

Thanks

   -- Daniel
Maxime Coquelin March 27, 2015, 12:33 p.m. UTC | #4
2015-03-27 9:36 GMT+01:00 Daniel Lezcano <daniel.lezcano@linaro.org>:
> On 03/26/2015 09:19 PM, Maxime Coquelin wrote:
>>
>> Hi Daniel,
>>
>>    Thanks for the review. Please find my answers below.
>>
>> 2015-03-26 10:50 GMT+01:00 Daniel Lezcano <daniel.lezcano@linaro.org>:
>>>
>>> On 03/12/2015 10:55 PM, Maxime Coquelin wrote:
>>>>
>>>>
>>>> From: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>>>>
>>>> This patch adds clocksource support for ARMv7-M's System timer,
>>>> also known as SysTick.
>>>>
>>>> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
>>>
>>>
>>>
>>> Hi Maxime,
>>>
>>> the driver looks good. Three comments below.
>>>
>>>    -- Daniel
>>>
>>>
>
> [ ... ]
>
>
>>>> +static void __init system_timer_of_register(struct device_node *np)
>>>> +{
>>>> +       struct clk *clk;
>>>> +       void __iomem *base;
>>>> +       u32 rate = 0;
>>>> +       int ret;
>>>> +
>>>> +       base = of_iomap(np, 0);
>>>> +       if (!base) {
>>>> +               pr_warn("system-timer: invalid base address\n");
>>>> +               return;
>>>> +       }
>>>> +
>>>> +       clk = of_clk_get(np, 0);
>>>> +       if (!IS_ERR(clk)) {
>>>> +               ret = clk_prepare_enable(clk);
>>>> +               if (ret) {
>>>> +                       clk_put(clk);
>>>> +                       goto out_unmap;
>>>> +               }
>>>> +
>>>> +               rate = clk_get_rate(clk);
>>>> +       }
>>>> +
>>>> +       /* If no clock found, try to get clock-frequency property */
>>>> +       if (!rate) {
>>>> +               ret = of_property_read_u32(np, "clock-frequency",
>>>> &rate);
>>>> +               if (ret)
>>>> +                       goto out_unmap;
>>>
>>>
>>>
>>> Shouldn't be 'goto out_clk_disable' ?
>>
>>
>> No, because I assumed !rate means we failed to get the clock.
>> Actually, clk_get_rate could return 0, so relying on rate value is not
>> safe.
>>
>> I propose to get clock-frequency property if IS_ERR(clk).
>>
>> Is it fine for you?
>
>
> Why not invert the conditions ? If the 'clock-frequency' is specified in the
> DT then it overrides the clk_get_rate(). So the resulting code will be:
>
> ret = of_property_read_u32(np, "clock-frequency", &rate);
> if (ret) {
>         clk = of_clk_get(np, 0);
>         if (IS_ERR(clk))
>                 goto out_unmap;
>
>         ret = clk_prepare_enable(clk);
>         if (ret)
>                 goto out_clk_put;
>
>         rate = clk_get_rate(clk);
>         if (!rate)
>                 goto out_clk_unprepare;
>
> }

Ok, it looks sensible.
I will do this in next version.

>
>
>
>>>> +       }
>>>> +
>>>> +       writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
>>>> +       writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
>>>> +
>>>> +       ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer",
>>>> rate,
>>>> +                       200, 24, clocksource_mmio_readl_down);
>>>> +       if (ret) {
>>>> +               pr_err("failed to init clocksource (%d)\n", ret);
>>>> +               goto out_clk_disable;
>>>> +       }
>>>> +
>>>> +       pr_info("ARM System timer initialized as clocksource\n");
>>>> +
>>>> +       return;
>>>> +
>>>> +out_clk_disable:
>>>> +       if (!IS_ERR(clk))
>>>
>>>
>>>
>>> Why do you need this check ?
>>
>>
>> To handle the case were no clock was found, but a clk-frequency value
>> was provided.
>>
>>>
>>> It isn't missing a clk_put ?
>>
>>
>> Right, thanks for spotting this.
>>
>> I wonder if it makes sense to implement the error path.
>> If we fail to initialize the clocksource, the system will be unusable.
>>
>> Maybe I should just perform a BUG_ON() in the error cases, as most of
>> the other clocksource drivers do.
>> What is your view?
>
>
> I prefer to not BUG_ON in the init functions because it already happen that
> drivers were bugging at init time and when a driver was reused on another
> platform with several timers available, the board was not able to boot
> because one timer was not used, hence not defined in the DT. I don't know if
> that could be the case for this platform but I prefer to keep thing going
> smoothly and return from init even if that lead to a kernel hang. Of course,
> the errors must be displayed (pr_warn, pr_err, pr_notice, etc ...).

Ok, let's keep the error path, that's much cleaner indeed.

>
>>>
>>>> +               clk_disable_unprepare(clk);
>>>> +out_unmap:
>>>> +       iounmap(base);
>>>> +       WARN(ret, "ARM System timer register failed (%d)\n", ret);
>
>
> pr_warn

Sure, will fix.

Thanks,
Maxime

>
> Thanks
>
>   -- Daniel
>
>
> --
>  <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
>
> Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
> <http://twitter.com/#!/linaroorg> Twitter |
> <http://www.linaro.org/linaro-blog/> Blog
>
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 1c2506f..b82e58b 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -134,6 +134,13 @@  config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
 	help
 	 Use ARM global timer clock source as sched_clock
 
+config ARMV7M_SYSTICK
+	bool
+	select CLKSRC_OF if OF
+	select CLKSRC_MMIO
+	help
+	  This options enables support for the ARMv7M system timer unit
+
 config ATMEL_PIT
 	select CLKSRC_OF if OF
 	def_bool SOC_AT91SAM9 || SOC_SAMA5
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 752d5c7..1c9a643 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -44,6 +44,7 @@  obj-$(CONFIG_MTK_TIMER)		+= mtk_timer.o
 
 obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o
 obj-$(CONFIG_ARM_GLOBAL_TIMER)		+= arm_global_timer.o
+obj-$(CONFIG_ARMV7M_SYSTICK)		+= armv7m_systick.o
 obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
 obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST)	+= dummy_timer.o
 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 0000000..23d8249
--- /dev/null
+++ b/drivers/clocksource/armv7m_systick.c
@@ -0,0 +1,78 @@ 
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/bitops.h>
+
+#define SYST_CSR	0x00
+#define SYST_RVR	0x04
+#define SYST_CVR	0x08
+#define SYST_CALIB	0x0c
+
+#define SYST_CSR_ENABLE BIT(0)
+
+#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
+
+static void __init system_timer_of_register(struct device_node *np)
+{
+	struct clk *clk;
+	void __iomem *base;
+	u32 rate = 0;
+	int ret;
+
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_warn("system-timer: invalid base address\n");
+		return;
+	}
+
+	clk = of_clk_get(np, 0);
+	if (!IS_ERR(clk)) {
+		ret = clk_prepare_enable(clk);
+		if (ret) {
+			clk_put(clk);
+			goto out_unmap;
+		}
+
+		rate = clk_get_rate(clk);
+	}
+
+	/* If no clock found, try to get clock-frequency property */
+	if (!rate) {
+		ret = of_property_read_u32(np, "clock-frequency", &rate);
+		if (ret)
+			goto out_unmap;
+	}
+
+	writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
+	writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
+
+	ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
+			200, 24, clocksource_mmio_readl_down);
+	if (ret) {
+		pr_err("failed to init clocksource (%d)\n", ret);
+		goto out_clk_disable;
+	}
+
+	pr_info("ARM System timer initialized as clocksource\n");
+
+	return;
+
+out_clk_disable:
+	if (!IS_ERR(clk))
+		clk_disable_unprepare(clk);
+out_unmap:
+	iounmap(base);
+	WARN(ret, "ARM System timer register failed (%d)\n", ret);
+}
+
+CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
+			system_timer_of_register);