// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2016 Freescale Semiconductor, Inc. * Copyright 2018,2021-2025 NXP * * NXP System Timer Module: * * STM supports commonly required system and application software * timing functions. STM includes a 32-bit count-up timer and four * 32-bit compare channels with a separate interrupt source for each * channel. The timer is driven by the STM module clock divided by an * 8-bit prescale value (1 to 256). It has ability to stop the timer * in Debug mode
*/ #include <linux/clk.h> #include <linux/clockchips.h> #include <linux/cpuhotplug.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/sched_clock.h> #include <linux/units.h>
/* * Global structure for multiple STMs initialization
*/ staticint stm_instances;
/* * This global lock is used to prevent race conditions with the * stm_instances in case the driver is using the ASYNC option
*/ static DEFINE_MUTEX(stm_instances_lock);
val = nxp_stm_clockevent_read_counter(stm_timer) + delta;
writel(val, STM_CMP0(stm_timer->base));
/* * The counter is shared across the channels and can not be * stopped while we are setting the next event. If the delta * is very small it is possible the counter increases above * the computed 'val'. The min_delta value specified when * registering the clockevent will prevent that. The second * case is if the counter wraps while we compute the 'val' and * before writing the comparator register. We read the counter, * check if we are back in time and abort the timer with -ETIME.
*/ if (val > nxp_stm_clockevent_read_counter(stm_timer) + delta) return -ETIME;
ret = irq_force_affinity(stm_timer->ced.irq, cpumask_of(cpu)); if (ret) return ret;
/* * The timings measurement show reading the counter register * and writing to the comparator register takes as a maximum * value 1100 ns at 133MHz rate frequency. The timer must be * set above this value and to be secure we set the minimum * value equal to 2000ns, so 2us. * * minimum ticks = (rate / MICRO) * 2
*/
clockevents_config_and_register(&stm_timer->ced, stm_timer->rate,
(stm_timer->rate / MICRO) * 2, ULONG_MAX);
/* * The interrupt is shared across the channels in the * module. But this one is configured to run only one channel, * consequently it is pointless to test the interrupt flags * before and we can directly reset the channel 0 irq flag * register.
*/
writel(STM_CIR_CIF, STM_CIR0(stm_timer->base));
/* * Update STM_CMP value using the counter value
*/
val = nxp_stm_clockevent_read_counter(stm_timer) + stm_timer->delta;
writel(val, STM_CMP0(stm_timer->base));
/* * stm hardware doesn't support oneshot, it will generate an * interrupt and start the counter again so software needs to * disable the timer to stop the counter loop in ONESHOT mode.
*/ if (likely(clockevent_state_oneshot(ced)))
nxp_stm_clockevent_disable(stm_timer);
/* * The device tree can have multiple STM nodes described, so * it makes this driver a good candidate for the async probe. * It is still unclear if the time framework correctly handles * parallel loading of the timers but at least this driver is * ready to support the option.
*/
guard(stm_instances)(&stm_instances_lock);
/* * The S32Gx are SoCs featuring a diverse set of cores. Linux * is expected to run on Cortex-A53 cores, while other * software stacks will operate on Cortex-M cores. The number * of STM instances has been sized to include at most one * instance per core. * * As we need a clocksource and a clockevent per cpu, we * simply initialize a clocksource per cpu along with the * clockevent which makes the resulting code simpler. * * However if the device tree is describing more STM instances * than the number of cores, then we ignore them.
*/ if (stm_instances >= num_possible_cpus()) return 0;
base = devm_of_iomap(dev, np, 0, NULL); if (IS_ERR(base)) return dev_err_probe(dev, PTR_ERR(base), "Failed to iomap %pOFn\n", np);
irq = platform_get_irq(pdev, 0); if (irq < 0) return dev_err_probe(dev, irq, "Failed to get IRQ\n");
clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "Clock not found\n");
stm_timer = devm_kzalloc(dev, sizeof(*stm_timer), GFP_KERNEL); if (!stm_timer) return -ENOMEM;
ret = devm_request_irq(dev, irq, nxp_stm_module_interrupt,
IRQF_TIMER | IRQF_NOBALANCING, name, stm_timer); if (ret) return dev_err_probe(dev, ret, "Unable to allocate interrupt line\n");
ret = nxp_stm_clocksource_init(dev, stm_timer, name, base, clk); if (ret) return ret;
/* * Next probed STM will be a per CPU clockevent, until we * probe as many as we have CPUs available on the system, we * do a partial initialization
*/
ret = nxp_stm_clockevent_per_cpu_init(dev, stm_timer, name,
base, irq, clk,
stm_instances); if (ret) return ret;
stm_instances++;
/* * The number of probed STMs for per CPU clockevent is * equal to the number of available CPUs on the * system. We install the cpu hotplug to finish the * initialization by registering the clockevents
*/ if (stm_instances == num_possible_cpus()) {
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "STM timer:starting",
nxp_stm_clockevent_starting_cpu, NULL); if (ret < 0) return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.