Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support

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>
Link: https://lore.kernel.org/r/20250804152344.1109310-21-daniel.lezcano@linaro.org

+110 -20
+110 -20
drivers/clocksource/timer-nxp-pit.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 3 * Copyright 2012-2013 Freescale Semiconductor, Inc. 4 + * Copyright 2018,2021-2025 NXP 4 5 */ 5 - 6 6 #include <linux/interrupt.h> 7 7 #include <linux/clockchips.h> 8 + #include <linux/cpuhotplug.h> 8 9 #include <linux/clk.h> 9 10 #include <linux/of_address.h> 10 11 #include <linux/of_irq.h> 11 12 #include <linux/sched_clock.h> 13 + #include <linux/platform_device.h> 12 14 13 15 /* 14 16 * Each pit takes 0x10 Bytes register space ··· 39 37 struct pit_timer { 40 38 void __iomem *clksrc_base; 41 39 void __iomem *clkevt_base; 42 - unsigned long cycle_per_jiffy; 43 40 struct clock_event_device ced; 44 41 struct clocksource cs; 42 + int rate; 45 43 }; 44 + 45 + struct pit_timer_data { 46 + int max_pit_instances; 47 + }; 48 + 49 + static DEFINE_PER_CPU(struct pit_timer *, pit_timers); 50 + 51 + /* 52 + * Global structure for multiple PITs initialization 53 + */ 54 + static int pit_instances; 55 + static int max_pit_instances = 1; 46 56 47 57 static void __iomem *sched_clock_base; 48 58 ··· 112 98 return (u64)~readl(PITCVAL(pit->clksrc_base)); 113 99 } 114 100 115 - static int __init pit_clocksource_init(struct pit_timer *pit, const char *name, 116 - void __iomem *base, unsigned long rate) 101 + static int pit_clocksource_init(struct pit_timer *pit, const char *name, 102 + void __iomem *base, unsigned long rate) 117 103 { 118 104 /* 119 105 * The channels 0 and 1 can be chained to build a 64-bit ··· 169 155 { 170 156 struct pit_timer *pit = ced_to_pit(ced); 171 157 172 - pit_set_next_event(pit->cycle_per_jiffy, ced); 158 + pit_set_next_event(pit->rate / HZ, ced); 173 159 174 160 return 0; 175 161 } ··· 195 181 return IRQ_HANDLED; 196 182 } 197 183 198 - static int __init pit_clockevent_init(struct pit_timer *pit, const char *name, 199 - void __iomem *base, unsigned long rate, 200 - int irq, unsigned int cpu) 184 + static int pit_clockevent_per_cpu_init(struct pit_timer *pit, const char *name, 185 + void __iomem *base, unsigned long rate, 186 + int irq, unsigned int cpu) 201 187 { 188 + int ret; 189 + 202 190 /* 203 191 * The channels 0 and 1 can be chained to build a 64-bit 204 192 * timer. Let's use the channel 3 as a clockevent and leave 205 193 * the channels 0 and 1 unused for anyone else who needs them 206 194 */ 207 195 pit->clkevt_base = base + PIT_CH(3); 208 - pit->cycle_per_jiffy = rate / (HZ); 196 + pit->rate = rate; 209 197 210 198 pit_timer_disable(pit->clkevt_base); 211 199 212 200 pit_timer_irqack(pit); 213 201 214 - BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 215 - name, &pit->ced)); 202 + ret = request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_NOBALANCING, 203 + name, &pit->ced); 204 + if (ret) 205 + return ret; 216 206 217 207 pit->ced.cpumask = cpumask_of(cpu); 218 208 pit->ced.irq = irq; ··· 228 210 pit->ced.set_next_event = pit_set_next_event; 229 211 pit->ced.rating = 300; 230 212 213 + per_cpu(pit_timers, cpu) = pit; 214 + 215 + return 0; 216 + } 217 + 218 + static void pit_clockevent_per_cpu_exit(struct pit_timer *pit, unsigned int cpu) 219 + { 220 + pit_timer_disable(pit->clkevt_base); 221 + free_irq(pit->ced.irq, &pit->ced); 222 + per_cpu(pit_timers, cpu) = NULL; 223 + } 224 + 225 + static int pit_clockevent_starting_cpu(unsigned int cpu) 226 + { 227 + struct pit_timer *pit = per_cpu(pit_timers, cpu); 228 + int ret; 229 + 230 + if (!pit) 231 + return 0; 232 + 233 + ret = irq_force_affinity(pit->ced.irq, cpumask_of(cpu)); 234 + if (ret) { 235 + pit_clockevent_per_cpu_exit(pit, cpu); 236 + return ret; 237 + } 238 + 231 239 /* 232 240 * The value for the LDVAL register trigger is calculated as: 233 241 * LDVAL trigger = (period / clock period) - 1 ··· 262 218 * LDVAL trigger value is 1. And then the min_delta is 263 219 * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. 264 220 */ 265 - clockevents_config_and_register(&pit->ced, rate, 2, 0xffffffff); 221 + clockevents_config_and_register(&pit->ced, pit->rate, 2, 0xffffffff); 266 222 267 223 return 0; 268 224 } 269 225 270 - static int __init pit_timer_init(struct device_node *np) 226 + static int pit_timer_init(struct device_node *np) 271 227 { 272 228 struct pit_timer *pit; 273 229 struct clk *pit_clk; ··· 297 253 pit_clk = of_clk_get(np, 0); 298 254 if (IS_ERR(pit_clk)) { 299 255 ret = PTR_ERR(pit_clk); 300 - goto out_iounmap; 256 + goto out_irq_dispose_mapping; 301 257 } 302 258 303 259 ret = clk_prepare_enable(pit_clk); ··· 306 262 307 263 clk_rate = clk_get_rate(pit_clk); 308 264 265 + pit_module_disable(timer_base); 266 + 267 + ret = pit_clocksource_init(pit, name, timer_base, clk_rate); 268 + if (ret) { 269 + pr_err("Failed to initialize clocksource '%pOF'\n", np); 270 + goto out_pit_module_disable; 271 + } 272 + 273 + ret = pit_clockevent_per_cpu_init(pit, name, timer_base, clk_rate, irq, pit_instances); 274 + if (ret) { 275 + pr_err("Failed to initialize clockevent '%pOF'\n", np); 276 + goto out_pit_clocksource_unregister; 277 + } 278 + 309 279 /* enable the pit module */ 310 280 pit_module_enable(timer_base); 311 281 312 - ret = pit_clocksource_init(pit, name, timer_base, clk_rate); 313 - if (ret) 314 - goto out_pit_module_disable; 282 + pit_instances++; 315 283 316 - ret = pit_clockevent_init(pit, name, timer_base, clk_rate, irq, 0); 317 - if (ret) 318 - goto out_pit_clocksource_unregister; 284 + if (pit_instances == max_pit_instances) { 285 + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "PIT timer:starting", 286 + pit_clockevent_starting_cpu, NULL); 287 + if (ret < 0) 288 + goto out_pit_clocksource_unregister; 289 + } 319 290 320 291 return 0; 321 292 ··· 341 282 clk_disable_unprepare(pit_clk); 342 283 out_clk_put: 343 284 clk_put(pit_clk); 285 + out_irq_dispose_mapping: 286 + irq_dispose_mapping(irq); 344 287 out_iounmap: 345 288 iounmap(timer_base); 346 289 out_kfree: ··· 350 289 351 290 return ret; 352 291 } 292 + 293 + static int pit_timer_probe(struct platform_device *pdev) 294 + { 295 + const struct pit_timer_data *pit_timer_data; 296 + 297 + pit_timer_data = of_device_get_match_data(&pdev->dev); 298 + if (pit_timer_data) 299 + max_pit_instances = pit_timer_data->max_pit_instances; 300 + 301 + return pit_timer_init(pdev->dev.of_node); 302 + } 303 + 304 + static struct pit_timer_data s32g2_data = { .max_pit_instances = 2 }; 305 + 306 + static const struct of_device_id pit_timer_of_match[] = { 307 + { .compatible = "nxp,s32g2-pit", .data = &s32g2_data }, 308 + { } 309 + }; 310 + MODULE_DEVICE_TABLE(of, pit_timer_of_match); 311 + 312 + static struct platform_driver nxp_pit_driver = { 313 + .driver = { 314 + .name = "nxp-pit", 315 + .of_match_table = pit_timer_of_match, 316 + }, 317 + .probe = pit_timer_probe, 318 + }; 319 + module_platform_driver(nxp_pit_driver); 320 + 353 321 TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);