diff options
-rw-r--r-- | drivers/clocksource/timer-nxp-pit.c | 128 |
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); |