linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver
@ 2018-09-13 11:30 Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 1/7] ARM: at91: add TCB registers definitions Alexandre Belloni
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

Hi,

This series reworks the Atmel TCB drivers. It introduces a new driver to handle
the clocksource and clockevent devices.

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 now that AVR32
is gone.

changes in v7:
 - fixed a warning when building on 64 bit platforms

changes in v6:
 - rebased on v4.19-rc1
 - separated the clocksource/clockevent and the single clockevent in two
   different patches
 - removed struct tc_clkevt_device and simply use struct atmel_tcb_clksrc
 - removed struct atmel_tcb_info
 - moved tcb_clk_get and tcb_irq_get to users

changes in v5:
 - rebased on v4.18-rc1
 - fixed the clock enabling/disabling in atomic context under preempt-rt

Changes in v4:
 - rebased on top of v4.17-rc1
 - fixed an issue when setting max_delta for clockevents_config_and_register

Alexandre Belloni (7):
  ARM: at91: add TCB registers definitions
  clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
  clocksource/drivers: timer-atmel-tcb: add clockevent device on
    separate channel
  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 | 617 ++++++++++++++++++++++++++
 include/soc/at91/atmel_tcb.h          | 183 ++++++++
 7 files changed, 841 insertions(+), 4 deletions(-)
 create mode 100644 drivers/clocksource/timer-atmel-tcb.c
 create mode 100644 include/soc/at91/atmel_tcb.h

-- 
2.19.0


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v7 1/7] ARM: at91: add TCB registers definitions
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks Alexandre Belloni
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

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

Tested-by: Alexander Dahl <ada@thorsis.com>
Tested-by: Andras Szemzo <szemzo.andras@gmail.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
 include/soc/at91/atmel_tcb.h | 183 +++++++++++++++++++++++++++++++++++
 1 file changed, 183 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..657e234b1483
--- /dev/null
+++ b/include/soc/at91/atmel_tcb.h
@@ -0,0 +1,183 @@
+//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 const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, };
+
+static const struct of_device_id atmel_tcb_dt_ids[] = {
+	{
+		.compatible = "atmel,at91rm9200-tcb",
+		.data = (void *)16,
+	}, {
+		.compatible = "atmel,at91sam9x5-tcb",
+		.data = (void *)32,
+	}, {
+		/* sentinel */
+	}
+};
+
+#endif /* __SOC_ATMEL_TCB_H */
-- 
2.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 1/7] ARM: at91: add TCB registers definitions Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-24  1:59   ` Daniel Lezcano
  2018-09-13 11:30 ` [PATCH v7 3/7] clocksource/drivers: timer-atmel-tcb: add clockevent device on separate channel Alexandre Belloni
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

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

Tested-by: Alexander Dahl <ada@thorsis.com>
Tested-by: Andras Szemzo <szemzo.andras@gmail.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
 drivers/clocksource/Kconfig           |   8 +
 drivers/clocksource/Makefile          |   3 +-
 drivers/clocksource/timer-atmel-tcb.c | 410 ++++++++++++++++++++++++++
 3 files changed, 420 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clocksource/timer-atmel-tcb.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index a11f4ba98b05..8c7324e409f6 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -403,6 +403,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_EXYNOS_MCT
 	bool "Exynos multi core timer driver" if COMPILE_TEST
 	depends on ARM || ARM64
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index db51b2427e8a..0df9384a1230 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..21fbe430f91b
--- /dev/null
+++ b/drivers/clocksource/timer-atmel-tcb.c
@@ -0,0 +1,410 @@
+// 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>
+
+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;
+	bool clk_enabled;
+};
+
+static struct atmel_tcb_clksrc tc;
+
+static 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");
+}
+
+/*
+ * 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;
+	tc.clksrc.rating = 200;
+	tc.clksrc.mask = CLOCKSOURCE_MASK(32);
+	tc.clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+	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;
+	tc.clkevt.features = CLOCK_EVT_FEAT_ONESHOT;
+	tc.clkevt.rating = 125;
+
+	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;
+	struct regmap *regmap;
+	void __iomem *tcb_base;
+	u32 channel;
+	int irq, err, chan1 = -1;
+	unsigned bits;
+
+	if (tc.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);
+	bits = (uintptr_t)match->data;
+
+	err = of_property_read_u32_index(node, "reg", 0, &channel);
+	if (err)
+		return err;
+
+	irq = of_irq_get(node->parent, channel);
+	if (irq < 0) {
+		irq = of_irq_get(node->parent, 0);
+		if (irq < 0)
+			return irq;
+	}
+
+	if (bits == 16) {
+		of_property_read_u32_index(node, "reg", 1, &chan1);
+		if (chan1 == -1) {
+			pr_err("%s: clocksource needs two channels\n",
+			       node->parent->full_name);
+			return -EINVAL;
+		}
+	}
+
+	return tcb_clksrc_register(node, regmap, tcb_base, channel, chan1, irq,
+				   bits);
+}
+TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init);
-- 
2.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v7 3/7] clocksource/drivers: timer-atmel-tcb: add clockevent device on separate channel
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 1/7] ARM: at91: add TCB registers definitions Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 4/7] clocksource/drivers: atmel-pit: make option silent Alexandre Belloni
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

Add an other clockevent device that uses a separate TCB channel when
available.

Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
 drivers/clocksource/timer-atmel-tcb.c | 217 +++++++++++++++++++++++++-
 1 file changed, 212 insertions(+), 5 deletions(-)

diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c
index 21fbe430f91b..63ce3b69338a 100644
--- a/drivers/clocksource/timer-atmel-tcb.c
+++ b/drivers/clocksource/timer-atmel-tcb.c
@@ -32,7 +32,7 @@ struct atmel_tcb_clksrc {
 	bool clk_enabled;
 };
 
-static struct atmel_tcb_clksrc tc;
+static struct atmel_tcb_clksrc tc, tce;
 
 static struct clk *tcb_clk_get(struct device_node *node, int channel)
 {
@@ -47,6 +47,203 @@ static struct clk *tcb_clk_get(struct device_node *node, int channel)
 	return of_clk_get_by_name(node->parent, "t0_clk");
 }
 
+/*
+ * Clockevent device using its own channel
+ */
+
+static void tc_clkevt2_clk_disable(struct clock_event_device *d)
+{
+	clk_disable(tce.clk[0]);
+	tce.clk_enabled = false;
+}
+
+static void tc_clkevt2_clk_enable(struct clock_event_device *d)
+{
+	if (tce.clk_enabled)
+		return;
+	clk_enable(tce.clk[0]);
+	tce.clk_enabled = true;
+}
+
+static int tc_clkevt2_stop(struct clock_event_device *d)
+{
+	writel(0xff, tce.base + ATMEL_TC_IDR(tce.channels[0]));
+	writel(ATMEL_TC_CCR_CLKDIS, tce.base + ATMEL_TC_CCR(tce.channels[0]));
+
+	return 0;
+}
+
+static int tc_clkevt2_shutdown(struct clock_event_device *d)
+{
+	tc_clkevt2_stop(d);
+	if (!clockevent_state_detached(d))
+		tc_clkevt2_clk_disable(d);
+
+	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_stop(d);
+
+	tc_clkevt2_clk_enable(d);
+
+	/* 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.channels[0]));
+	writel(ATMEL_TC_CPCS, tce.base + ATMEL_TC_IER(tce.channels[0]));
+
+	return 0;
+}
+
+static int tc_clkevt2_set_periodic(struct clock_event_device *d)
+{
+	if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
+		tc_clkevt2_stop(d);
+
+	/* By not making the gentime core emulate periodic mode on top
+	 * of oneshot, we get lower overhead and improved accuracy.
+	 */
+	tc_clkevt2_clk_enable(d);
+
+	/* 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.channels[0]));
+	writel((32768 + HZ / 2) / HZ, tce.base + ATMEL_TC_RC(tce.channels[0]));
+
+	/* Enable clock and interrupts on RC compare */
+	writel(ATMEL_TC_CPCS, tce.base + ATMEL_TC_IER(tce.channels[0]));
+	writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG,
+	       tce.base + ATMEL_TC_CCR(tce.channels[0]));
+
+	return 0;
+}
+
+static int tc_clkevt2_next_event(unsigned long delta,
+				 struct clock_event_device *d)
+{
+	writel(delta, tce.base + ATMEL_TC_RC(tce.channels[0]));
+	writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG,
+	       tce.base + ATMEL_TC_CCR(tce.channels[0]));
+
+	return 0;
+}
+
+static irqreturn_t tc_clkevt2_irq(int irq, void *handle)
+{
+	unsigned int sr;
+
+	sr = readl(tce.base + ATMEL_TC_SR(tce.channels[0]));
+	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[0].cmr = readl(tce.base + ATMEL_TC_CMR(tce.channels[0]));
+	tce.cache[0].imr = readl(tce.base + ATMEL_TC_IMR(tce.channels[0]));
+	tce.cache[0].rc = readl(tce.base + ATMEL_TC_RC(tce.channels[0]));
+	tce.cache[0].clken = !!(readl(tce.base + ATMEL_TC_SR(tce.channels[0])) &
+				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[0].cmr, tc.base + ATMEL_TC_CMR(tce.channels[0]));
+	writel(tce.cache[0].rc, tc.base + ATMEL_TC_RC(tce.channels[0]));
+	writel(0, tc.base + ATMEL_TC_RA(tce.channels[0]));
+	writel(0, tc.base + ATMEL_TC_RB(tce.channels[0]));
+	/* Disable all the interrupts */
+	writel(0xff, tc.base + ATMEL_TC_IDR(tce.channels[0]));
+	/* Reenable interrupts that were enabled before suspending */
+	writel(tce.cache[0].imr, tc.base + ATMEL_TC_IER(tce.channels[0]));
+
+	/* Start the clock if it was used */
+	if (tce.cache[0].clken)
+		writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG,
+		       tc.base + ATMEL_TC_CCR(tce.channels[0]));
+}
+
+static int __init tc_clkevt_register(struct device_node *node,
+				     struct regmap *regmap, void __iomem *base,
+				     int channel, int irq, int bits)
+{
+	int ret;
+	struct clk *slow_clk;
+
+	tce.regmap = regmap;
+	tce.base = base;
+	tce.channels[0] = channel;
+	tce.irq = irq;
+
+	slow_clk = of_clk_get_by_name(node->parent, "slow_clk");
+	if (IS_ERR(slow_clk))
+		return PTR_ERR(slow_clk);
+
+	ret = clk_prepare_enable(slow_clk);
+	if (ret)
+		return ret;
+
+	tce.clk[0] = tcb_clk_get(node, tce.channels[0]);
+	if (IS_ERR(tce.clk[0])) {
+		ret = PTR_ERR(tce.clk[0]);
+		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,
+	tce.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	tce.clkevt.rating = 140;
+
+	/* try to enable clk to avoid future errors in mode change */
+	ret = clk_prepare_enable(tce.clk[0]);
+	if (ret)
+		goto err_slow;
+	clk_disable(tce.clk[0]);
+
+	clockevents_config_and_register(&tce.clkevt, 32768, 1,
+					CLOCKSOURCE_MASK(bits));
+
+	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[0]);
+err_slow:
+	clk_disable_unprepare(slow_clk);
+
+	return ret;
+}
+
 /*
  * Clocksource and clockevent using the same channel(s)
  */
@@ -363,7 +560,7 @@ static int __init tcb_clksrc_init(struct device_node *node)
 	int irq, err, chan1 = -1;
 	unsigned bits;
 
-	if (tc.registered)
+	if (tc.registered && tce.registered)
 		return -ENODEV;
 
 	/*
@@ -395,12 +592,22 @@ static int __init tcb_clksrc_init(struct device_node *node)
 			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) {
-			pr_err("%s: clocksource needs two channels\n",
-			       node->parent->full_name);
-			return -EINVAL;
+			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);
+			}
 		}
 	}
 
-- 
2.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v7 4/7] clocksource/drivers: atmel-pit: make option silent
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
                   ` (2 preceding siblings ...)
  2018-09-13 11:30 ` [PATCH v7 3/7] clocksource/drivers: timer-atmel-tcb: add clockevent device on separate channel Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 5/7] ARM: at91: Implement clocksource selection Alexandre Belloni
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

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

Tested-by: Alexander Dahl <ada@thorsis.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
 drivers/clocksource/Kconfig | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8c7324e409f6..2388fee46e99 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -392,8 +392,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.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v7 5/7] ARM: at91: Implement clocksource selection
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
                   ` (3 preceding siblings ...)
  2018-09-13 11:30 ` [PATCH v7 4/7] clocksource/drivers: atmel-pit: make option silent Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 6/7] ARM: configs: at91: use new TCB timer driver Alexandre Belloni
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

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

Tested-by: Alexander Dahl <ada@thorsis.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
 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 903f23c309df..fa493a86e2bb 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -107,6 +107,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.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v7 6/7] ARM: configs: at91: use new TCB timer driver
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
                   ` (4 preceding siblings ...)
  2018-09-13 11:30 ` [PATCH v7 5/7] ARM: at91: Implement clocksource selection Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-13 11:30 ` [PATCH v7 7/7] ARM: configs: at91: unselect PIT Alexandre Belloni
  2018-09-22 11:29 ` [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Daniel Lezcano
  7 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

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

Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
 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.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v7 7/7] ARM: configs: at91: unselect PIT
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
                   ` (5 preceding siblings ...)
  2018-09-13 11:30 ` [PATCH v7 6/7] ARM: configs: at91: use new TCB timer driver Alexandre Belloni
@ 2018-09-13 11:30 ` Alexandre Belloni
  2018-09-22 11:29 ` [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Daniel Lezcano
  7 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-13 11:30 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel,
	Alexandre Belloni

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 <alexandre.belloni@bootlin.com>
---
 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.19.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver
  2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
                   ` (6 preceding siblings ...)
  2018-09-13 11:30 ` [PATCH v7 7/7] ARM: configs: at91: unselect PIT Alexandre Belloni
@ 2018-09-22 11:29 ` Daniel Lezcano
  2018-09-25 20:14   ` Alexandre Belloni
  7 siblings, 1 reply; 17+ messages in thread
From: Daniel Lezcano @ 2018-09-22 11:29 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel

On 13/09/2018 13:30, Alexandre Belloni wrote:
> Hi,
> 
> This series reworks the Atmel TCB drivers. It introduces a new driver to handle
> the clocksource and clockevent devices.
> 
> 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)

You say for rt the PIT is not suitable because of the shared irq but in
the driver, the interrupt is flagged as shared.

>  - 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 now that AVR32
> is gone.
> 
> changes in v7:
>  - fixed a warning when building on 64 bit platforms
> 
> changes in v6:
>  - rebased on v4.19-rc1
>  - separated the clocksource/clockevent and the single clockevent in two
>    different patches
>  - removed struct tc_clkevt_device and simply use struct atmel_tcb_clksrc
>  - removed struct atmel_tcb_info
>  - moved tcb_clk_get and tcb_irq_get to users
> 
> changes in v5:
>  - rebased on v4.18-rc1
>  - fixed the clock enabling/disabling in atomic context under preempt-rt
> 
> Changes in v4:
>  - rebased on top of v4.17-rc1
>  - fixed an issue when setting max_delta for clockevents_config_and_register
> 
> Alexandre Belloni (7):
>   ARM: at91: add TCB registers definitions
>   clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
>   clocksource/drivers: timer-atmel-tcb: add clockevent device on
>     separate channel
>   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 | 617 ++++++++++++++++++++++++++
>  include/soc/at91/atmel_tcb.h          | 183 ++++++++
>  7 files changed, 841 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/clocksource/timer-atmel-tcb.c
>  create mode 100644 include/soc/at91/atmel_tcb.h
> 


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


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
  2018-09-13 11:30 ` [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks Alexandre Belloni
@ 2018-09-24  1:59   ` Daniel Lezcano
  2018-09-25 21:16     ` Alexandre Belloni
  0 siblings, 1 reply; 17+ messages in thread
From: Daniel Lezcano @ 2018-09-24  1:59 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel

On 13/09/2018 13:30, Alexandre Belloni wrote:
> 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. 

Sorry, I don't get it. Can you elaborate?

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

I don't get the regmap reasoning here.

My main concern with this driver is the 16bits chained support. See
below in the comments.


> Tested-by: Alexander Dahl <ada@thorsis.com>
> Tested-by: Andras Szemzo <szemzo.andras@gmail.com>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> ---
>  drivers/clocksource/Kconfig           |   8 +
>  drivers/clocksource/Makefile          |   3 +-
>  drivers/clocksource/timer-atmel-tcb.c | 410 ++++++++++++++++++++++++++
>  3 files changed, 420 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/clocksource/timer-atmel-tcb.c
> 
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index a11f4ba98b05..8c7324e409f6 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -403,6 +403,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_EXYNOS_MCT
>  	bool "Exynos multi core timer driver" if COMPILE_TEST
>  	depends on ARM || ARM64
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index db51b2427e8a..0df9384a1230 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..21fbe430f91b
> --- /dev/null
> +++ b/drivers/clocksource/timer-atmel-tcb.c
> @@ -0,0 +1,410 @@
> +// 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>
> +
> +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];

You can reasonably remove this field and use directly the ones in the
clocksrc/evt.

> +	int channels[2];
> +	int bits;
> +	int irq;

After removing the request_irq/free_irq calls below (see comment), this
field can be removed.

> +	struct {
> +		u32 cmr;
> +		u32 imr;
> +		u32 rc;
> +		bool clken;

Not sure clken is needed, 16/32 is enough information.

> +	} cache[2];
> +	u32 bmr_cache;

Are you sure you should save the bmr content ?

> +	bool registered;
> +	bool clk_enabled;

Not used.

> +};
> +
> +static struct atmel_tcb_clksrc tc;
> +
> +static 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");

Can you explain why returning "t0_clk" is better than returning an error?

> +}
> +
> +/*
> + * 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]));


How this is compatible with 16bits as defined in the init function ?

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

Why are you requesting and freeing the irq instead of using the
disable/enable register operations ?

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

I suppose you meant here 'break' ? Use atmel_tc_divisors[] = { 2, 8, 32,
128 }; And then the ARRAY_SIZE macro.

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

What is a best divisor ? The highest one or the one closer to 5MHz ?

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

This is very confusing. If the function tcb_clk_get() fails with this
channel, it will return "t0_clk" and will be used here ? Why ?

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

Using two channels to emulate a 32bits timer has a significant cost,
especially in the sched_clock function which is part of the hot kernel
path. In addition it makes the code less maintainable and readable.

Why don't you just stick to a specific rate with the prescalar value and
reduce the rating of the timer ? (example in the stm32 timer,
stm32_timer_set_prescaler and init function).

It will be less precise (thus the lower rating) but will make the system
faster by preventing multiple register reads in the sched_clock.

Is it an acceptable trade-off ?

> +	tc.clksrc.name = tc.name;
> +	tc.clksrc.suspend = tc_clksrc_suspend;
> +	tc.clksrc.resume = tc_clksrc_resume;
> +	tc.clksrc.rating = 200;
> +	tc.clksrc.mask = CLOCKSOURCE_MASK(32);
> +	tc.clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
> +
> +	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;
> +	tc.clkevt.features = CLOCK_EVT_FEAT_ONESHOT;
> +	tc.clkevt.rating = 125;
> +
> +	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;
> +	struct regmap *regmap;
> +	void __iomem *tcb_base;
> +	u32 channel;
> +	int irq, err, chan1 = -1;
> +	unsigned bits;
> +
> +	if (tc.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__);

Remove those debug information and replace them by a proper error message.

> +		return -ENXIO;
> +	}
> +
> +	match = of_match_node(atmel_tcb_dt_ids, node->parent);
> +	bits = (uintptr_t)match->data;
> +
> +	err = of_property_read_u32_index(node, "reg", 0, &channel);
> +	if (err)
> +		return err;
> +
> +	irq = of_irq_get(node->parent, channel);
> +	if (irq < 0) {

if (irq <= 0) {

> +		irq = of_irq_get(node->parent, 0);

Why ?

> +		if (irq < 0)

if (irq <= 0) {

> +			return irq;
> +	}
> +
> +	if (bits == 16) {
> +		of_property_read_u32_index(node, "reg", 1, &chan1);
> +		if (chan1 == -1) {
> +			pr_err("%s: clocksource needs two channels\n",
> +			       node->parent->full_name);

Think about it. The code is giving up at this point in the boot process.
So of two things, you consider there is an alternate clocksource /
clockevent or the system hangs:

 - If there is an alternate clocksource why support 32bits by chaining
the channels with the cost it introduces instead of using the alternate
one ?

 - If there is no alternate clocksource why not support a 16bits less
precise timer and give up with the 32bits emulation and the complexity
it introduces in this driver ?

> +			return -EINVAL;
> +		}
> +	}
> +	return tcb_clksrc_register(node, regmap, tcb_base, channel, chan1, irq,
> +				   bits);
> +}
> +TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init);
> 


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


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver
  2018-09-22 11:29 ` [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Daniel Lezcano
@ 2018-09-25 20:14   ` Alexandre Belloni
  2018-11-08 12:43     ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-25 20:14 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel

On 22/09/2018 13:29:48+0200, Daniel Lezcano wrote:
> On 13/09/2018 13:30, Alexandre Belloni wrote:
> > Hi,
> > 
> > This series reworks the Atmel TCB drivers. It introduces a new driver to handle
> > the clocksource and clockevent devices.
> > 
> > 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)
> 
> You say for rt the PIT is not suitable because of the shared irq but in
> the driver, the interrupt is flagged as shared.
> 

Well, it is not simply sharing the interrupt that is an issue, it is the
mismatch between the PIT and the UART interrupt flags and that only
happens when using preempt-rt.

But still, the TCB is flagged as shared because it may be shared between
multiple TCB channels (it is the case for the more recent SoCs).
However, what happens is that the DBGU UART is always enabled on the
boards so when using the PIT, the interrupt is always shared. But, for
the TCB, the only driver currently able to use the interrupt is the
clockevent driver so the interrupt as almost no chance to actually be
shared.

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
  2018-09-24  1:59   ` Daniel Lezcano
@ 2018-09-25 21:16     ` Alexandre Belloni
  2018-10-01 21:24       ` Daniel Lezcano
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Belloni @ 2018-09-25 21:16 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel

On 24/09/2018 03:59:55+0200, Daniel Lezcano wrote:
> On 13/09/2018 13:30, Alexandre Belloni wrote:
> > 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. 
> 
> Sorry, I don't get it. Can you elaborate?
> 

The current existing way of sharing TCB channels is getting probed to
late in the boot process to be used as the clocksource so currently, the
PIT is necessary to act as the clocksource until the TCB clocksource can
be probed.

This is a big issue for SoCs without a PIT, they simply can't boot.

This also solves:
33d8c15559df Revert "clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock"
7b9f1d16e6d1 clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock


> > 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).
> 
> I don't get the regmap reasoning here.

Because there are 3 channels per TCB, some TCB can have channels handled
by different drivers (say channel 0 for clocksource, channel 1 for
clockevent and channel 2 for PWM). There are configuration registers that
are shared for all the channels and so the access needs to be handled
properly. But as we discussed on a previous version of the patch, we
don't want to lock/unlock each time we read the clocksource so for the
channel specific registers, readl/writel is used directly.

> 
> My main concern with this driver is the 16bits chained support. See
> below in the comments.
> 
> 
> > +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];
> 
> You can reasonably remove this field and use directly the ones in the
> clocksrc/evt.
> 

name in struct clocksource is a pointer to a string, we still need a
place to store that string.

> > +	int channels[2];
> > +	int bits;
> > +	int irq;
> 
> After removing the request_irq/free_irq calls below (see comment), this
> field can be removed.
> 
> > +	struct {
> > +		u32 cmr;
> > +		u32 imr;
> > +		u32 rc;
> > +		bool clken;
> 
> Not sure clken is needed, 16/32 is enough information.
> 

This as nothing to do with 16/32. We always need to now whether the
timer was running or not.

> > +	} cache[2];
> > +	u32 bmr_cache;
> 
> Are you sure you should save the bmr content ?
> 

We need to restore at least part of it. We may need to be more clever
about it but this is the current behaviour and it has been working fine.

> > +	bool registered;
> > +	bool clk_enabled;
> 
> Not used.
> 

I guess they are use in the following patch.

> > +};
> > +
> > +static struct atmel_tcb_clksrc tc;
> > +
> > +static 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");
> 
> Can you explain why returning "t0_clk" is better than returning an error?
> 

This is the current tclib behavior and doing otherwise would break the
DT ABI.
The reason for this behavior is that some TCB may have a clock
per channel while others have one clock for the whole block.

> > +}
> > +
> > +/*
> > + * 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]));
> 
> 
> How this is compatible with 16bits as defined in the init function ?
> 

This is working fine because it is the lower bits channel and in that
case, clockevents_config_and_register is call with the proper mask (16
lower bits sets).

> > +	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);
> 
> Why are you requesting and freeing the irq instead of using the
> disable/enable register operations ?
> 

To avoid going through two interrupt handlers when we know that one is
never used (that is when we have a separate channel for the clockevent,
see following patch).

> > +	/* 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;
> 
> I suppose you meant here 'break' ? Use atmel_tc_divisors[] = { 2, 8, 32,
> 128 }; And then the ARRAY_SIZE macro.
> 
> > +		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;
> 
> What is a best divisor ? The highest one or the one closer to 5MHz ?
> 

The whole divisor selection is coming for the previous driver and I'd
rather not change it at this point, this is the topic of an other
series.

It chooses the first divisor that gives a counting rate over 5MHz


> > +	}
> > +
> > +	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;
> 
> This is very confusing. If the function tcb_clk_get() fails with this
> channel, it will return "t0_clk" and will be used here ? Why ?
> 

See earlier explanation.

> > +		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);
> 
> Using two channels to emulate a 32bits timer has a significant cost,
> especially in the sched_clock function which is part of the hot kernel
> path. In addition it makes the code less maintainable and readable.
> 
> Why don't you just stick to a specific rate with the prescalar value and
> reduce the rating of the timer ? (example in the stm32 timer,
> stm32_timer_set_prescaler and init function).
> 
> It will be less precise (thus the lower rating) but will make the system
> faster by preventing multiple register reads in the sched_clock.
> 
> Is it an acceptable trade-off ?
> 

Not at this point, the goal is to not change the current behaviour.
Some customer rely on the fast timer (they are bitbanging some RF
protocols) and counting at more that 5MHz using a 16 bit timer is
definitively too fast.

This is something that could be changed once we implement timer rate
selection (but I doubt it will make the code more readable).

I'm not saying we shouldn't question what was done 10 years ago but I'd
rather not change it is this series.

Also, the goal is to get rid of the tcb_clksrc driver now that avr32 is
gone. This will be done once the pwm driver is converted (I did that in
v1).

> > +	tcb_base = of_iomap(node->parent, 0);
> > +	if (!tcb_base) {
> > +		pr_err("%s +%d %s\n", __FILE__, __LINE__, __func__);
> 
> Remove those debug information and replace them by a proper error message.
> 

My mistake, this will be simply removed.

> > +		return -ENXIO;
> > +	}
> > +
> > +	match = of_match_node(atmel_tcb_dt_ids, node->parent);
> > +	bits = (uintptr_t)match->data;
> > +
> > +	err = of_property_read_u32_index(node, "reg", 0, &channel);
> > +	if (err)
> > +		return err;
> > +
> > +	irq = of_irq_get(node->parent, channel);
> > +	if (irq < 0) {
> 
> if (irq <= 0) {
> 
> > +		irq = of_irq_get(node->parent, 0);
> 
> Why ?
> 

See the binding, the timer is a child of the TCB and the TCB node has
the irq info. So, the TCB is defined in the dtsi and the child nodes are
in the board dts.

> > +		if (irq < 0)
> 
> if (irq <= 0) {
> 
> > +			return irq;
> > +	}
> > +
> > +	if (bits == 16) {
> > +		of_property_read_u32_index(node, "reg", 1, &chan1);
> > +		if (chan1 == -1) {
> > +			pr_err("%s: clocksource needs two channels\n",
> > +			       node->parent->full_name);
> 
> Think about it. The code is giving up at this point in the boot process.
> So of two things, you consider there is an alternate clocksource /
> clockevent or the system hangs:
> 
>  - If there is an alternate clocksource why support 32bits by chaining
> the channels with the cost it introduces instead of using the alternate
> one ?
> 

The PIT is almost always the worse clocksource as it is very slow.

>  - If there is no alternate clocksource why not support a 16bits less
> precise timer and give up with the 32bits emulation and the complexity
> it introduces in this driver ?
> 

If there is not alternate clocksource, the TCB is 32bit.



-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
  2018-09-25 21:16     ` Alexandre Belloni
@ 2018-10-01 21:24       ` Daniel Lezcano
  2018-10-03 22:26         ` Alexandre Belloni
  0 siblings, 1 reply; 17+ messages in thread
From: Daniel Lezcano @ 2018-10-01 21:24 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel

On 25/09/2018 23:16, Alexandre Belloni wrote:
> On 24/09/2018 03:59:55+0200, Daniel Lezcano wrote:
>> On 13/09/2018 13:30, Alexandre Belloni wrote:
>>> 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. 
>>
>> Sorry, I don't get it. Can you elaborate?
>>
> 
> The current existing way of sharing TCB channels is getting probed to
> late in the boot process to be used as the clocksource so currently, the
> PIT is necessary to act as the clocksource until the TCB clocksource can
> be probed.
> 
> This is a big issue for SoCs without a PIT, they simply can't boot.

I'm still missing the point. The timer (clocksource + clocksource) is
probed very early with TIMER_OF_DECLARE.


> This also solves:
> 33d8c15559df Revert "clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock"
> 7b9f1d16e6d1 clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock
> 
> 
>>> 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).
>>
>> I don't get the regmap reasoning here.
> 
> Because there are 3 channels per TCB, some TCB can have channels handled
> by different drivers (say channel 0 for clocksource, channel 1 for
> clockevent and channel 2 for PWM). There are configuration registers that
> are shared for all the channels and so the access needs to be handled
> properly. But as we discussed on a previous version of the patch, we
> don't want to lock/unlock each time we read the clocksource so for the
> channel specific registers, readl/writel is used directly.

Can you point me to the code where we have racy access to the
ATMEL_TC_BMR register ?


>> My main concern with this driver is the 16bits chained support. See
>> below in the comments.
>>
>>
>>> +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];
>>
>> You can reasonably remove this field and use directly the ones in the
>> clocksrc/evt.
>>
> 
> name in struct clocksource is a pointer to a string, we still need a
> place to store that string.

Come on!

char *name = kasprintf(...);

tc.clkevt.name = name;
tc.clksrc.name = name;

no need to add a specific field for this.

Alternatively, you can make use of the
kbasename(node->parent->full_name) only without the channel numbering.

>>> +	int channels[2];
>>> +	int bits;
>>> +	int irq;
>>
>> After removing the request_irq/free_irq calls below (see comment), this
>> field can be removed.
>>
>>> +	struct {
>>> +		u32 cmr;
>>> +		u32 imr;
>>> +		u32 rc;
>>> +		bool clken;
>>
>> Not sure clken is needed, 16/32 is enough information.
>>
> 
> This as nothing to do with 16/32. We always need to now whether the
> timer was running or not.

Mmh, not sure why I said that. Anyway ...

>>> +	} cache[2];
>>> +	u32 bmr_cache;
>>
>> Are you sure you should save the bmr content ?
>>
> 
> We need to restore at least part of it. We may need to be more clever
> about it but this is the current behaviour and it has been working fine.
> 
>>> +	bool registered;
>>> +	bool clk_enabled;
>>
>> Not used.
>>
> 
> I guess they are use in the following patch.

Move them to the patch making use of it.

>>> +};
>>> +
>>> +static struct atmel_tcb_clksrc tc;
>>> +
>>> +static 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");
>>
>> Can you explain why returning "t0_clk" is better than returning an error?
>>
> 
> This is the current tclib behavior and doing otherwise would break the
> DT ABI.
> The reason for this behavior is that some TCB may have a clock
> per channel while others have one clock for the whole block.

What are the DT ABI? Can you point the snippets ?


>>> +}
>>> +
>>> +/*
>>> + * 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]));
>>
>>
>> How this is compatible with 16bits as defined in the init function ?
>>
> 
> This is working fine because it is the lower bits channel and in that
> case, clockevents_config_and_register is call with the proper mask (16
> lower bits sets).
> 
>>> +	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);
>>
>> Why are you requesting and freeing the irq instead of using the
>> disable/enable register operations ?
> 
> To avoid going through two interrupt handlers when we know that one is
> never used (that is when we have a separate channel for the clockevent,
> see following patch).

This explanation is not convincing. I will let you look at the
request_irq / free_irq internals (including __setup_irq) to figure out
why this is not possible.


>>> +	/* 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;
>>
>> I suppose you meant here 'break' ? Use atmel_tc_divisors[] = { 2, 8, 32,
>> 128 }; And then the ARRAY_SIZE macro.
>>
>>> +		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;
>>
>> What is a best divisor ? The highest one or the one closer to 5MHz ?
>>
> 
> The whole divisor selection is coming for the previous driver and I'd
> rather not change it at this point, this is the topic of an other
> series.
> It chooses the first divisor that gives a counting rate over 5MHz

So why not stop the loop as soon as the rate / divisor is >= to 5MHz ?

>>> +	}
>>> +
>>> +	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;
>>
>> This is very confusing. If the function tcb_clk_get() fails with this
>> channel, it will return "t0_clk" and will be used here ? Why ?
>>
> 
> See earlier explanation.
> 
>>> +		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);
>>
>> Using two channels to emulate a 32bits timer has a significant cost,
>> especially in the sched_clock function which is part of the hot kernel
>> path. In addition it makes the code less maintainable and readable.
>>
>> Why don't you just stick to a specific rate with the prescalar value and
>> reduce the rating of the timer ? (example in the stm32 timer,
>> stm32_timer_set_prescaler and init function).
>>
>> It will be less precise (thus the lower rating) but will make the system
>> faster by preventing multiple register reads in the sched_clock.
>>
>> Is it an acceptable trade-off ?
>>
> 
> Not at this point, the goal is to not change the current behaviour.
> Some customer rely on the fast timer (they are bitbanging some RF
> protocols) and counting at more that 5MHz using a 16 bit timer is
> definitively too fast.

Not if you use the prescalar.

> This is something that could be changed once we implement timer rate
> selection (but I doubt it will make the code more readable).
> 
> I'm not saying we shouldn't question what was done 10 years ago but I'd
> rather not change it is this series.
> 
> Also, the goal is to get rid of the tcb_clksrc driver now that avr32 is
> gone. This will be done once the pwm driver is converted (I did that in
> v1).

You want to get rid of the tcb_clksrc by adding a new driver which is
very similar without taking into consideration to do a move to something
cleaner and putting in question what was already done.



>>> +	tcb_base = of_iomap(node->parent, 0);
>>> +	if (!tcb_base) {
>>> +		pr_err("%s +%d %s\n", __FILE__, __LINE__, __func__);
>>
>> Remove those debug information and replace them by a proper error message.
>>
> 
> My mistake, this will be simply removed.
> 
>>> +		return -ENXIO;
>>> +	}
>>> +
>>> +	match = of_match_node(atmel_tcb_dt_ids, node->parent);
>>> +	bits = (uintptr_t)match->data;
>>> +
>>> +	err = of_property_read_u32_index(node, "reg", 0, &channel);
>>> +	if (err)
>>> +		return err;
>>> +
>>> +	irq = of_irq_get(node->parent, channel);
>>> +	if (irq < 0) {
>>
>> if (irq <= 0) {
>>
>>> +		irq = of_irq_get(node->parent, 0);
>>
>> Why ?
>>
> 
> See the binding, 

Ok, can you point me to the code ?

> the timer is a child of the TCB and the TCB node has
> the irq info. So, the TCB is defined in the dtsi and the child nodes are
> in the board dts.
> 
>>> +		if (irq < 0)
>>
>> if (irq <= 0) {
>>
>>> +			return irq;
>>> +	}
>>> +
>>> +	if (bits == 16) {
>>> +		of_property_read_u32_index(node, "reg", 1, &chan1);
>>> +		if (chan1 == -1) {
>>> +			pr_err("%s: clocksource needs two channels\n",
>>> +			       node->parent->full_name);
>>
>> Think about it. The code is giving up at this point in the boot process.
>> So of two things, you consider there is an alternate clocksource /
>> clockevent or the system hangs:
>>
>>  - If there is an alternate clocksource why support 32bits by chaining
>> the channels with the cost it introduces instead of using the alternate
>> one ?
>>
> 
> The PIT is almost always the worse clocksource as it is very slow.

What is slow here ?

>>  - If there is no alternate clocksource why not support a 16bits less
>> precise timer and give up with the 32bits emulation and the complexity
>> it introduces in this driver ?
>>
> 
> If there is not alternate clocksource, the TCB is 32bit.





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


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
  2018-10-01 21:24       ` Daniel Lezcano
@ 2018-10-03 22:26         ` Alexandre Belloni
  0 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2018-10-03 22:26 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	Sebastian Andrzej Siewior, linux-arm-kernel, linux-kernel

On 01/10/2018 23:24:11+0200, Daniel Lezcano wrote:
> On 25/09/2018 23:16, Alexandre Belloni wrote:
> > On 24/09/2018 03:59:55+0200, Daniel Lezcano wrote:
> >> On 13/09/2018 13:30, Alexandre Belloni wrote:
> >>> 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. 
> >>
> >> Sorry, I don't get it. Can you elaborate?
> >>
> > 
> > The current existing way of sharing TCB channels is getting probed to
> > late in the boot process to be used as the clocksource so currently, the
> > PIT is necessary to act as the clocksource until the TCB clocksource can
> > be probed.
> > 
> > This is a big issue for SoCs without a PIT, they simply can't boot.
> 
> I'm still missing the point. The timer (clocksource + clocksource) is
> probed very early with TIMER_OF_DECLARE.
> 

No, tcb_clksrc is probed very latebecause it needs tclib to be probed
first and this happens at arch_initcall.
> 
> > This also solves:
> > 33d8c15559df Revert "clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock"
> > 7b9f1d16e6d1 clocksource/drivers/tcb_clksrc: Use 32 bit tcb as sched_clock
> > 
> > 
> >>> 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).
> >>
> >> I don't get the regmap reasoning here.
> > 
> > Because there are 3 channels per TCB, some TCB can have channels handled
> > by different drivers (say channel 0 for clocksource, channel 1 for
> > clockevent and channel 2 for PWM). There are configuration registers that
> > are shared for all the channels and so the access needs to be handled
> > properly. But as we discussed on a previous version of the patch, we
> > don't want to lock/unlock each time we read the clocksource so for the
> > channel specific registers, readl/writel is used directly.
> 
> Can you point me to the code where we have racy access to the
> ATMEL_TC_BMR register ?
> 

There is non currently.

> 
> >> My main concern with this driver is the 16bits chained support. See
> >> below in the comments.
> >>
> >>
> >>> +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];
> >>
> >> You can reasonably remove this field and use directly the ones in the
> >> clocksrc/evt.
> >>
> > 
> > name in struct clocksource is a pointer to a string, we still need a
> > place to store that string.
> 
> Come on!
> 
> char *name = kasprintf(...);
> 
> tc.clkevt.name = name;
> tc.clksrc.name = name;
> 
> no need to add a specific field for this.
> 

And how is unconditionally dynamically allocating memory better than
having a field in the struct?

> >> Can you explain why returning "t0_clk" is better than returning an error?
> >>
> > 
> > This is the current tclib behavior and doing otherwise would break the
> > DT ABI.
> > The reason for this behavior is that some TCB may have a clock
> > per channel while others have one clock for the whole block.
> 
> What are the DT ABI? Can you point the snippets ?
> 

It is documented in Documentation/devicetree/bindings/mfd/atmel-tcb.txt

> >> Using two channels to emulate a 32bits timer has a significant cost,
> >> especially in the sched_clock function which is part of the hot kernel
> >> path. In addition it makes the code less maintainable and readable.
> >>
> >> Why don't you just stick to a specific rate with the prescalar value and
> >> reduce the rating of the timer ? (example in the stm32 timer,
> >> stm32_timer_set_prescaler and init function).
> >>
> >> It will be less precise (thus the lower rating) but will make the system
> >> faster by preventing multiple register reads in the sched_clock.
> >>
> >> Is it an acceptable trade-off ?
> >>
> > 
> > Not at this point, the goal is to not change the current behaviour.
> > Some customer rely on the fast timer (they are bitbanging some RF
> > protocols) and counting at more that 5MHz using a 16 bit timer is
> > definitively too fast.
> 
> Not if you use the prescalar.
> 

I'm not sure what you mean. 16bits at 5MHz (and it is actually faster than that)
wraps up every 13ms.

> > This is something that could be changed once we implement timer rate
> > selection (but I doubt it will make the code more readable).
> > 
> > I'm not saying we shouldn't question what was done 10 years ago but I'd
> > rather not change it is this series.
> > 
> > Also, the goal is to get rid of the tcb_clksrc driver now that avr32 is
> > gone. This will be done once the pwm driver is converted (I did that in
> > v1).
> 
> You want to get rid of the tcb_clksrc by adding a new driver which is
> very similar without taking into consideration to do a move to something
> cleaner and putting in question what was already done.
> 
> 
> 
> >>> +	tcb_base = of_iomap(node->parent, 0);
> >>> +	if (!tcb_base) {
> >>> +		pr_err("%s +%d %s\n", __FILE__, __LINE__, __func__);
> >>
> >> Remove those debug information and replace them by a proper error message.
> >>
> > 
> > My mistake, this will be simply removed.
> > 
> >>> +		return -ENXIO;
> >>> +	}
> >>> +
> >>> +	match = of_match_node(atmel_tcb_dt_ids, node->parent);
> >>> +	bits = (uintptr_t)match->data;
> >>> +
> >>> +	err = of_property_read_u32_index(node, "reg", 0, &channel);
> >>> +	if (err)
> >>> +		return err;
> >>> +
> >>> +	irq = of_irq_get(node->parent, channel);
> >>> +	if (irq < 0) {
> >>
> >> if (irq <= 0) {
> >>
> >>> +		irq = of_irq_get(node->parent, 0);
> >>
> >> Why ?
> >>
> > 
> > See the binding, 
> 
> Ok, can you point me to the code ?
> 
> > the timer is a child of the TCB and the TCB node has
> > the irq info. So, the TCB is defined in the dtsi and the child nodes are
> > in the board dts.
> > 
> >>> +		if (irq < 0)
> >>
> >> if (irq <= 0) {
> >>
> >>> +			return irq;
> >>> +	}
> >>> +
> >>> +	if (bits == 16) {
> >>> +		of_property_read_u32_index(node, "reg", 1, &chan1);
> >>> +		if (chan1 == -1) {
> >>> +			pr_err("%s: clocksource needs two channels\n",
> >>> +			       node->parent->full_name);
> >>
> >> Think about it. The code is giving up at this point in the boot process.
> >> So of two things, you consider there is an alternate clocksource /
> >> clockevent or the system hangs:
> >>
> >>  - If there is an alternate clocksource why support 32bits by chaining
> >> the channels with the cost it introduces instead of using the alternate
> >> one ?
> >>
> > 
> > The PIT is almost always the worse clocksource as it is very slow.
> 
> What is slow here ?
> 

Well, my wording was very poor. The main issue with the pit is the
resolution of the clockevent (95 to 160 ms). Also, it wrap every 10
seconds or so.

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver
  2018-09-25 20:14   ` Alexandre Belloni
@ 2018-11-08 12:43     ` Sebastian Andrzej Siewior
  2018-11-08 14:09       ` Alexandre Belloni
  0 siblings, 1 reply; 17+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-11-08 12:43 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Daniel Lezcano, Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	linux-arm-kernel, linux-kernel

On 2018-09-25 22:14:56 [+0200], Alexandre Belloni wrote:
> On 22/09/2018 13:29:48+0200, Daniel Lezcano wrote:
> > You say for rt the PIT is not suitable because of the shared irq but in
> > the driver, the interrupt is flagged as shared.
> > 
> 
> Well, it is not simply sharing the interrupt that is an issue, it is the
> mismatch between the PIT and the UART interrupt flags and that only
> happens when using preempt-rt.

This should also happen on !RT with the threadedirq command line switch.
The UART will be threaded and the PIT will not, and this is the problem.
So we need to split those or disable the UART. The other important thing
for RT is the higher resolution of the clocksource/clockevents device (I
don't know if this is part of the series or not…).

I'm currently replacing the v6 with this v7 in my RT tree. What is the
status of this series upstream wise?

Sebastian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver
  2018-11-08 12:43     ` Sebastian Andrzej Siewior
@ 2018-11-08 14:09       ` Alexandre Belloni
  2018-11-08 14:30         ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Belloni @ 2018-11-08 14:09 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: Daniel Lezcano, Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	linux-arm-kernel, linux-kernel

On 08/11/2018 13:43:27+0100, Sebastian Andrzej Siewior wrote:
> On 2018-09-25 22:14:56 [+0200], Alexandre Belloni wrote:
> > On 22/09/2018 13:29:48+0200, Daniel Lezcano wrote:
> > > You say for rt the PIT is not suitable because of the shared irq but in
> > > the driver, the interrupt is flagged as shared.
> > > 
> > 
> > Well, it is not simply sharing the interrupt that is an issue, it is the
> > mismatch between the PIT and the UART interrupt flags and that only
> > happens when using preempt-rt.
> 
> This should also happen on !RT with the threadedirq command line switch.
> The UART will be threaded and the PIT will not, and this is the problem.
> So we need to split those or disable the UART. The other important thing
> for RT is the higher resolution of the clocksource/clockevents device (I
> don't know if this is part of the series or not…).
> 

It is not part of that series as I prefer to keep the discussion of how
to configure that for later. This has been raised previously without any
conclusion and I'd really like to see this rework enter upstream soon.

> I'm currently replacing the v6 with this v7 in my RT tree. What is the
> status of this series upstream wise?
> 

I'll send v8 sometimes next week.

-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver
  2018-11-08 14:09       ` Alexandre Belloni
@ 2018-11-08 14:30         ` Sebastian Andrzej Siewior
  0 siblings, 0 replies; 17+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-11-08 14:30 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Daniel Lezcano, Thomas Gleixner, Nicolas Ferre, Alexander Dahl,
	linux-arm-kernel, linux-kernel

On 2018-11-08 15:09:35 [+0100], Alexandre Belloni wrote:
> It is not part of that series as I prefer to keep the discussion of how
> to configure that for later. This has been raised previously without any
> conclusion and I'd really like to see this rework enter upstream soon.

Okay. Does it make sense to keep the lower resolution?

> > I'm currently replacing the v6 with this v7 in my RT tree. What is the
> > status of this series upstream wise?
> > 
> 
> I'll send v8 sometimes next week.
Thanks.

Sebastian

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2018-11-08 14:30 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-13 11:30 [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 1/7] ARM: at91: add TCB registers definitions Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 2/7] clocksource/drivers: Add a new driver for the Atmel ARM TC blocks Alexandre Belloni
2018-09-24  1:59   ` Daniel Lezcano
2018-09-25 21:16     ` Alexandre Belloni
2018-10-01 21:24       ` Daniel Lezcano
2018-10-03 22:26         ` Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 3/7] clocksource/drivers: timer-atmel-tcb: add clockevent device on separate channel Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 4/7] clocksource/drivers: atmel-pit: make option silent Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 5/7] ARM: at91: Implement clocksource selection Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 6/7] ARM: configs: at91: use new TCB timer driver Alexandre Belloni
2018-09-13 11:30 ` [PATCH v7 7/7] ARM: configs: at91: unselect PIT Alexandre Belloni
2018-09-22 11:29 ` [PATCH v7 0/7] clocksource: rework Atmel TCB timer driver Daniel Lezcano
2018-09-25 20:14   ` Alexandre Belloni
2018-11-08 12:43     ` Sebastian Andrzej Siewior
2018-11-08 14:09       ` Alexandre Belloni
2018-11-08 14:30         ` Sebastian Andrzej Siewior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).