linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Lezcano <daniel.lezcano@linaro.org>
To: daniel.lezcano@linaro.org, tglx@linutronix.de
Cc: S32@nxp.com, linux-kernel@vger.kernel.org,
	ghennadi.procopciuc@oss.nxp.com
Subject: [PATCH v2 20/20] clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support
Date: Wed, 30 Jul 2025 10:27:22 +0200	[thread overview]
Message-ID: <20250730082725.183133-21-daniel.lezcano@linaro.org> (raw)
In-Reply-To: <20250730082725.183133-1-daniel.lezcano@linaro.org>

The previous changes put in place the encapsulation of the code in
order to allow multiple instances of the driver.

The S32G platform has two Periodic Interrupt Timer (PIT). The IP is
exactly the same as the VF platform.

Each PIT has four channels which are 32 bits wide and counting
down. The two first channels can be chained to implement a 64 bits
counter. The channel usage is kept unchanged with the original driver,
channel 2 is used as a clocksource, channel 3 is used as a
clockevent. Other channels are unused.

In order to support the S32G platform which has two PIT, we initialize
the timer and bind it to a CPU. The S32G platforms can have 2, 4 or 8
CPUs and this kind of configuration can appear unusual as we may endup
with two PIT used as a clockevent for the two first CPUs while the
other CPUs use the architected timers. However, in the context of the
automotive, the platform can be partioned to assign 2 CPUs for Linux
and the others CPUs to third party OS. The PIT is then used with their
specifities like the ability to freeze the time which is needed for
instance for debugging purpose.

The setup found for this platform is each timer instance is bound to
CPU0 and CPU1.

A counter is incremented when a timer is successfully initialized and
assigned to a CPU. This counter is used as an index for the CPU number
and to detect when we reach the maximum possible instances for the
platform. That in turn triggers the CPU hotplug callbacks to achieve
the per CPU setup. It is the exact same mechanism found in the NXP STM
driver.

If the timers must be bound to different CPUs, it would require an
additionnal mechanism which is not part of these changes.

Tested on a s32g274a-rdb2.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/clocksource/timer-nxp-pit.c | 115 +++++++++++++++++++++++-----
 1 file changed, 97 insertions(+), 18 deletions(-)

diff --git a/drivers/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c
index 2a0ee4109ead..f2534172b9d4 100644
--- a/drivers/clocksource/timer-nxp-pit.c
+++ b/drivers/clocksource/timer-nxp-pit.c
@@ -1,14 +1,16 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ * Copyright 2018,2021-2025 NXP
  */
-
 #include <linux/interrupt.h>
 #include <linux/clockchips.h>
+#include <linux/cpuhotplug.h>
 #include <linux/clk.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/sched_clock.h>
+#include <linux/platform_device.h>
 
 /*
  * Each pit takes 0x10 Bytes register space
@@ -37,11 +39,23 @@
 struct pit_timer {
 	void __iomem *clksrc_base;
 	void __iomem *clkevt_base;
-	unsigned long cycle_per_jiffy;
 	struct clock_event_device ced;
 	struct clocksource cs;
+	int rate;
+};
+
+struct pit_timer_data {
+	int max_pit_instances;
 };
 
+static DEFINE_PER_CPU(struct pit_timer *, pit_timers);
+
+/*
+ * Global structure for multiple PITs initialization
+ */
+static int pit_instances;
+static int max_pit_instances = 1;
+
 static void __iomem *sched_clock_base;
 
 static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced)
@@ -98,8 +112,8 @@ static u64 pit_timer_clocksource_read(struct clocksource *cs)
 	return (u64)~readl(PITCVAL(pit->clksrc_base));
 }
 
-static int __init pit_clocksource_init(struct pit_timer *pit, const char *name,
-				       void __iomem *base, unsigned long rate)
+static int pit_clocksource_init(struct pit_timer *pit, const char *name,
+				void __iomem *base, unsigned long rate)
 {
 	/*
 	 * The channels 0 and 1 can be chained to build a 64-bit
@@ -155,7 +169,7 @@ static int pit_set_periodic(struct clock_event_device *ced)
 {
 	struct pit_timer *pit = ced_to_pit(ced);
 
-	pit_set_next_event(pit->cycle_per_jiffy, ced);
+	pit_set_next_event(pit->rate / HZ, ced);
 
 	return 0;
 }
@@ -181,24 +195,28 @@ static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static int __init pit_clockevent_init(struct pit_timer *pit, const char *name,
-				      void __iomem *base, unsigned long rate,
-				      int irq, unsigned int cpu)
+static int pit_clockevent_per_cpu_init(struct pit_timer *pit, const char *name,
+				       void __iomem *base, unsigned long rate,
+				       int irq, unsigned int cpu)
 {
+	int ret;
+
 	/*
 	 * The channels 0 and 1 can be chained to build a 64-bit
 	 * timer. Let's use the channel 3 as a clockevent and leave
 	 * the channels 0 and 1 unused for anyone else who needs them
 	 */
 	pit->clkevt_base = base + PIT_CH(3);
-	pit->cycle_per_jiffy = rate / (HZ);
+	pit->rate = rate;
 
 	pit_timer_disable(pit->clkevt_base);
 
 	pit_timer_irqack(pit);
 
-	BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
-			   name, &pit->ced));
+	ret = request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_NOBALANCING,
+			  name, &pit->ced);
+	if (ret)
+		return ret;
 
 	pit->ced.cpumask = cpumask_of(cpu);
 	pit->ced.irq = irq;
@@ -210,6 +228,23 @@ static int __init pit_clockevent_init(struct pit_timer *pit, const char *name,
 	pit->ced.set_next_event	= pit_set_next_event;
 	pit->ced.rating	= 300;
 
+	per_cpu(pit_timers, cpu) = pit;
+
+	return 0;
+}
+
+static int pit_clockevent_starting_cpu(unsigned int cpu)
+{
+	struct pit_timer *pit = per_cpu(pit_timers, cpu);
+	int ret;
+
+	if (!pit)
+		return 0;
+
+	ret = irq_force_affinity(pit->ced.irq, cpumask_of(cpu));
+	if (ret)
+		return ret;
+
 	/*
 	 * The value for the LDVAL register trigger is calculated as:
 	 * LDVAL trigger = (period / clock period) - 1
@@ -218,12 +253,12 @@ static int __init pit_clockevent_init(struct pit_timer *pit, const char *name,
 	 * LDVAL trigger value is 1. And then the min_delta is
 	 * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit.
 	 */
-	clockevents_config_and_register(&pit->ced, rate, 2, 0xffffffff);
+	clockevents_config_and_register(&pit->ced, pit->rate, 2, 0xffffffff);
 
 	return 0;
 }
 
-static int __init pit_timer_init(struct device_node *np)
+static int pit_timer_init(struct device_node *np)
 {
 	struct pit_timer *pit;
 	struct clk *pit_clk;
@@ -262,16 +297,31 @@ static int __init pit_timer_init(struct device_node *np)
 
 	clk_rate = clk_get_rate(pit_clk);
 
-	/* enable the pit module */
-	pit_module_enable(timer_base);
+	pit_module_disable(timer_base);
 
 	ret = pit_clocksource_init(pit, name, timer_base, clk_rate);
-	if (ret)
+	if (ret) {
+		pr_err("Failed to initialize clocksource '%pOF'\n", np);
 		goto out_pit_module_disable;
+	}
 
-	ret = pit_clockevent_init(pit, name, timer_base, clk_rate, irq, 0);
-	if (ret)
+	ret = pit_clockevent_per_cpu_init(pit, name, timer_base, clk_rate, irq, pit_instances);
+	if (ret) {
+		pr_err("Failed to initialize clockevent '%pOF'\n", np);
 		goto out_pit_clocksource_unregister;
+	}
+
+	/* enable the pit module */
+	pit_module_enable(timer_base);
+
+	pit_instances++;
+
+	if (pit_instances == max_pit_instances) {
+		ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "PIT timer:starting",
+					pit_clockevent_starting_cpu, NULL);
+		if (ret < 0)
+			goto out_pit_clocksource_unregister;
+	}
 
 	return 0;
 
@@ -289,4 +339,33 @@ static int __init pit_timer_init(struct device_node *np)
 
 	return ret;
 }
+
+static int pit_timer_probe(struct platform_device *pdev)
+{
+	const struct pit_timer_data *pit_timer_data;
+
+	pit_timer_data = of_device_get_match_data(&pdev->dev);
+	if (pit_timer_data)
+		max_pit_instances = pit_timer_data->max_pit_instances;
+
+	return pit_timer_init(pdev->dev.of_node);
+}
+
+static struct pit_timer_data s32g2_data = { .max_pit_instances = 2 };
+
+static const struct of_device_id pit_timer_of_match[] = {
+	{ .compatible = "nxp,s32g2-pit", .data = &s32g2_data },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pit_timer_of_match);
+
+static struct platform_driver nxp_pit_driver = {
+	.driver = {
+		.name = "nxp-pit",
+		.of_match_table = pit_timer_of_match,
+	},
+	.probe = pit_timer_probe,
+};
+module_platform_driver(nxp_pit_driver);
+
 TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
-- 
2.43.0


  parent reply	other threads:[~2025-07-30  8:28 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-30  8:27 [PATCH v2 00/20] Add support for the NXP automotive S32G PIT Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 01/20] clocksource/drivers/vf-pit: Replace raw_readl/writel to reald/writel Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 02/20] clocksource/drivers/vf-pit: Add COMPILE_TEST option Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 03/20] clocksource/drivers/vf-pit: Set the scene for multiple timers Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 04/20] clocksource/drivers/vf-pit: Rework the base address usage Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 05/20] clocksource/drivers/vf-pit: Pass the cpu number as parameter Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 06/20] clocksource/drivers/vf-pit: Encapsulate the initialization of the cycles_per_jiffy Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 07/20] clocksource/drivers/vf-pit: Allocate the struct timer at init time Daniel Lezcano
2025-08-01  7:33   ` Ghennadi Procopciuc
2025-08-04  9:12     ` Daniel Lezcano
2025-08-04 10:02       ` Ghennadi Procopciuc
2025-07-30  8:27 ` [PATCH v2 08/20] clocksource/drivers/vf-pit: Convert raw values to BIT macros Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 09/20] clocksource/drivers/vf-pit: Register the clocksource from the driver Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 10/20] clocksource/drivers/vf-pit: Encapsulate the macros Daniel Lezcano
2025-08-01  7:33   ` Ghennadi Procopciuc
2025-07-30  8:27 ` [PATCH v2 11/20] clocksource/drivers/vf-pit: Encapsulate the PTLCVAL macro Daniel Lezcano
2025-08-01  7:34   ` Ghennadi Procopciuc
2025-07-30  8:27 ` [PATCH v2 12/20] clocksource/drivers/vf-pit: Use the node name for the interrupt and timer names Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 13/20] clocksource/drivers/vf-pit: Encapsulate clocksource enable / disable Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 14/20] clocksource/drivers/vf-pit: Enable and disable module on error Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 15/20] clocksource/drivers/vf-pit: Encapsulate set counter function Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 16/20] clocksource/drivers/vf-pit: Consolidate calls to pit_*_disable/enable Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 17/20] clocksource/drivers/vf-pit: Unify the function name for irq ack Daniel Lezcano
2025-08-01  7:34   ` Ghennadi Procopciuc
2025-07-30  8:27 ` [PATCH v2 18/20] clocksource/drivers/vf-pit: Rename the VF PIT to NXP PIT Daniel Lezcano
2025-08-01  7:35   ` Ghennadi Procopciuc
2025-08-01  8:48     ` Daniel Lezcano
2025-07-30  8:27 ` [PATCH v2 19/20] dt: bindings: fsl,vf610-pit: Add compatible for s32g2 and s32g3 Daniel Lezcano
2025-07-30 23:36   ` Rob Herring
2025-07-31  7:41     ` Daniel Lezcano
2025-07-31  7:50       ` Krzysztof Kozlowski
2025-07-31  8:24         ` Daniel Lezcano
2025-07-30  8:27 ` Daniel Lezcano [this message]
2025-08-01  7:36   ` [PATCH v2 20/20] clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support Ghennadi Procopciuc

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250730082725.183133-21-daniel.lezcano@linaro.org \
    --to=daniel.lezcano@linaro.org \
    --cc=S32@nxp.com \
    --cc=ghennadi.procopciuc@oss.nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).