All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andy Shevchenko <andy.shevchenko@gmail.com>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>,
	"Andreas Färber" <afaerber@suse.de>,
	"Geert Uytterhoeven" <geert@linux-m68k.org>,
	"Rob Herring" <robh+dt@kernel.org>,
	"Philipp Zabel" <p.zabel@pengutronix.de>,
	"Linus Walleij" <linus.walleij@linaro.org>,
	"Arnd Bergmann" <arnd@arndb.de>, "Stefan Agner" <stefan@agner.ch>,
	"Peter Meerwald" <pmeerw@pmeerw.net>,
	"Paul Bolle" <pebolle@tiscali.nl>,
	"Peter Hurley" <peter@hurleysoftware.com>,
	cw00.choi@samsung.com, "Russell King" <linux@arm.linux.org.uk>,
	"Daniel Lezcano" <daniel.lezcano@linaro.org>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Pawel Moll" <pawel.moll@arm.com>,
	"Mark Rutland" <mark.rutland@arm.com>,
	"Ian Campbell" <ijc+devicetree@hellion.org.uk>,
	"Kumar Gala" <galak@codeaurora.org>,
	"Thomas Gleixner" <tglx@linutronix.de>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Jiri Slaby" <jslaby@suse.cz>
Subject: Re: [PATCH v5 08/15] clockevents/drivers: Add STM32 Timer driver
Date: Sat, 4 Apr 2015 01:09:41 +0300	[thread overview]
Message-ID: <CAHp75VfPn_+sg_1iR_CdR2KuTyWcR+SKFsuHCbCkCKB0Oeqgfg@mail.gmail.com> (raw)
In-Reply-To: <1428080481-18591-9-git-send-email-mcoquelin.stm32@gmail.com>

On Fri, Apr 3, 2015 at 8:01 PM, Maxime Coquelin
<mcoquelin.stm32@gmail.com> wrote:
> STM32 MCUs feature 16 and 32 bits general purpose timers with prescalers.
> The drivers detects whether the time is 16 or 32 bits, and applies a
> 1024 prescaler value if it is 16 bits.
>

Few comments below.

> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> ---
>  drivers/clocksource/Kconfig       |   8 ++
>  drivers/clocksource/Makefile      |   1 +
>  drivers/clocksource/timer-stm32.c | 184 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 193 insertions(+)
>  create mode 100644 drivers/clocksource/timer-stm32.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index b82e58b..519304b 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -101,6 +101,14 @@ config CLKSRC_EFM32
>           Support to use the timers of EFM32 SoCs as clock source and clock
>           event device.
>
> +config CLKSRC_STM32
> +       bool "Clocksource for STM32 SoCs" if !ARCH_STM32
> +       depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
> +       select CLKSRC_MMIO
> +       default ARCH_STM32
> +       help
> +         Support to use the timers of STM32 SoCs as clock event device.
> +
>  config ARM_ARCH_TIMER
>         bool
>         select CLKSRC_OF if OF
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 1c9a643..525dafe 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_NSPIRE)     += zevio-timer.o
>  obj-$(CONFIG_ARCH_BCM_MOBILE)  += bcm_kona_timer.o
>  obj-$(CONFIG_CADENCE_TTC_TIMER)        += cadence_ttc_timer.o
>  obj-$(CONFIG_CLKSRC_EFM32)     += time-efm32.o
> +obj-$(CONFIG_CLKSRC_STM32)     += timer-stm32.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
> diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
> new file mode 100644
> index 0000000..fad2e2e
> --- /dev/null
> +++ b/drivers/clocksource/timer-stm32.c
> @@ -0,0 +1,184 @@
> +/*
> + * Copyright (C) Maxime Coquelin 2015
> + * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + *
> + * Inspired by time-efm32.c from Uwe Kleine-Koenig
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/clocksource.h>
> +#include <linux/clockchips.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +#define TIM_CR1                0x00
> +#define TIM_DIER       0x0c
> +#define TIM_SR         0x10
> +#define TIM_EGR                0x14
> +#define TIM_PSC                0x28
> +#define TIM_ARR                0x2c
> +
> +#define TIM_CR1_CEN    BIT(0)
> +#define TIM_CR1_OPM    BIT(3)
> +#define TIM_CR1_ARPE   BIT(7)
> +
> +#define TIM_DIER_UIE   BIT(0)
> +
> +#define TIM_SR_UIF     BIT(0)
> +
> +#define TIM_EGR_UG     BIT(0)
> +
> +struct stm32_clock_event_ddata {
> +       struct clock_event_device evtdev;
> +       unsigned periodic_top;
> +       void __iomem *base;
> +};
> +
> +static void stm32_clock_event_set_mode(enum clock_event_mode mode,
> +                                      struct clock_event_device *evtdev)
> +{
> +       struct stm32_clock_event_ddata *data =
> +               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
> +       void *base = data->base;
> +
> +       switch (mode) {
> +       case CLOCK_EVT_MODE_PERIODIC:
> +               writel_relaxed(data->periodic_top, base + TIM_ARR);
> +               writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
> +               break;
> +
> +       case CLOCK_EVT_MODE_ONESHOT:
> +       default:
> +               writel_relaxed(0, base + TIM_CR1);
> +               break;
> +       }
> +}
> +
> +static int stm32_clock_event_set_next_event(unsigned long evt,
> +                                           struct clock_event_device *evtdev)
> +{
> +       struct stm32_clock_event_ddata *data =
> +               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
> +
> +       writel_relaxed(evt, data->base + TIM_ARR);
> +       writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
> +                      data->base + TIM_CR1);
> +
> +       return 0;
> +}
> +
> +static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
> +{
> +       struct stm32_clock_event_ddata *data = dev_id;
> +
> +       writel_relaxed(0, data->base + TIM_SR);
> +
> +       data->evtdev.event_handler(&data->evtdev);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static struct stm32_clock_event_ddata clock_event_ddata = {
> +       .evtdev = {
> +               .name = "stm32 clockevent",
> +               .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
> +               .set_mode = stm32_clock_event_set_mode,
> +               .set_next_event = stm32_clock_event_set_next_event,
> +               .rating = 200,
> +       },
> +};
> +
> +static void __init stm32_clockevent_init(struct device_node *np)
> +{
> +       struct stm32_clock_event_ddata *data = &clock_event_ddata;
> +       struct clk *clk;
> +       struct reset_control *rstc;
> +       unsigned long rate, max_delta;
> +       int irq, ret, bits, prescaler = 1;
> +
> +       clk = of_clk_get(np, 0);
> +       if (IS_ERR(clk)) {
> +               ret = PTR_ERR(clk);
> +               pr_err("failed to get clock for clockevent (%d)\n", ret);

Why not dev_err(); ?

> +               goto err_clk_get;
> +       }
> +
> +       ret = clk_prepare_enable(clk);
> +       if (ret) {
> +               pr_err("failed to enable timer clock for clockevent (%d)\n",
> +                      ret);

Ditto.

> +               goto err_clk_enable;
> +       }
> +
> +       rate = clk_get_rate(clk);
> +
> +       rstc = of_reset_control_get(np, NULL);
> +       if (!IS_ERR(rstc)) {
> +               reset_control_assert(rstc);
> +               reset_control_deassert(rstc);
> +       }
> +
> +       data->base = of_iomap(np, 0);
> +       if (!data->base) {
> +               pr_err("failed to map registers for clockevent\n");

Ditto.

> +               goto err_iomap;
> +       }
> +
> +       irq = irq_of_parse_and_map(np, 0);
> +       if (!irq) {
> +               pr_err("%s: failed to get irq.\n", np->full_name);

Ditto.

> +               goto err_get_irq;
> +       }
> +
> +       /* Detect whether the timer is 16 or 32 bits */
> +       writel_relaxed(~0UL, data->base + TIM_ARR);
> +       max_delta = readl_relaxed(data->base + TIM_ARR);
> +       if (max_delta == ~0UL) {
> +               prescaler = 1;
> +               bits = 32;
> +       } else {
> +               prescaler = 1024;
> +               bits = 16;
> +       }
> +       writel_relaxed(0, data->base + TIM_ARR);
> +
> +       writel_relaxed(prescaler - 1, data->base + TIM_PSC);
> +       writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
> +       writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
> +       writel_relaxed(0, data->base + TIM_SR);
> +
> +       data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);
> +
> +       clockevents_config_and_register(&data->evtdev,
> +                                       DIV_ROUND_CLOSEST(rate, prescaler),
> +                                       0x1, max_delta);
> +
> +       ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
> +                       "stm32 clockevent", data);
> +       if (ret) {
> +               pr_err("%s: failed to request irq.\n", np->full_name);

Ditto.

> +               goto err_get_irq;
> +       }
> +
> +       pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
> +                       np->full_name, bits);

dev_info(); ?

> +
> +       return;
> +
> +err_get_irq:
> +       iounmap(data->base);
> +err_iomap:
> +       clk_disable_unprepare(clk);
> +err_clk_enable:
> +       clk_put(clk);
> +err_clk_get:
> +       return;
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
> --
> 1.9.1
>



-- 
With Best Regards,
Andy Shevchenko

WARNING: multiple messages have this Message-ID (diff)
From: Andy Shevchenko <andy.shevchenko@gmail.com>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>,
	"Andreas Färber" <afaerber@suse.de>,
	"Geert Uytterhoeven" <geert@linux-m68k.org>,
	"Rob Herring" <robh+dt@kernel.org>,
	"Philipp Zabel" <p.zabel@pengutronix.de>,
	"Linus Walleij" <linus.walleij@linaro.org>,
	"Arnd Bergmann" <arnd@arndb.de>, "Stefan Agner" <stefan@agner.ch>,
	"Peter Meerwald" <pmeerw@pmeerw.net>,
	"Paul Bolle" <pebolle@tiscali.nl>,
	"Peter Hurley" <peter@hurleysoftware.com>,
	cw00.choi@samsung.com, "Russell King" <linux@arm.linux.org.uk>,
	"Daniel Lezcano" <daniel.lezcano@linaro.org>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Pawel Moll" <pawel.moll@arm.com>,
	"Mark Rutland" <mark.rutland@arm.com>,
	"Ian Campbell" <ijc+devicetree@hellion.org.uk>,
	"Kumar Gala" <galak@codeaurora.org>,
	"Thomas Gleixner" <tglx@linutronix.de>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Jiri Slaby" <jslaby@suse.cz>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"David S. Miller" <davem@davemloft.net>,
	"Mauro Carvalho Chehab" <mchehab@osg.samsung.com>,
	"Joe Perches" <joe@perches.com>, "Antti Palosaari" <crope@iki.fi>,
	"Tejun Heo" <tj@kernel.org>, "Will Deacon" <will.deacon@arm.com>,
	"Nikolay Borisov" <Nikolay.Borisov@arm.com>,
	"Rusty Russell" <rusty@rustcorp.com.au>,
	"Kees Cook" <keescook@chromium.org>,
	"Michal Marek" <mmarek@suse.cz>,
	"Linux Documentation List" <linux-doc@vger.kernel.org>,
	"linux-arm Mailing List" <linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	devicetree <devicetree@vger.kernel.org>,
	"linux-gpio@vger.kernel.org" <linux-gpio@vger.kernel.org>,
	"linux-serial@vger.kernel.org" <linux-serial@vger.kernel.org>,
	Linux-Arch <linux-arch@vger.kernel.org>,
	"linux-api@vger.kernel.org" <linux-api@vger.kernel.org>
Subject: Re: [PATCH v5 08/15] clockevents/drivers: Add STM32 Timer driver
Date: Sat, 4 Apr 2015 01:09:41 +0300	[thread overview]
Message-ID: <CAHp75VfPn_+sg_1iR_CdR2KuTyWcR+SKFsuHCbCkCKB0Oeqgfg@mail.gmail.com> (raw)
In-Reply-To: <1428080481-18591-9-git-send-email-mcoquelin.stm32@gmail.com>

On Fri, Apr 3, 2015 at 8:01 PM, Maxime Coquelin
<mcoquelin.stm32@gmail.com> wrote:
> STM32 MCUs feature 16 and 32 bits general purpose timers with prescalers.
> The drivers detects whether the time is 16 or 32 bits, and applies a
> 1024 prescaler value if it is 16 bits.
>

Few comments below.

> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> ---
>  drivers/clocksource/Kconfig       |   8 ++
>  drivers/clocksource/Makefile      |   1 +
>  drivers/clocksource/timer-stm32.c | 184 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 193 insertions(+)
>  create mode 100644 drivers/clocksource/timer-stm32.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index b82e58b..519304b 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -101,6 +101,14 @@ config CLKSRC_EFM32
>           Support to use the timers of EFM32 SoCs as clock source and clock
>           event device.
>
> +config CLKSRC_STM32
> +       bool "Clocksource for STM32 SoCs" if !ARCH_STM32
> +       depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
> +       select CLKSRC_MMIO
> +       default ARCH_STM32
> +       help
> +         Support to use the timers of STM32 SoCs as clock event device.
> +
>  config ARM_ARCH_TIMER
>         bool
>         select CLKSRC_OF if OF
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 1c9a643..525dafe 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_NSPIRE)     += zevio-timer.o
>  obj-$(CONFIG_ARCH_BCM_MOBILE)  += bcm_kona_timer.o
>  obj-$(CONFIG_CADENCE_TTC_TIMER)        += cadence_ttc_timer.o
>  obj-$(CONFIG_CLKSRC_EFM32)     += time-efm32.o
> +obj-$(CONFIG_CLKSRC_STM32)     += timer-stm32.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
> diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
> new file mode 100644
> index 0000000..fad2e2e
> --- /dev/null
> +++ b/drivers/clocksource/timer-stm32.c
> @@ -0,0 +1,184 @@
> +/*
> + * Copyright (C) Maxime Coquelin 2015
> + * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + *
> + * Inspired by time-efm32.c from Uwe Kleine-Koenig
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/clocksource.h>
> +#include <linux/clockchips.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +#define TIM_CR1                0x00
> +#define TIM_DIER       0x0c
> +#define TIM_SR         0x10
> +#define TIM_EGR                0x14
> +#define TIM_PSC                0x28
> +#define TIM_ARR                0x2c
> +
> +#define TIM_CR1_CEN    BIT(0)
> +#define TIM_CR1_OPM    BIT(3)
> +#define TIM_CR1_ARPE   BIT(7)
> +
> +#define TIM_DIER_UIE   BIT(0)
> +
> +#define TIM_SR_UIF     BIT(0)
> +
> +#define TIM_EGR_UG     BIT(0)
> +
> +struct stm32_clock_event_ddata {
> +       struct clock_event_device evtdev;
> +       unsigned periodic_top;
> +       void __iomem *base;
> +};
> +
> +static void stm32_clock_event_set_mode(enum clock_event_mode mode,
> +                                      struct clock_event_device *evtdev)
> +{
> +       struct stm32_clock_event_ddata *data =
> +               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
> +       void *base = data->base;
> +
> +       switch (mode) {
> +       case CLOCK_EVT_MODE_PERIODIC:
> +               writel_relaxed(data->periodic_top, base + TIM_ARR);
> +               writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
> +               break;
> +
> +       case CLOCK_EVT_MODE_ONESHOT:
> +       default:
> +               writel_relaxed(0, base + TIM_CR1);
> +               break;
> +       }
> +}
> +
> +static int stm32_clock_event_set_next_event(unsigned long evt,
> +                                           struct clock_event_device *evtdev)
> +{
> +       struct stm32_clock_event_ddata *data =
> +               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
> +
> +       writel_relaxed(evt, data->base + TIM_ARR);
> +       writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
> +                      data->base + TIM_CR1);
> +
> +       return 0;
> +}
> +
> +static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
> +{
> +       struct stm32_clock_event_ddata *data = dev_id;
> +
> +       writel_relaxed(0, data->base + TIM_SR);
> +
> +       data->evtdev.event_handler(&data->evtdev);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static struct stm32_clock_event_ddata clock_event_ddata = {
> +       .evtdev = {
> +               .name = "stm32 clockevent",
> +               .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
> +               .set_mode = stm32_clock_event_set_mode,
> +               .set_next_event = stm32_clock_event_set_next_event,
> +               .rating = 200,
> +       },
> +};
> +
> +static void __init stm32_clockevent_init(struct device_node *np)
> +{
> +       struct stm32_clock_event_ddata *data = &clock_event_ddata;
> +       struct clk *clk;
> +       struct reset_control *rstc;
> +       unsigned long rate, max_delta;
> +       int irq, ret, bits, prescaler = 1;
> +
> +       clk = of_clk_get(np, 0);
> +       if (IS_ERR(clk)) {
> +               ret = PTR_ERR(clk);
> +               pr_err("failed to get clock for clockevent (%d)\n", ret);

Why not dev_err(); ?

> +               goto err_clk_get;
> +       }
> +
> +       ret = clk_prepare_enable(clk);
> +       if (ret) {
> +               pr_err("failed to enable timer clock for clockevent (%d)\n",
> +                      ret);

Ditto.

> +               goto err_clk_enable;
> +       }
> +
> +       rate = clk_get_rate(clk);
> +
> +       rstc = of_reset_control_get(np, NULL);
> +       if (!IS_ERR(rstc)) {
> +               reset_control_assert(rstc);
> +               reset_control_deassert(rstc);
> +       }
> +
> +       data->base = of_iomap(np, 0);
> +       if (!data->base) {
> +               pr_err("failed to map registers for clockevent\n");

Ditto.

> +               goto err_iomap;
> +       }
> +
> +       irq = irq_of_parse_and_map(np, 0);
> +       if (!irq) {
> +               pr_err("%s: failed to get irq.\n", np->full_name);

Ditto.

> +               goto err_get_irq;
> +       }
> +
> +       /* Detect whether the timer is 16 or 32 bits */
> +       writel_relaxed(~0UL, data->base + TIM_ARR);
> +       max_delta = readl_relaxed(data->base + TIM_ARR);
> +       if (max_delta == ~0UL) {
> +               prescaler = 1;
> +               bits = 32;
> +       } else {
> +               prescaler = 1024;
> +               bits = 16;
> +       }
> +       writel_relaxed(0, data->base + TIM_ARR);
> +
> +       writel_relaxed(prescaler - 1, data->base + TIM_PSC);
> +       writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
> +       writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
> +       writel_relaxed(0, data->base + TIM_SR);
> +
> +       data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);
> +
> +       clockevents_config_and_register(&data->evtdev,
> +                                       DIV_ROUND_CLOSEST(rate, prescaler),
> +                                       0x1, max_delta);
> +
> +       ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
> +                       "stm32 clockevent", data);
> +       if (ret) {
> +               pr_err("%s: failed to request irq.\n", np->full_name);

Ditto.

> +               goto err_get_irq;
> +       }
> +
> +       pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
> +                       np->full_name, bits);

dev_info(); ?

> +
> +       return;
> +
> +err_get_irq:
> +       iounmap(data->base);
> +err_iomap:
> +       clk_disable_unprepare(clk);
> +err_clk_enable:
> +       clk_put(clk);
> +err_clk_get:
> +       return;
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
> --
> 1.9.1
>



-- 
With Best Regards,
Andy Shevchenko

WARNING: multiple messages have this Message-ID (diff)
From: andy.shevchenko@gmail.com (Andy Shevchenko)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v5 08/15] clockevents/drivers: Add STM32 Timer driver
Date: Sat, 4 Apr 2015 01:09:41 +0300	[thread overview]
Message-ID: <CAHp75VfPn_+sg_1iR_CdR2KuTyWcR+SKFsuHCbCkCKB0Oeqgfg@mail.gmail.com> (raw)
In-Reply-To: <1428080481-18591-9-git-send-email-mcoquelin.stm32@gmail.com>

On Fri, Apr 3, 2015 at 8:01 PM, Maxime Coquelin
<mcoquelin.stm32@gmail.com> wrote:
> STM32 MCUs feature 16 and 32 bits general purpose timers with prescalers.
> The drivers detects whether the time is 16 or 32 bits, and applies a
> 1024 prescaler value if it is 16 bits.
>

Few comments below.

> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> ---
>  drivers/clocksource/Kconfig       |   8 ++
>  drivers/clocksource/Makefile      |   1 +
>  drivers/clocksource/timer-stm32.c | 184 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 193 insertions(+)
>  create mode 100644 drivers/clocksource/timer-stm32.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index b82e58b..519304b 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -101,6 +101,14 @@ config CLKSRC_EFM32
>           Support to use the timers of EFM32 SoCs as clock source and clock
>           event device.
>
> +config CLKSRC_STM32
> +       bool "Clocksource for STM32 SoCs" if !ARCH_STM32
> +       depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
> +       select CLKSRC_MMIO
> +       default ARCH_STM32
> +       help
> +         Support to use the timers of STM32 SoCs as clock event device.
> +
>  config ARM_ARCH_TIMER
>         bool
>         select CLKSRC_OF if OF
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 1c9a643..525dafe 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_NSPIRE)     += zevio-timer.o
>  obj-$(CONFIG_ARCH_BCM_MOBILE)  += bcm_kona_timer.o
>  obj-$(CONFIG_CADENCE_TTC_TIMER)        += cadence_ttc_timer.o
>  obj-$(CONFIG_CLKSRC_EFM32)     += time-efm32.o
> +obj-$(CONFIG_CLKSRC_STM32)     += timer-stm32.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
> diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
> new file mode 100644
> index 0000000..fad2e2e
> --- /dev/null
> +++ b/drivers/clocksource/timer-stm32.c
> @@ -0,0 +1,184 @@
> +/*
> + * Copyright (C) Maxime Coquelin 2015
> + * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + *
> + * Inspired by time-efm32.c from Uwe Kleine-Koenig
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/clocksource.h>
> +#include <linux/clockchips.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +#define TIM_CR1                0x00
> +#define TIM_DIER       0x0c
> +#define TIM_SR         0x10
> +#define TIM_EGR                0x14
> +#define TIM_PSC                0x28
> +#define TIM_ARR                0x2c
> +
> +#define TIM_CR1_CEN    BIT(0)
> +#define TIM_CR1_OPM    BIT(3)
> +#define TIM_CR1_ARPE   BIT(7)
> +
> +#define TIM_DIER_UIE   BIT(0)
> +
> +#define TIM_SR_UIF     BIT(0)
> +
> +#define TIM_EGR_UG     BIT(0)
> +
> +struct stm32_clock_event_ddata {
> +       struct clock_event_device evtdev;
> +       unsigned periodic_top;
> +       void __iomem *base;
> +};
> +
> +static void stm32_clock_event_set_mode(enum clock_event_mode mode,
> +                                      struct clock_event_device *evtdev)
> +{
> +       struct stm32_clock_event_ddata *data =
> +               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
> +       void *base = data->base;
> +
> +       switch (mode) {
> +       case CLOCK_EVT_MODE_PERIODIC:
> +               writel_relaxed(data->periodic_top, base + TIM_ARR);
> +               writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
> +               break;
> +
> +       case CLOCK_EVT_MODE_ONESHOT:
> +       default:
> +               writel_relaxed(0, base + TIM_CR1);
> +               break;
> +       }
> +}
> +
> +static int stm32_clock_event_set_next_event(unsigned long evt,
> +                                           struct clock_event_device *evtdev)
> +{
> +       struct stm32_clock_event_ddata *data =
> +               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
> +
> +       writel_relaxed(evt, data->base + TIM_ARR);
> +       writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
> +                      data->base + TIM_CR1);
> +
> +       return 0;
> +}
> +
> +static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
> +{
> +       struct stm32_clock_event_ddata *data = dev_id;
> +
> +       writel_relaxed(0, data->base + TIM_SR);
> +
> +       data->evtdev.event_handler(&data->evtdev);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static struct stm32_clock_event_ddata clock_event_ddata = {
> +       .evtdev = {
> +               .name = "stm32 clockevent",
> +               .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
> +               .set_mode = stm32_clock_event_set_mode,
> +               .set_next_event = stm32_clock_event_set_next_event,
> +               .rating = 200,
> +       },
> +};
> +
> +static void __init stm32_clockevent_init(struct device_node *np)
> +{
> +       struct stm32_clock_event_ddata *data = &clock_event_ddata;
> +       struct clk *clk;
> +       struct reset_control *rstc;
> +       unsigned long rate, max_delta;
> +       int irq, ret, bits, prescaler = 1;
> +
> +       clk = of_clk_get(np, 0);
> +       if (IS_ERR(clk)) {
> +               ret = PTR_ERR(clk);
> +               pr_err("failed to get clock for clockevent (%d)\n", ret);

Why not dev_err(); ?

> +               goto err_clk_get;
> +       }
> +
> +       ret = clk_prepare_enable(clk);
> +       if (ret) {
> +               pr_err("failed to enable timer clock for clockevent (%d)\n",
> +                      ret);

Ditto.

> +               goto err_clk_enable;
> +       }
> +
> +       rate = clk_get_rate(clk);
> +
> +       rstc = of_reset_control_get(np, NULL);
> +       if (!IS_ERR(rstc)) {
> +               reset_control_assert(rstc);
> +               reset_control_deassert(rstc);
> +       }
> +
> +       data->base = of_iomap(np, 0);
> +       if (!data->base) {
> +               pr_err("failed to map registers for clockevent\n");

Ditto.

> +               goto err_iomap;
> +       }
> +
> +       irq = irq_of_parse_and_map(np, 0);
> +       if (!irq) {
> +               pr_err("%s: failed to get irq.\n", np->full_name);

Ditto.

> +               goto err_get_irq;
> +       }
> +
> +       /* Detect whether the timer is 16 or 32 bits */
> +       writel_relaxed(~0UL, data->base + TIM_ARR);
> +       max_delta = readl_relaxed(data->base + TIM_ARR);
> +       if (max_delta == ~0UL) {
> +               prescaler = 1;
> +               bits = 32;
> +       } else {
> +               prescaler = 1024;
> +               bits = 16;
> +       }
> +       writel_relaxed(0, data->base + TIM_ARR);
> +
> +       writel_relaxed(prescaler - 1, data->base + TIM_PSC);
> +       writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
> +       writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
> +       writel_relaxed(0, data->base + TIM_SR);
> +
> +       data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);
> +
> +       clockevents_config_and_register(&data->evtdev,
> +                                       DIV_ROUND_CLOSEST(rate, prescaler),
> +                                       0x1, max_delta);
> +
> +       ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
> +                       "stm32 clockevent", data);
> +       if (ret) {
> +               pr_err("%s: failed to request irq.\n", np->full_name);

Ditto.

> +               goto err_get_irq;
> +       }
> +
> +       pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
> +                       np->full_name, bits);

dev_info(); ?

> +
> +       return;
> +
> +err_get_irq:
> +       iounmap(data->base);
> +err_iomap:
> +       clk_disable_unprepare(clk);
> +err_clk_enable:
> +       clk_put(clk);
> +err_clk_get:
> +       return;
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
> --
> 1.9.1
>



-- 
With Best Regards,
Andy Shevchenko

  reply	other threads:[~2015-04-03 22:09 UTC|newest]

Thread overview: 84+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-03 17:01 [PATCH v5 00/15] Add support to STMicroelectronics STM32 family Maxime Coquelin
2015-04-03 17:01 ` Maxime Coquelin
2015-04-03 17:01 ` Maxime Coquelin
2015-04-03 17:01 ` [PATCH v5 02/15] ARM: ARMv7-M: Enlarge vector table up to 256 entries Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01 ` [PATCH v5 03/15] dt-bindings: Document the ARM System timer bindings Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:09   ` Rob Herring
2015-04-03 17:09     ` Rob Herring
2015-04-03 17:09     ` Rob Herring
2015-04-03 17:01 ` [PATCH v5 04/15] clocksource/drivers: Add ARM System timer driver Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01 ` [PATCH v5 05/15] dt-bindings: Document the STM32 reset bindings Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01 ` [PATCH v5 07/15] dt-bindings: Document the STM32 timer bindings Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:10   ` Rob Herring
2015-04-03 17:10     ` Rob Herring
2015-04-03 17:10     ` Rob Herring
2015-04-03 17:01 ` [PATCH v5 08/15] clockevents/drivers: Add STM32 Timer driver Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 22:09   ` Andy Shevchenko [this message]
2015-04-03 22:09     ` Andy Shevchenko
2015-04-03 22:09     ` Andy Shevchenko
     [not found]     ` <CAHp75VfPn_+sg_1iR_CdR2KuTyWcR+SKFsuHCbCkCKB0Oeqgfg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-04-03 22:14       ` Andy Shevchenko
2015-04-03 22:14         ` Andy Shevchenko
2015-04-03 22:14         ` Andy Shevchenko
2015-04-03 17:01 ` [PATCH v5 10/15] serial: stm32-usart: Add STM32 USART Driver Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:29   ` Peter Hurley
2015-04-03 17:29     ` Peter Hurley
2015-04-03 17:29     ` Peter Hurley
     [not found]   ` <1428080481-18591-11-git-send-email-mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-04-03 17:43     ` Joe Perches
2015-04-03 17:43       ` Joe Perches
2015-04-03 17:43       ` Joe Perches
2015-04-04 17:47       ` [PATCH] checkpatch: Add uart_ops to normally const structs Joe Perches
2015-04-07  7:55         ` Maxime Coquelin
2015-04-07  7:56       ` [PATCH v5 10/15] serial: stm32-usart: Add STM32 USART Driver Maxime Coquelin
2015-04-07  7:56         ` Maxime Coquelin
2015-04-07  7:56         ` Maxime Coquelin
2015-04-03 22:04   ` Andy Shevchenko
2015-04-03 22:04     ` Andy Shevchenko
2015-04-03 22:04     ` Andy Shevchenko
2015-04-07 16:05     ` Maxime Coquelin
2015-04-07 16:05       ` Maxime Coquelin
2015-04-07 16:05       ` Maxime Coquelin
     [not found] ` <1428080481-18591-1-git-send-email-mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-04-03 17:01   ` [PATCH v5 01/15] scripts: link-vmlinux: Don't pass page offset to kallsyms if XIP Kernel Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01   ` [PATCH v5 06/15] drivers: reset: Add STM32 reset driver Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01   ` [PATCH v5 09/15] dt-bindings: Document the STM32 USART bindings Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
     [not found]     ` <1428080481-18591-10-git-send-email-mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-04-03 17:14       ` Rob Herring
2015-04-03 17:14         ` Rob Herring
2015-04-03 17:14         ` Rob Herring
2015-04-07 15:49         ` Maxime Coquelin
2015-04-07 15:49           ` Maxime Coquelin
2015-04-07 15:49           ` Maxime Coquelin
2015-04-03 17:01   ` [PATCH v5 11/15] ARM: Add STM32 family machine Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01   ` [PATCH v5 13/15] ARM: dts: Introduce STM32F429 MCU Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01   ` [PATCH v5 15/15] MAINTAINERS: Add entry for STM32 MCUs Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01     ` Maxime Coquelin
2015-04-03 17:01 ` [PATCH v5 12/15] ARM: dts: Add ARM System timer as clockevent in armv7m Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01 ` [PATCH v5 14/15] ARM: configs: Add STM32 defconfig Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin
2015-04-03 17:01   ` Maxime Coquelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAHp75VfPn_+sg_1iR_CdR2KuTyWcR+SKFsuHCbCkCKB0Oeqgfg@mail.gmail.com \
    --to=andy.shevchenko@gmail.com \
    --cc=afaerber@suse.de \
    --cc=arnd@arndb.de \
    --cc=corbet@lwn.net \
    --cc=cw00.choi@samsung.com \
    --cc=daniel.lezcano@linaro.org \
    --cc=galak@codeaurora.org \
    --cc=geert@linux-m68k.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=ijc+devicetree@hellion.org.uk \
    --cc=jslaby@suse.cz \
    --cc=linus.walleij@linaro.org \
    --cc=linux@arm.linux.org.uk \
    --cc=mark.rutland@arm.com \
    --cc=mcoquelin.stm32@gmail.com \
    --cc=p.zabel@pengutronix.de \
    --cc=pawel.moll@arm.com \
    --cc=pebolle@tiscali.nl \
    --cc=peter@hurleysoftware.com \
    --cc=pmeerw@pmeerw.net \
    --cc=robh+dt@kernel.org \
    --cc=stefan@agner.ch \
    --cc=tglx@linutronix.de \
    --cc=u.kleine-koenig@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.