Skip to content

Commit f19d838

Browse files
XBurstdlezcano
authored andcommitted
clocksource/drivers/ingenic: Add high resolution timer support for SMP/SMT.
Enable clock event handling on per CPU core basis. Make sure that interrupts raised on the first core execute event handlers on the correct CPU core. This driver is required by Ingenic processors that support SMP/SMT, such as JZ4780 and X2000. Tested-by: H. Nikolaus Schaller <[email protected]> Tested-by: Paul Boddie <[email protected]> Signed-off-by: Paul Cercueil <[email protected]> Signed-off-by: 周琰杰 (Zhou Yanjie) <[email protected]> Signed-off-by: Daniel Lezcano <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 467ae18 commit f19d838

File tree

1 file changed

+124
-58
lines changed

1 file changed

+124
-58
lines changed

drivers/clocksource/ingenic-timer.c

Lines changed: 124 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// SPDX-License-Identifier: GPL-2.0
22
/*
3-
* JZ47xx SoCs TCU IRQ driver
3+
* Ingenic SoCs TCU IRQ driver
44
* Copyright (C) 2019 Paul Cercueil <[email protected]>
5+
* Copyright (C) 2020 周琰杰 (Zhou Yanjie) <[email protected]>
56
*/
67

78
#include <linux/bitops.h>
@@ -15,24 +16,35 @@
1516
#include <linux/of_address.h>
1617
#include <linux/of_irq.h>
1718
#include <linux/of_platform.h>
19+
#include <linux/overflow.h>
1820
#include <linux/platform_device.h>
1921
#include <linux/regmap.h>
2022
#include <linux/sched_clock.h>
2123

2224
#include <dt-bindings/clock/ingenic,tcu.h>
2325

26+
static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
27+
2428
struct ingenic_soc_info {
2529
unsigned int num_channels;
2630
};
2731

32+
struct ingenic_tcu_timer {
33+
unsigned int cpu;
34+
unsigned int channel;
35+
struct clock_event_device cevt;
36+
struct clk *clk;
37+
char name[8];
38+
};
39+
2840
struct ingenic_tcu {
2941
struct regmap *map;
30-
struct clk *timer_clk, *cs_clk;
31-
unsigned int timer_channel, cs_channel;
32-
struct clock_event_device cevt;
42+
struct device_node *np;
43+
struct clk *cs_clk;
44+
unsigned int cs_channel;
3345
struct clocksource cs;
34-
char name[4];
3546
unsigned long pwm_channels_mask;
47+
struct ingenic_tcu_timer timers[];
3648
};
3749

3850
static struct ingenic_tcu *ingenic_tcu;
@@ -52,44 +64,65 @@ static u64 notrace ingenic_tcu_timer_cs_read(struct clocksource *cs)
5264
return ingenic_tcu_timer_read();
5365
}
5466

55-
static inline struct ingenic_tcu *to_ingenic_tcu(struct clock_event_device *evt)
67+
static inline struct ingenic_tcu *
68+
to_ingenic_tcu(struct ingenic_tcu_timer *timer)
69+
{
70+
return container_of(timer, struct ingenic_tcu, timers[timer->cpu]);
71+
}
72+
73+
static inline struct ingenic_tcu_timer *
74+
to_ingenic_tcu_timer(struct clock_event_device *evt)
5675
{
57-
return container_of(evt, struct ingenic_tcu, cevt);
76+
return container_of(evt, struct ingenic_tcu_timer, cevt);
5877
}
5978

6079
static int ingenic_tcu_cevt_set_state_shutdown(struct clock_event_device *evt)
6180
{
62-
struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
81+
struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt);
82+
struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
6383

64-
regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
84+
regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel));
6585

6686
return 0;
6787
}
6888

6989
static int ingenic_tcu_cevt_set_next(unsigned long next,
7090
struct clock_event_device *evt)
7191
{
72-
struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
92+
struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt);
93+
struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
7394

7495
if (next > 0xffff)
7596
return -EINVAL;
7697

77-
regmap_write(tcu->map, TCU_REG_TDFRc(tcu->timer_channel), next);
78-
regmap_write(tcu->map, TCU_REG_TCNTc(tcu->timer_channel), 0);
79-
regmap_write(tcu->map, TCU_REG_TESR, BIT(tcu->timer_channel));
98+
regmap_write(tcu->map, TCU_REG_TDFRc(timer->channel), next);
99+
regmap_write(tcu->map, TCU_REG_TCNTc(timer->channel), 0);
100+
regmap_write(tcu->map, TCU_REG_TESR, BIT(timer->channel));
80101

81102
return 0;
82103
}
83104

105+
static void ingenic_per_cpu_event_handler(void *info)
106+
{
107+
struct clock_event_device *cevt = (struct clock_event_device *) info;
108+
109+
cevt->event_handler(cevt);
110+
}
111+
84112
static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
85113
{
86-
struct clock_event_device *evt = dev_id;
87-
struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
114+
struct ingenic_tcu_timer *timer = dev_id;
115+
struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
116+
call_single_data_t *csd;
88117

89-
regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
118+
regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel));
90119

91-
if (evt->event_handler)
92-
evt->event_handler(evt);
120+
if (timer->cevt.event_handler) {
121+
csd = &per_cpu(ingenic_cevt_csd, timer->cpu);
122+
csd->info = (void *) &timer->cevt;
123+
csd->func = ingenic_per_cpu_event_handler;
124+
smp_call_function_single_async(timer->cpu, csd);
125+
}
93126

94127
return IRQ_HANDLED;
95128
}
@@ -105,64 +138,66 @@ static struct clk * __init ingenic_tcu_get_clock(struct device_node *np, int id)
105138
return of_clk_get_from_provider(&args);
106139
}
107140

108-
static int __init ingenic_tcu_timer_init(struct device_node *np,
109-
struct ingenic_tcu *tcu)
141+
static int ingenic_tcu_setup_cevt(unsigned int cpu)
110142
{
111-
unsigned int timer_virq, channel = tcu->timer_channel;
143+
struct ingenic_tcu *tcu = ingenic_tcu;
144+
struct ingenic_tcu_timer *timer = &tcu->timers[cpu];
145+
unsigned int timer_virq;
112146
struct irq_domain *domain;
113147
unsigned long rate;
114148
int err;
115149

116-
tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
117-
if (IS_ERR(tcu->timer_clk))
118-
return PTR_ERR(tcu->timer_clk);
150+
timer->clk = ingenic_tcu_get_clock(tcu->np, timer->channel);
151+
if (IS_ERR(timer->clk))
152+
return PTR_ERR(timer->clk);
119153

120-
err = clk_prepare_enable(tcu->timer_clk);
154+
err = clk_prepare_enable(timer->clk);
121155
if (err)
122156
goto err_clk_put;
123157

124-
rate = clk_get_rate(tcu->timer_clk);
158+
rate = clk_get_rate(timer->clk);
125159
if (!rate) {
126160
err = -EINVAL;
127161
goto err_clk_disable;
128162
}
129163

130-
domain = irq_find_host(np);
164+
domain = irq_find_host(tcu->np);
131165
if (!domain) {
132166
err = -ENODEV;
133167
goto err_clk_disable;
134168
}
135169

136-
timer_virq = irq_create_mapping(domain, channel);
170+
timer_virq = irq_create_mapping(domain, timer->channel);
137171
if (!timer_virq) {
138172
err = -EINVAL;
139173
goto err_clk_disable;
140174
}
141175

142-
snprintf(tcu->name, sizeof(tcu->name), "TCU");
176+
snprintf(timer->name, sizeof(timer->name), "TCU%u", timer->channel);
143177

144178
err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
145-
tcu->name, &tcu->cevt);
179+
timer->name, timer);
146180
if (err)
147181
goto err_irq_dispose_mapping;
148182

149-
tcu->cevt.cpumask = cpumask_of(smp_processor_id());
150-
tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
151-
tcu->cevt.name = tcu->name;
152-
tcu->cevt.rating = 200;
153-
tcu->cevt.set_state_shutdown = ingenic_tcu_cevt_set_state_shutdown;
154-
tcu->cevt.set_next_event = ingenic_tcu_cevt_set_next;
183+
timer->cpu = smp_processor_id();
184+
timer->cevt.cpumask = cpumask_of(smp_processor_id());
185+
timer->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
186+
timer->cevt.name = timer->name;
187+
timer->cevt.rating = 200;
188+
timer->cevt.set_state_shutdown = ingenic_tcu_cevt_set_state_shutdown;
189+
timer->cevt.set_next_event = ingenic_tcu_cevt_set_next;
155190

156-
clockevents_config_and_register(&tcu->cevt, rate, 10, 0xffff);
191+
clockevents_config_and_register(&timer->cevt, rate, 10, 0xffff);
157192

158193
return 0;
159194

160195
err_irq_dispose_mapping:
161196
irq_dispose_mapping(timer_virq);
162197
err_clk_disable:
163-
clk_disable_unprepare(tcu->timer_clk);
198+
clk_disable_unprepare(timer->clk);
164199
err_clk_put:
165-
clk_put(tcu->timer_clk);
200+
clk_put(timer->clk);
166201
return err;
167202
}
168203

@@ -238,52 +273,73 @@ static int __init ingenic_tcu_init(struct device_node *np)
238273
{
239274
const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np);
240275
const struct ingenic_soc_info *soc_info = id->data;
276+
struct ingenic_tcu_timer *timer;
241277
struct ingenic_tcu *tcu;
242278
struct regmap *map;
279+
unsigned int cpu;
280+
int ret, last_bit = -1;
243281
long rate;
244-
int ret;
245282

246283
of_node_clear_flag(np, OF_POPULATED);
247284

248285
map = device_node_to_regmap(np);
249286
if (IS_ERR(map))
250287
return PTR_ERR(map);
251288

252-
tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
289+
tcu = kzalloc(struct_size(tcu, timers, num_possible_cpus()),
290+
GFP_KERNEL);
253291
if (!tcu)
254292
return -ENOMEM;
255293

256-
/* Enable all TCU channels for PWM use by default except channels 0/1 */
257-
tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2);
294+
/*
295+
* Enable all TCU channels for PWM use by default except channels 0/1,
296+
* and channel 2 if target CPU is JZ4780/X2000 and SMP is selected.
297+
*/
298+
tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
299+
num_possible_cpus() + 1);
258300
of_property_read_u32(np, "ingenic,pwm-channels-mask",
259301
(u32 *)&tcu->pwm_channels_mask);
260302

261-
/* Verify that we have at least two free channels */
262-
if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels - 2) {
303+
/* Verify that we have at least num_possible_cpus() + 1 free channels */
304+
if (hweight8(tcu->pwm_channels_mask) >
305+
soc_info->num_channels - num_possible_cpus() + 1) {
263306
pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
264307
tcu->pwm_channels_mask);
265308
ret = -EINVAL;
266309
goto err_free_ingenic_tcu;
267310
}
268311

269312
tcu->map = map;
313+
tcu->np = np;
270314
ingenic_tcu = tcu;
271315

272-
tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask,
273-
soc_info->num_channels);
316+
for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
317+
timer = &tcu->timers[cpu];
318+
319+
timer->cpu = cpu;
320+
timer->channel = find_next_zero_bit(&tcu->pwm_channels_mask,
321+
soc_info->num_channels,
322+
last_bit + 1);
323+
last_bit = timer->channel;
324+
}
325+
274326
tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
275327
soc_info->num_channels,
276-
tcu->timer_channel + 1);
328+
last_bit + 1);
277329

278330
ret = ingenic_tcu_clocksource_init(np, tcu);
279331
if (ret) {
280332
pr_crit("%s: Unable to init clocksource: %d\n", __func__, ret);
281333
goto err_free_ingenic_tcu;
282334
}
283335

284-
ret = ingenic_tcu_timer_init(np, tcu);
285-
if (ret)
336+
/* Setup clock events on each CPU core */
337+
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst: online",
338+
ingenic_tcu_setup_cevt, NULL);
339+
if (ret < 0) {
340+
pr_crit("%s: Unable to start CPU timers: %d\n", __func__, ret);
286341
goto err_tcu_clocksource_cleanup;
342+
}
287343

288344
/* Register the sched_clock at the end as there's no way to undo it */
289345
rate = clk_get_rate(tcu->cs_clk);
@@ -315,28 +371,38 @@ static int __init ingenic_tcu_probe(struct platform_device *pdev)
315371
static int __maybe_unused ingenic_tcu_suspend(struct device *dev)
316372
{
317373
struct ingenic_tcu *tcu = dev_get_drvdata(dev);
374+
unsigned int cpu;
318375

319376
clk_disable(tcu->cs_clk);
320-
clk_disable(tcu->timer_clk);
377+
378+
for (cpu = 0; cpu < num_online_cpus(); cpu++)
379+
clk_disable(tcu->timers[cpu].clk);
380+
321381
return 0;
322382
}
323383

324384
static int __maybe_unused ingenic_tcu_resume(struct device *dev)
325385
{
326386
struct ingenic_tcu *tcu = dev_get_drvdata(dev);
387+
unsigned int cpu;
327388
int ret;
328389

329-
ret = clk_enable(tcu->timer_clk);
330-
if (ret)
331-
return ret;
390+
for (cpu = 0; cpu < num_online_cpus(); cpu++) {
391+
ret = clk_enable(tcu->timers[cpu].clk);
392+
if (ret)
393+
goto err_timer_clk_disable;
394+
}
332395

333396
ret = clk_enable(tcu->cs_clk);
334-
if (ret) {
335-
clk_disable(tcu->timer_clk);
336-
return ret;
337-
}
397+
if (ret)
398+
goto err_timer_clk_disable;
338399

339400
return 0;
401+
402+
err_timer_clk_disable:
403+
for (; cpu > 0; cpu--)
404+
clk_disable(tcu->timers[cpu - 1].clk);
405+
return ret;
340406
}
341407

342408
static const struct dev_pm_ops __maybe_unused ingenic_tcu_pm_ops = {

0 commit comments

Comments
 (0)