* [PATCH v3 0/5] Reintroduce i.MX EPIT Timer
@ 2018-05-29 17:04 Clément Péron
2018-05-29 17:04 ` [PATCH v3 1/5] ARM: imx: remove inexistant EPIT timer init Clément Péron
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Clément Péron @ 2018-05-29 17:04 UTC (permalink / raw)
To: linux-arm-kernel
From: Cl?ment Peron <clement.peron@devialet.com>
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
--
2.17.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 1/5] ARM: imx: remove inexistant EPIT timer init 2018-05-29 17:04 [PATCH v3 0/5] Reintroduce i.MX EPIT Timer Clément Péron @ 2018-05-29 17:04 ` Clément Péron 2018-05-29 17:04 ` [PATCH v3 2/5] clk: imx6: add EPIT clock support Clément Péron ` (3 subsequent siblings) 4 siblings, 0 replies; 12+ messages in thread From: Clément Péron @ 2018-05-29 17:04 UTC (permalink / raw) To: linux-arm-kernel From: Cl?ment Peron <clement.peron@devialet.com> i.MX EPIT timer has been removed but not the init function declaration. Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com> --- 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.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 2/5] clk: imx6: add EPIT clock support 2018-05-29 17:04 [PATCH v3 0/5] Reintroduce i.MX EPIT Timer Clément Péron 2018-05-29 17:04 ` [PATCH v3 1/5] ARM: imx: remove inexistant EPIT timer init Clément Péron @ 2018-05-29 17:04 ` Clément Péron 2018-06-02 2:19 ` Stephen Boyd 2018-05-29 17:04 ` [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding Clément Péron ` (2 subsequent siblings) 4 siblings, 1 reply; 12+ messages in thread From: Clément Péron @ 2018-05-29 17:04 UTC (permalink / raw) To: linux-arm-kernel From: Colin Didier <colin.didier@devialet.com> Add EPIT clock support to the i.MX6Q clocking infrastructure. Signed-off-by: Colin Didier <colin.didier@devialet.com> Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com> --- 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.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 2/5] clk: imx6: add EPIT clock support 2018-05-29 17:04 ` [PATCH v3 2/5] clk: imx6: add EPIT clock support Clément Péron @ 2018-06-02 2:19 ` Stephen Boyd 0 siblings, 0 replies; 12+ messages in thread From: Stephen Boyd @ 2018-06-02 2:19 UTC (permalink / raw) To: linux-arm-kernel Quoting Cl?ment P?ron (2018-05-29 10:04:33) > From: Colin Didier <colin.didier@devialet.com> > > Add EPIT clock support to the i.MX6Q clocking infrastructure. > > Signed-off-by: Colin Didier <colin.didier@devialet.com> > Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> > Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com> > --- Applied to clk-next ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding 2018-05-29 17:04 [PATCH v3 0/5] Reintroduce i.MX EPIT Timer Clément Péron 2018-05-29 17:04 ` [PATCH v3 1/5] ARM: imx: remove inexistant EPIT timer init Clément Péron 2018-05-29 17:04 ` [PATCH v3 2/5] clk: imx6: add EPIT clock support Clément Péron @ 2018-05-29 17:04 ` Clément Péron 2018-05-30 7:27 ` Vladimir Zapolskiy 2018-05-29 17:04 ` [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer Clément Péron 2018-05-29 17:04 ` [PATCH v3 5/5] ARM: dts: imx6qdl: add missing compatible and clock properties for EPIT Clément Péron 4 siblings, 1 reply; 12+ messages in thread From: Clément Péron @ 2018-05-29 17:04 UTC (permalink / raw) To: linux-arm-kernel From: Cl?ment Peron <clement.peron@devialet.com> Add devicetree binding document for NXP's i.MX SoC specific EPIT timer driver. Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> --- .../devicetree/bindings/clock/imx6q,epit.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt diff --git a/Documentation/devicetree/bindings/clock/imx6q,epit.txt b/Documentation/devicetree/bindings/clock/imx6q,epit.txt new file mode 100644 index 000000000000..a84a60c6ae35 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx6q,epit.txt @@ -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"; + }; -- 2.17.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding 2018-05-29 17:04 ` [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding Clément Péron @ 2018-05-30 7:27 ` Vladimir Zapolskiy 2018-05-30 7:38 ` Vladimir Zapolskiy 0 siblings, 1 reply; 12+ messages in thread From: Vladimir Zapolskiy @ 2018-05-30 7:27 UTC (permalink / raw) To: linux-arm-kernel Hi Cl?ment, On 05/29/2018 08:04 PM, Cl?ment P?ron wrote: > From: Cl?ment Peron <clement.peron@devialet.com> > > Add devicetree binding document for NXP's i.MX SoC specific > EPIT timer driver. > > Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> > --- > .../devicetree/bindings/clock/imx6q,epit.txt | 24 +++++++++++++++++++ > 1 file changed, 24 insertions(+) > create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt > > diff --git a/Documentation/devicetree/bindings/clock/imx6q,epit.txt b/Documentation/devicetree/bindings/clock/imx6q,epit.txt > new file mode 100644 > index 000000000000..a84a60c6ae35 > --- /dev/null > +++ b/Documentation/devicetree/bindings/clock/imx6q,epit.txt 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/ > @@ -0,0 +1,24 @@ > +Binding for the i.MX6 EPIT timer Let's change it to 'Binding for the i.MX 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" 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. > +- 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"; > + 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. > + }; > -- With best wishes, Vladimir ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding 2018-05-30 7:27 ` Vladimir Zapolskiy @ 2018-05-30 7:38 ` Vladimir Zapolskiy 0 siblings, 0 replies; 12+ messages in thread From: Vladimir Zapolskiy @ 2018-05-30 7:38 UTC (permalink / raw) To: linux-arm-kernel On 05/30/2018 10:27 AM, Vladimir Zapolskiy wrote: > Hi Cl?ment, > > On 05/29/2018 08:04 PM, Cl?ment P?ron wrote: >> From: Cl?ment Peron <clement.peron@devialet.com> >> >> Add devicetree binding document for NXP's i.MX SoC specific >> EPIT timer driver. >> >> Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> >> --- >> .../devicetree/bindings/clock/imx6q,epit.txt | 24 +++++++++++++++++++ >> 1 file changed, 24 insertions(+) >> create mode 100644 Documentation/devicetree/bindings/clock/imx6q,epit.txt >> >> diff --git a/Documentation/devicetree/bindings/clock/imx6q,epit.txt b/Documentation/devicetree/bindings/clock/imx6q,epit.txt >> new file mode 100644 >> index 000000000000..a84a60c6ae35 >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/clock/imx6q,epit.txt > > 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/ > And linux-clk mailing list for publishing seems to be improper one, instead please add Daniel Lezcano <daniel.lezcano@linaro.org> and Thomas Gleixner <tglx@linutronix.de> to the list of addressees, and the proper mailing list address is linux-kernel at vger.kernel.org -- With best wishes, Vladimir ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer 2018-05-29 17:04 [PATCH v3 0/5] Reintroduce i.MX EPIT Timer Clément Péron ` (2 preceding siblings ...) 2018-05-29 17:04 ` [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding Clément Péron @ 2018-05-29 17:04 ` Clément Péron 2018-05-29 18:16 ` Fabio Estevam 2018-05-30 6:56 ` Vladimir Zapolskiy 2018-05-29 17:04 ` [PATCH v3 5/5] ARM: dts: imx6qdl: add missing compatible and clock properties for EPIT Clément Péron 4 siblings, 2 replies; 12+ messages in thread From: Clément Péron @ 2018-05-29 17:04 UTC (permalink / raw) To: linux-arm-kernel From: Colin Didier <colin.didier@devialet.com> Add driver for NXP's EPIT timer used in i.MX 6 family of SoC. Signed-off-by: Colin Didier <colin.didier@devialet.com> Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> --- 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 diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 8e8a09755d10..920a0874f3a4 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -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 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..87025d5f3a97 --- /dev/null +++ b/drivers/clocksource/timer-imx-epit.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i.MX EPIT Timer + * + * Copyright (C) 2010 Sascha Hauer <s.hauer@pengutronix.de> + * Copyright (C) 2018 Colin Didier <colin.didier@devialet.com> + * Copyright (C) 2018 Cl?ment P?ron <clement.peron@devialet.com> + */ + +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of.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_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) +{ + 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); +} + +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; + 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); + + 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; + } + + /* 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_per_disable; + } + + ret = epit_clockevent_init(epittm); + if(ret) { + 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); +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); -- 2.17.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer 2018-05-29 17:04 ` [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer Clément Péron @ 2018-05-29 18:16 ` Fabio Estevam 2018-05-30 6:56 ` Vladimir Zapolskiy 1 sibling, 0 replies; 12+ messages in thread From: Fabio Estevam @ 2018-05-29 18:16 UTC (permalink / raw) To: linux-arm-kernel On Tue, May 29, 2018 at 2:04 PM, Cl?ment P?ron <peron.clem@gmail.com> wrote: > +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; This should be: goto out_iounmap; ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer 2018-05-29 17:04 ` [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer Clément Péron 2018-05-29 18:16 ` Fabio Estevam @ 2018-05-30 6:56 ` Vladimir Zapolskiy 2018-05-30 9:53 ` Clément PERON 1 sibling, 1 reply; 12+ messages in thread From: Vladimir Zapolskiy @ 2018-05-30 6:56 UTC (permalink / raw) To: linux-arm-kernel Hi Cl?ment, please find some more review comments. On 05/29/2018 08:04 PM, Cl?ment P?ron wrote: > From: Colin Didier <colin.didier@devialet.com> > > Add driver for NXP's EPIT timer used in i.MX 6 family of SoC. > > Signed-off-by: Colin Didier <colin.didier@devialet.com> > Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> > --- > 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 > > diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig > index 8e8a09755d10..920a0874f3a4 100644 > --- a/drivers/clocksource/Kconfig > +++ b/drivers/clocksource/Kconfig > @@ -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. > + select TIMER_OF The driver does not have this dependency. > + 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..87025d5f3a97 > --- /dev/null > +++ b/drivers/clocksource/timer-imx-epit.c > @@ -0,0 +1,283 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * i.MX EPIT Timer > + * > + * Copyright (C) 2010 Sascha Hauer <s.hauer@pengutronix.de> > + * Copyright (C) 2018 Colin Didier <colin.didier@devialet.com> > + * Copyright (C) 2018 Cl?ment P?ron <clement.peron@devialet.com> > + */ > + > +#include <linux/clk.h> > +#include <linux/clockchips.h> > +#include <linux/err.h> The include above can be dropped. > +#include <linux/interrupt.h> > +#include <linux/irq.h> The include above can be dropped. > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <linux/of.h> The include above can be dropped. > +#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) > +{ > + 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(). > +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; > + 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. > + > + 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; > + } > + > + /* 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. > + 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. > + 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); > +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 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer 2018-05-30 6:56 ` Vladimir Zapolskiy @ 2018-05-30 9:53 ` Clément PERON 0 siblings, 0 replies; 12+ messages in thread From: Clément PERON @ 2018-05-30 9:53 UTC (permalink / raw) To: linux-arm-kernel Hi Vladimir, Le mer. 30 mai 2018 ? 08:56, Vladimir Zapolskiy < vladimir_zapolskiy@mentor.com> a ?crit : > Hi Cl?ment, > please find some more review comments. > On 05/29/2018 08:04 PM, Cl?ment P?ron wrote: > > From: Colin Didier <colin.didier@devialet.com> > > > > Add driver for NXP's EPIT timer used in i.MX 6 family of SoC. > > > > Signed-off-by: Colin Didier <colin.didier@devialet.com> > > Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> > > --- > > 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 > > > > diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig > > index 8e8a09755d10..920a0874f3a4 100644 > > --- a/drivers/clocksource/Kconfig > > +++ b/drivers/clocksource/Kconfig > > @@ -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. 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. > > + select TIMER_OF > The driver does not have this dependency. > > + 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..87025d5f3a97 > > --- /dev/null > > +++ b/drivers/clocksource/timer-imx-epit.c > > @@ -0,0 +1,283 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * i.MX EPIT Timer > > + * > > + * Copyright (C) 2010 Sascha Hauer <s.hauer@pengutronix.de> > > + * Copyright (C) 2018 Colin Didier <colin.didier@devialet.com> > > + * Copyright (C) 2018 Cl?ment P?ron <clement.peron@devialet.com> > > + */ > > + > > +#include <linux/clk.h> > > +#include <linux/clockchips.h> > > +#include <linux/err.h> > The include above can be dropped. > > +#include <linux/interrupt.h> > > +#include <linux/irq.h> > The include above can be dropped. > > +#include <linux/of_address.h> > > +#include <linux/of_irq.h> > > +#include <linux/of.h> > The include above can be dropped. > > +#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) > > +{ > > + 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(). > > +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; > > + 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. > > + > > + 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; > > + } > > + > > + /* 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. > > + 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. > > + 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); > > +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 Thanks for your review, Clement -- https://www.devialet.com/buy-phantom <https://www.devialet.com/buy-phantom> - Confidential - ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 5/5] ARM: dts: imx6qdl: add missing compatible and clock properties for EPIT 2018-05-29 17:04 [PATCH v3 0/5] Reintroduce i.MX EPIT Timer Clément Péron ` (3 preceding siblings ...) 2018-05-29 17:04 ` [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer Clément Péron @ 2018-05-29 17:04 ` Clément Péron 4 siblings, 0 replies; 12+ messages in thread From: Clément Péron @ 2018-05-29 17:04 UTC (permalink / raw) To: linux-arm-kernel From: Colin Didier <colin.didier@devialet.com> Add missing compatible and clock properties for EPIT node. Signed-off-by: Colin Didier <colin.didier@devialet.com> Signed-off-by: Cl?ment Peron <clement.peron@devialet.com> Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com> --- arch/arm/boot/dts/imx6qdl.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index c003e62bf290..75bbaca34cbc 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -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 { -- 2.17.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2018-06-02 2:19 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-05-29 17:04 [PATCH v3 0/5] Reintroduce i.MX EPIT Timer Clément Péron 2018-05-29 17:04 ` [PATCH v3 1/5] ARM: imx: remove inexistant EPIT timer init Clément Péron 2018-05-29 17:04 ` [PATCH v3 2/5] clk: imx6: add EPIT clock support Clément Péron 2018-06-02 2:19 ` Stephen Boyd 2018-05-29 17:04 ` [PATCH v3 3/5] Documentation: DT: add i.MX EPIT timer binding Clément Péron 2018-05-30 7:27 ` Vladimir Zapolskiy 2018-05-30 7:38 ` Vladimir Zapolskiy 2018-05-29 17:04 ` [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer Clément Péron 2018-05-29 18:16 ` Fabio Estevam 2018-05-30 6:56 ` Vladimir Zapolskiy 2018-05-30 9:53 ` Clément PERON 2018-05-29 17:04 ` [PATCH v3 5/5] ARM: dts: imx6qdl: add missing compatible and clock properties for EPIT Clément Péron
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox