Skip to contentSkip to conversation

Reintroduce i.MX EPIT Timer

PATCH Superseded Clément Péron 5 participants 12 messages May 29, 2018 – Jun 2, 2018

Subsystems (7)

v3 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v3 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 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
Documentation: DT: 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: imx6qdl: add missing compatible and clock properties for
EPIT

 .../devicetree/bindings/clock/imx6q,epit.txt  |  24 ++
 arch/arm/boot/dts/imx6qdl.dtsi                |  10 +
 arch/arm/mach-imx/common.h                    |   1 -
 drivers/clk/imx/clk-imx6q.c                   |   2 +
 drivers/clocksource/Kconfig                   |  12 +
 drivers/clocksource/Makefile                  |   1 +
 drivers/clocksource/timer-imx-epit.c          | 283 ++++++++++++++++++
 include/dt-bindings/clock/imx6qdl-clock.h     |   4 +-
 8 files changed, 335 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt
 create mode 100644 drivers/clocksource/timer-imx-epit.c
Signature · 1 line
2.17.0
#1 lore raw

PATCH v3 1/5 ARM: imx: remove inexistant EPIT timer init

From: 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
index c8d68e918b2f..18aae76fa2da 100644
@@ -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.0
#2 lore raw

PATCH v3 2/5 clk: imx6: add EPIT clock support

In reply to Clément Péron

From: Colin Didier <[email protected]>

Add EPIT clock support to the i.MX6Q clocking infrastructure.

---
 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
index 8d518ad5dc13..b9ea7037e193 100644
@@ -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
index da59fd9cdb5e..7ad171b8f3bf 100644
@@ -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.0
#4 lore raw

PATCH v3 3/5 Documentation: DT: add i.MX EPIT timer binding

In reply to Clément Péron

From: Cl?ment Peron <[email protected]>

Add devicetree binding document for NXP's i.MX SoC specific EPIT timer driver.

---
 .../devicetree/bindings/clock/imx6q,epit.txt  | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +24 -0
new file mode 100644
index 000000000000..a84a60c6ae35
@@ -0,0 +1,24 @@
+Binding for the i.MX6 EPIT timer
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: should be "fsl,imx6q-epit"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: Should contain EPIT controller interrupt
+- clocks: list of clock specifiers, must contain an entry for each required
+  entry in clock-names
+- clock-names : should include entries "ipg", "per"
+
+Example:
+	epit1: epit at 20d0000 {
+		compatible = "fsl,imx6q-epit";
+		reg = <0x020d0000 0x4000>;
+		interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clks IMX6QDL_CLK_EPIT1>,
+			<&clks IMX6QDL_CLK_IPG_PER>;
+		clock-names = "ipg", "per";
+	};
Signature · 1 line
2.17.0
#5 lore raw

PATCH v3 3/5 Documentation: DT: add i.MX EPIT timer binding

Hi Cl?ment,

Documentation/devicetree/bindings/clock/imx6q,epit.txt

Quoted 16 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.

---
 .../devicetree/bindings/clock/imx6q,epit.txt  | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +0 -0
new file mode 100644
index 000000000000..a84a60c6ae35

The file should be renamed to a more generic name like 'imx,epit.txt' or 'imx31,epit.txt'.

Also note that the folder is incorrectly selected, it must be Documentation/devicetree/bindings/timer/

Documentation/devicetree/bindings/clock/imx6q,epit.txt line 1

Quoted 2 lines
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +1 -0
@@ -0,0 +1,24 @@
+Binding for the i.MX6 EPIT timer

Let's change it to 'Binding for the i.MX EPIT timer'.

Documentation/devicetree/bindings/clock/imx6q,epit.txt line 8

Quoted 7 lines
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +7 -0
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: should be "fsl,imx6q-epit"

Compatible should be "fsl,imx31-epit", i.MX31 SoC seems to be the first one in the series, which has the timer module.

There should be added more compatibles specific to later SoC versions.

Documentation/devicetree/bindings/clock/imx6q,epit.txt line 18

Quoted 10 lines
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +10 -0
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: Should contain EPIT controller interrupt
+- clocks: list of clock specifiers, must contain an entry for each required
+  entry in clock-names
+- clock-names : should include entries "ipg", "per"
+
+Example:
+	epit1: epit at 20d0000 {
+		compatible = "fsl,imx6q-epit";

For i.MX6Q example a list of compatibles should be updated like

	compatible = "fsl,imx6q-epit", "fsl,imx31-epit";

Documentation/devicetree/bindings/clock/imx6q,epit.txt line 23

Quoted 5 lines
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +5 -0
+		reg = <0x020d0000 0x4000>;
+		interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clks IMX6QDL_CLK_EPIT1>,
+			<&clks IMX6QDL_CLK_IPG_PER>;
+		clock-names = "ipg", "per";

It looks like "ipg" and "per" clocks are swapped, please double check it.

Documentation/devicetree/bindings/clock/imx6q,epit.txt line 24

Quoted 2 lines
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +1 -0
+	};

--
With best wishes,
Vladimir

#6 lore raw

PATCH v3 3/5 Documentation: DT: add i.MX EPIT timer binding

Quoted 23 lines · Vladimir Zapolskiy

Hi Cl?ment,

Documentation/devicetree/bindings/clock/imx6q,epit.txt

Quoted 16 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.

---
 .../devicetree/bindings/clock/imx6q,epit.txt  | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt
Documentation/devicetree/bindings/clock/imx6q,epit.txt new file +0 -0
new file mode 100644
index 000000000000..a84a60c6ae35

The file should be renamed to a more generic name like 'imx,epit.txt' or 'imx31,epit.txt'.

Quoted 3 lines

Also note that the folder is incorrectly selected, it must be Documentation/devicetree/bindings/timer/

And linux-clk mailing list for publishing seems to be improper one, instead please add Daniel Lezcano <[email protected]> and Thomas Gleixner <[email protected]> to the list of addressees, and the proper mailing list address is linux-kernel at vger.kernel.org

--
With best wishes,
Vladimir

#7 lore raw

PATCH v3 4/5 clocksource: add driver for i.MX EPIT timer

In reply to Clément Péron

From: Colin Didier <[email protected]>

Add driver for NXP's EPIT timer used in i.MX 6 family of SoC.

---
 drivers/clocksource/Kconfig          |  12 ++
 drivers/clocksource/Makefile         |   1 +
 drivers/clocksource/timer-imx-epit.c | 283 +++++++++++++++++++++++++++
 3 files changed, 296 insertions(+)
 create mode 100644 drivers/clocksource/timer-imx-epit.c
drivers/clocksource/Kconfig +12 -0
index 8e8a09755d10..920a0874f3a4 100644
@@ -576,6 +576,18 @@ 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 ARM && CLKDEV_LOOKUP && OF && (ARCH_MXC || COMPILE_TEST)
+	select TIMER_OF
+	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
index 00caf37e52f9..d9426f69ec69 100644
@@ -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 +283 -0

283 lines not on this page · raw message

Signature · 1 line
2.17.0
reviewer
#8 lore raw

PATCH v3 4/5 clocksource: add driver for i.MX EPIT timer

Quoted 23 lines · Cl?ment P?ron
drivers/clocksource/timer-imx-epit.c new file +23 -0
+static int __init epit_timer_init(struct device_node *np)
+{
+       struct epit_timer *epittm;
+       struct clk *clk_ipg;
+       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;
+       }
+
+       clk_ipg = of_clk_get_by_name(np, "ipg");

drivers/clocksource/timer-imx-epit.c line 236

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -0
+       if (IS_ERR(clk_ipg)) {
+               pr_err("i.MX EPIT: unable to get clk_ipg\n");
+               ret = PTR_ERR(clk_ipg);
+               goto out_iounmap;
+       }
+
+       ret = clk_prepare_enable(clk_ipg);
+       if (ret) {
+               pr_err("i.MX EPIT: unable to prepare+enable clk_ipg\n");
+               goto out_clk_ipg_disable;

This should be: goto out_iounmap;

#9 lore raw

PATCH v3 4/5 clocksource: add driver for i.MX EPIT timer

In reply to Clément Péron

Hi Cl?ment,

please find some more review comments.

Quoted 18 lines · Cl?ment P?ron

From: Colin Didier <[email protected]>

Add driver for NXP's EPIT timer used in i.MX 6 family of SoC.

---
 drivers/clocksource/Kconfig          |  12 ++
 drivers/clocksource/Makefile         |   1 +
 drivers/clocksource/timer-imx-epit.c | 283 +++++++++++++++++++++++++++
 3 files changed, 296 insertions(+)
 create mode 100644 drivers/clocksource/timer-imx-epit.c
drivers/clocksource/Kconfig +0 -0
index 8e8a09755d10..920a0874f3a4 100644

drivers/clocksource/Kconfig config H8300_TPU · line 581

Quoted 6 lines
drivers/clocksource/Kconfig +3 -0
@@ -576,6 +576,18 @@ 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 ARM && CLKDEV_LOOKUP && OF && (ARCH_MXC || COMPILE_TEST)

Here 'depends on ARM' can be removed, because ARCH_MXC implies it.

Also ARCH_MXC implies ARCH_MULTIPLATFORM, which implies USE_OF in turn, so I would say that the following line is correct, sorry about a previous comment asking to add an explicit OF dependency:

	depends on CLKDEV_LOOKUP && (ARCH_MXC || COMPILE_TEST)

However most of the clocksource drivers follow 'bool "..." if COMPILE_TEST' pattern, and it might be preferable to maintainers.

drivers/clocksource/Kconfig config H8300_TPU · line 582

Quoted 1 line
drivers/clocksource/Kconfig +1 -0
+	select TIMER_OF

The driver does not have this dependency.

Quoted 31 lines
drivers/clocksource/Kconfig +8 -0
+	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
index 00caf37e52f9..d9426f69ec69 100644
@@ -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 +2 -0
new file mode 100644
index 000000000000..87025d5f3a97
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*

drivers/clocksource/timer-imx-epit.c line 12

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -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/err.h>

The include above can be dropped.

drivers/clocksource/timer-imx-epit.c line 14

Quoted 2 lines
drivers/clocksource/timer-imx-epit.c new file +2 -0
+#include <linux/interrupt.h>
+#include <linux/irq.h>

The include above can be dropped.

drivers/clocksource/timer-imx-epit.c line 17

Quoted 3 lines
drivers/clocksource/timer-imx-epit.c new file +3 -0
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>

The include above can be dropped.

Quoted 73 lines
drivers/clocksource/timer-imx-epit.c new file +73 -0
+#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_per;
+	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 __init epit_clocksource_init(struct epit_timer *epittm)

drivers/clocksource/timer-imx-epit.c line 100

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -0
+{
+	unsigned int c = clk_get_rate(epittm->clk_per);
+
+	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);
+}
+

I would suggest to place epit_clocksource_init() function right before epit_timer_init().

Quoted 83 lines
drivers/clocksource/timer-imx-epit.c new file +83 -0
+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_clockevent_init(struct epit_timer *epittm)
+{
+	struct clock_event_device *ced = &epittm->ced;
+	struct irqaction *act = &epittm->act;
+
+	ced->name = "epit";

drivers/clocksource/timer-imx-epit.c line 193

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -0
+	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_per),
+		0xff, 0xfffffffe);

Please indent the wrapped line.

Quoted 56 lines
drivers/clocksource/timer-imx-epit.c new file +56 -0
+
+	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;
+	struct clk *clk_ipg;
+	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;
+	}
+
+	clk_ipg = of_clk_get_by_name(np, "ipg");
+	if (IS_ERR(clk_ipg)) {
+		pr_err("i.MX EPIT: unable to get clk_ipg\n");
+		ret = PTR_ERR(clk_ipg);
+		goto out_iounmap;
+	}
+
+	ret = clk_prepare_enable(clk_ipg);
+	if (ret) {
+		pr_err("i.MX EPIT: unable to prepare+enable clk_ipg\n");
+		goto out_clk_ipg_disable;
+	}
+
+	epittm->clk_per = of_clk_get_by_name(np, "per");
+	if (IS_ERR(epittm->clk_per)) {
+		pr_err("i.MX EPIT: unable to get clk_per\n");
+		ret = PTR_ERR(epittm->clk_per);
+		goto out_clk_ipg_disable;
+	}
+
+	ret = clk_prepare_enable(epittm->clk_per);
+	if (ret) {
+		pr_err("i.MX EPIT: unable to prepare+enable clk_per\n");
+		goto out_clk_ipg_disable;

drivers/clocksource/timer-imx-epit.c line 259

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -0
+	}
+
+	/* 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) {

Add a space before left parenthesis.

drivers/clocksource/timer-imx-epit.c line 265

Quoted 6 lines
drivers/clocksource/timer-imx-epit.c new file +6 -0
+		pr_err("i.MX EPIT: failed to init clocksource\n");
+		goto out_clk_per_disable;
+	}
+
+	ret = epit_clockevent_init(epittm);
+	if(ret) {

Add a space before left parenthesis.

Quoted 9 lines
drivers/clocksource/timer-imx-epit.c new file +8 -0
+		pr_err("i.MX EPIT: failed to init clockevent\n");
+		goto out_clk_per_disable;
+	}
+
+	return 0;
+
+out_clk_per_disable:
+	clk_disable_unprepare(epittm->clk_per);

drivers/clocksource/timer-imx-epit.c line 283

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -0
+out_clk_ipg_disable:
+	clk_disable_unprepare(clk_ipg);
+out_iounmap:
+	iounmap(epittm->base);
+out_kfree:
+	kfree(epittm);
+
+	return ret;
+}
+TIMER_OF_DECLARE(mx6q_timer, "fsl,imx6q-epit", epit_timer_init);

Here "fsl,imx31-epit" would be way better than "fsl,imx6q-epit", please fix it in the documentation as well.

--
With best wishes,
Vladimir

#10 lore raw

PATCH v3 4/5 clocksource: add driver for i.MX EPIT timer

Hi Vladimir,

Le mer. 30 mai 2018 ? 08:56, Vladimir Zapolskiy <
[email protected]> a ?crit :

Quoted 26 lines

Hi Cl?ment,

please find some more review comments.

Quoted 21 lines · Cl?ment P?ron

From: Colin Didier <[email protected]>

Add driver for NXP's EPIT timer used in i.MX 6 family of SoC.

---
 drivers/clocksource/Kconfig          |  12 ++
 drivers/clocksource/Makefile         |   1 +
 drivers/clocksource/timer-imx-epit.c | 283 +++++++++++++++++++++++++++
 3 files changed, 296 insertions(+)
 create mode 100644 drivers/clocksource/timer-imx-epit.c
drivers/clocksource/Kconfig +0 -0
index 8e8a09755d10..920a0874f3a4 100644
@@ -576,6 +576,18 @@ config H8300_TPU
        This enables the clocksource for the H8300 platform with the
        H8S2678 cpu.
 
Quoted 3 lines

drivers/clocksource/Kconfig config H8300_TPU

Quoted 3 lines
drivers/clocksource/Kconfig +3 -0
+config CLKSRC_IMX_EPIT
+     bool "Clocksource using i.MX EPIT"
+     depends on ARM && CLKDEV_LOOKUP && OF && (ARCH_MXC ||

COMPILE_TEST)

Quoted 9 lines

Here 'depends on ARM' can be removed, because ARCH_MXC implies it.

Also ARCH_MXC implies ARCH_MULTIPLATFORM, which implies USE_OF in turn, so I would say that the following line is correct, sorry about a previous comment asking to add an explicit OF dependency:

         depends on CLKDEV_LOOKUP && (ARCH_MXC || COMPILE_TEST)

However most of the clocksource drivers follow 'bool "..." if

COMPILE_TEST'

Quoted 1 line

pattern, and it might be preferable to maintainers.

Usually, the timer driver are selected in the arch Kconfig. But in this case,
we want to keep the i.MX GPT except if the user explicitly select the i.MX EPIT.
With the "if COMPILE_TEST" the user can't choose it.

Quoted 4 lines

drivers/clocksource/Kconfig config H8300_TPU · line 582

Quoted 1 line
drivers/clocksource/Kconfig +1 -0
+     select TIMER_OF

The driver does not have this dependency.

Quoted 16 lines

drivers/clocksource/Makefile obj-$(CONFIG_INTEGRATOR_AP_TIMER) +=

Quoted 16 lines
drivers/clocksource/Kconfig +8 -0
+     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 +0 -0
index 00caf37e52f9..d9426f69ec69 100644
@@ -69,6 +69,7 @@ obj-$(CONFIG_INTEGRATOR_AP_TIMER)   +=

timer-integrator-ap.o

Quoted 8 lines

Review

Quoted 8 lines
drivers/clocksource/Makefile +1 -0
 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
(quoted diff) +0 -0

b/drivers/clocksource/timer-imx-epit.c

Quoted 65 lines
Quoted 7 lines

new file mode 100644
index 000000000000..87025d5f3a97

drivers/clocksource/timer-imx-epit.c new file +2 -0
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*

drivers/clocksource/timer-imx-epit.c line 12

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -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/err.h>

The include above can be dropped.

drivers/clocksource/timer-imx-epit.c line 14

Quoted 2 lines
drivers/clocksource/timer-imx-epit.c new file +2 -0
+#include <linux/interrupt.h>
+#include <linux/irq.h>

The include above can be dropped.

drivers/clocksource/timer-imx-epit.c line 17

Quoted 3 lines
drivers/clocksource/timer-imx-epit.c new file +3 -0
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>

The include above can be dropped.

Quoted 34 lines
drivers/clocksource/timer-imx-epit.c new file +34 -0
+#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;
Quoted 8 lines

drivers/clocksource/timer-imx-epit.c

Quoted 8 lines
drivers/clocksource/timer-imx-epit.c new file +8 -0
+     struct clk *clk_per;
+     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)

Quoted 30 lines
Quoted 30 lines
drivers/clocksource/timer-imx-epit.c new file +30 -0
+{
+     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);
+}
+
Quoted 8 lines

drivers/clocksource/timer-imx-epit.c

Quoted 8 lines
drivers/clocksource/timer-imx-epit.c new file +8 -0
+static int __init epit_clocksource_init(struct epit_timer *epittm)
+{
+     unsigned int c = clk_get_rate(epittm->clk_per);
+
+     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,

Quoted 91 lines

drivers/clocksource/timer-imx-epit.c line 100

Quoted 3 lines
drivers/clocksource/timer-imx-epit.c new file +3 -0
+                     clocksource_mmio_readl_down);
+}
+

I would suggest to place epit_clocksource_init() function right before epit_timer_init().

Quoted 84 lines
drivers/clocksource/timer-imx-epit.c new file +84 -0
+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_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;
Quoted 8 lines

drivers/clocksource/timer-imx-epit.c

Quoted 8 lines
drivers/clocksource/timer-imx-epit.c new file +8 -0
+     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_per),

Quoted 55 lines

drivers/clocksource/timer-imx-epit.c line 193

Quoted 1 line
drivers/clocksource/timer-imx-epit.c new file +1 -0
+             0xff, 0xfffffffe);

Please indent the wrapped line.

Quoted 51 lines
drivers/clocksource/timer-imx-epit.c new file +51 -0
+
+     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;
+     struct clk *clk_ipg;
+     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;
+     }
+
+     clk_ipg = of_clk_get_by_name(np, "ipg");
+     if (IS_ERR(clk_ipg)) {
+             pr_err("i.MX EPIT: unable to get clk_ipg\n");
+             ret = PTR_ERR(clk_ipg);
+             goto out_iounmap;
+     }
+
+     ret = clk_prepare_enable(clk_ipg);
+     if (ret) {
+             pr_err("i.MX EPIT: unable to prepare+enable clk_ipg\n");
+             goto out_clk_ipg_disable;
+     }
+
+     epittm->clk_per = of_clk_get_by_name(np, "per");
+     if (IS_ERR(epittm->clk_per)) {
+             pr_err("i.MX EPIT: unable to get clk_per\n");
+             ret = PTR_ERR(epittm->clk_per);
+             goto out_clk_ipg_disable;
+     }
Quoted 8 lines

drivers/clocksource/timer-imx-epit.c

Quoted 8 lines
drivers/clocksource/timer-imx-epit.c new file +8 -0
+
+     ret = clk_prepare_enable(epittm->clk_per);
+     if (ret) {
+             pr_err("i.MX EPIT: unable to prepare+enable clk_per\n");
+             goto out_clk_ipg_disable;
+     }
+
+     /* Initialise to a known state (all timers off, and timing reset)

*/

Quoted 42 lines

drivers/clocksource/timer-imx-epit.c line 259

Quoted 7 lines
drivers/clocksource/timer-imx-epit.c new file +7 -0
+     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) {

Add a space before left parenthesis.

drivers/clocksource/timer-imx-epit.c line 265

Quoted 6 lines
drivers/clocksource/timer-imx-epit.c new file +6 -0
+             pr_err("i.MX EPIT: failed to init clocksource\n");
+             goto out_clk_per_disable;
+     }
+
+     ret = epit_clockevent_init(epittm);
+     if(ret) {

Add a space before left parenthesis.

Quoted 9 lines
drivers/clocksource/timer-imx-epit.c new file +8 -0
+             pr_err("i.MX EPIT: failed to init clockevent\n");
+             goto out_clk_per_disable;
+     }
+
+     return 0;
+
+out_clk_per_disable:
+     clk_disable_unprepare(epittm->clk_per);

drivers/clocksource/timer-imx-epit.c line 283

Quoted 10 lines
drivers/clocksource/timer-imx-epit.c new file +10 -0
+out_clk_ipg_disable:
+     clk_disable_unprepare(clk_ipg);
+out_iounmap:
+     iounmap(epittm->base);
+out_kfree:
+     kfree(epittm);
+
+     return ret;
+}
+TIMER_OF_DECLARE(mx6q_timer, "fsl,imx6q-epit", epit_timer_init);

Here "fsl,imx31-epit" would be way better than "fsl,imx6q-epit", please fix it in the documentation as well.

Quoted 3 lines

--
With best wishes,
Vladimir

Thanks for your review,
Clement

Signature · 5 lines
https://www.devialet.com/buy-phantom <https://www.devialet.com/buy-phantom>



- Confidential -
#11 lore raw

PATCH v3 5/5 ARM: dts: imx6qdl: add missing compatible and clock properties for EPIT

In reply to Clément Péron

From: Colin Didier <[email protected]>

Add missing compatible and clock properties for EPIT node.

---
 arch/arm/boot/dts/imx6qdl.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)
arch/arm/boot/dts/imx6qdl.dtsi +10 -0
index c003e62bf290..75bbaca34cbc 100644
@@ -844,13 +844,23 @@
 			};
 
 			epit1: epit at 20d0000 { /* EPIT1 */
+				compatible = "fsl,imx6q-epit";
 				reg = <0x020d0000 0x4000>;
 				interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clks IMX6QDL_CLK_EPIT1>,
+					<&clks IMX6QDL_CLK_IPG_PER>;
+				clock-names = "ipg", "per";
+				status = "disabled";
 			};
 
 			epit2: epit at 20d4000 { /* EPIT2 */
+				compatible = "fsl,imx6q-epit";
 				reg = <0x020d4000 0x4000>;
 				interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clks IMX6QDL_CLK_EPIT2>,
+					<&clks IMX6QDL_CLK_IPG_PER>;
+				clock-names = "ipg", "per";
+				status = "disabled";
 			};
 
 			src: src at 20d8000 {
Signature · 1 line
2.17.0