2021-12-17 19:57:44

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 0/6] Add timers for Mstar SoCs

This patches series adds a new driver for the timers found in the Mstar
MSC313e SoCs and newer. It adds a basic clocksource driver, the
corresponding devicetree bindings and its documentation.

Changes since v2:
- Reworded commit 2/6
- Added Rob's 'Reviewed-by:' tag

Changes since v1:
- Make delay conditionnal for ARM, so it builds on all platforms
- Improved commit message for the timer driver
- MSC313E_TIMER is now silent and enabled by platform
- Updated msc313e_timer_setup() to make it safe
- Updated msc313e_timer_current_value() to make it safe
- Renamed the driver variant for ssd20xd to "sstar,ssd20xd-timer"
- Moved timer settings for the ssd202d from
mstar-infinity2m-ssd20xd.dtsi to mstar-infinity2m.dtsi.
- Select MSC313E_TIMER on mstar platforms
- Fixed wrong dt-bindings doc that included an rtc property (whoops!)

Romain Perier (6):
clocksource: Add MStar MSC313e timer support
clocksource: msc313e: Add support for ssd20xd-based platforms
ARM: mstar: Select MSC313E_TIMER
dt-bindings: timer: Add Mstar MSC313e timer devicetree bindings
documentation
ARM: dts: mstar: Add timers device nodes
ARM: dts: mstar: Switch to compatible "sstar,ssd20xd-timer" on
infinity2m

.../bindings/timer/mstar,msc313e-timer.yaml | 46 ++++
MAINTAINERS | 1 +
arch/arm/boot/dts/mstar-infinity2m.dtsi | 18 ++
arch/arm/boot/dts/mstar-v7.dtsi | 20 ++
arch/arm/mach-mstar/Kconfig | 1 +
drivers/clocksource/Kconfig | 9 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-msc313e.c | 253 ++++++++++++++++++
8 files changed, 349 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml
create mode 100644 drivers/clocksource/timer-msc313e.c

--
2.34.1



2021-12-17 19:57:46

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 1/6] clocksource: Add MStar MSC313e timer support

The MSC313e-compatible SoCs have 3 timer hardware blocks. All of these
are free running 32-bit increasing counters and can generate interrupts.
Based onto a maximum value register, each timer can either count from 0
to max, one time then stop (which generates interrupts) or can count
from 0 to max and then roll. This commit adds basic support for these
timers, the first timer block being used as clocksource/sched_clock
and delay, while the others will be used as clockevents.

Signed-off-by: Romain Perier <[email protected]>
Co-developed-by: Daniel Palmer <[email protected]>
Signed-off-by: Daniel Palmer <[email protected]>
---
MAINTAINERS | 1 +
drivers/clocksource/Kconfig | 9 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-msc313e.c | 244 ++++++++++++++++++++++++++++
4 files changed, 255 insertions(+)
create mode 100644 drivers/clocksource/timer-msc313e.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 43007f2d29e0..4dbc122c7937 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2291,6 +2291,7 @@ F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml
F: arch/arm/boot/dts/mstar-*
F: arch/arm/mach-mstar/
F: drivers/clk/mstar/
+F: drivers/clocksource/timer-msc313e.c
F: drivers/gpio/gpio-msc313.c
F: drivers/rtc/rtc-msc313.c
F: drivers/watchdog/msc313e_wdt.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index f65e31bab9ae..ba46d6860e2d 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -671,6 +671,15 @@ config MILBEAUT_TIMER
help
Enables the support for Milbeaut timer driver.

+config MSC313E_TIMER
+ bool "MSC313E timer driver" if COMPILE_TEST
+ select TIMER_OF
+ select CLKSRC_MMIO
+ help
+ Enables support for the MStar MSC313E timer driver.
+ This provides access to multiple interrupt generating
+ programmable 32-bit free running incrementing counters.
+
config INGENIC_TIMER
bool "Clocksource/timer using the TCU in Ingenic JZ SoCs"
default MACH_INGENIC
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index c17ee32a7151..fa5f624eadb6 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -88,3 +88,4 @@ obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o
obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o
obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o
+obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o
diff --git a/drivers/clocksource/timer-msc313e.c b/drivers/clocksource/timer-msc313e.c
new file mode 100644
index 000000000000..154e73444a0c
--- /dev/null
+++ b/drivers/clocksource/timer-msc313e.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MStar timer driver
+ *
+ * Copyright (C) 2021 Daniel Palmer
+ * Copyright (C) 2021 Romain Perier
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#ifdef CONFIG_ARM
+#include <linux/delay.h>
+#endif
+
+#include "timer-of.h"
+
+#define TIMER_NAME "msc313e_timer"
+
+#define MSC313E_REG_CTRL 0x00
+#define MSC313E_REG_CTRL_TIMER_EN BIT(0)
+#define MSC313E_REG_CTRL_TIMER_TRIG BIT(1)
+#define MSC313E_REG_CTRL_TIMER_INT_EN BIT(8)
+#define MSC313E_REG_TIMER_MAX_LOW 0x08
+#define MSC313E_REG_TIMER_MAX_HIGH 0x0c
+#define MSC313E_REG_COUNTER_LOW 0x10
+#define MSC313E_REG_COUNTER_HIGH 0x14
+
+#define TIMER_SYNC_TICKS 3
+
+#ifdef CONFIG_ARM
+struct msc313e_delay {
+ void __iomem *base;
+ struct delay_timer delay;
+};
+static struct msc313e_delay msc313e_delay;
+#endif
+
+static void __iomem *msc313e_clksrc;
+
+static void msc313e_timer_stop(void __iomem *base)
+{
+ writew(0, base + MSC313E_REG_CTRL);
+}
+
+static void msc313e_timer_start(void __iomem *base, bool periodic)
+{
+ u16 reg;
+
+ reg = readw(base + MSC313E_REG_CTRL);
+ if (periodic)
+ reg |= MSC313E_REG_CTRL_TIMER_EN;
+ else
+ reg |= MSC313E_REG_CTRL_TIMER_TRIG;
+ writew(reg | MSC313E_REG_CTRL_TIMER_INT_EN, base + MSC313E_REG_CTRL);
+}
+
+static void msc313e_timer_setup(void __iomem *base, unsigned long delay)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ writew(delay >> 16, base + MSC313E_REG_TIMER_MAX_HIGH);
+ writew(delay & 0xffff, base + MSC313E_REG_TIMER_MAX_LOW);
+ local_irq_restore(flags);
+}
+
+static unsigned long msc313e_timer_current_value(void __iomem *base)
+{
+ unsigned long flags;
+ u16 l, h;
+
+ local_irq_save(flags);
+ l = readw(base + MSC313E_REG_COUNTER_LOW);
+ h = readw(base + MSC313E_REG_COUNTER_HIGH);
+ local_irq_restore(flags);
+
+ return (((u32)h) << 16 | l);
+}
+
+static int msc313e_timer_clkevt_shutdown(struct clock_event_device *evt)
+{
+ struct timer_of *timer = to_timer_of(evt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+
+ return 0;
+}
+
+static int msc313e_timer_clkevt_set_oneshot(struct clock_event_device *evt)
+{
+ struct timer_of *timer = to_timer_of(evt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+ msc313e_timer_start(timer_of_base(timer), false);
+
+ return 0;
+}
+
+static int msc313e_timer_clkevt_set_periodic(struct clock_event_device *evt)
+{
+ struct timer_of *timer = to_timer_of(evt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+ msc313e_timer_setup(timer_of_base(timer), timer_of_period(timer));
+ msc313e_timer_start(timer_of_base(timer), true);
+
+ return 0;
+}
+
+static int msc313e_timer_clkevt_next_event(unsigned long evt, struct clock_event_device *clkevt)
+{
+ struct timer_of *timer = to_timer_of(clkevt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+ msc313e_timer_setup(timer_of_base(timer), evt);
+ msc313e_timer_start(timer_of_base(timer), false);
+
+ return 0;
+}
+
+static irqreturn_t msc313e_timer_clkevt_irq(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static u64 msc313e_timer_clksrc_read(struct clocksource *cs)
+{
+ return msc313e_timer_current_value(msc313e_clksrc) & cs->mask;
+}
+
+#ifdef CONFIG_ARM
+static unsigned long msc313e_read_delay_timer_read(void)
+{
+ return msc313e_timer_current_value(msc313e_delay.base);
+}
+#endif
+
+static u64 msc313e_timer_sched_clock_read(void)
+{
+ return msc313e_timer_current_value(msc313e_clksrc);
+}
+
+static struct clock_event_device msc313e_clkevt = {
+ .name = TIMER_NAME,
+ .rating = 300,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_state_shutdown = msc313e_timer_clkevt_shutdown,
+ .set_state_periodic = msc313e_timer_clkevt_set_periodic,
+ .set_state_oneshot = msc313e_timer_clkevt_set_oneshot,
+ .tick_resume = msc313e_timer_clkevt_shutdown,
+ .set_next_event = msc313e_timer_clkevt_next_event,
+};
+
+static int __init msc313e_clkevt_init(struct device_node *np)
+{
+ int ret;
+ struct timer_of *to;
+
+ to = kzalloc(sizeof(*to), GFP_KERNEL);
+ if (!to)
+ return -ENOMEM;
+
+ to->flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE;
+ to->of_irq.handler = msc313e_timer_clkevt_irq;
+ ret = timer_of_init(np, to);
+ if (ret)
+ return ret;
+
+ msc313e_clkevt.cpumask = cpu_possible_mask;
+ msc313e_clkevt.irq = to->of_irq.irq;
+ to->clkevt = msc313e_clkevt;
+
+ clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
+ TIMER_SYNC_TICKS, 0xffffffff);
+ return 0;
+}
+
+static int __init msc313e_clksrc_init(struct device_node *np)
+{
+ struct timer_of to = { 0 };
+ int ret;
+ u16 reg;
+
+ to.flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
+ ret = timer_of_init(np, &to);
+ if (ret)
+ return ret;
+
+ msc313e_clksrc = timer_of_base(&to);
+ reg = readw(msc313e_clksrc + MSC313E_REG_CTRL);
+ reg |= MSC313E_REG_CTRL_TIMER_EN;
+ writew(reg, msc313e_clksrc + MSC313E_REG_CTRL);
+
+#ifdef CONFIG_ARM
+ msc313e_delay.base = timer_of_base(&to);
+ msc313e_delay.delay.read_current_timer = msc313e_read_delay_timer_read;
+ msc313e_delay.delay.freq = timer_of_rate(&to);
+
+ register_current_timer_delay(&msc313e_delay.delay);
+#endif
+
+ sched_clock_register(msc313e_timer_sched_clock_read, 32, timer_of_rate(&to));
+ return clocksource_mmio_init(timer_of_base(&to), TIMER_NAME, timer_of_rate(&to), 300, 32,
+ msc313e_timer_clksrc_read);
+}
+
+static int __init msc313e_timer_init(struct device_node *np)
+{
+ int ret = 0;
+ static int num_called;
+
+ switch (num_called) {
+ case 0:
+ ret = msc313e_clksrc_init(np);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ ret = msc313e_clkevt_init(np);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ num_called++;
+
+ return 0;
+}
+
+TIMER_OF_DECLARE(msc313, "mstar,msc313e-timer", msc313e_timer_init);
--
2.34.1


2021-12-17 19:57:48

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 2/6] clocksource: msc313e: Add support for ssd20xd-based platforms

On SSD20X family SoCs the timers are connected to a 432MHz clock instead
of 12MHz that all the previous chips used. There is no way to reduce or
divide these clocks in the clktree yet as we do not know exactly where
the 432MHz clock comes from but it is enabled at boot.

The SSD20X timers have an input clock divider within the timer itself
to configure the frequency. timer0 is preconfigured at power up to run
at 12MHz so it is backwards compatible and doesn't need special handling
right now. timer1 and timer2 run at 432Mhz at power up so are not
backward compatible.

This commit adds support for the input clock divider register and sets
timer1 and timer2 to run at 48Mhz for clockevents.

Signed-off-by: Romain Perier <[email protected]>
---
drivers/clocksource/timer-msc313e.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/drivers/clocksource/timer-msc313e.c b/drivers/clocksource/timer-msc313e.c
index 154e73444a0c..54c54ca7c786 100644
--- a/drivers/clocksource/timer-msc313e.c
+++ b/drivers/clocksource/timer-msc313e.c
@@ -33,7 +33,9 @@
#define MSC313E_REG_TIMER_MAX_HIGH 0x0c
#define MSC313E_REG_COUNTER_LOW 0x10
#define MSC313E_REG_COUNTER_HIGH 0x14
+#define MSC313E_REG_TIMER_DIVIDE 0x18

+#define MSC313E_CLK_DIVIDER 9
#define TIMER_SYNC_TICKS 3

#ifdef CONFIG_ARM
@@ -179,6 +181,12 @@ static int __init msc313e_clkevt_init(struct device_node *np)
if (ret)
return ret;

+ if (of_device_is_compatible(np, "sstar,ssd20xd-timer")) {
+ to->of_clk.rate = clk_get_rate(to->of_clk.clk) / MSC313E_CLK_DIVIDER;
+ to->of_clk.period = DIV_ROUND_UP(to->of_clk.rate, HZ);
+ writew(MSC313E_CLK_DIVIDER - 1, timer_of_base(to) + MSC313E_REG_TIMER_DIVIDE);
+ }
+
msc313e_clkevt.cpumask = cpu_possible_mask;
msc313e_clkevt.irq = to->of_irq.irq;
to->clkevt = msc313e_clkevt;
@@ -242,3 +250,4 @@ static int __init msc313e_timer_init(struct device_node *np)
}

TIMER_OF_DECLARE(msc313, "mstar,msc313e-timer", msc313e_timer_init);
+TIMER_OF_DECLARE(ssd20xd, "sstar,ssd20xd-timer", msc313e_timer_init);
--
2.34.1


2021-12-17 19:57:52

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 3/6] ARM: mstar: Select MSC313E_TIMER

All of the ARCH_MSTARV7 have timers that can act as clocksource or
clockevents so select the corresponding driver.

Signed-off-by: Romain Perier <[email protected]>
---
arch/arm/mach-mstar/Kconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arm/mach-mstar/Kconfig b/arch/arm/mach-mstar/Kconfig
index cd300eeedc20..d079d567bb72 100644
--- a/arch/arm/mach-mstar/Kconfig
+++ b/arch/arm/mach-mstar/Kconfig
@@ -3,6 +3,7 @@ menuconfig ARCH_MSTARV7
depends on ARCH_MULTI_V7
select ARM_GIC
select ARM_HEAVY_MB
+ select MSC313E_TIMER
select MST_IRQ
select MSTAR_MSC313_MPLL
help
--
2.34.1


2021-12-17 19:57:54

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 5/6] ARM: dts: mstar: Add timers device nodes

This adds the definition of the timers device node.

Signed-off-by: Romain Perier <[email protected]>
---
arch/arm/boot/dts/mstar-v7.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/mstar-v7.dtsi b/arch/arm/boot/dts/mstar-v7.dtsi
index 89ebfe4f29da..7ede4cec0af9 100644
--- a/arch/arm/boot/dts/mstar-v7.dtsi
+++ b/arch/arm/boot/dts/mstar-v7.dtsi
@@ -123,6 +123,26 @@ watchdog@6000 {
clocks = <&xtal_div2>;
};

+ timer@6040 {
+ compatible = "mstar,msc313e-timer";
+ reg = <0x6040 0x40>;
+ clocks = <&xtal_div2>;
+ interrupts-extended = <&intc_fiq GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ timer1: timer@6080 {
+ compatible = "mstar,msc313e-timer";
+ reg = <0x6080 0x40>;
+ clocks = <&xtal_div2>;
+ interrupts-extended = <&intc_fiq GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ timer2: timer@60c0 {
+ compatible = "mstar,msc313e-timer";
+ reg = <0x60c0 0x40>;
+ clocks = <&xtal_div2>;
+ interrupts-extended = <&intc_fiq GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ };

intc_fiq: interrupt-controller@201310 {
compatible = "mstar,mst-intc";
--
2.34.1


2021-12-17 19:57:56

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 4/6] dt-bindings: timer: Add Mstar MSC313e timer devicetree bindings documentation

This adds the documentation for the devicetree bindings of the Mstar
MSC313e timer driver, found from MSC313e SoCs and newer.

Signed-off-by: Romain Perier <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
---
.../bindings/timer/mstar,msc313e-timer.yaml | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml

diff --git a/Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml b/Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml
new file mode 100644
index 000000000000..03d5dba5d5b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/timer/mstar,msc313e-timer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mstar MSC313e Timer Device Tree Bindings
+
+maintainers:
+ - Daniel Palmer <[email protected]>
+ - Romain Perier <[email protected]>
+
+properties:
+ compatible:
+ enum:
+ - mstar,msc313e-timer
+ - sstar,ssd20xd-timer
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ timer@6040 {
+ compatible = "mstar,msc313e-timer";
+ reg = <0x6040 0x40>;
+ clocks = <&xtal_div2>;
+ interrupts-extended = <&intc_fiq GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ };
+...
--
2.34.1


2021-12-17 19:57:58

by Romain Perier

[permalink] [raw]
Subject: [PATCH v3 6/6] ARM: dts: mstar: Switch to compatible "sstar,ssd20xd-timer" on infinity2m

This defines the real oscillators as input of timer1 and timer2 and
switch to "sstar,ssd20xd-timer".

Signed-off-by: Romain Perier <[email protected]>
---
arch/arm/boot/dts/mstar-infinity2m.dtsi | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/arch/arm/boot/dts/mstar-infinity2m.dtsi b/arch/arm/boot/dts/mstar-infinity2m.dtsi
index 6d4d1d224e96..080a18b9effb 100644
--- a/arch/arm/boot/dts/mstar-infinity2m.dtsi
+++ b/arch/arm/boot/dts/mstar-infinity2m.dtsi
@@ -6,6 +6,14 @@

#include "mstar-infinity.dtsi"

+/ {
+ clk_timer: timer_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <432000000>;
+ };
+};
+
&cpus {
cpu1: cpu@1 {
device_type = "cpu";
@@ -20,3 +28,13 @@ smpctrl: smpctrl@204000 {
status = "disabled";
};
};
+
+&timer1 {
+ compatible = "sstar,ssd20xd-timer";
+ clocks = <&clk_timer>;
+};
+
+&timer2 {
+ compatible = "sstar,ssd20xd-timer";
+ clocks = <&clk_timer>;
+};
--
2.34.1


Subject: [tip: timers/core] clocksource/drivers/msc313e: Add support for ssd20xd-based platforms

The following commit has been merged into the timers/core branch of tip:

Commit-ID: e64da64f410cf4f9697e25ab76cdfc679f4fb6db
Gitweb: https://git.kernel.org/tip/e64da64f410cf4f9697e25ab76cdfc679f4fb6db
Author: Romain Perier <[email protected]>
AuthorDate: Fri, 17 Dec 2021 20:57:23 +01:00
Committer: Daniel Lezcano <[email protected]>
CommitterDate: Mon, 20 Dec 2021 13:28:39 +01:00

clocksource/drivers/msc313e: Add support for ssd20xd-based platforms

On SSD20X family SoCs the timers are connected to a 432MHz clock instead
of 12MHz that all the previous chips used. There is no way to reduce or
divide these clocks in the clktree yet as we do not know exactly where
the 432MHz clock comes from but it is enabled at boot.

The SSD20X timers have an input clock divider within the timer itself
to configure the frequency. timer0 is preconfigured at power up to run
at 12MHz so it is backwards compatible and doesn't need special handling
right now. timer1 and timer2 run at 432Mhz at power up so are not
backward compatible.

This commit adds support for the input clock divider register and sets
timer1 and timer2 to run at 48Mhz for clockevents.

Signed-off-by: Romain Perier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Daniel Lezcano <[email protected]>
---
drivers/clocksource/timer-msc313e.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/drivers/clocksource/timer-msc313e.c b/drivers/clocksource/timer-msc313e.c
index 154e734..54c54ca 100644
--- a/drivers/clocksource/timer-msc313e.c
+++ b/drivers/clocksource/timer-msc313e.c
@@ -33,7 +33,9 @@
#define MSC313E_REG_TIMER_MAX_HIGH 0x0c
#define MSC313E_REG_COUNTER_LOW 0x10
#define MSC313E_REG_COUNTER_HIGH 0x14
+#define MSC313E_REG_TIMER_DIVIDE 0x18

+#define MSC313E_CLK_DIVIDER 9
#define TIMER_SYNC_TICKS 3

#ifdef CONFIG_ARM
@@ -179,6 +181,12 @@ static int __init msc313e_clkevt_init(struct device_node *np)
if (ret)
return ret;

+ if (of_device_is_compatible(np, "sstar,ssd20xd-timer")) {
+ to->of_clk.rate = clk_get_rate(to->of_clk.clk) / MSC313E_CLK_DIVIDER;
+ to->of_clk.period = DIV_ROUND_UP(to->of_clk.rate, HZ);
+ writew(MSC313E_CLK_DIVIDER - 1, timer_of_base(to) + MSC313E_REG_TIMER_DIVIDE);
+ }
+
msc313e_clkevt.cpumask = cpu_possible_mask;
msc313e_clkevt.irq = to->of_irq.irq;
to->clkevt = msc313e_clkevt;
@@ -242,3 +250,4 @@ static int __init msc313e_timer_init(struct device_node *np)
}

TIMER_OF_DECLARE(msc313, "mstar,msc313e-timer", msc313e_timer_init);
+TIMER_OF_DECLARE(ssd20xd, "sstar,ssd20xd-timer", msc313e_timer_init);

Subject: [tip: timers/core] dt-bindings: timer: Add Mstar MSC313e timer devicetree bindings documentation

The following commit has been merged into the timers/core branch of tip:

Commit-ID: 7647204c2e81b28b4a7c4eec7d539f998d48eaf0
Gitweb: https://git.kernel.org/tip/7647204c2e81b28b4a7c4eec7d539f998d48eaf0
Author: Romain Perier <[email protected]>
AuthorDate: Fri, 17 Dec 2021 20:57:25 +01:00
Committer: Daniel Lezcano <[email protected]>
CommitterDate: Mon, 20 Dec 2021 13:28:47 +01:00

dt-bindings: timer: Add Mstar MSC313e timer devicetree bindings documentation

This adds the documentation for the devicetree bindings of the Mstar
MSC313e timer driver, found from MSC313e SoCs and newer.

Signed-off-by: Romain Perier <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Daniel Lezcano <[email protected]>
---
Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml

diff --git a/Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml b/Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml
new file mode 100644
index 0000000..03d5dba
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/mstar,msc313e-timer.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/timer/mstar,msc313e-timer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mstar MSC313e Timer Device Tree Bindings
+
+maintainers:
+ - Daniel Palmer <[email protected]>
+ - Romain Perier <[email protected]>
+
+properties:
+ compatible:
+ enum:
+ - mstar,msc313e-timer
+ - sstar,ssd20xd-timer
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ timer@6040 {
+ compatible = "mstar,msc313e-timer";
+ reg = <0x6040 0x40>;
+ clocks = <&xtal_div2>;
+ interrupts-extended = <&intc_fiq GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ };
+...

Subject: [tip: timers/core] clocksource/drivers: Add MStar MSC313e timer support

The following commit has been merged into the timers/core branch of tip:

Commit-ID: 5fc1f93f69989e72be7a84ded6ee867d6415f690
Gitweb: https://git.kernel.org/tip/5fc1f93f69989e72be7a84ded6ee867d6415f690
Author: Romain Perier <[email protected]>
AuthorDate: Fri, 17 Dec 2021 20:57:22 +01:00
Committer: Daniel Lezcano <[email protected]>
CommitterDate: Mon, 20 Dec 2021 13:28:28 +01:00

clocksource/drivers: Add MStar MSC313e timer support

The MSC313e-compatible SoCs have 3 timer hardware blocks. All of these
are free running 32-bit increasing counters and can generate interrupts.
Based onto a maximum value register, each timer can either count from 0
to max, one time then stop (which generates interrupts) or can count
from 0 to max and then roll. This commit adds basic support for these
timers, the first timer block being used as clocksource/sched_clock
and delay, while the others will be used as clockevents.

Signed-off-by: Romain Perier <[email protected]>
Co-developed-by: Daniel Palmer <[email protected]>
Signed-off-by: Daniel Palmer <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Daniel Lezcano <[email protected]>
---
MAINTAINERS | 1 +-
drivers/clocksource/Kconfig | 9 +-
drivers/clocksource/Makefile | 1 +-
drivers/clocksource/timer-msc313e.c | 244 +++++++++++++++++++++++++++-
4 files changed, 255 insertions(+)
create mode 100644 drivers/clocksource/timer-msc313e.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a2345c..f39a161 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2282,6 +2282,7 @@ F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml
F: arch/arm/boot/dts/mstar-*
F: arch/arm/mach-mstar/
F: drivers/clk/mstar/
+F: drivers/clocksource/timer-msc313e.c
F: drivers/gpio/gpio-msc313.c
F: drivers/rtc/rtc-msc313.c
F: drivers/watchdog/msc313e_wdt.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 821ea4a..cfb8ea0 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -672,6 +672,15 @@ config MILBEAUT_TIMER
help
Enables the support for Milbeaut timer driver.

+config MSC313E_TIMER
+ bool "MSC313E timer driver" if COMPILE_TEST
+ select TIMER_OF
+ select CLKSRC_MMIO
+ help
+ Enables support for the MStar MSC313E timer driver.
+ This provides access to multiple interrupt generating
+ programmable 32-bit free running incrementing counters.
+
config INGENIC_TIMER
bool "Clocksource/timer using the TCU in Ingenic JZ SoCs"
default MACH_INGENIC
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index c17ee32..fa5f624 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -88,3 +88,4 @@ obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o
obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o
obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o
+obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o
diff --git a/drivers/clocksource/timer-msc313e.c b/drivers/clocksource/timer-msc313e.c
new file mode 100644
index 0000000..154e734
--- /dev/null
+++ b/drivers/clocksource/timer-msc313e.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MStar timer driver
+ *
+ * Copyright (C) 2021 Daniel Palmer
+ * Copyright (C) 2021 Romain Perier
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#ifdef CONFIG_ARM
+#include <linux/delay.h>
+#endif
+
+#include "timer-of.h"
+
+#define TIMER_NAME "msc313e_timer"
+
+#define MSC313E_REG_CTRL 0x00
+#define MSC313E_REG_CTRL_TIMER_EN BIT(0)
+#define MSC313E_REG_CTRL_TIMER_TRIG BIT(1)
+#define MSC313E_REG_CTRL_TIMER_INT_EN BIT(8)
+#define MSC313E_REG_TIMER_MAX_LOW 0x08
+#define MSC313E_REG_TIMER_MAX_HIGH 0x0c
+#define MSC313E_REG_COUNTER_LOW 0x10
+#define MSC313E_REG_COUNTER_HIGH 0x14
+
+#define TIMER_SYNC_TICKS 3
+
+#ifdef CONFIG_ARM
+struct msc313e_delay {
+ void __iomem *base;
+ struct delay_timer delay;
+};
+static struct msc313e_delay msc313e_delay;
+#endif
+
+static void __iomem *msc313e_clksrc;
+
+static void msc313e_timer_stop(void __iomem *base)
+{
+ writew(0, base + MSC313E_REG_CTRL);
+}
+
+static void msc313e_timer_start(void __iomem *base, bool periodic)
+{
+ u16 reg;
+
+ reg = readw(base + MSC313E_REG_CTRL);
+ if (periodic)
+ reg |= MSC313E_REG_CTRL_TIMER_EN;
+ else
+ reg |= MSC313E_REG_CTRL_TIMER_TRIG;
+ writew(reg | MSC313E_REG_CTRL_TIMER_INT_EN, base + MSC313E_REG_CTRL);
+}
+
+static void msc313e_timer_setup(void __iomem *base, unsigned long delay)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ writew(delay >> 16, base + MSC313E_REG_TIMER_MAX_HIGH);
+ writew(delay & 0xffff, base + MSC313E_REG_TIMER_MAX_LOW);
+ local_irq_restore(flags);
+}
+
+static unsigned long msc313e_timer_current_value(void __iomem *base)
+{
+ unsigned long flags;
+ u16 l, h;
+
+ local_irq_save(flags);
+ l = readw(base + MSC313E_REG_COUNTER_LOW);
+ h = readw(base + MSC313E_REG_COUNTER_HIGH);
+ local_irq_restore(flags);
+
+ return (((u32)h) << 16 | l);
+}
+
+static int msc313e_timer_clkevt_shutdown(struct clock_event_device *evt)
+{
+ struct timer_of *timer = to_timer_of(evt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+
+ return 0;
+}
+
+static int msc313e_timer_clkevt_set_oneshot(struct clock_event_device *evt)
+{
+ struct timer_of *timer = to_timer_of(evt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+ msc313e_timer_start(timer_of_base(timer), false);
+
+ return 0;
+}
+
+static int msc313e_timer_clkevt_set_periodic(struct clock_event_device *evt)
+{
+ struct timer_of *timer = to_timer_of(evt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+ msc313e_timer_setup(timer_of_base(timer), timer_of_period(timer));
+ msc313e_timer_start(timer_of_base(timer), true);
+
+ return 0;
+}
+
+static int msc313e_timer_clkevt_next_event(unsigned long evt, struct clock_event_device *clkevt)
+{
+ struct timer_of *timer = to_timer_of(clkevt);
+
+ msc313e_timer_stop(timer_of_base(timer));
+ msc313e_timer_setup(timer_of_base(timer), evt);
+ msc313e_timer_start(timer_of_base(timer), false);
+
+ return 0;
+}
+
+static irqreturn_t msc313e_timer_clkevt_irq(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static u64 msc313e_timer_clksrc_read(struct clocksource *cs)
+{
+ return msc313e_timer_current_value(msc313e_clksrc) & cs->mask;
+}
+
+#ifdef CONFIG_ARM
+static unsigned long msc313e_read_delay_timer_read(void)
+{
+ return msc313e_timer_current_value(msc313e_delay.base);
+}
+#endif
+
+static u64 msc313e_timer_sched_clock_read(void)
+{
+ return msc313e_timer_current_value(msc313e_clksrc);
+}
+
+static struct clock_event_device msc313e_clkevt = {
+ .name = TIMER_NAME,
+ .rating = 300,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_state_shutdown = msc313e_timer_clkevt_shutdown,
+ .set_state_periodic = msc313e_timer_clkevt_set_periodic,
+ .set_state_oneshot = msc313e_timer_clkevt_set_oneshot,
+ .tick_resume = msc313e_timer_clkevt_shutdown,
+ .set_next_event = msc313e_timer_clkevt_next_event,
+};
+
+static int __init msc313e_clkevt_init(struct device_node *np)
+{
+ int ret;
+ struct timer_of *to;
+
+ to = kzalloc(sizeof(*to), GFP_KERNEL);
+ if (!to)
+ return -ENOMEM;
+
+ to->flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE;
+ to->of_irq.handler = msc313e_timer_clkevt_irq;
+ ret = timer_of_init(np, to);
+ if (ret)
+ return ret;
+
+ msc313e_clkevt.cpumask = cpu_possible_mask;
+ msc313e_clkevt.irq = to->of_irq.irq;
+ to->clkevt = msc313e_clkevt;
+
+ clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
+ TIMER_SYNC_TICKS, 0xffffffff);
+ return 0;
+}
+
+static int __init msc313e_clksrc_init(struct device_node *np)
+{
+ struct timer_of to = { 0 };
+ int ret;
+ u16 reg;
+
+ to.flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
+ ret = timer_of_init(np, &to);
+ if (ret)
+ return ret;
+
+ msc313e_clksrc = timer_of_base(&to);
+ reg = readw(msc313e_clksrc + MSC313E_REG_CTRL);
+ reg |= MSC313E_REG_CTRL_TIMER_EN;
+ writew(reg, msc313e_clksrc + MSC313E_REG_CTRL);
+
+#ifdef CONFIG_ARM
+ msc313e_delay.base = timer_of_base(&to);
+ msc313e_delay.delay.read_current_timer = msc313e_read_delay_timer_read;
+ msc313e_delay.delay.freq = timer_of_rate(&to);
+
+ register_current_timer_delay(&msc313e_delay.delay);
+#endif
+
+ sched_clock_register(msc313e_timer_sched_clock_read, 32, timer_of_rate(&to));
+ return clocksource_mmio_init(timer_of_base(&to), TIMER_NAME, timer_of_rate(&to), 300, 32,
+ msc313e_timer_clksrc_read);
+}
+
+static int __init msc313e_timer_init(struct device_node *np)
+{
+ int ret = 0;
+ static int num_called;
+
+ switch (num_called) {
+ case 0:
+ ret = msc313e_clksrc_init(np);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ ret = msc313e_clkevt_init(np);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ num_called++;
+
+ return 0;
+}
+
+TIMER_OF_DECLARE(msc313, "mstar,msc313e-timer", msc313e_timer_init);