2018-06-07 14:09:58

by Clément Péron

[permalink] [raw]
Subject: [PATCH v6 0/5] 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

--
2.17.1



2018-06-07 14:07:26

by Clément Péron

[permalink] [raw]
Subject: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding

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

diff --git a/Documentation/devicetree/bindings/timer/fsl,imxepit.txt b/Documentation/devicetree/bindings/timer/fsl,imxepit.txt
new file mode 100644
index 000000000000..819d6458a860
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/fsl,imxepit.txt
@@ -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@20d0000 {
+ compatible = "fsl,imx6qdl-epit", "fsl,imx31-epit";
+ reg = <0x020d0000 0x4000>;
+ interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX6QDL_CLK_EPIT1>;
+ };
--
2.17.1


2018-06-07 14:07:40

by Clément Péron

[permalink] [raw]
Subject: [PATCH v6 5/5] ARM: dts: imx: add missing compatible and clock properties for EPIT

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]>
---
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(-)

diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi
index cf70df20b19c..15fd4308dad8 100644
--- a/arch/arm/boot/dts/imx25.dtsi
+++ b/arch/arm/boot/dts/imx25.dtsi
@@ -396,15 +396,19 @@
};

epit1: timer@53f94000 {
- compatible = "fsl,imx25-epit";
+ compatible = "fsl,imx25-epit", "fsl,imx31-epit";
reg = <0x53f94000 0x4000>;
interrupts = <28>;
+ clocks = <&clks 83>;
+ status = "disabled";
};

epit2: timer@53f98000 {
- compatible = "fsl,imx25-epit";
+ compatible = "fsl,imx25-epit", "fsl,imx31-epit";
reg = <0x53f98000 0x4000>;
interrupts = <27>;
+ clocks = <&clks 84>;
+ status = "disabled";
};

gpio4: gpio@53f9c000 {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index c003e62bf290..65c4ee07454c 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -843,14 +843,20 @@
};
};

- epit1: epit@20d0000 { /* EPIT1 */
+ epit1: timer@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@20d4000 { /* EPIT2 */
+ epit2: timer@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@20d8000 {
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index ab6a7e2e7e8f..d63f8ebbc8a1 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -671,14 +671,20 @@
};
};

- epit1: epit@20d0000 {
+ epit1: timer@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@20d4000 {
+ epit2: timer@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@20d8000 {
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index 49c7205b8db8..2b30559d3270 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -736,14 +736,20 @@
};
};

- epit1: epit@20d0000 {
+ epit1: timer@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@20d4000 {
+ epit2: timer@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@20d8000 {
diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi
index 1241972b16ba..d5f765da1ee2 100644
--- a/arch/arm/boot/dts/imx6ul.dtsi
+++ b/arch/arm/boot/dts/imx6ul.dtsi
@@ -658,14 +658,20 @@
};
};

- epit1: epit@20d0000 {
+ epit1: timer@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@20d4000 {
+ epit2: timer@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@20d8000 {
--
2.17.1


2018-06-07 14:07:43

by Clément Péron

[permalink] [raw]
Subject: [PATCH v6 2/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.

Signed-off-by: Clément Peron <[email protected]>
Reviewed-by: Fabio Estevam <[email protected]>
---
arch/arm/mach-imx/common.h | 1 -
1 file changed, 1 deletion(-)

diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index c8d68e918b2f..18aae76fa2da 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -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);
--
2.17.1


2018-06-07 14:08:25

by Clément Péron

[permalink] [raw]
Subject: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer

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.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8e8a09755d10..790478afd02c 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -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
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 00caf37e52f9..d9426f69ec69 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -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
diff --git a/drivers/clocksource/timer-imx-epit.c b/drivers/clocksource/timer-imx-epit.c
new file mode 100644
index 000000000000..15f70e210fad
--- /dev/null
+++ b/drivers/clocksource/timer-imx-epit.c
@@ -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;
+ 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;
+ }
+
+ 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);
--
2.17.1


2018-06-07 14:09:46

by Clément Péron

[permalink] [raw]
Subject: [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(-)

diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index 8d518ad5dc13..b9ea7037e193 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -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);
diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h
index da59fd9cdb5e..7ad171b8f3bf 100644
--- a/include/dt-bindings/clock/imx6qdl-clock.h
+++ b/include/dt-bindings/clock/imx6qdl-clock.h
@@ -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 */
--
2.17.1


2018-06-11 12:33:23

by Stefan Agner

[permalink] [raw]
Subject: Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer

On 07.06.2018 16:05, Clément Péron wrote:
> 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.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 8e8a09755d10..790478afd02c 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -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
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 00caf37e52f9..d9426f69ec69 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -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
> diff --git a/drivers/clocksource/timer-imx-epit.c
> b/drivers/clocksource/timer-imx-epit.c
> new file mode 100644
> index 000000000000..15f70e210fad
> --- /dev/null
> +++ b/drivers/clocksource/timer-imx-epit.c
> @@ -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;
> + 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

> +
> + 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);

2018-06-11 12:42:58

by Clément Péron

[permalink] [raw]
Subject: Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer

Hi Stefan,


> > +
> > +#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 */
> > + 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.

>
> There is a helper library in drivers/clocksource/timer-of.c which might
> be useful for this driver.
Indeed, but will require a bit of rewrite.

Clement

>
> --
> Stefan
>
> > +
> > + 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);

2018-06-11 12:47:13

by Stefan Agner

[permalink] [raw]
Subject: Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer

On 11.06.2018 14:42, Clément Péron wrote:
> Hi Stefan,
>
>
>> > +
>> > +#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 */
>> > + 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.
>
>>
>> There is a helper library in drivers/clocksource/timer-of.c which 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

>
> Clement
>
>>
>> --
>> Stefan
>>
>> > +
>> > + 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);

2018-06-11 14:40:31

by Vladimir Zapolskiy

[permalink] [raw]
Subject: Re: [PATCH v6 0/5] Reintroduce i.MX EPIT Timer

Hi Clément,

On 06/07/2018 05:05 PM, Clément Péron wrote:
> 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.
>

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

2018-06-11 14:40:55

by Vladimir Zapolskiy

[permalink] [raw]
Subject: Re: [PATCH v6 4/5] clocksource: add driver for i.MX EPIT timer

On 06/07/2018 05:05 PM, Clément Péron wrote:
> 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]>

Reviewed-by: Vladimir Zapolskiy <[email protected]>
Tested-by: Vladimir Zapolskiy <[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

2018-06-11 14:41:48

by Vladimir Zapolskiy

[permalink] [raw]
Subject: Re: [PATCH v6 2/5] ARM: imx: remove inexistant EPIT timer init

On 06/07/2018 05:05 PM, Clément Péron wrote:
> 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]>

Reviewed-by: Vladimir Zapolskiy <[email protected]>
Tested-by: Vladimir Zapolskiy <[email protected]>

--
With best wishes,
Vladimir

2018-06-11 14:41:52

by Vladimir Zapolskiy

[permalink] [raw]
Subject: Re: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding

On 06/07/2018 05:05 PM, Clément Péron wrote:
> 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]>

Reviewed-by: Vladimir Zapolskiy <[email protected]>

--
With best wishes,
Vladimir

2018-06-11 16:27:20

by Vladimir Zapolskiy

[permalink] [raw]
Subject: Re: [PATCH v6 5/5] ARM: dts: imx: add missing compatible and clock properties for EPIT

On 06/07/2018 05:05 PM, Clément Péron wrote:
> 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]>

Reviewed-by: Vladimir Zapolskiy <[email protected]>

--
With best wishes,
Vladimir

2018-06-11 16:27:26

by Clément Péron

[permalink] [raw]
Subject: Re: [PATCH v6 0/5] Reintroduce i.MX EPIT Timer

Hi Vladimir,
On Mon, 11 Jun 2018 at 16:39, Vladimir Zapolskiy <[email protected]> wrote:
>
> Hi Clément,
>
> On 06/07/2018 05:05 PM, Clément Péron wrote:
> > 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.
> >
>
> 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

>
> --
> Best wishes,
> Vladimir

2018-06-11 19:36:57

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding

On Thu, Jun 07, 2018 at 04:05:42PM +0200, Cl?ment P?ron wrote:
> 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

Reviewed-by: Rob Herring <[email protected]>

2018-06-12 08:07:56

by Clément Péron

[permalink] [raw]
Subject: Re: [PATCH v6 3/5] dt-bindings: timer: add i.MX EPIT timer binding

Hi Rob,

On Mon, 11 Jun 2018 at 20:10, Rob Herring <[email protected]> wrote:
>
> On Thu, Jun 07, 2018 at 04:05:42PM +0200, Clément Péron wrote:
> > 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
>
> Reviewed-by: Rob Herring <[email protected]>

Thanks, I posted the v7 yesterday, could you add your tag to the new release ?

Regards,
Clement