Reintroduce i.MX EPIT Timer
Subsystems (7)
v6 cover Reintroduce i.MX EPIT Timer
From: Cl?ment Peron <[email protected]>
As suggested in the commit message we have added the device tree support, proper bindings and we moved the driver into the correct folder.
Moreover we made some changes like use of relaxed IO accesor, implement sched_clock, delay_timer and reduce the clockevents min_delta.
Changes since v5:
- change epit to timer in doc example
- fix typo in imx6sl.dtsi
Changes since v4:
- removed ipg clk
- change in dt epit to timer
- add introduction in doc
- add all compatibles in doc
- update epit entry for other i.MX device-trees
Changes since v3:
- Clean Kconfig
- Rename imx6q-epit to imx31-epit
- Update doc and bindings
- Indent and fix
Changes since v2 (Thanks Fabio Estevam):
- Removed unused ckil clock
- Add out_iounmap
- Check and handle if clk_prepare_enable failed
- Fix comment typo
Changes since v1 (Thanks Vladimir Zapolskiy):
- Add OF dependency in Kconfig
- Sort header
- Use BIT macro
- Remove useless comments
- Fix incorrect indent
- Fix memory leak
- Add check and handle possible returned error
Cl?ment Peron (2):
ARM: imx: remove inexistant EPIT timer init
dt-bindings: timer: add i.MX EPIT timer binding
Colin Didier (3):
clk: imx6: add EPIT clock support
clocksource: add driver for i.MX EPIT timer
ARM: dts: imx: add missing compatible and clock properties for EPIT
.../devicetree/bindings/timer/fsl,imxepit.txt | 21 ++ arch/arm/boot/dts/imx25.dtsi | 8 +- arch/arm/boot/dts/imx6qdl.dtsi | 10 +- arch/arm/boot/dts/imx6sl.dtsi | 10 +- arch/arm/boot/dts/imx6sx.dtsi | 10 +- arch/arm/boot/dts/imx6ul.dtsi | 10 +- arch/arm/mach-imx/common.h | 1 - drivers/clk/imx/clk-imx6q.c | 2 + drivers/clocksource/Kconfig | 11 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-imx-epit.c | 265 ++++++++++++++++++ include/dt-bindings/clock/imx6qdl-clock.h | 4 +- 12 files changed, 341 insertions(+), 12 deletions(-) create mode 100644 Documentation/devicetree/bindings/timer/fsl,imxepit.txt create mode 100644 drivers/clocksource/timer-imx-epit.c
Signature · 1 line
2.17.1
PATCH v6 1/5 clk: imx6: add EPIT clock support
From: Colin Didier <[email protected]>
Please ignore this commit.
It has already been merged in clk-next.
Leave it here to avoid error with automatic CI.
--- drivers/clk/imx/clk-imx6q.c | 2 ++ include/dt-bindings/clock/imx6qdl-clock.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-)
drivers/clk/imx/clk-imx6q.c +2 -0
@@ -753,6 +753,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)else clk[IMX6Q_CLK_ECSPI5 ] =imx_clk_gate2 ("ecspi5" ,"ecspi_root" , base +0x6c ,8 ); clk[IMX6QDL_CLK_ENET ] =imx_clk_gate2 ("enet" ,"ipg" , base +0x6c ,10 ); + clk[IMX6QDL_CLK_EPIT1 ] =imx_clk_gate2 ("epit1" ,"ipg" , base +0x6c ,12 ); + clk[IMX6QDL_CLK_EPIT2 ] =imx_clk_gate2 ("epit2" ,"ipg" , base +0x6c ,14 ); clk[IMX6QDL_CLK_ESAI_EXTAL ] =imx_clk_gate2_shared ("esai_extal" ,"esai_podf" , base +0x6c ,16 , &share_count_esai); clk[IMX6QDL_CLK_ESAI_IPG ] =imx_clk_gate2_shared ("esai_ipg" ,"ahb" , base +0x6c ,16 , &share_count_esai); clk[IMX6QDL_CLK_ESAI_MEM ] =imx_clk_gate2_shared ("esai_mem" ,"ahb" , base +0x6c ,16 , &share_count_esai);
include/dt-bindings/clock/imx6qdl-clock.h +3 -1
@@ -271,6 +271,8 @@#define IMX6QDL_CLK_PRE_AXI 258#define IMX6QDL_CLK_MLB_SEL 259#define IMX6QDL_CLK_MLB_PODF 260 -#define IMX6QDL_CLK_END 261 +#define IMX6QDL_CLK_EPIT1 261 +#define IMX6QDL_CLK_EPIT2 262 +#define IMX6QDL_CLK_END 263#endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */
Signature · 1 line
2.17.1
PATCH v6 2/5 ARM: imx: remove inexistant EPIT timer init
In reply to Clément PéronFrom: Cl?ment Peron <[email protected]>
i.MX EPIT timer has been removed but not the init function declaration.
--- arch/arm/mach-imx/common.h | 1 - 1 file changed, 1 deletion(-)
arch/arm/mach-imx/common.h +0 -1
@@ -38,7 +38,6 @@ void imx21_soc_init(void);void imx27_soc_init (void );void imx31_soc_init (void );void imx35_soc_init (void ); -void epit_timer_init (void __iomem *base,int irq);int mx21_clocks_init (unsigned long lref,unsigned long fref);int mx27_clocks_init (unsigned long fref);int mx31_clocks_init (unsigned long fref);
Signature · 1 line
2.17.1
Re: [PATCH v6 2/5] ARM: imx: remove inexistant EPIT timer init
Quoted 6 lines · Cl?ment P?ron
From: Cl?ment Peron <[email protected]>
i.MX EPIT timer has been removed but not the init function declaration.
Signed-off-by Cl?ment Peron <[email protected]>Reviewed-by Fabio Estevam <[email protected]>
--
With best wishes,
Vladimir
PATCH v6 3/5 dt-bindings: timer: add i.MX EPIT timer binding
In reply to Clément PéronFrom: Cl?ment Peron <[email protected]>
Add devicetree binding document for NXP's i.MX SoC specific EPIT timer driver.
--- .../devicetree/bindings/timer/fsl,imxepit.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/fsl,imxepit.txt
Documentation/devicetree/bindings/timer/fsl,imxepit.txt new file +21 -0
@@ -0,0 +1,21 @@ +Binding for the i.MX Enhanced Periodic Interrupt Timer (EPIT) + +The Enhanced Periodic Interrupt Timer (EPIT) is a 32-bit set-and-forget timer +that is capable of providing precise interrupts at regular intervals with +minimal processor intervention. + +Required properties: +- compatible: should be "fsl,<chip>-epit", "fsl,imx31-epit" where <chip> is + imx25, imx6qdl, imx6sl, imx6sul or imx6sx. +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: Should contain EPIT controller interrupt +- clocks : The clock provided by the SoC to drive the timer. + +Example for i.MX6QDL: + epit1: timer at 20d0000 { + compatible = "fsl,imx6qdl-epit", "fsl,imx31-epit"; + reg = <0x020d0000 0x4000>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6QDL_CLK_EPIT1>; + };
Signature · 1 line
2.17.1
Re: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding
Quoted 6 lines · Cl?ment P?ron
From: Cl?ment Peron <[email protected]>
Add devicetree binding document for NXP's i.MX SoC specific EPIT timer driver.
Signed-off-by Cl?ment Peron <[email protected]>
--
With best wishes,
Vladimir
Re: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding
In reply to Clément PéronQuoted 10 lines · Cl?ment P?ron
From: Cl?ment Peron <[email protected]>
Add devicetree binding document for NXP's i.MX SoC specific EPIT timer driver.
Signed-off-by Cl?ment Peron <[email protected]>--- .../devicetree/bindings/timer/fsl,imxepit.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/fsl,imxepit.txt
Re: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding
Hi Rob,
Quoted 7 lines · Rob Herring
Quoted 5 lines · Cl?ment P?ron
From: Cl?ment Peron <[email protected]>
Add devicetree binding document for NXP's i.MX SoC specific EPIT timer driver.
Quoted 7 lines
Quoted 5 lines
Signed-off-by Cl?ment Peron <[email protected]>--- .../devicetree/bindings/timer/fsl,imxepit.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/fsl,imxepit.txtReviewed-by Rob Herring <[email protected]>
Thanks, I posted the v7 yesterday, could you add your tag to the new release ?
Regards,
Clement
PATCH v6 4/5 clocksource: add driver for i.MX EPIT timer
In reply to Clément PéronFrom: Colin Didier <[email protected]>
Add driver for NXP's EPIT timer used in i.MX SoC.
--- drivers/clocksource/Kconfig | 11 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-imx-epit.c | 265 +++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 drivers/clocksource/timer-imx-epit.c
drivers/clocksource/Kconfig +11 -0
@@ -576,6 +576,17 @@ config H8300_TPU This enables the clocksource for the H8300 platform with the H8S2678 cpu. +config CLKSRC_IMX_EPIT + bool "Clocksource using i.MX EPIT" + depends on CLKDEV_LOOKUP && (ARCH_MXC || COMPILE_TEST) + select CLKSRC_MMIO + help + This enables EPIT support available on some i.MX platforms. + Normally you don't have a reason to do so as the EPIT has + the same features and uses the same clocks as the GPT. + Anyway, on some systems the GPT may be in use for other + purposes. + config CLKSRC_IMX_GPT bool "Clocksource using i.MX GPT" if COMPILE_TEST depends on ARM && CLKDEV_LOOKUP
drivers/clocksource/Makefile +1 -0
@@ -69,6 +69,7 @@ obj-$(CONFIG_INTEGRATOR_AP_TIMER) += timer-integrator-ap.o obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o obj-$(CONFIG_CLKSRC_TANGO_XTAL) += tango_xtal.o +obj-$(CONFIG_CLKSRC_IMX_EPIT) += timer-imx-epit.o obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o obj-$(CONFIG_CLKSRC_IMX_TPM) += timer-imx-tpm.o obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
drivers/clocksource/timer-imx-epit.c new file +265 -0
265 lines not on this page · raw message
Signature · 1 line
2.17.1
Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer
Quoted 272 lines · Cl?ment P?ron
From: Colin Didier <[email protected]>
Add driver for NXP's EPIT timer used in i.MX SoC.
Signed-off-by Colin Didier <[email protected]>Signed-off-by Cl?ment Peron <[email protected]>--- drivers/clocksource/Kconfig | 11 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-imx-epit.c | 265 +++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 drivers/clocksource/timer-imx-epit.cdrivers/clocksource/Kconfig +11
-0@@ -576,6 +576,17 @@ config H8300_TPU This enables the clocksource for the H8300 platform with the H8S2678 cpu. +config CLKSRC_IMX_EPIT + bool "Clocksource using i.MX EPIT" + depends on CLKDEV_LOOKUP && (ARCH_MXC || COMPILE_TEST) + select CLKSRC_MMIO + help + This enables EPIT support available on some i.MX platforms. + Normally you don't have a reason to do so as the EPIT has + the same features and uses the same clocks as the GPT. + Anyway, on some systems the GPT may be in use for other + purposes. + config CLKSRC_IMX_GPT bool "Clocksource using i.MX GPT" if COMPILE_TEST depends on ARM && CLKDEV_LOOKUPdrivers/clocksource/Makefile +1
-0@@ -69,6 +69,7 @@ obj-$(CONFIG_INTEGRATOR_AP_TIMER) += timer-integrator-ap.o obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o obj-$(CONFIG_CLKSRC_TANGO_XTAL) += tango_xtal.o +obj-$(CONFIG_CLKSRC_IMX_EPIT) += timer-imx-epit.o obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o obj-$(CONFIG_CLKSRC_IMX_TPM) += timer-imx-tpm.o obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.odrivers/clocksource/timer-imx-epit.c new file +218
-0@@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* i.MX EPIT Timer +* +* Copyright (C) 2010 Sascha Hauer <[email protected]> +* Copyright (C) 2018 Colin Didier <[email protected]> +* Copyright (C) 2018 Cl?ment P?ron <[email protected]> +*/ + +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/sched_clock.h> +#include <linux/slab.h> + +#define EPITCR 0x00 +#define EPITSR 0x04 +#define EPITLR 0x08 +#define EPITCMPR 0x0c +#define EPITCNR 0x10 + +#define EPITCR_EN BIT(0) +#define EPITCR_ENMOD BIT(1) +#define EPITCR_OCIEN BIT(2) +#define EPITCR_RLD BIT(3) +#define EPITCR_PRESC (x) (((x) & 0xfff) << 4) +#define EPITCR_SWR BIT(16) +#define EPITCR_IOVW BIT(17) +#define EPITCR_DBGEN BIT(18) +#define EPITCR_WAITEN BIT(19) +#define EPITCR_RES BIT(20) +#define EPITCR_STOPEN BIT(21) +#define EPITCR_OM_DISCON (0 << 22) +#define EPITCR_OM_TOGGLE (1 << 22) +#define EPITCR_OM_CLEAR (2 << 22) +#define EPITCR_OM_SET (3 << 22) +#define EPITCR_CLKSRC_OFF (0 << 24) +#define EPITCR_CLKSRC_PERIPHERAL (1 << 24) +#define EPITCR_CLKSRC_REF_HIGH (2 << 24) +#define EPITCR_CLKSRC_REF_LOW (3 << 24) + +#define EPITSR_OCIF BIT(0) + +struct epit_timer { +void __iomem *base; +int irq; +struct clk *clk; +struct clock_event_device ced; +struct irqaction act; +}; + +static void __iomem *sched_clock_reg; + +static inline struct epit_timer *to_epit_timer (struct clock_event_device *ced) +{ + return container_of(ced, struct epit_timer, ced); +} + +static inline void epit_irq_disable (struct epit_timer *epittm) +{ +u32 val; + + val =readl_relaxed (epittm->base +EPITCR ); +writel_relaxed (val & ~EPITCR_OCIEN , epittm->base +EPITCR ); +} + +static inline void epit_irq_enable (struct epit_timer *epittm) +{ +u32 val; + + val =readl_relaxed (epittm->base +EPITCR ); +writel_relaxed (val |EPITCR_OCIEN , epittm->base +EPITCR ); +} + +static void epit_irq_acknowledge (struct epit_timer *epittm) +{ +writel_relaxed (EPITSR_OCIF , epittm->base +EPITSR ); +} + +static u64 notrace epit_read_sched_clock(void) +{ +return ~readl_relaxed (sched_clock_reg); +} + +static int epit_set_next_event (unsigned long cycles, +struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long tcmp; + + tcmp =readl_relaxed (epittm->base +EPITCNR ) - cycles; +writel_relaxed (tcmp, epittm->base +EPITCMPR ); + +return 0 ; +} + +/* Left event sources disabled, no more interrupts appear */ +static int epit_shutdown (struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long flags; + +/* +* The timer interrupt generation is disabled at least +* for enough time to call epit_set_next_event() +*/ +local_irq_save (flags); + +/* Disable interrupt in EPIT module */ +epit_irq_disable (epittm); + +/* Clear pending interrupt */ +epit_irq_acknowledge (epittm); + +local_irq_restore (flags); + +return 0 ; +} + +static int epit_set_oneshot (struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long flags; + +/* +* The timer interrupt generation is disabled at least +* for enough time to call epit_set_next_event() +*/ +local_irq_save (flags); + +/* Disable interrupt in EPIT module */ +epit_irq_disable (epittm); + +/* Clear pending interrupt, only while switching mode */ +if (!clockevent_state_oneshot (ced)) +epit_irq_acknowledge (epittm); + +/* +* Do not put overhead of interrupt enable/disable into +* epit_set_next_event(), the core has about 4 minutes +* to call epit_set_next_event() or shutdown clock after +* mode switching +*/ +epit_irq_enable (epittm); +local_irq_restore (flags); + +return 0 ; +} + +static irqreturn_t epit_timer_interrupt (int irq,void *dev_id) +{ +struct clock_event_device *ced = dev_id; +struct epit_timer *epittm =to_epit_timer (ced); + +epit_irq_acknowledge (epittm); + + ced->event_handler (ced); + +return IRQ_HANDLED ; +} + +static int __init epit_clocksource_init (struct epit_timer *epittm) +{ +unsigned int c =clk_get_rate (epittm->clk); + + sched_clock_reg = epittm->base +EPITCNR ; +sched_clock_register (epit_read_sched_clock,32 , c); + +return clocksource_mmio_init (epittm->base +EPITCNR ,"epit" , c,200 ,32 , + clocksource_mmio_readl_down); +} + +static int __init epit_clockevent_init (struct epit_timer *epittm) +{ +struct clock_event_device *ced = &epittm->ced; +struct irqaction *act = &epittm->act; + + ced->name ="epit" ; + ced->features =CLOCK_EVT_FEAT_ONESHOT |CLOCK_EVT_FEAT_DYNIRQ ; + ced->set_state_shutdown = epit_shutdown; + ced->tick_resume = epit_shutdown; + ced->set_state_oneshot = epit_set_oneshot; + ced->set_next_event = epit_set_next_event; + ced->rating =200 ; + ced->cpumask =cpumask_of (0 ); + ced->irq = epittm->irq; +clockevents_config_and_register (ced,clk_get_rate (epittm->clk), +0xff ,0xfffffffe ); + + act->name ="i.MX EPIT Timer Tick" , + act->flags =IRQF_TIMER |IRQF_IRQPOLL ; + act->handler = epit_timer_interrupt; + act->dev_id = ced; + +/* Make irqs happen */ +return setup_irq (epittm->irq, act); +} + +static int __init epit_timer_init (struct device_node *np) +{ +struct epit_timer *epittm; +int ret; + + epittm =kzalloc (sizeof (*epittm),GFP_KERNEL ); +if (!epittm) +return -ENOMEM ; + + epittm->base =of_iomap (np,0 ); +if (!epittm->base) { + ret = -ENXIO ; + goto out_kfree; + } + + epittm->irq =irq_of_parse_and_map (np,0 ); +if (!epittm->irq) { + ret = -EINVAL ;
drivers/clocksource/timer-imx-epit.c line 228
Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10
-0+ goto out_iounmap; + } + +/* Get EPIT clock */ + epittm->clk =of_clk_get (np,0 ); +if (IS_ERR (epittm->clk)) { +pr_err ("i.MX EPIT: unable to get clk\n" ); + ret =PTR_ERR (epittm->clk); + goto out_iounmap; + }
There is something off with indent here.
There is a helper library in drivers/clocksource/timer-of.c which might be useful for this driver.
--
Stefan
Quoted 37 lines
drivers/clocksource/timer-imx-epit.c new file +37
-0+ + ret =clk_prepare_enable (epittm->clk); +if (ret) { +pr_err ("i.MX EPIT: unable to prepare+enable clk\n" ); + goto out_iounmap; + } + +/* Initialise to a known state (all timers off, and timing reset) */ +writel_relaxed (0x0 , epittm->base +EPITCR ); +writel_relaxed (0xffffffff , epittm->base +EPITLR ); +writel_relaxed (EPITCR_EN |EPITCR_CLKSRC_REF_HIGH |EPITCR_WAITEN , + epittm->base +EPITCR ); + + ret =epit_clocksource_init (epittm); +if (ret) { +pr_err ("i.MX EPIT: failed to init clocksource\n" ); + goto out_clk_disable; + } + + ret =epit_clockevent_init (epittm); +if (ret) { +pr_err ("i.MX EPIT: failed to init clockevent\n" ); + goto out_clk_disable; + } + +return 0 ; + +out_clk_disable: +clk_disable_unprepare (epittm->clk); +out_iounmap: +iounmap (epittm->base); +out_kfree: +kfree (epittm); + +return ret; +} +TIMER_OF_DECLARE (epit_timer,"fsl,imx31-epit" , epit_timer_init);
Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer
Hi Stefan,
Quoted 206 lines
Quoted 206 lines
drivers/clocksource/timer-imx-epit.c new file +206
-0+ +#define EPITCR 0x00 +#define EPITSR 0x04 +#define EPITLR 0x08 +#define EPITCMPR 0x0c +#define EPITCNR 0x10 + +#define EPITCR_EN BIT(0) +#define EPITCR_ENMOD BIT(1) +#define EPITCR_OCIEN BIT(2) +#define EPITCR_RLD BIT(3) +#define EPITCR_PRESC (x) (((x) & 0xfff) << 4) +#define EPITCR_SWR BIT(16) +#define EPITCR_IOVW BIT(17) +#define EPITCR_DBGEN BIT(18) +#define EPITCR_WAITEN BIT(19) +#define EPITCR_RES BIT(20) +#define EPITCR_STOPEN BIT(21) +#define EPITCR_OM_DISCON (0 << 22) +#define EPITCR_OM_TOGGLE (1 << 22) +#define EPITCR_OM_CLEAR (2 << 22) +#define EPITCR_OM_SET (3 << 22) +#define EPITCR_CLKSRC_OFF (0 << 24) +#define EPITCR_CLKSRC_PERIPHERAL (1 << 24) +#define EPITCR_CLKSRC_REF_HIGH (2 << 24) +#define EPITCR_CLKSRC_REF_LOW (3 << 24) + +#define EPITSR_OCIF BIT(0) + +struct epit_timer { +void __iomem *base; +int irq; +struct clk *clk; +struct clock_event_device ced; +struct irqaction act; +}; + +static void __iomem *sched_clock_reg; + +static inline struct epit_timer *to_epit_timer (struct clock_event_device *ced) +{ + return container_of(ced, struct epit_timer, ced); +} + +static inline void epit_irq_disable (struct epit_timer *epittm) +{ +u32 val; + + val =readl_relaxed (epittm->base +EPITCR ); +writel_relaxed (val & ~EPITCR_OCIEN , epittm->base +EPITCR ); +} + +static inline void epit_irq_enable (struct epit_timer *epittm) +{ +u32 val; + + val =readl_relaxed (epittm->base +EPITCR ); +writel_relaxed (val |EPITCR_OCIEN , epittm->base +EPITCR ); +} + +static void epit_irq_acknowledge (struct epit_timer *epittm) +{ +writel_relaxed (EPITSR_OCIF , epittm->base +EPITSR ); +} + +static u64 notrace epit_read_sched_clock(void) +{ +return ~readl_relaxed (sched_clock_reg); +} + +static int epit_set_next_event (unsigned long cycles, +struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long tcmp; + + tcmp =readl_relaxed (epittm->base +EPITCNR ) - cycles; +writel_relaxed (tcmp, epittm->base +EPITCMPR ); + +return 0 ; +} + +/* Left event sources disabled, no more interrupts appear */ +static int epit_shutdown (struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long flags; + +/* +* The timer interrupt generation is disabled at least +* for enough time to call epit_set_next_event() +*/ +local_irq_save (flags); + +/* Disable interrupt in EPIT module */ +epit_irq_disable (epittm); + +/* Clear pending interrupt */ +epit_irq_acknowledge (epittm); + +local_irq_restore (flags); + +return 0 ; +} + +static int epit_set_oneshot (struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long flags; + +/* +* The timer interrupt generation is disabled at least +* for enough time to call epit_set_next_event() +*/ +local_irq_save (flags); + +/* Disable interrupt in EPIT module */ +epit_irq_disable (epittm); + +/* Clear pending interrupt, only while switching mode */ +if (!clockevent_state_oneshot (ced)) +epit_irq_acknowledge (epittm); + +/* +* Do not put overhead of interrupt enable/disable into +* epit_set_next_event(), the core has about 4 minutes +* to call epit_set_next_event() or shutdown clock after +* mode switching +*/ +epit_irq_enable (epittm); +local_irq_restore (flags); + +return 0 ; +} + +static irqreturn_t epit_timer_interrupt (int irq,void *dev_id) +{ +struct clock_event_device *ced = dev_id; +struct epit_timer *epittm =to_epit_timer (ced); + +epit_irq_acknowledge (epittm); + + ced->event_handler (ced); + +return IRQ_HANDLED ; +} + +static int __init epit_clocksource_init (struct epit_timer *epittm) +{ +unsigned int c =clk_get_rate (epittm->clk); + + sched_clock_reg = epittm->base +EPITCNR ; +sched_clock_register (epit_read_sched_clock,32 , c); + +return clocksource_mmio_init (epittm->base +EPITCNR ,"epit" , c,200 ,32 , + clocksource_mmio_readl_down); +} + +static int __init epit_clockevent_init (struct epit_timer *epittm) +{ +struct clock_event_device *ced = &epittm->ced; +struct irqaction *act = &epittm->act; + + ced->name ="epit" ; + ced->features =CLOCK_EVT_FEAT_ONESHOT |CLOCK_EVT_FEAT_DYNIRQ ; + ced->set_state_shutdown = epit_shutdown; + ced->tick_resume = epit_shutdown; + ced->set_state_oneshot = epit_set_oneshot; + ced->set_next_event = epit_set_next_event; + ced->rating =200 ; + ced->cpumask =cpumask_of (0 ); + ced->irq = epittm->irq; +clockevents_config_and_register (ced,clk_get_rate (epittm->clk), +0xff ,0xfffffffe ); + + act->name ="i.MX EPIT Timer Tick" , + act->flags =IRQF_TIMER |IRQF_IRQPOLL ; + act->handler = epit_timer_interrupt; + act->dev_id = ced; + +/* Make irqs happen */ +return setup_irq (epittm->irq, act); +} + +static int __init epit_timer_init (struct device_node *np) +{ +struct epit_timer *epittm; +int ret; + + epittm =kzalloc (sizeof (*epittm),GFP_KERNEL ); +if (!epittm) +return -ENOMEM ; + + epittm->base =of_iomap (np,0 ); +if (!epittm->base) { + ret = -ENXIO ; + goto out_kfree; + } + + epittm->irq =irq_of_parse_and_map (np,0 ); +if (!epittm->irq) { + ret = -EINVAL ; + goto out_iounmap; + } + +/* Get EPIT clock */
Quoted 8 lines
drivers/clocksource/timer-imx-epit.c line 228
Quoted 6 lines
drivers/clocksource/timer-imx-epit.c new file +6
-0+ epittm->clk =of_clk_get (np,0 ); +if (IS_ERR (epittm->clk)) { +pr_err ("i.MX EPIT: unable to get clk\n" ); + ret =PTR_ERR (epittm->clk); + goto out_iounmap; + }There is something off with indent here.
Thanks for pointing out that, I will fix it.
Quoted 3 lines
There is a helper library in
drivers/clocksource/timer-of.cwhich might be useful for this driver.
Indeed, but will require a bit of rewrite.
Clement
Quoted 41 lines
--
StefanQuoted 37 lines
drivers/clocksource/timer-imx-epit.c new file +37
-0+ + ret =clk_prepare_enable (epittm->clk); +if (ret) { +pr_err ("i.MX EPIT: unable to prepare+enable clk\n" ); + goto out_iounmap; + } + +/* Initialise to a known state (all timers off, and timing reset) */ +writel_relaxed (0x0 , epittm->base +EPITCR ); +writel_relaxed (0xffffffff , epittm->base +EPITLR ); +writel_relaxed (EPITCR_EN |EPITCR_CLKSRC_REF_HIGH |EPITCR_WAITEN , + epittm->base +EPITCR ); + + ret =epit_clocksource_init (epittm); +if (ret) { +pr_err ("i.MX EPIT: failed to init clocksource\n" ); + goto out_clk_disable; + } + + ret =epit_clockevent_init (epittm); +if (ret) { +pr_err ("i.MX EPIT: failed to init clockevent\n" ); + goto out_clk_disable; + } + +return 0 ; + +out_clk_disable: +clk_disable_unprepare (epittm->clk); +out_iounmap: +iounmap (epittm->base); +out_kfree: +kfree (epittm); + +return ret; +} +TIMER_OF_DECLARE (epit_timer,"fsl,imx31-epit" , epit_timer_init);
Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer
Quoted 217 lines · Cl?ment P?ron
Hi Stefan,
Quoted 206 lines
Quoted 206 lines
drivers/clocksource/timer-imx-epit.c new file +206
-0+ +#define EPITCR 0x00 +#define EPITSR 0x04 +#define EPITLR 0x08 +#define EPITCMPR 0x0c +#define EPITCNR 0x10 + +#define EPITCR_EN BIT(0) +#define EPITCR_ENMOD BIT(1) +#define EPITCR_OCIEN BIT(2) +#define EPITCR_RLD BIT(3) +#define EPITCR_PRESC (x) (((x) & 0xfff) << 4) +#define EPITCR_SWR BIT(16) +#define EPITCR_IOVW BIT(17) +#define EPITCR_DBGEN BIT(18) +#define EPITCR_WAITEN BIT(19) +#define EPITCR_RES BIT(20) +#define EPITCR_STOPEN BIT(21) +#define EPITCR_OM_DISCON (0 << 22) +#define EPITCR_OM_TOGGLE (1 << 22) +#define EPITCR_OM_CLEAR (2 << 22) +#define EPITCR_OM_SET (3 << 22) +#define EPITCR_CLKSRC_OFF (0 << 24) +#define EPITCR_CLKSRC_PERIPHERAL (1 << 24) +#define EPITCR_CLKSRC_REF_HIGH (2 << 24) +#define EPITCR_CLKSRC_REF_LOW (3 << 24) + +#define EPITSR_OCIF BIT(0) + +struct epit_timer { +void __iomem *base; +int irq; +struct clk *clk; +struct clock_event_device ced; +struct irqaction act; +}; + +static void __iomem *sched_clock_reg; + +static inline struct epit_timer *to_epit_timer (struct clock_event_device *ced) +{ + return container_of(ced, struct epit_timer, ced); +} + +static inline void epit_irq_disable (struct epit_timer *epittm) +{ +u32 val; + + val =readl_relaxed (epittm->base +EPITCR ); +writel_relaxed (val & ~EPITCR_OCIEN , epittm->base +EPITCR ); +} + +static inline void epit_irq_enable (struct epit_timer *epittm) +{ +u32 val; + + val =readl_relaxed (epittm->base +EPITCR ); +writel_relaxed (val |EPITCR_OCIEN , epittm->base +EPITCR ); +} + +static void epit_irq_acknowledge (struct epit_timer *epittm) +{ +writel_relaxed (EPITSR_OCIF , epittm->base +EPITSR ); +} + +static u64 notrace epit_read_sched_clock(void) +{ +return ~readl_relaxed (sched_clock_reg); +} + +static int epit_set_next_event (unsigned long cycles, +struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long tcmp; + + tcmp =readl_relaxed (epittm->base +EPITCNR ) - cycles; +writel_relaxed (tcmp, epittm->base +EPITCMPR ); + +return 0 ; +} + +/* Left event sources disabled, no more interrupts appear */ +static int epit_shutdown (struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long flags; + +/* +* The timer interrupt generation is disabled at least +* for enough time to call epit_set_next_event() +*/ +local_irq_save (flags); + +/* Disable interrupt in EPIT module */ +epit_irq_disable (epittm); + +/* Clear pending interrupt */ +epit_irq_acknowledge (epittm); + +local_irq_restore (flags); + +return 0 ; +} + +static int epit_set_oneshot (struct clock_event_device *ced) +{ +struct epit_timer *epittm =to_epit_timer (ced); +unsigned long flags; + +/* +* The timer interrupt generation is disabled at least +* for enough time to call epit_set_next_event() +*/ +local_irq_save (flags); + +/* Disable interrupt in EPIT module */ +epit_irq_disable (epittm); + +/* Clear pending interrupt, only while switching mode */ +if (!clockevent_state_oneshot (ced)) +epit_irq_acknowledge (epittm); + +/* +* Do not put overhead of interrupt enable/disable into +* epit_set_next_event(), the core has about 4 minutes +* to call epit_set_next_event() or shutdown clock after +* mode switching +*/ +epit_irq_enable (epittm); +local_irq_restore (flags); + +return 0 ; +} + +static irqreturn_t epit_timer_interrupt (int irq,void *dev_id) +{ +struct clock_event_device *ced = dev_id; +struct epit_timer *epittm =to_epit_timer (ced); + +epit_irq_acknowledge (epittm); + + ced->event_handler (ced); + +return IRQ_HANDLED ; +} + +static int __init epit_clocksource_init (struct epit_timer *epittm) +{ +unsigned int c =clk_get_rate (epittm->clk); + + sched_clock_reg = epittm->base +EPITCNR ; +sched_clock_register (epit_read_sched_clock,32 , c); + +return clocksource_mmio_init (epittm->base +EPITCNR ,"epit" , c,200 ,32 , + clocksource_mmio_readl_down); +} + +static int __init epit_clockevent_init (struct epit_timer *epittm) +{ +struct clock_event_device *ced = &epittm->ced; +struct irqaction *act = &epittm->act; + + ced->name ="epit" ; + ced->features =CLOCK_EVT_FEAT_ONESHOT |CLOCK_EVT_FEAT_DYNIRQ ; + ced->set_state_shutdown = epit_shutdown; + ced->tick_resume = epit_shutdown; + ced->set_state_oneshot = epit_set_oneshot; + ced->set_next_event = epit_set_next_event; + ced->rating =200 ; + ced->cpumask =cpumask_of (0 ); + ced->irq = epittm->irq; +clockevents_config_and_register (ced,clk_get_rate (epittm->clk), +0xff ,0xfffffffe ); + + act->name ="i.MX EPIT Timer Tick" , + act->flags =IRQF_TIMER |IRQF_IRQPOLL ; + act->handler = epit_timer_interrupt; + act->dev_id = ced; + +/* Make irqs happen */ +return setup_irq (epittm->irq, act); +} + +static int __init epit_timer_init (struct device_node *np) +{ +struct epit_timer *epittm; +int ret; + + epittm =kzalloc (sizeof (*epittm),GFP_KERNEL ); +if (!epittm) +return -ENOMEM ; + + epittm->base =of_iomap (np,0 ); +if (!epittm->base) { + ret = -ENXIO ; + goto out_kfree; + } + + epittm->irq =irq_of_parse_and_map (np,0 ); +if (!epittm->irq) { + ret = -EINVAL ; + goto out_iounmap; + } + +/* Get EPIT clock */ Quoted 8 lines
drivers/clocksource/timer-imx-epit.c line 228
Quoted 6 lines
drivers/clocksource/timer-imx-epit.c new file +6
-0+ epittm->clk =of_clk_get (np,0 ); +if (IS_ERR (epittm->clk)) { +pr_err ("i.MX EPIT: unable to get clk\n" ); + ret =PTR_ERR (epittm->clk); + goto out_iounmap; + }There is something off with indent here.
Quoted 6 lines
Thanks for pointing out that, I will fix it.
Quoted 3 lines
There is a helper library in
drivers/clocksource/timer-of.cwhich might be useful for this driver.Indeed, but will require a bit of rewrite.
That is fine for me ;-) Afaict, if should be simplify code a quite a bit, and doable with reasonable amount of work.
It is typically the expectation for new drivers to make use of such libraries. In the end it is up to the clocksource maintainer(s) to decide whether they make it as an requirement for this driver.
--
Stefan
Quoted 44 lines
Clement
Quoted 41 lines
--
StefanQuoted 37 lines
drivers/clocksource/timer-imx-epit.c new file +37
-0+ + ret =clk_prepare_enable (epittm->clk); +if (ret) { +pr_err ("i.MX EPIT: unable to prepare+enable clk\n" ); + goto out_iounmap; + } + +/* Initialise to a known state (all timers off, and timing reset) */ +writel_relaxed (0x0 , epittm->base +EPITCR ); +writel_relaxed (0xffffffff , epittm->base +EPITLR ); +writel_relaxed (EPITCR_EN |EPITCR_CLKSRC_REF_HIGH |EPITCR_WAITEN , + epittm->base +EPITCR ); + + ret =epit_clocksource_init (epittm); +if (ret) { +pr_err ("i.MX EPIT: failed to init clocksource\n" ); + goto out_clk_disable; + } + + ret =epit_clockevent_init (epittm); +if (ret) { +pr_err ("i.MX EPIT: failed to init clockevent\n" ); + goto out_clk_disable; + } + +return 0 ; + +out_clk_disable: +clk_disable_unprepare (epittm->clk); +out_iounmap: +iounmap (epittm->base); +out_kfree: +kfree (epittm); + +return ret; +} +TIMER_OF_DECLARE (epit_timer,"fsl,imx31-epit" , epit_timer_init);
Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer
In reply to Clément PéronQuoted 6 lines · Cl?ment P?ron
From: Colin Didier <[email protected]>
Add driver for NXP's EPIT timer used in i.MX SoC.
Signed-off-by Colin Didier <[email protected]>Signed-off-by Cl?ment Peron <[email protected]>
I tested the driver on i.MX31 only, and I didn't find any problems.
Please fix the indentation issue found by Stefan in v7.
Regarding utilization of timer-of.c it can be postponed IMHO, but it's up to clocksource maintainers and you to decide, and if you do such an update for v7, then please don't add my tags, I'll review and test it again.
--
With best wishes,
Vladimir
PATCH v6 5/5 ARM: dts: imx: add missing compatible and clock properties for EPIT
In reply to Clément PéronFrom: Colin Didier <[email protected]>
Add missing compatible and clock properties for EPIT node.
--- arch/arm/boot/dts/imx25.dtsi | 8 ++++++-- arch/arm/boot/dts/imx6qdl.dtsi | 10 ++++++++-- arch/arm/boot/dts/imx6sl.dtsi | 10 ++++++++-- arch/arm/boot/dts/imx6sx.dtsi | 10 ++++++++-- arch/arm/boot/dts/imx6ul.dtsi | 10 ++++++++-- 5 files changed, 38 insertions(+), 10 deletions(-)
arch/arm/boot/dts/imx25.dtsi +6 -2
@@ -396,15 +396,19 @@ }; epit1: timer at 53f94000 { - compatible = "fsl,imx25-epit"; + compatible = "fsl,imx25-epit", "fsl,imx31-epit"; reg = <0x53f94000 0x4000>; interrupts = <28>; + clocks = <&clks 83>; + status = "disabled"; }; epit2: timer at 53f98000 { - compatible = "fsl,imx25-epit"; + compatible = "fsl,imx25-epit", "fsl,imx31-epit"; reg = <0x53f98000 0x4000>; interrupts = <27>; + clocks = <&clks 84>; + status = "disabled"; }; gpio4: gpio at 53f9c000 {
arch/arm/boot/dts/imx6qdl.dtsi +8 -2
@@ -843,14 +843,20 @@ }; }; - epit1: epit at 20d0000 { /* EPIT1 */ + epit1: timer at 20d0000 { + compatible = "fsl,imx6qdl-epit", "fsl,imx31-epit"; reg = <0x020d0000 0x4000>; interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6QDL_CLK_EPIT1>; + status = "disabled"; }; - epit2: epit at 20d4000 { /* EPIT2 */ + epit2: timer at 20d4000 { + compatible = "fsl,imx6qdl-epit", "fsl,imx31-epit"; reg = <0x020d4000 0x4000>; interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6QDL_CLK_EPIT2>; + status = "disabled"; }; src: src at 20d8000 {
arch/arm/boot/dts/imx6sl.dtsi +8 -2
@@ -671,14 +671,20 @@ }; }; - epit1: epit at 20d0000 { + epit1: timer at 20d0000 { + compatible = "fsl,imx6sl-epit", "fsl,imx31-epit"; reg = <0x020d0000 0x4000>; interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_EPIT1>; + status = "disabled"; }; - epit2: epit at 20d4000 { + epit2: timer at 20d4000 { + compatible = "fsl,imx6sl-epit", "fsl,imx31-epit"; reg = <0x020d4000 0x4000>; interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SL_CLK_EPIT2>; + status = "disabled"; }; src: src at 20d8000 {
arch/arm/boot/dts/imx6sx.dtsi +8 -2
@@ -736,14 +736,20 @@ }; }; - epit1: epit at 20d0000 { + epit1: timer at 20d0000 { + compatible = "fsl,imx6sx-epit", "fsl,imx31-epit"; reg = <0x020d0000 0x4000>; interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SX_CLK_EPIT1>; + status = "disabled"; }; - epit2: epit at 20d4000 { + epit2: timer at 20d4000 { + compatible = "fsl,imx6sx-epit", "fsl,imx31-epit"; reg = <0x020d4000 0x4000>; interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6SX_CLK_EPIT2>; + status = "disabled"; }; src: src at 20d8000 {
arch/arm/boot/dts/imx6ul.dtsi +8 -2
@@ -658,14 +658,20 @@ }; }; - epit1: epit at 20d0000 { + epit1: timer at 20d0000 { + compatible = "fsl,imx6ul-epit", "fsl,imx31-epit"; reg = <0x020d0000 0x4000>; interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6UL_CLK_EPIT1>; + status = "disabled"; }; - epit2: epit at 20d4000 { + epit2: timer at 20d4000 { + compatible = "fsl,imx6ul-epit", "fsl,imx31-epit"; reg = <0x020d4000 0x4000>; interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6UL_CLK_EPIT2>; + status = "disabled"; }; src: src at 20d8000 {
Signature · 1 line
2.17.1
Re: [PATCH v6 5/5] ARM: dts: imx: add missing compatible and clock properties for EPIT
Quoted 6 lines · Cl?ment P?ron
From: Colin Didier <[email protected]>
Add missing compatible and clock properties for EPIT node.
Signed-off-by Colin Didier <[email protected]>Signed-off-by Cl?ment Peron <[email protected]>
--
With best wishes,
Vladimir
Hi Cl?ment,
Quoted 8 lines · Cl?ment P?ron
From: Cl?ment Peron <[email protected]>
As suggested in the commit message we have added the device tree support, proper bindings and we moved the driver into the correct folder.
Moreover we made some changes like use of relaxed IO accesor, implement
sched_clock,delay_timerand reduce the clockeventsmin_delta.
I reviewed and tested the driver on i.MX31, as expected it works fine, and I'll give my tags per a commit, please add them to v7 changes.
--
Best wishes,
Vladimir
Hi Vladimir,
Quoted 9 lines · Vladimir Zapolskiy
Hi Cl?ment,
Quoted 5 lines · Cl?ment P?ron
From: Cl?ment Peron <[email protected]>
As suggested in the commit message we have added the device tree support, proper bindings and we moved the driver into the correct folder.
Quoted 6 lines
Quoted 3 lines
Moreover we made some changes like use of relaxed IO accesor, implement
sched_clock,delay_timerand reduce the clockeventsmin_delta.I reviewed and tested the driver on i.MX31, as expected it works fine, and I'll give my tags per a commit, please add them to v7 changes.
Thanks, i will
Quoted 4 lines
--
Best wishes,
Vladimir