2018-02-23 17:19:11

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hi,

This series gets back on the TCB drivers rework. It introduces a new driver to
handle the clocksource and clockevent devices.

As a reminder, this is necessary because:
- the current tcb_clksrc driver is probed too late to be able to be used at
boot and we now have SoCs that don't have a PIT. They currently are not able
to boot a mainline kernel.
- using the PIT doesn't work well with preempt-rt because its interrupt is
shared (in particular with the UART and their interrupt flags are
incompatible)
- the current solution is wasting some TCB channels

The plan is to get this driver upstream, then convert the TCB PWM driver to be
able to get rid of the tcb_clksrc driver along with atmel_tclib.

changes in v3:
- use SPDX tag in include/soc/at91/atmel_tcb.h
- rework option selection to make the clocksource option silent

Main changes in v2:
- use direct IO instead of regmap when accessing channel specific registers to
avoid the regmap locking
- implement suspend/resume


Alexandre Belloni (6):
ARM: at91: add TCB registers definitions
clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
clocksource/drivers: atmel-pit: make option silent
ARM: at91: Implement clocksource selection
ARM: configs: at91: use new TCB timer driver
ARM: configs: at91: unselect PIT

arch/arm/configs/at91_dt_defconfig | 2 +-
arch/arm/configs/sama5_defconfig | 2 +-
arch/arm/mach-at91/Kconfig | 25 ++
drivers/clocksource/Kconfig | 13 +-
drivers/clocksource/Makefile | 3 +-
drivers/clocksource/timer-atmel-tcb.c | 608 ++++++++++++++++++++++++++++++++++
include/soc/at91/atmel_tcb.h | 216 ++++++++++++
7 files changed, 865 insertions(+), 4 deletions(-)
create mode 100644 drivers/clocksource/timer-atmel-tcb.c
create mode 100644 include/soc/at91/atmel_tcb.h

--
2.16.1



2018-02-23 17:17:28

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 4/6] ARM: at91: Implement clocksource selection

Allow selecting and unselecting the PIT clocksource driver so it doesn't
have to be compile when unused.

Signed-off-by: Alexandre Belloni <[email protected]>
---
arch/arm/mach-at91/Kconfig | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 6d870421a7a6..2d8ca72f9ecf 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -106,6 +106,31 @@ config SOC_AT91SAM9
AT91SAM9X35
AT91SAM9XE

+comment "Clocksource driver selection"
+
+config ATMEL_CLOCKSOURCE_PIT
+ bool "Periodic Interval Timer (PIT) support"
+ depends on SOC_AT91SAM9 || SOC_SAMA5
+ default SOC_AT91SAM9 || SOC_SAMA5
+ select ATMEL_PIT
+ help
+ Select this to get a clocksource based on the Atmel Periodic Interval
+ Timer. It has a relatively low resolution and the TC Block clocksource
+ should be preferred.
+
+config ATMEL_CLOCKSOURCE_TCB
+ bool "Timer Counter Blocks (TCB) support"
+ depends on SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAMA5 || COMPILE_TEST
+ default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAMA5
+ depends on !ATMEL_TCLIB
+ select ATMEL_ARM_TCB_CLKSRC
+ help
+ Select this to get a high precision clocksource based on a
+ TC block with a 5+ MHz base clock rate.
+ On platforms with 16-bit counters, two timer channels are combined
+ to make a single 32-bit timer.
+ It can also be used as a clock event device supporting oneshot mode.
+
config HAVE_AT91_UTMI
bool

--
2.16.1


2018-02-23 17:17:45

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 6/6] ARM: configs: at91: unselect PIT

The PIT is not required anymore to successfully boot and may actually harm
in case preempt-rt is used because the PIT interrupt is shared.
Disable it so the TCB clocksource is used.

Signed-off-by: Alexandre Belloni <[email protected]>
---
arch/arm/configs/at91_dt_defconfig | 1 +
arch/arm/configs/sama5_defconfig | 1 +
2 files changed, 2 insertions(+)

diff --git a/arch/arm/configs/at91_dt_defconfig b/arch/arm/configs/at91_dt_defconfig
index 09f262e59fef..f4b253bd05ed 100644
--- a/arch/arm/configs/at91_dt_defconfig
+++ b/arch/arm/configs/at91_dt_defconfig
@@ -19,6 +19,7 @@ CONFIG_ARCH_MULTI_V5=y
CONFIG_ARCH_AT91=y
CONFIG_SOC_AT91RM9200=y
CONFIG_SOC_AT91SAM9=y
+# CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
CONFIG_AEABI=y
CONFIG_UACCESS_WITH_MEMCPY=y
CONFIG_ZBOOT_ROM_TEXT=0x0
diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index f2bbc6339ca6..be92871ab155 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -20,6 +20,7 @@ CONFIG_ARCH_AT91=y
CONFIG_SOC_SAMA5D2=y
CONFIG_SOC_SAMA5D3=y
CONFIG_SOC_SAMA5D4=y
+# CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
CONFIG_AEABI=y
CONFIG_UACCESS_WITH_MEMCPY=y
CONFIG_ZBOOT_ROM_TEXT=0x0
--
2.16.1


2018-02-23 17:18:00

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 5/6] ARM: configs: at91: use new TCB timer driver

Unselecting ATMEL_TCLIB switches the TCB timer driver from tcb_clksrc to
timer-atmel-tcb.

Signed-off-by: Alexandre Belloni <[email protected]>
---
arch/arm/configs/at91_dt_defconfig | 1 -
arch/arm/configs/sama5_defconfig | 1 -
2 files changed, 2 deletions(-)

diff --git a/arch/arm/configs/at91_dt_defconfig b/arch/arm/configs/at91_dt_defconfig
index e4b1be66b3f5..09f262e59fef 100644
--- a/arch/arm/configs/at91_dt_defconfig
+++ b/arch/arm/configs/at91_dt_defconfig
@@ -64,7 +64,6 @@ CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=4
CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_ATMEL_TCLIB=y
CONFIG_ATMEL_SSC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index 2080025556b5..f2bbc6339ca6 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -75,7 +75,6 @@ CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=4
CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_ATMEL_TCLIB=y
CONFIG_ATMEL_SSC=y
CONFIG_EEPROM_AT24=y
CONFIG_SCSI=y
--
2.16.1


2018-02-23 17:19:53

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 3/6] clocksource/drivers: atmel-pit: make option silent

To conform with the other option, make the ATMEL_PIT option silent so it
can be selected from the platform

Signed-off-by: Alexandre Belloni <[email protected]>
---
drivers/clocksource/Kconfig | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d540852c9175..87f326b849cd 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -381,8 +381,11 @@ config ARMV7M_SYSTICK
This options enables support for the ARMv7M system timer unit

config ATMEL_PIT
+ bool "Microchip ARM Periodic Interval Timer (PIT)" if COMPILE_TEST
select TIMER_OF if OF
- def_bool SOC_AT91SAM9 || SOC_SAMA5
+ help
+ This enables build of clocksource and clockevent driver for
+ the integrated PIT in Microchip ARM SoCs.

config ATMEL_ST
bool "Atmel ST timer support" if COMPILE_TEST
--
2.16.1


2018-02-23 17:20:07

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 1/6] ARM: at91: add TCB registers definitions

Add registers and bits definitions for the timer counter blocks found on
Atmel ARM SoCs.

Signed-off-by: Alexandre Belloni <[email protected]>
---
include/soc/at91/atmel_tcb.h | 216 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 216 insertions(+)
create mode 100644 include/soc/at91/atmel_tcb.h

diff --git a/include/soc/at91/atmel_tcb.h b/include/soc/at91/atmel_tcb.h
new file mode 100644
index 000000000000..d263ea677225
--- /dev/null
+++ b/include/soc/at91/atmel_tcb.h
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018 Microchip */
+
+#ifndef __SOC_ATMEL_TCB_H
+#define __SOC_ATMEL_TCB_H
+
+/* Channel registers */
+#define ATMEL_TC_COFFS(c) ((c) * 0x40)
+#define ATMEL_TC_CCR(c) ATMEL_TC_COFFS(c)
+#define ATMEL_TC_CMR(c) (ATMEL_TC_COFFS(c) + 0x4)
+#define ATMEL_TC_SMMR(c) (ATMEL_TC_COFFS(c) + 0x8)
+#define ATMEL_TC_RAB(c) (ATMEL_TC_COFFS(c) + 0xc)
+#define ATMEL_TC_CV(c) (ATMEL_TC_COFFS(c) + 0x10)
+#define ATMEL_TC_RA(c) (ATMEL_TC_COFFS(c) + 0x14)
+#define ATMEL_TC_RB(c) (ATMEL_TC_COFFS(c) + 0x18)
+#define ATMEL_TC_RC(c) (ATMEL_TC_COFFS(c) + 0x1c)
+#define ATMEL_TC_SR(c) (ATMEL_TC_COFFS(c) + 0x20)
+#define ATMEL_TC_IER(c) (ATMEL_TC_COFFS(c) + 0x24)
+#define ATMEL_TC_IDR(c) (ATMEL_TC_COFFS(c) + 0x28)
+#define ATMEL_TC_IMR(c) (ATMEL_TC_COFFS(c) + 0x2c)
+#define ATMEL_TC_EMR(c) (ATMEL_TC_COFFS(c) + 0x30)
+
+/* Block registers */
+#define ATMEL_TC_BCR 0xc0
+#define ATMEL_TC_BMR 0xc4
+#define ATMEL_TC_QIER 0xc8
+#define ATMEL_TC_QIDR 0xcc
+#define ATMEL_TC_QIMR 0xd0
+#define ATMEL_TC_QISR 0xd4
+#define ATMEL_TC_FMR 0xd8
+#define ATMEL_TC_WPMR 0xe4
+
+/* CCR fields */
+#define ATMEL_TC_CCR_CLKEN BIT(0)
+#define ATMEL_TC_CCR_CLKDIS BIT(1)
+#define ATMEL_TC_CCR_SWTRG BIT(2)
+
+/* Common CMR fields */
+#define ATMEL_TC_CMR_TCLKS_MSK GENMASK(2, 0)
+#define ATMEL_TC_CMR_TCLK(x) (x)
+#define ATMEL_TC_CMR_XC(x) ((x) + 5)
+#define ATMEL_TC_CMR_CLKI BIT(3)
+#define ATMEL_TC_CMR_BURST_MSK GENMASK(5, 4)
+#define ATMEL_TC_CMR_BURST_XC(x) (((x) + 1) << 4)
+#define ATMEL_TC_CMR_WAVE BIT(15)
+
+/* Capture mode CMR fields */
+#define ATMEL_TC_CMR_LDBSTOP BIT(6)
+#define ATMEL_TC_CMR_LDBDIS BIT(7)
+#define ATMEL_TC_CMR_ETRGEDG_MSK GENMASK(9, 8)
+#define ATMEL_TC_CMR_ETRGEDG_NONE (0 << 8)
+#define ATMEL_TC_CMR_ETRGEDG_RISING (1 << 8)
+#define ATMEL_TC_CMR_ETRGEDG_FALLING (2 << 8)
+#define ATMEL_TC_CMR_ETRGEDG_BOTH (3 << 8)
+#define ATMEL_TC_CMR_ABETRG BIT(10)
+#define ATMEL_TC_CMR_CPCTRG BIT(14)
+#define ATMEL_TC_CMR_LDRA_MSK GENMASK(17, 16)
+#define ATMEL_TC_CMR_LDRA_NONE (0 << 16)
+#define ATMEL_TC_CMR_LDRA_RISING (1 << 16)
+#define ATMEL_TC_CMR_LDRA_FALLING (2 << 16)
+#define ATMEL_TC_CMR_LDRA_BOTH (3 << 16)
+#define ATMEL_TC_CMR_LDRB_MSK GENMASK(19, 18)
+#define ATMEL_TC_CMR_LDRB_NONE (0 << 18)
+#define ATMEL_TC_CMR_LDRB_RISING (1 << 18)
+#define ATMEL_TC_CMR_LDRB_FALLING (2 << 18)
+#define ATMEL_TC_CMR_LDRB_BOTH (3 << 18)
+#define ATMEL_TC_CMR_SBSMPLR_MSK GENMASK(22, 20)
+#define ATMEL_TC_CMR_SBSMPLR(x) ((x) << 20)
+
+/* Waveform mode CMR fields */
+#define ATMEL_TC_CMR_CPCSTOP BIT(6)
+#define ATMEL_TC_CMR_CPCDIS BIT(7)
+#define ATMEL_TC_CMR_EEVTEDG_MSK GENMASK(9, 8)
+#define ATMEL_TC_CMR_EEVTEDG_NONE (0 << 8)
+#define ATMEL_TC_CMR_EEVTEDG_RISING (1 << 8)
+#define ATMEL_TC_CMR_EEVTEDG_FALLING (2 << 8)
+#define ATMEL_TC_CMR_EEVTEDG_BOTH (3 << 8)
+#define ATMEL_TC_CMR_EEVT_MSK GENMASK(11, 10)
+#define ATMEL_TC_CMR_EEVT_XC(x) (((x) + 1) << 10)
+#define ATMEL_TC_CMR_ENETRG BIT(12)
+#define ATMEL_TC_CMR_WAVESEL_MSK GENMASK(14, 13)
+#define ATMEL_TC_CMR_WAVESEL_UP (0 << 13)
+#define ATMEL_TC_CMR_WAVESEL_UPDOWN (1 << 13)
+#define ATMEL_TC_CMR_WAVESEL_UPRC (2 << 13)
+#define ATMEL_TC_CMR_WAVESEL_UPDOWNRC (3 << 13)
+#define ATMEL_TC_CMR_ACPA_MSK GENMASK(17, 16)
+#define ATMEL_TC_CMR_ACPA(a) (ATMEL_TC_CMR_ACTION_##a << 16)
+#define ATMEL_TC_CMR_ACPC_MSK GENMASK(19, 18)
+#define ATMEL_TC_CMR_ACPC(a) (ATMEL_TC_CMR_ACTION_##a << 18)
+#define ATMEL_TC_CMR_AEEVT_MSK GENMASK(21, 20)
+#define ATMEL_TC_CMR_AEEVT(a) (ATMEL_TC_CMR_ACTION_##a << 20)
+#define ATMEL_TC_CMR_ASWTRG_MSK GENMASK(23, 22)
+#define ATMEL_TC_CMR_ASWTRG(a) (ATMEL_TC_CMR_ACTION_##a << 22)
+#define ATMEL_TC_CMR_BCPB_MSK GENMASK(25, 24)
+#define ATMEL_TC_CMR_BCPB(a) (ATMEL_TC_CMR_ACTION_##a << 24)
+#define ATMEL_TC_CMR_BCPC_MSK GENMASK(27, 26)
+#define ATMEL_TC_CMR_BCPC(a) (ATMEL_TC_CMR_ACTION_##a << 26)
+#define ATMEL_TC_CMR_BEEVT_MSK GENMASK(29, 28)
+#define ATMEL_TC_CMR_BEEVT(a) (ATMEL_TC_CMR_ACTION_##a << 28)
+#define ATMEL_TC_CMR_BSWTRG_MSK GENMASK(31, 30)
+#define ATMEL_TC_CMR_BSWTRG(a) (ATMEL_TC_CMR_ACTION_##a << 30)
+#define ATMEL_TC_CMR_ACTION_NONE 0
+#define ATMEL_TC_CMR_ACTION_SET 1
+#define ATMEL_TC_CMR_ACTION_CLEAR 2
+#define ATMEL_TC_CMR_ACTION_TOGGLE 3
+
+/* SMMR fields */
+#define ATMEL_TC_SMMR_GCEN BIT(0)
+#define ATMEL_TC_SMMR_DOWN BIT(1)
+
+/* SR/IER/IDR/IMR fields */
+#define ATMEL_TC_COVFS BIT(0)
+#define ATMEL_TC_LOVRS BIT(1)
+#define ATMEL_TC_CPAS BIT(2)
+#define ATMEL_TC_CPBS BIT(3)
+#define ATMEL_TC_CPCS BIT(4)
+#define ATMEL_TC_LDRAS BIT(5)
+#define ATMEL_TC_LDRBS BIT(6)
+#define ATMEL_TC_ETRGS BIT(7)
+#define ATMEL_TC_CLKSTA BIT(16)
+#define ATMEL_TC_MTIOA BIT(17)
+#define ATMEL_TC_MTIOB BIT(18)
+
+/* EMR fields */
+#define ATMEL_TC_EMR_TRIGSRCA_MSK GENMASK(1, 0)
+#define ATMEL_TC_EMR_TRIGSRCA_TIOA 0
+#define ATMEL_TC_EMR_TRIGSRCA_PWMX 1
+#define ATMEL_TC_EMR_TRIGSRCB_MSK GENMASK(5, 4)
+#define ATMEL_TC_EMR_TRIGSRCB_TIOB (0 << 4)
+#define ATMEL_TC_EMR_TRIGSRCB_PWM (1 << 4)
+#define ATMEL_TC_EMR_NOCLKDIV BIT(8)
+
+/* BCR fields */
+#define ATMEL_TC_BCR_SYNC BIT(0)
+
+/* BMR fields */
+#define ATMEL_TC_BMR_TCXC_MSK(c) GENMASK(((c) * 2) + 1, (c) * 2)
+#define ATMEL_TC_BMR_TCXC(x, c) ((x) << (2 * (c)))
+#define ATMEL_TC_BMR_QDEN BIT(8)
+#define ATMEL_TC_BMR_POSEN BIT(9)
+#define ATMEL_TC_BMR_SPEEDEN BIT(10)
+#define ATMEL_TC_BMR_QDTRANS BIT(11)
+#define ATMEL_TC_BMR_EDGPHA BIT(12)
+#define ATMEL_TC_BMR_INVA BIT(13)
+#define ATMEL_TC_BMR_INVB BIT(14)
+#define ATMEL_TC_BMR_INVIDX BIT(15)
+#define ATMEL_TC_BMR_SWAP BIT(16)
+#define ATMEL_TC_BMR_IDXPHB BIT(17)
+#define ATMEL_TC_BMR_AUTOC BIT(18)
+#define ATMEL_TC_MAXFILT_MSK GENMASK(25, 20)
+#define ATMEL_TC_MAXFILT(x) (((x) - 1) << 20)
+#define ATMEL_TC_MAXCMP_MSK GENMASK(29, 26)
+#define ATMEL_TC_MAXCMP(x) ((x) << 26)
+
+/* QEDC fields */
+#define ATMEL_TC_QEDC_IDX BIT(0)
+#define ATMEL_TC_QEDC_DIRCHG BIT(1)
+#define ATMEL_TC_QEDC_QERR BIT(2)
+#define ATMEL_TC_QEDC_MPE BIT(3)
+#define ATMEL_TC_QEDC_DIR BIT(8)
+
+/* FMR fields */
+#define ATMEL_TC_FMR_ENCF(x) BIT(x)
+
+/* WPMR fields */
+#define ATMEL_TC_WPMR_WPKEY (0x54494d << 8)
+#define ATMEL_TC_WPMR_WPEN BIT(0)
+
+static inline struct clk *tcb_clk_get(struct device_node *node, int channel)
+{
+ struct clk *clk;
+ char clk_name[] = "t0_clk";
+
+ clk_name[1] += channel;
+ clk = of_clk_get_by_name(node->parent, clk_name);
+ if (!IS_ERR(clk))
+ return clk;
+
+ return of_clk_get_by_name(node->parent, "t0_clk");
+}
+
+static inline int tcb_irq_get(struct device_node *node, int channel)
+{
+ int irq;
+
+ irq = of_irq_get(node->parent, channel);
+ if (irq > 0)
+ return irq;
+
+ return of_irq_get(node->parent, 0);
+}
+
+static const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, };
+
+struct atmel_tcb_info {
+ int bits;
+};
+
+static const struct atmel_tcb_info atmel_tcb_infos[] = {
+ { .bits = 16 },
+ { .bits = 32 },
+};
+
+static const struct of_device_id atmel_tcb_dt_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-tcb",
+ .data = &atmel_tcb_infos[0],
+ }, {
+ .compatible = "atmel,at91sam9x5-tcb",
+ .data = &atmel_tcb_infos[1],
+ }, {
+ /* sentinel */
+ }
+};
+
+#endif /* __SOC_ATMEL_TCB_H */
--
2.16.1


2018-02-23 17:20:18

by Alexandre Belloni

[permalink] [raw]
Subject: [PATCH v3 2/6] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks

Add a driver for the Atmel Timer Counter Blocks. This driver provides a
clocksource and two clockevent devices.

One of the clockevent device is linked to the clocksource counter and so it
will run at the same frequency. This will be used when there is only on TCB
channel available for timers.

The other clockevent device runs on a separate TCB channel when available.

This driver uses regmap and syscon to be able to probe early in the boot
and avoid having to switch on the TCB clocksource later. Using regmap also
means that unused TCB channels may be used by other drivers (PWM for
example). read/writel are still used to access channel specific registers
to avoid the performance impact of regmap (mainly locking).

Signed-off-by: Alexandre Belloni <[email protected]>
---
drivers/clocksource/Kconfig | 8 +
drivers/clocksource/Makefile | 3 +-
drivers/clocksource/timer-atmel-tcb.c | 608 ++++++++++++++++++++++++++++++++++
3 files changed, 618 insertions(+), 1 deletion(-)
create mode 100644 drivers/clocksource/timer-atmel-tcb.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index b3b4ed9b6874..d540852c9175 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -391,6 +391,14 @@ config ATMEL_ST
help
Support for the Atmel ST timer.

+config ATMEL_ARM_TCB_CLKSRC
+ bool "Microchip ARM TC Block" if COMPILE_TEST
+ select REGMAP_MMIO
+ depends on GENERIC_CLOCKEVENTS
+ help
+ This enables build of clocksource and clockevent driver for
+ the integrated Timer Counter Blocks in Microchip ARM SoCs.
+
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index d6dec4489d66..c3351bc5d6aa 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -3,7 +3,8 @@ obj-$(CONFIG_TIMER_OF) += timer-of.o
obj-$(CONFIG_TIMER_PROBE) += timer-probe.o
obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o
-obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
+obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
+obj-$(CONFIG_ATMEL_ARM_TCB_CLKSRC) += timer-atmel-tcb.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
new file mode 100644
index 000000000000..7fde9cfbf203
--- /dev/null
+++ b/drivers/clocksource/timer-atmel-tcb.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/sched_clock.h>
+#include <soc/at91/atmel_tcb.h>
+
+static struct atmel_tcb_clksrc {
+ struct clocksource clksrc;
+ struct clock_event_device clkevt;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *clk[2];
+ char name[20];
+ int channels[2];
+ int bits;
+ int irq;
+ struct {
+ u32 cmr;
+ u32 imr;
+ u32 rc;
+ bool clken;
+ } cache[2];
+ u32 bmr_cache;
+ bool registered;
+} tc = {
+ .clksrc = {
+ .rating = 200,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ },
+ .clkevt = {
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ /* Should be lower than at91rm9200's system timer */
+ .rating = 125,
+ },
+};
+
+static struct tc_clkevt_device {
+ struct clock_event_device clkevt;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk *slow_clk;
+ struct clk *clk;
+ char name[20];
+ int channel;
+ int irq;
+ struct {
+ u32 cmr;
+ u32 imr;
+ u32 rc;
+ bool clken;
+ } cache;
+ bool registered;
+} tce = {
+ .clkevt = {
+ .features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT,
+ /*
+ * Should be lower than at91rm9200's system timer
+ * but higher than tc.clkevt.rating
+ */
+ .rating = 140,
+ },
+};
+
+/*
+ * Clockevent device using its own channel
+ */
+static int tc_clkevt2_shutdown(struct clock_event_device *d)
+{
+ writel(0xff, tce.base + ATMEL_TC_IDR(tce.channel));
+ writel(ATMEL_TC_CCR_CLKDIS, tce.base + ATMEL_TC_CCR(tce.channel));
+ if (!clockevent_state_detached(d))
+ clk_disable(tce.clk);
+
+ return 0;
+}
+
+/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
+ * because using one of the divided clocks would usually mean the
+ * tick rate can never be less than several dozen Hz (vs 0.5 Hz).
+ *
+ * A divided clock could be good for high resolution timers, since
+ * 30.5 usec resolution can seem "low".
+ */
+static int tc_clkevt2_set_oneshot(struct clock_event_device *d)
+{
+ if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
+ tc_clkevt2_shutdown(d);
+
+ clk_enable(tce.clk);
+
+ /* slow clock, count up to RC, then irq and stop */
+ writel(ATMEL_TC_CMR_TCLK(4) | ATMEL_TC_CMR_CPCSTOP |
+ ATMEL_TC_CMR_WAVE | ATMEL_TC_CMR_WAVESEL_UPRC,
+ tce.base + ATMEL_TC_CMR(tce.channel));
+ writel(ATMEL_TC_CPCS, tce.base + ATMEL_TC_IER(tce.channel));
+
+ return 0;
+}
+
+static int tc_clkevt2_set_periodic(struct clock_event_device *d)
+{
+ if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
+ tc_clkevt2_shutdown(d);
+
+ /* By not making the gentime core emulate periodic mode on top
+ * of oneshot, we get lower overhead and improved accuracy.
+ */
+ clk_enable(tce.clk);
+
+ /* slow clock, count up to RC, then irq and restart */
+ writel(ATMEL_TC_CMR_TCLK(4) | ATMEL_TC_CMR_WAVE |
+ ATMEL_TC_CMR_WAVESEL_UPRC,
+ tce.base + ATMEL_TC_CMR(tce.channel));
+ writel((32768 + HZ / 2) / HZ, tce.base + ATMEL_TC_RC(tce.channel));
+
+ /* Enable clock and interrupts on RC compare */
+ writel(ATMEL_TC_CPCS, tce.base + ATMEL_TC_IER(tce.channel));
+ writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG,
+ tce.base + ATMEL_TC_CCR(tce.channel));
+
+ return 0;
+}
+
+static int tc_clkevt2_next_event(unsigned long delta,
+ struct clock_event_device *d)
+{
+ writel(delta, tce.base + ATMEL_TC_RC(tce.channel));
+ writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG,
+ tce.base + ATMEL_TC_CCR(tce.channel));
+
+ return 0;
+}
+
+static irqreturn_t tc_clkevt2_irq(int irq, void *handle)
+{
+ unsigned int sr;
+
+ sr = readl(tce.base + ATMEL_TC_SR(tce.channel));
+ if (sr & ATMEL_TC_CPCS) {
+ tce.clkevt.event_handler(&tce.clkevt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void tc_clkevt2_suspend(struct clock_event_device *d)
+{
+ tce.cache.cmr = readl(tce.base + ATMEL_TC_CMR(tce.channel));
+ tce.cache.imr = readl(tce.base + ATMEL_TC_IMR(tce.channel));
+ tce.cache.rc = readl(tce.base + ATMEL_TC_RC(tce.channel));
+ tce.cache.clken = !!(readl(tce.base + ATMEL_TC_SR(tce.channel)) &
+ ATMEL_TC_CLKSTA);
+}
+
+static void tc_clkevt2_resume(struct clock_event_device *d)
+{
+ /* Restore registers for the channel, RA and RB are not used */
+ writel(tce.cache.cmr, tc.base + ATMEL_TC_CMR(tce.channel));
+ writel(tce.cache.rc, tc.base + ATMEL_TC_RC(tce.channel));
+ writel(0, tc.base + ATMEL_TC_RA(tce.channel));
+ writel(0, tc.base + ATMEL_TC_RB(tce.channel));
+ /* Disable all the interrupts */
+ writel(0xff, tc.base + ATMEL_TC_IDR(tce.channel));
+ /* Reenable interrupts that were enabled before suspending */
+ writel(tce.cache.imr, tc.base + ATMEL_TC_IER(tce.channel));
+
+ /* Start the clock if it was used */
+ if (tce.cache.clken)
+ writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG,
+ tc.base + ATMEL_TC_CCR(tce.channel));
+}
+
+static int __init tc_clkevt_register(struct device_node *node,
+ struct regmap *regmap, void __iomem *base,
+ int channel, int irq, int bits)
+{
+ int ret;
+
+ tce.regmap = regmap;
+ tce.base = base;
+ tce.channel = channel;
+ tce.irq = irq;
+
+ tce.slow_clk = of_clk_get_by_name(node->parent, "slow_clk");
+ if (IS_ERR(tce.slow_clk))
+ return PTR_ERR(tce.slow_clk);
+
+ ret = clk_prepare_enable(tce.slow_clk);
+ if (ret)
+ return ret;
+
+ tce.clk = tcb_clk_get(node, tce.channel);
+ if (IS_ERR(tce.clk)) {
+ ret = PTR_ERR(tce.clk);
+ goto err_slow;
+ }
+
+ snprintf(tce.name, sizeof(tce.name), "%s:%d",
+ kbasename(node->parent->full_name), channel);
+ tce.clkevt.cpumask = cpumask_of(0);
+ tce.clkevt.name = tce.name;
+ tce.clkevt.set_next_event = tc_clkevt2_next_event,
+ tce.clkevt.set_state_shutdown = tc_clkevt2_shutdown,
+ tce.clkevt.set_state_periodic = tc_clkevt2_set_periodic,
+ tce.clkevt.set_state_oneshot = tc_clkevt2_set_oneshot,
+ tce.clkevt.suspend = tc_clkevt2_suspend,
+ tce.clkevt.resume = tc_clkevt2_resume,
+
+ /* try to enable clk to avoid future errors in mode change */
+ ret = clk_prepare_enable(tce.clk);
+ if (ret)
+ goto err_slow;
+ clk_disable(tce.clk);
+
+ clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
+
+ ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
+ tce.clkevt.name, &tce);
+ if (ret)
+ goto err_clk;
+
+ tce.registered = true;
+
+ return 0;
+
+err_clk:
+ clk_unprepare(tce.clk);
+err_slow:
+ clk_disable_unprepare(tce.slow_clk);
+
+ return ret;
+}
+
+/*
+ * Clocksource and clockevent using the same channel(s)
+ */
+static u64 tc_get_cycles(struct clocksource *cs)
+{
+ u32 lower, upper;
+
+ do {
+ upper = readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[1]));
+ lower = readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[0]));
+ } while (upper != readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[1])));
+
+ return (upper << 16) | lower;
+}
+
+static u64 tc_get_cycles32(struct clocksource *cs)
+{
+ return readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[0]));
+}
+
+static u64 notrace tc_sched_clock_read(void)
+{
+ return tc_get_cycles(&tc.clksrc);
+}
+
+static u64 notrace tc_sched_clock_read32(void)
+{
+ return tc_get_cycles32(&tc.clksrc);
+}
+
+static int tcb_clkevt_next_event(unsigned long delta,
+ struct clock_event_device *d)
+{
+ u32 old, next, cur;
+
+
+ old = readl(tc.base + ATMEL_TC_CV(tc.channels[0]));
+ next = old + delta;
+ writel(next, tc.base + ATMEL_TC_RC(tc.channels[0]));
+ cur = readl(tc.base + ATMEL_TC_CV(tc.channels[0]));
+
+ /* check whether the delta elapsed while setting the register */
+ if ((next < old && cur < old && cur > next) ||
+ (next > old && (cur < old || cur > next))) {
+ /*
+ * Clear the CPCS bit in the status register to avoid
+ * generating a spurious interrupt next time a valid
+ * timer event is configured.
+ */
+ old = readl(tc.base + ATMEL_TC_SR(tc.channels[0]));
+ return -ETIME;
+ }
+
+ writel(ATMEL_TC_CPCS, tc.base + ATMEL_TC_IER(tc.channels[0]));
+
+ return 0;
+}
+
+static irqreturn_t tc_clkevt_irq(int irq, void *handle)
+{
+ unsigned int sr;
+
+ sr = readl(tc.base + ATMEL_TC_SR(tc.channels[0]));
+ if (sr & ATMEL_TC_CPCS) {
+ tc.clkevt.event_handler(&tc.clkevt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int tcb_clkevt_oneshot(struct clock_event_device *dev)
+{
+ if (clockevent_state_oneshot(dev))
+ return 0;
+
+ /*
+ * Because both clockevent devices may share the same IRQ, we don't want
+ * the less likely one to stay requested
+ */
+ return request_irq(tc.irq, tc_clkevt_irq, IRQF_TIMER | IRQF_SHARED,
+ tc.name, &tc);
+}
+
+static int tcb_clkevt_shutdown(struct clock_event_device *dev)
+{
+ writel(0xff, tc.base + ATMEL_TC_IDR(tc.channels[0]));
+ if (tc.bits == 16)
+ writel(0xff, tc.base + ATMEL_TC_IDR(tc.channels[1]));
+
+ if (!clockevent_state_detached(dev))
+ free_irq(tc.irq, &tc);
+
+ return 0;
+}
+
+static void __init tcb_setup_dual_chan(struct atmel_tcb_clksrc *tc,
+ int mck_divisor_idx)
+{
+ /* first channel: waveform mode, input mclk/8, clock TIOA on overflow */
+ writel(mck_divisor_idx /* likely divide-by-8 */
+ | ATMEL_TC_CMR_WAVE
+ | ATMEL_TC_CMR_WAVESEL_UP /* free-run */
+ | ATMEL_TC_CMR_ACPA(SET) /* TIOA rises at 0 */
+ | ATMEL_TC_CMR_ACPC(CLEAR), /* (duty cycle 50%) */
+ tc->base + ATMEL_TC_CMR(tc->channels[0]));
+ writel(0x0000, tc->base + ATMEL_TC_RA(tc->channels[0]));
+ writel(0x8000, tc->base + ATMEL_TC_RC(tc->channels[0]));
+ writel(0xff, tc->base + ATMEL_TC_IDR(tc->channels[0])); /* no irqs */
+ writel(ATMEL_TC_CCR_CLKEN, tc->base + ATMEL_TC_CCR(tc->channels[0]));
+
+ /* second channel: waveform mode, input TIOA */
+ writel(ATMEL_TC_CMR_XC(tc->channels[1]) /* input: TIOA */
+ | ATMEL_TC_CMR_WAVE
+ | ATMEL_TC_CMR_WAVESEL_UP, /* free-run */
+ tc->base + ATMEL_TC_CMR(tc->channels[1]));
+ writel(0xff, tc->base + ATMEL_TC_IDR(tc->channels[1])); /* no irqs */
+ writel(ATMEL_TC_CCR_CLKEN, tc->base + ATMEL_TC_CCR(tc->channels[1]));
+
+ /* chain both channel, we assume the previous channel */
+ regmap_write(tc->regmap, ATMEL_TC_BMR,
+ ATMEL_TC_BMR_TCXC(1 + tc->channels[1], tc->channels[1]));
+ /* then reset all the timers */
+ regmap_write(tc->regmap, ATMEL_TC_BCR, ATMEL_TC_BCR_SYNC);
+}
+
+static void __init tcb_setup_single_chan(struct atmel_tcb_clksrc *tc,
+ int mck_divisor_idx)
+{
+ /* channel 0: waveform mode, input mclk/8 */
+ writel(mck_divisor_idx /* likely divide-by-8 */
+ | ATMEL_TC_CMR_WAVE
+ | ATMEL_TC_CMR_WAVESEL_UP, /* free-run */
+ tc->base + ATMEL_TC_CMR(tc->channels[0]));
+ writel(0xff, tc->base + ATMEL_TC_IDR(tc->channels[0])); /* no irqs */
+ writel(ATMEL_TC_CCR_CLKEN, tc->base + ATMEL_TC_CCR(tc->channels[0]));
+
+ /* then reset all the timers */
+ regmap_write(tc->regmap, ATMEL_TC_BCR, ATMEL_TC_BCR_SYNC);
+}
+
+static void tc_clksrc_suspend(struct clocksource *cs)
+{
+ int i;
+
+ for (i = 0; i < 1 + (tc.bits == 16); i++) {
+ tc.cache[i].cmr = readl(tc.base + ATMEL_TC_CMR(tc.channels[i]));
+ tc.cache[i].imr = readl(tc.base + ATMEL_TC_IMR(tc.channels[i]));
+ tc.cache[i].rc = readl(tc.base + ATMEL_TC_RC(tc.channels[i]));
+ tc.cache[i].clken = !!(readl(tc.base +
+ ATMEL_TC_SR(tc.channels[i])) &
+ ATMEL_TC_CLKSTA);
+ }
+
+ if (tc.bits == 16)
+ regmap_read(tc.regmap, ATMEL_TC_BMR, &tc.bmr_cache);
+}
+
+static void tc_clksrc_resume(struct clocksource *cs)
+{
+ int i;
+
+ for (i = 0; i < 1 + (tc.bits == 16); i++) {
+ /* Restore registers for the channel, RA and RB are not used */
+ writel(tc.cache[i].cmr, tc.base + ATMEL_TC_CMR(tc.channels[i]));
+ writel(tc.cache[i].rc, tc.base + ATMEL_TC_RC(tc.channels[i]));
+ writel(0, tc.base + ATMEL_TC_RA(tc.channels[i]));
+ writel(0, tc.base + ATMEL_TC_RB(tc.channels[i]));
+ /* Disable all the interrupts */
+ writel(0xff, tc.base + ATMEL_TC_IDR(tc.channels[i]));
+ /* Reenable interrupts that were enabled before suspending */
+ writel(tc.cache[i].imr, tc.base + ATMEL_TC_IER(tc.channels[i]));
+
+ /* Start the clock if it was used */
+ if (tc.cache[i].clken)
+ writel(ATMEL_TC_CCR_CLKEN, tc.base +
+ ATMEL_TC_CCR(tc.channels[i]));
+ }
+
+ /* in case of dual channel, chain channels */
+ if (tc.bits == 16)
+ regmap_write(tc.regmap, ATMEL_TC_BMR, tc.bmr_cache);
+ /* Finally, trigger all the channels*/
+ regmap_write(tc.regmap, ATMEL_TC_BCR, ATMEL_TC_BCR_SYNC);
+}
+
+static int __init tcb_clksrc_register(struct device_node *node,
+ struct regmap *regmap, void __iomem *base,
+ int channel, int channel1, int irq,
+ int bits)
+{
+ u32 rate, divided_rate = 0;
+ int best_divisor_idx = -1;
+ int i, err = -1;
+ u64 (*tc_sched_clock)(void);
+
+ tc.regmap = regmap;
+ tc.base = base;
+ tc.channels[0] = channel;
+ tc.channels[1] = channel1;
+ tc.irq = irq;
+ tc.bits = bits;
+
+ tc.clk[0] = tcb_clk_get(node, tc.channels[0]);
+ if (IS_ERR(tc.clk[0]))
+ return PTR_ERR(tc.clk[0]);
+ err = clk_prepare_enable(tc.clk[0]);
+ if (err) {
+ pr_debug("can't enable T0 clk\n");
+ goto err_clk;
+ }
+
+ /* How fast will we be counting? Pick something over 5 MHz. */
+ rate = (u32)clk_get_rate(tc.clk[0]);
+ for (i = 0; i < 5; i++) {
+ unsigned int divisor = atmel_tc_divisors[i];
+ unsigned int tmp;
+
+ if (!divisor)
+ continue;
+
+ tmp = rate / divisor;
+ pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
+ if (best_divisor_idx > 0) {
+ if (tmp < 5 * 1000 * 1000)
+ continue;
+ }
+ divided_rate = tmp;
+ best_divisor_idx = i;
+ }
+
+ if (tc.bits == 32) {
+ tc.clksrc.read = tc_get_cycles32;
+ tcb_setup_single_chan(&tc, best_divisor_idx);
+ tc_sched_clock = tc_sched_clock_read32;
+ snprintf(tc.name, sizeof(tc.name), "%s:%d",
+ kbasename(node->parent->full_name), tc.channels[0]);
+ } else {
+ tc.clk[1] = tcb_clk_get(node, tc.channels[1]);
+ if (IS_ERR(tc.clk[1]))
+ goto err_disable_t0;
+
+ err = clk_prepare_enable(tc.clk[1]);
+ if (err) {
+ pr_debug("can't enable T1 clk\n");
+ goto err_clk1;
+ }
+ tc.clksrc.read = tc_get_cycles,
+ tcb_setup_dual_chan(&tc, best_divisor_idx);
+ tc_sched_clock = tc_sched_clock_read;
+ snprintf(tc.name, sizeof(tc.name), "%s:%d,%d",
+ kbasename(node->parent->full_name), tc.channels[0],
+ tc.channels[1]);
+ }
+
+ pr_debug("%s at %d.%03d MHz\n", tc.name,
+ divided_rate / 1000000,
+ ((divided_rate + 500000) % 1000000) / 1000);
+
+ tc.clksrc.name = tc.name;
+ tc.clksrc.suspend = tc_clksrc_suspend;
+ tc.clksrc.resume = tc_clksrc_resume;
+
+ err = clocksource_register_hz(&tc.clksrc, divided_rate);
+ if (err)
+ goto err_disable_t1;
+
+ sched_clock_register(tc_sched_clock, 32, divided_rate);
+
+ tc.registered = true;
+
+ /* Set up and register clockevents */
+ tc.clkevt.name = tc.name;
+ tc.clkevt.cpumask = cpumask_of(0);
+ tc.clkevt.set_next_event = tcb_clkevt_next_event;
+ tc.clkevt.set_state_oneshot = tcb_clkevt_oneshot;
+ tc.clkevt.set_state_shutdown = tcb_clkevt_shutdown;
+ clockevents_config_and_register(&tc.clkevt, divided_rate, 1,
+ BIT(tc.bits) - 1);
+
+ return 0;
+
+err_disable_t1:
+ if (tc.bits == 16)
+ clk_disable_unprepare(tc.clk[1]);
+
+err_clk1:
+ if (tc.bits == 16)
+ clk_put(tc.clk[1]);
+
+err_disable_t0:
+ clk_disable_unprepare(tc.clk[0]);
+
+err_clk:
+ clk_put(tc.clk[0]);
+
+ pr_err("%s: unable to register clocksource/clockevent\n",
+ tc.clksrc.name);
+
+ return err;
+}
+
+static int __init tcb_clksrc_init(struct device_node *node)
+{
+ const struct of_device_id *match;
+ const struct atmel_tcb_info *tcb_info;
+ struct regmap *regmap;
+ void __iomem *tcb_base;
+ u32 channel;
+ int bits, irq, err, chan1 = -1;
+
+ if (tc.registered && tce.registered)
+ return -ENODEV;
+
+ /*
+ * The regmap has to be used to access registers that are shared
+ * between channels on the same TCB but we keep direct IO access for
+ * the counters to avoid the impact on performance
+ */
+ regmap = syscon_node_to_regmap(node->parent);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ tcb_base = of_iomap(node->parent, 0);
+ if (!tcb_base) {
+ pr_err("%s +%d %s\n", __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ match = of_match_node(atmel_tcb_dt_ids, node->parent);
+ tcb_info = match->data;
+ bits = tcb_info->bits;
+
+ err = of_property_read_u32_index(node, "reg", 0, &channel);
+ if (err)
+ return err;
+
+ irq = tcb_irq_get(node, channel);
+ if (irq < 0)
+ return irq;
+
+ if (tc.registered)
+ return tc_clkevt_register(node, regmap, tcb_base, channel, irq,
+ bits);
+
+ if (bits == 16) {
+ of_property_read_u32_index(node, "reg", 1, &chan1);
+ if (chan1 == -1) {
+ if (tce.registered) {
+ pr_err("%s: clocksource needs two channels\n",
+ node->parent->full_name);
+ return -EINVAL;
+ } else {
+ return tc_clkevt_register(node, regmap,
+ tcb_base, channel,
+ irq, bits);
+ }
+ }
+ }
+
+ return tcb_clksrc_register(node, regmap, tcb_base, channel, chan1, irq,
+ bits);
+}
+CLOCKSOURCE_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer",
+ tcb_clksrc_init);
--
2.16.1


2018-03-22 15:35:50

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hi Daniel,

I'd really like to get this in the kernel soon as we have platform that
still can't boot with a mainline kernel because they don't have a PIT.

On 23/02/2018 at 18:15:52 +0100, Alexandre Belloni wrote:
> Hi,
>
> This series gets back on the TCB drivers rework. It introduces a new driver to
> handle the clocksource and clockevent devices.
>
> As a reminder, this is necessary because:
> - the current tcb_clksrc driver is probed too late to be able to be used at
> boot and we now have SoCs that don't have a PIT. They currently are not able
> to boot a mainline kernel.
> - using the PIT doesn't work well with preempt-rt because its interrupt is
> shared (in particular with the UART and their interrupt flags are
> incompatible)
> - the current solution is wasting some TCB channels
>
> The plan is to get this driver upstream, then convert the TCB PWM driver to be
> able to get rid of the tcb_clksrc driver along with atmel_tclib.
>
> changes in v3:
> - use SPDX tag in include/soc/at91/atmel_tcb.h
> - rework option selection to make the clocksource option silent
>
> Main changes in v2:
> - use direct IO instead of regmap when accessing channel specific registers to
> avoid the regmap locking
> - implement suspend/resume
>
>
> Alexandre Belloni (6):
> ARM: at91: add TCB registers definitions
> clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
> clocksource/drivers: atmel-pit: make option silent
> ARM: at91: Implement clocksource selection
> ARM: configs: at91: use new TCB timer driver
> ARM: configs: at91: unselect PIT
>
> arch/arm/configs/at91_dt_defconfig | 2 +-
> arch/arm/configs/sama5_defconfig | 2 +-
> arch/arm/mach-at91/Kconfig | 25 ++
> drivers/clocksource/Kconfig | 13 +-
> drivers/clocksource/Makefile | 3 +-
> drivers/clocksource/timer-atmel-tcb.c | 608 ++++++++++++++++++++++++++++++++++
> include/soc/at91/atmel_tcb.h | 216 ++++++++++++
> 7 files changed, 865 insertions(+), 4 deletions(-)
> create mode 100644 drivers/clocksource/timer-atmel-tcb.c
> create mode 100644 include/soc/at91/atmel_tcb.h
>
> --
> 2.16.1
>

--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-27 10:50:23

by Alexander Dahl

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hello Alexandre,

Am Freitag, 23. Februar 2018, 18:15:52 CEST schrieb Alexandre Belloni:
> - using the PIT doesn't work well with preempt-rt because its interrupt is
> shared (in particular with the UART and their interrupt flags are
> incompatible)

This is actually quite annoying when using the UART as a serial console,
producing garbage when you type too fast. That's why I tested this patch
series (on top of v4.16-rc7). Target is a at91sam9g20 based board, quite close
to, but not actually an at91sam9g20-ek.

First test was with the previous kernel config updated by oldconfig, so
basically still using the PIT:

+CONFIG_ATMEL_CLOCKSOURCE_PIT=y

On the serial console with v4.16 and the patch set without switching to the
new options, I see this on boot (no surprise, this is what it also prints with
v4.9):

clocksource: pit: mask: 0xfffffff max_cycles: 0xfffffff, max_idle_ns:
14468723050 ns

clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns:
19112604462750000 ns

clocksource: tcb_clksrc: mask: 0xffffffff max_cycles: 0xffffffff,
max_idle_ns: 115749784805 ns

clocksource: Switched to clocksource tcb_clksrc

Interrupts here:

$ cat /proc/interrupts
CPU0
16: 4802 atmel-aic 1 Level at91_tick, rtc0, ttyS0
19: 24041 atmel-aic 19 Level tc_clkevt
26: 124 atmel-aic 21 Level eth0
28: 1 GPIO 11 Edge reset
Err: 0

Second test with tcb block added to dts like in
32ce250b0af3b6971fc746445fce87861a9f5628 and with changed kernel config
according to defconfig changes in patch 5 and 6:

+# CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
+CONFIG_ATMEL_CLOCKSOURCE_TCB=y
-CONFIG_ATMEL_TCLIB=y
-CONFIG_ATMEL_TCB_CLKSRC=y
-CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
+# CONFIG_ATMEL_TCLIB is not set
-CONFIG_ATMEL_PIT=y
+CONFIG_ATMEL_ARM_TCB_CLKSRC=y
-CONFIG_PWM_ATMEL_TCB=m

The output on the serial console contains this:

clocksource: timer@fffa0000:0,1: mask: 0xffffffff max_cycles: 0xffffffff,
max_idle_ns: 115749784805 ns

clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns:
19112604462750000 ns

clocksource: Switched to clocksource timer@fffa0000:0,1

Interrupts here:

$ cat /proc/interrupts
CPU0
17: 5727291 atmel-aic 19 Level timer@fffa0000:2
18: 5523 atmel-aic 1 Level rtc0, ttyS0
26: 5031 atmel-aic 21 Level eth0
28: 1 GPIO 11 Edge reset
Err: 0

So, the board boots and runs like usual. I don't know if that qualifies for a
Tested-by? Hope that helps.

Greets
Alex


2018-03-27 11:32:51

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 27/03/2018 12:41, Alexander Dahl wrote:
> Hello Alexandre,
>
> Am Freitag, 23. Februar 2018, 18:15:52 CEST schrieb Alexandre Belloni:
>> - using the PIT doesn't work well with preempt-rt because its interrupt is
>> shared (in particular with the UART and their interrupt flags are
>> incompatible)
>
> This is actually quite annoying when using the UART as a serial console,
> producing garbage when you type too fast. That's why I tested this patch
> series (on top of v4.16-rc7). Target is a at91sam9g20 based board, quite close
> to, but not actually an at91sam9g20-ek.
>
> First test was with the previous kernel config updated by oldconfig, so
> basically still using the PIT:
>
> +CONFIG_ATMEL_CLOCKSOURCE_PIT=y
>
> On the serial console with v4.16 and the patch set without switching to the
> new options, I see this on boot (no surprise, this is what it also prints with
> v4.9):
>
> clocksource: pit: mask: 0xfffffff max_cycles: 0xfffffff, max_idle_ns:
> 14468723050 ns
> …
> clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns:
> 19112604462750000 ns
> …
> clocksource: tcb_clksrc: mask: 0xffffffff max_cycles: 0xffffffff,
> max_idle_ns: 115749784805 ns
> …
> clocksource: Switched to clocksource tcb_clksrc
>
> Interrupts here:
>
> $ cat /proc/interrupts
> CPU0
> 16: 4802 atmel-aic 1 Level at91_tick, rtc0, ttyS0
> 19: 24041 atmel-aic 19 Level tc_clkevt
> 26: 124 atmel-aic 21 Level eth0
> 28: 1 GPIO 11 Edge reset
> Err: 0
>
> Second test with tcb block added to dts like in
> 32ce250b0af3b6971fc746445fce87861a9f5628 and with changed kernel config
> according to defconfig changes in patch 5 and 6:
>
> +# CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
> +CONFIG_ATMEL_CLOCKSOURCE_TCB=y
> -CONFIG_ATMEL_TCLIB=y
> -CONFIG_ATMEL_TCB_CLKSRC=y
> -CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
> +# CONFIG_ATMEL_TCLIB is not set
> -CONFIG_ATMEL_PIT=y
> +CONFIG_ATMEL_ARM_TCB_CLKSRC=y
> -CONFIG_PWM_ATMEL_TCB=m
>
> The output on the serial console contains this:
>
> clocksource: timer@fffa0000:0,1: mask: 0xffffffff max_cycles: 0xffffffff,
> max_idle_ns: 115749784805 ns
> …
> clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns:
> 19112604462750000 ns
> …
> clocksource: Switched to clocksource timer@fffa0000:0,1
>
> Interrupts here:
>
> $ cat /proc/interrupts
> CPU0
> 17: 5727291 atmel-aic 19 Level timer@fffa0000:2
> 18: 5523 atmel-aic 1 Level rtc0, ttyS0
> 26: 5031 atmel-aic 21 Level eth0
> 28: 1 GPIO 11 Edge reset
> Err: 0
>
> So, the board boots and runs like usual. I don't know if that qualifies for a
> Tested-by? Hope that helps.

Can you can give a rough amount for the irq rate on the timer ?




--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2018-03-28 10:31:27

by Alexander Dahl

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hello Daniel,

Am Dienstag, 27. M?rz 2018, 13:30:22 CEST schrieb Daniel Lezcano:
> Can you can give a rough amount for the irq rate on the timer ?

I used itop [1] now to get a rough estimate. First with kernel v4.14.29-rt25
(fully preempt RT):

INT NAME RATE MAX
19 [ vel tc_clkevt] 397 Ints/s (max: 432)
26 [ vel eth0] 4 Ints/s (max: 38)

Next test with kernel v4.15.13 gives (slightly slower, but non-RT):

INT NAME RATE MAX
19 [ vel tc_clkevt] 248 Ints/s (max: 273)
26 [ vel eth0] 4 Ints/s (max: 11)

With kernel v4.16-rc7 plus this patch series and tcb as clocksource:

INT NAME RATE MAX
17 [vel timer@fffa] 2164 Ints/s (max: 2183)
26 [ vel eth0] 5 Ints/s (max: 10)

Is this the information you wanted? If not, could you point me on how to get
the requested irq rate?

Greets
Alex

[1] https://packages.debian.org/stretch/itop

2018-03-28 13:06:32

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 28/03/2018 12:29, Alexander Dahl wrote:
> Hello Daniel,
>
> Am Dienstag, 27. März 2018, 13:30:22 CEST schrieb Daniel Lezcano:
>> Can you can give a rough amount for the irq rate on the timer ?
>
> I used itop [1] now to get a rough estimate. First with kernel v4.14.29-rt25
> (fully preempt RT):
>
> INT NAME RATE MAX
> 19 [ vel tc_clkevt] 397 Ints/s (max: 432)
> 26 [ vel eth0] 4 Ints/s (max: 38)
>
> Next test with kernel v4.15.13 gives (slightly slower, but non-RT):
>
> INT NAME RATE MAX
> 19 [ vel tc_clkevt] 248 Ints/s (max: 273)
> 26 [ vel eth0] 4 Ints/s (max: 11)
>
> With kernel v4.16-rc7 plus this patch series and tcb as clocksource:
>
> INT NAME RATE MAX
> 17 [vel timer@fffa] 2164 Ints/s (max: 2183)
> 26 [ vel eth0] 5 Ints/s (max: 10)
>
> Is this the information you wanted? If not, could you point me on how to get
> the requested irq rate?

It is perfect. Thanks!

It confirms what I was worried about: the clocksource wraps up too
quickly thus raising an interrupt every 400us. That is why I asked
Alexande about a prescalar register.



--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2018-03-28 14:18:35

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 28/03/2018 at 15:03:11 +0200, Daniel Lezcano wrote:
> On 28/03/2018 12:29, Alexander Dahl wrote:
> > Hello Daniel,
> >
> > Am Dienstag, 27. M?rz 2018, 13:30:22 CEST schrieb Daniel Lezcano:
> >> Can you can give a rough amount for the irq rate on the timer ?
> >
> > I used itop [1] now to get a rough estimate. First with kernel v4.14.29-rt25
> > (fully preempt RT):
> >
> > INT NAME RATE MAX
> > 19 [ vel tc_clkevt] 397 Ints/s (max: 432)
> > 26 [ vel eth0] 4 Ints/s (max: 38)
> >
> > Next test with kernel v4.15.13 gives (slightly slower, but non-RT):
> >
> > INT NAME RATE MAX
> > 19 [ vel tc_clkevt] 248 Ints/s (max: 273)
> > 26 [ vel eth0] 4 Ints/s (max: 11)
> >
> > With kernel v4.16-rc7 plus this patch series and tcb as clocksource:
> >
> > INT NAME RATE MAX
> > 17 [vel timer@fffa] 2164 Ints/s (max: 2183)
> > 26 [ vel eth0] 5 Ints/s (max: 10)
> >
> > Is this the information you wanted? If not, could you point me on how to get
> > the requested irq rate?
>
> It is perfect. Thanks!
>
> It confirms what I was worried about: the clocksource wraps up too
> quickly thus raising an interrupt every 400us. That is why I asked
> Alexande about a prescalar register.
>

The code should behave exactly the same between the previous and the new
driver. The interrupt is not coming from the clocksource but from the
clockevent and it is already on the slowest clock, the 32kHz one.


--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-28 14:37:51

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 28/03/2018 16:16, Alexandre Belloni wrote:
> On 28/03/2018 at 15:03:11 +0200, Daniel Lezcano wrote:
>> On 28/03/2018 12:29, Alexander Dahl wrote:
>>> Hello Daniel,
>>>
>>> Am Dienstag, 27. März 2018, 13:30:22 CEST schrieb Daniel Lezcano:
>>>> Can you can give a rough amount for the irq rate on the timer ?
>>>
>>> I used itop [1] now to get a rough estimate. First with kernel v4.14.29-rt25
>>> (fully preempt RT):
>>>
>>> INT NAME RATE MAX
>>> 19 [ vel tc_clkevt] 397 Ints/s (max: 432)
>>> 26 [ vel eth0] 4 Ints/s (max: 38)
>>>
>>> Next test with kernel v4.15.13 gives (slightly slower, but non-RT):
>>>
>>> INT NAME RATE MAX
>>> 19 [ vel tc_clkevt] 248 Ints/s (max: 273)
>>> 26 [ vel eth0] 4 Ints/s (max: 11)
>>>
>>> With kernel v4.16-rc7 plus this patch series and tcb as clocksource:
>>>
>>> INT NAME RATE MAX
>>> 17 [vel timer@fffa] 2164 Ints/s (max: 2183)
>>> 26 [ vel eth0] 5 Ints/s (max: 10)
>>>
>>> Is this the information you wanted? If not, could you point me on how to get
>>> the requested irq rate?
>>
>> It is perfect. Thanks!
>>
>> It confirms what I was worried about: the clocksource wraps up too
>> quickly thus raising an interrupt every 400us. That is why I asked
>> Alexande about a prescalar register.
>>
>
> The code should behave exactly the same between the previous and the new
> driver. The interrupt is not coming from the clocksource but from the
> clockevent and it is already on the slowest clock, the 32kHz one.

Do you have an explanation of why the rate is much higher ?


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2018-03-28 15:33:49

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 28/03/2018 at 16:36:34 +0200, Daniel Lezcano wrote:
> On 28/03/2018 16:16, Alexandre Belloni wrote:
> > On 28/03/2018 at 15:03:11 +0200, Daniel Lezcano wrote:
> >> On 28/03/2018 12:29, Alexander Dahl wrote:
> >>> Hello Daniel,
> >>>
> >>> Am Dienstag, 27. M?rz 2018, 13:30:22 CEST schrieb Daniel Lezcano:
> >>>> Can you can give a rough amount for the irq rate on the timer ?
> >>>
> >>> I used itop [1] now to get a rough estimate. First with kernel v4.14.29-rt25
> >>> (fully preempt RT):
> >>>
> >>> INT NAME RATE MAX
> >>> 19 [ vel tc_clkevt] 397 Ints/s (max: 432)
> >>> 26 [ vel eth0] 4 Ints/s (max: 38)
> >>>
> >>> Next test with kernel v4.15.13 gives (slightly slower, but non-RT):
> >>>
> >>> INT NAME RATE MAX
> >>> 19 [ vel tc_clkevt] 248 Ints/s (max: 273)
> >>> 26 [ vel eth0] 4 Ints/s (max: 11)
> >>>
> >>> With kernel v4.16-rc7 plus this patch series and tcb as clocksource:
> >>>
> >>> INT NAME RATE MAX
> >>> 17 [vel timer@fffa] 2164 Ints/s (max: 2183)
> >>> 26 [ vel eth0] 5 Ints/s (max: 10)
> >>>
> >>> Is this the information you wanted? If not, could you point me on how to get
> >>> the requested irq rate?
> >>
> >> It is perfect. Thanks!
> >>
> >> It confirms what I was worried about: the clocksource wraps up too
> >> quickly thus raising an interrupt every 400us. That is why I asked
> >> Alexande about a prescalar register.
> >>
> >
> > The code should behave exactly the same between the previous and the new
> > driver. The interrupt is not coming from the clocksource but from the
> > clockevent and it is already on the slowest clock, the 32kHz one.
>
> Do you have an explanation of why the rate is much higher ?
>

The core is giving deltas of 31 clocks instead of much more than that, I
guess I messed up the initialization somewhere.


--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-28 15:52:01

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 28/03/2018 at 17:31:35 +0200, Alexandre Belloni wrote:
> > Do you have an explanation of why the rate is much higher ?
> >
>
> The core is giving deltas of 31 clocks instead of much more than that, I
> guess I messed up the initialization somewhere.
>

I did mess up.

Alexander, can you test that:

diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
index 7fde9cfbf203..bbbacf8c46b0 100644
--- a/drivers/clocksource/timer-atmel-tcb.c
+++ b/drivers/clocksource/timer-atmel-tcb.c
@@ -222,7 +222,7 @@ static int __init tc_clkevt_register(struct device_node *node,
goto err_slow;
clk_disable(tce.clk);

- clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
+ clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);

ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
tce.clkevt.name, &tce);

This will behave exactly the same as before on 16bits TCB and will have
much less interrupts on 32 bits platforms.

--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-29 08:02:45

by Alexander Dahl

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hei hei,

Am Mittwoch, 28. M?rz 2018, 17:50:33 CEST schrieb Alexandre Belloni:
> On 28/03/2018 at 17:31:35 +0200, Alexandre Belloni wrote:
> > > Do you have an explanation of why the rate is much higher ?
> >
> > The core is giving deltas of 31 clocks instead of much more than that, I
> > guess I messed up the initialization somewhere.
>
> I did mess up.
>
> Alexander, can you test that:

Well, I just did.

>
> diff --git a/drivers/clocksource/timer-atmel-tcb.c
> b/drivers/clocksource/timer-atmel-tcb.c index 7fde9cfbf203..bbbacf8c46b0
> 100644
> --- a/drivers/clocksource/timer-atmel-tcb.c
> +++ b/drivers/clocksource/timer-atmel-tcb.c
> @@ -222,7 +222,7 @@ static int __init tc_clkevt_register(struct device_node
> *node, goto err_slow;
> clk_disable(tce.clk);
>
> - clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
> + clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);
>
> ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
> tce.clkevt.name, &tce);
>
> This will behave exactly the same as before on 16bits TCB and will have
> much less interrupts on 32 bits platforms.

This is the result:

INT NAME RATE MAX
17 [vel timer@fffa] 1837 Ints/s (max: 1912)
26 [ vel eth0] 3 Ints/s (max: 11)

This is not much lower than the ~2150 I reported yesterday?

I'm sorry I can just test this on at91sam9g20 currently, I have no
understanding of the subsystem, I can't do a decent review.

Greets
Alex


2018-03-29 10:47:01

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 29/03/2018 at 10:01:26 +0200, Alexander Dahl wrote:
> Hei hei,
>
> Am Mittwoch, 28. M?rz 2018, 17:50:33 CEST schrieb Alexandre Belloni:
> > On 28/03/2018 at 17:31:35 +0200, Alexandre Belloni wrote:
> > > > Do you have an explanation of why the rate is much higher ?
> > >
> > > The core is giving deltas of 31 clocks instead of much more than that, I
> > > guess I messed up the initialization somewhere.
> >
> > I did mess up.
> >
> > Alexander, can you test that:
>
> Well, I just did.
>
> >
> > diff --git a/drivers/clocksource/timer-atmel-tcb.c
> > b/drivers/clocksource/timer-atmel-tcb.c index 7fde9cfbf203..bbbacf8c46b0
> > 100644
> > --- a/drivers/clocksource/timer-atmel-tcb.c
> > +++ b/drivers/clocksource/timer-atmel-tcb.c
> > @@ -222,7 +222,7 @@ static int __init tc_clkevt_register(struct device_node
> > *node, goto err_slow;
> > clk_disable(tce.clk);
> >
> > - clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
> > + clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);
> >
> > ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
> > tce.clkevt.name, &tce);
> >
> > This will behave exactly the same as before on 16bits TCB and will have
> > much less interrupts on 32 bits platforms.
>
> This is the result:
>
> INT NAME RATE MAX
> 17 [vel timer@fffa] 1837 Ints/s (max: 1912)
> 26 [ vel eth0] 3 Ints/s (max: 11)
>
> This is not much lower than the ~2150 I reported yesterday?
>
> I'm sorry I can just test this on at91sam9g20 currently, I have no
> understanding of the subsystem, I can't do a decent review.

Hum, are you sure, I went from:

INT NAME RATE MAX
16 [evel timer@fc0] 1027 Ints/s (max: 1028)
21 [ evel at_xdmac] 3 Ints/s (max: 3)
30 [ evel ttyS0] 2 Ints/s (max: 2)

to:

INT NAME RATE MAX
16 [evel timer@fc0] 6 Ints/s (max: 9)
21 [ evel at_xdmac] 2 Ints/s (max: 2)
30 [ evel ttyS0] 2 Ints/s (max: 2)



--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-29 11:32:26

by Alexander Dahl

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hello Alexandre,

Am Donnerstag, 29. M?rz 2018, 12:45:42 CEST schrieb Alexandre Belloni:
> > This is the result:
> >
> > INT NAME RATE MAX
> >
> > 17 [vel timer@fffa] 1837 Ints/s (max: 1912)
> > 26 [ vel eth0] 3 Ints/s (max: 11)
> >
> > This is not much lower than the ~2150 I reported yesterday?
> >
> > I'm sorry I can just test this on at91sam9g20 currently, I have no
> > understanding of the subsystem, I can't do a decent review.
>
> Hum, are you sure, I went from:
>
> INT NAME RATE MAX
> 16 [evel timer@fc0] 1027 Ints/s (max: 1028)
> 21 [ evel at_xdmac] 3 Ints/s (max: 3)
> 30 [ evel ttyS0] 2 Ints/s (max: 2)
>
> to:
>
> INT NAME RATE MAX
> 16 [evel timer@fc0] 6 Ints/s (max: 9)
> 21 [ evel at_xdmac] 2 Ints/s (max: 2)
> 30 [ evel ttyS0] 2 Ints/s (max: 2)

Pretty sure. I rebuilt the whole BSP and added another line to the kernel
source to see if the tree I applied the patches to, was actually built:


diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
index 7fde9cfbf203..f85affc74a86 100644
--- a/drivers/clocksource/timer-atmel-tcb.c
+++ b/drivers/clocksource/timer-atmel-tcb.c
@@ -222,7 +222,8 @@ static int __init tc_clkevt_register(struct device_node *node,
goto err_slow;
clk_disable(tce.clk);

- clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
+ pr_info( "*** bits: 0x%x, BIT(bits): 0x%lx\n", bits, BIT(bits) );
+ clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);

ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
tce.clkevt.name, &tce);


See the more complete serial console output (including the additional
message):


Starting kernel ...

** 15 printk messages dropped **
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 28624K/32768K available (2934K kernel code, 113K rwdata, 436K rodata, 144K init, 73K bss, 4144K reserved, 0K cma-reserved)
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
vmalloc : 0xc2800000 - 0xff800000 ( 976 MB)
lowmem : 0xc0000000 - 0xc2000000 ( 32 MB)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.text : 0x(ptrval) - 0x(ptrval) (2936 kB)
.init : 0x(ptrval) - 0x(ptrval) ( 144 kB)
.data : 0x(ptrval) - 0x(ptrval) ( 114 kB)
.bss : 0x(ptrval) - 0x(ptrval) ( 74 kB)
SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
Preemptible hierarchical RCU implementation.
Tasks RCU enabled.
NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
clocksource: timer@fffa0000:0,1: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 115749784805 ns
sched_clock: 32 bits at 16MHz, resolution 60ns, wraps every 130055938017ns
*** bits: 0x10, BIT(bits): 0x10000
Console: colour dummy device 80x30
Calibrating delay loop... 197.01 BogoMIPS (lpj=985088)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
Setting up static identity map for 0x20008400 - 0x2000843c
Hierarchical SRCU implementation.
devtmpfs: initialized
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: -1, 3072 bytes)
pinctrl core: initialized pinctrl subsystem
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
gpio-at91 fffff400.gpio: at address 82332323
gpio-at91 fffff600.gpio: at address a4bf53d9
gpio-at91 fffff800.gpio: at address 31e4ab7c
pinctrl-at91 ahb:apb:pinctrl@fffff400: initialized AT91 pinctrl driver
AT91: Detected SoC: at91sam9g20, revision 1
clocksource: Switched to clocksource timer@fffa0000:0,1
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 0, 4096 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
workingset: timestamp_bits=30 max_order=13 bucket_order=0
io scheduler noop registered (default)
fffff200.serial: ttyS0 at MMIO 0xfffff200 (irq = 18, base_baud = 8256000) is a ATMEL_SERIAL
console [ttyS0] enabled
rtc rtc0: invalid alarm value: 1900-1-29 0:0:0
rtc-at91sam9 fffffd20.rtc: rtc core: registered fffffd20.rtc as rtc0
nand: device found, Manufacturer ID: 0x01, Chip ID: 0xda
nand: AMD/Spansion S34ML02G1
nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64


Greets
Alex


2018-03-29 11:45:30

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 29/03/2018 at 13:31:18 +0200, Alexander Dahl wrote:
> Pretty sure. I rebuilt the whole BSP and added another line to the kernel
> source to see if the tree I applied the patches to, was actually built:
>
>
> diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
> index 7fde9cfbf203..f85affc74a86 100644
> --- a/drivers/clocksource/timer-atmel-tcb.c
> +++ b/drivers/clocksource/timer-atmel-tcb.c
> @@ -222,7 +222,8 @@ static int __init tc_clkevt_register(struct device_node *node,
> goto err_slow;
> clk_disable(tce.clk);
>
> - clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
> + pr_info( "*** bits: 0x%x, BIT(bits): 0x%lx\n", bits, BIT(bits) );
> + clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);
>
> ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
> tce.clkevt.name, &tce);
>
>

I've just tested on a g20, old driver:
INT NAME RATE MAX
16 [vel at91_tick,] 175 Ints/s (max: 231)
19 [ vel tc_clkevt] 129 Ints/s (max: 129)


new driver:
INT NAME RATE MAX
17 [vel timer@fffa] 129 Ints/s (max: 129)
18 [ vel ttyS0] 175 Ints/s (max: 231)


--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-29 12:09:16

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 29/03/2018 13:42, Alexandre Belloni wrote:
> On 29/03/2018 at 13:31:18 +0200, Alexander Dahl wrote:
>> Pretty sure. I rebuilt the whole BSP and added another line to the kernel
>> source to see if the tree I applied the patches to, was actually built:
>>
>>
>> diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
>> index 7fde9cfbf203..f85affc74a86 100644
>> --- a/drivers/clocksource/timer-atmel-tcb.c
>> +++ b/drivers/clocksource/timer-atmel-tcb.c
>> @@ -222,7 +222,8 @@ static int __init tc_clkevt_register(struct device_node *node,
>> goto err_slow;
>> clk_disable(tce.clk);
>>
>> - clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
>> + pr_info( "*** bits: 0x%x, BIT(bits): 0x%lx\n", bits, BIT(bits) );
>> + clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);
>>
>> ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
>> tce.clkevt.name, &tce);
>>
>>
>
> I've just tested on a g20, old driver:
> INT NAME RATE MAX
> 16 [vel at91_tick,] 175 Ints/s (max: 231)
> 19 [ vel tc_clkevt] 129 Ints/s (max: 129)
>
>
> new driver:
> INT NAME RATE MAX
> 17 [vel timer@fffa] 129 Ints/s (max: 129)
> 18 [ vel ttyS0] 175 Ints/s (max: 231)

Can you give in both platforms how fast the clocksource wraps up ?

--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


2018-03-29 13:04:44

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

On 29/03/2018 at 14:07:34 +0200, Daniel Lezcano wrote:
> On 29/03/2018 13:42, Alexandre Belloni wrote:
> > On 29/03/2018 at 13:31:18 +0200, Alexander Dahl wrote:
> >> Pretty sure. I rebuilt the whole BSP and added another line to the kernel
> >> source to see if the tree I applied the patches to, was actually built:
> >>
> >>
> >> diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
> >> index 7fde9cfbf203..f85affc74a86 100644
> >> --- a/drivers/clocksource/timer-atmel-tcb.c
> >> +++ b/drivers/clocksource/timer-atmel-tcb.c
> >> @@ -222,7 +222,8 @@ static int __init tc_clkevt_register(struct device_node *node,
> >> goto err_slow;
> >> clk_disable(tce.clk);
> >>
> >> - clockevents_config_and_register(&tce.clkevt, 32768, 1, bits - 1);
> >> + pr_info( "*** bits: 0x%x, BIT(bits): 0x%lx\n", bits, BIT(bits) );
> >> + clockevents_config_and_register(&tce.clkevt, 32768, 1, BIT(bits) - 1);
> >>
> >> ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED,
> >> tce.clkevt.name, &tce);
> >>
> >>
> >
> > I've just tested on a g20, old driver:
> > INT NAME RATE MAX
> > 16 [vel at91_tick,] 175 Ints/s (max: 231)
> > 19 [ vel tc_clkevt] 129 Ints/s (max: 129)
> >
> >
> > new driver:
> > INT NAME RATE MAX
> > 17 [vel timer@fffa] 129 Ints/s (max: 129)
> > 18 [ vel ttyS0] 175 Ints/s (max: 231)
>
> Can you give in both platforms how fast the clocksource wraps up ?
>

sam9g20, old:
clocksource: pit: mask: 0xfffffff max_cycles: 0xfffffff, max_idle_ns: 14370379250 ns
sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 21474836475000000ns
clocksource: tcb_clksrc: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 114963034405 ns

sam9g20, new:
clocksource: timer@fffa0000:0,1: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 114963034405 ns
sched_clock: 32 bits at 16MHz, resolution 60ns, wraps every 129171948513ns

sama5d4, old:
clocksource: pit: mask: 0x7ffffff max_cycles: 0x7ffffff, max_idle_ns: 10859434279 ns
sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 21474836475000000ns
clocksource: tcb_clksrc: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 173750949719 ns

sama5d4, new:
clocksource: timer@fc024000:0: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 173750949719 ns
sched_clock: 32 bits at 11MHz, resolution 90ns, wraps every 195225786322ns

--
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

2018-03-29 15:13:08

by Alexander Dahl

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] clocksource: rework Atmel TCB timer driver

Hei hei,

Am Donnerstag, 29. März 2018, 10:01:26 CEST schrieb Alexander Dahl:
> This is the result:
>
> INT NAME RATE MAX
> 17 [vel timer@fffa] 1837 Ints/s (max: 1912)
> 26 [ vel eth0] 3 Ints/s (max: 11)

Above was with v4.16-rc7+, and CONFIG_ATMEL_CLOCKSOURCE_TCB=y,
CONFIG_ATMEL_TCLIB=n (as one might see in the name column).

Now, v4.16-rc7+, with CONFIG_ATMEL_CLOCKSOURCE_PIT=y, CONFIG_ATMEL_TCLIB=y,
CONFIG_ATMEL_TCB_CLKSRC=y (old driver I guess):

INT NAME RATE MAX
19 [ vel tc_clkevt] 1898 Ints/s (max: 1945)
26 [ vel eth0] 3 Ints/s (max: 11)

So the rates here are roughly the same with old and new driver and the same
kernel source. As Alexandre stated in IRC, the rates should be the same with
old and new driver on the otherwise same kernel source.

I just double checked it, and with the other clocksource on v4.16-rc7+ I get:

INT NAME RATE MAX
17 [vel timer@fffa] 1904 Ints/s (max: 1922)
26 [ vel eth0] 6 Ints/s (max: 7)

The lower rates I reported yesterday were from older kernels v4.14.29-rt25 and
v4.15.13, so there might be the question why v4.16-rc7+ has a much higher rate
with tc_clkevt? But there's no real difference between tc_clkevt and
timer@fffa… when both measured with v4.16-rc7+ on this target. I know
Alexandre has lower rates though, may depend on other parameters.

So for completeness, I just tested the clean v4.16-rc7 without this patch
series:

INT NAME RATE MAX
19 [ vel tc_clkevt] 1903 Ints/s (max: 1930)
26 [ vel eth0] 7 Ints/s (max: 7)

Basically the same for this one, too.

HTH & Greets
Alex