summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clocksource/timer-nxp-pit.c128
1 files changed, 109 insertions, 19 deletions
diff --git a/drivers/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c
index 2a0ee4109ead..2d0a3554b6bf 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,32 @@ 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 void pit_clockevent_per_cpu_exit(struct pit_timer *pit, unsigned int cpu)
+{
+ pit_timer_disable(pit->clkevt_base);
+ free_irq(pit->ced.irq, &pit->ced);
+ per_cpu(pit_timers, cpu) = NULL;
+}
+
+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) {
+ pit_clockevent_per_cpu_exit(pit, cpu);
+ return ret;
+ }
+
/*
* The value for the LDVAL register trigger is calculated as:
* LDVAL trigger = (period / clock period) - 1
@@ -218,12 +262,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;
@@ -253,7 +297,7 @@ static int __init pit_timer_init(struct device_node *np)
pit_clk = of_clk_get(np, 0);
if (IS_ERR(pit_clk)) {
ret = PTR_ERR(pit_clk);
- goto out_iounmap;
+ goto out_irq_dispose_mapping;
}
ret = clk_prepare_enable(pit_clk);
@@ -262,16 +306,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;
@@ -282,6 +341,8 @@ out_pit_module_disable:
clk_disable_unprepare(pit_clk);
out_clk_put:
clk_put(pit_clk);
+out_irq_dispose_mapping:
+ irq_dispose_mapping(irq);
out_iounmap:
iounmap(timer_base);
out_kfree:
@@ -289,4 +350,33 @@ out_kfree:
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);