Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/counter/microchip-tcb-capture.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2020 Microchip
4
*
5
* Author: Kamel Bouhara <[email protected]>
6
*/
7
#include <linux/clk.h>
8
#include <linux/counter.h>
9
#include <linux/interrupt.h>
10
#include <linux/mfd/syscon.h>
11
#include <linux/module.h>
12
#include <linux/mutex.h>
13
#include <linux/of.h>
14
#include <linux/of_irq.h>
15
#include <linux/platform_device.h>
16
#include <linux/regmap.h>
17
#include <uapi/linux/counter/microchip-tcb-capture.h>
18
#include <soc/at91/atmel_tcb.h>
19
20
#define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \
21
ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \
22
ATMEL_TC_LDBSTOP)
23
24
#define ATMEL_TC_DEF_IRQS (ATMEL_TC_ETRGS | ATMEL_TC_COVFS | \
25
ATMEL_TC_LDRAS | ATMEL_TC_LDRBS | ATMEL_TC_CPCS)
26
27
#define ATMEL_TC_QDEN BIT(8)
28
#define ATMEL_TC_POSEN BIT(9)
29
30
struct mchp_tc_data {
31
const struct atmel_tcb_config *tc_cfg;
32
struct regmap *regmap;
33
int qdec_mode;
34
int num_channels;
35
int channel[2];
36
};
37
38
static const enum counter_function mchp_tc_count_functions[] = {
39
COUNTER_FUNCTION_INCREASE,
40
COUNTER_FUNCTION_QUADRATURE_X4,
41
};
42
43
static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
44
COUNTER_SYNAPSE_ACTION_NONE,
45
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
46
COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
47
COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
48
};
49
50
static struct counter_signal mchp_tc_count_signals[] = {
51
{
52
.id = 0,
53
.name = "Channel A",
54
},
55
{
56
.id = 1,
57
.name = "Channel B",
58
}
59
};
60
61
static struct counter_synapse mchp_tc_count_synapses[] = {
62
{
63
.actions_list = mchp_tc_synapse_actions,
64
.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
65
.signal = &mchp_tc_count_signals[0]
66
},
67
{
68
.actions_list = mchp_tc_synapse_actions,
69
.num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
70
.signal = &mchp_tc_count_signals[1]
71
}
72
};
73
74
static int mchp_tc_count_function_read(struct counter_device *counter,
75
struct counter_count *count,
76
enum counter_function *function)
77
{
78
struct mchp_tc_data *const priv = counter_priv(counter);
79
80
if (priv->qdec_mode)
81
*function = COUNTER_FUNCTION_QUADRATURE_X4;
82
else
83
*function = COUNTER_FUNCTION_INCREASE;
84
85
return 0;
86
}
87
88
static int mchp_tc_count_function_write(struct counter_device *counter,
89
struct counter_count *count,
90
enum counter_function function)
91
{
92
struct mchp_tc_data *const priv = counter_priv(counter);
93
u32 bmr, cmr;
94
95
regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);
96
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
97
98
/* Set capture mode */
99
cmr &= ~ATMEL_TC_WAVE;
100
101
switch (function) {
102
case COUNTER_FUNCTION_INCREASE:
103
priv->qdec_mode = 0;
104
/* Set highest rate based on whether soc has gclk or not */
105
bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
106
if (!priv->tc_cfg->has_gclk)
107
cmr |= ATMEL_TC_TIMER_CLOCK2;
108
else
109
cmr |= ATMEL_TC_TIMER_CLOCK1;
110
/* Setup the period capture mode */
111
cmr |= ATMEL_TC_CMR_MASK;
112
cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
113
break;
114
case COUNTER_FUNCTION_QUADRATURE_X4:
115
if (!priv->tc_cfg->has_qdec)
116
return -EINVAL;
117
/* In QDEC mode settings both channels 0 and 1 are required */
118
if (priv->num_channels < 2 || priv->channel[0] != 0 ||
119
priv->channel[1] != 1) {
120
pr_err("Invalid channels number or id for quadrature mode\n");
121
return -EINVAL;
122
}
123
priv->qdec_mode = 1;
124
bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;
125
cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;
126
break;
127
default:
128
/* should never reach this path */
129
return -EINVAL;
130
}
131
132
regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);
133
regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);
134
135
/* Enable clock and trigger counter */
136
regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),
137
ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
138
139
if (priv->qdec_mode) {
140
regmap_write(priv->regmap,
141
ATMEL_TC_REG(priv->channel[1], CMR), cmr);
142
regmap_write(priv->regmap,
143
ATMEL_TC_REG(priv->channel[1], CCR),
144
ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
145
}
146
147
return 0;
148
}
149
150
static int mchp_tc_count_signal_read(struct counter_device *counter,
151
struct counter_signal *signal,
152
enum counter_signal_level *lvl)
153
{
154
struct mchp_tc_data *const priv = counter_priv(counter);
155
bool sigstatus;
156
u32 sr;
157
158
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
159
160
if (signal->id == 1)
161
sigstatus = (sr & ATMEL_TC_MTIOB);
162
else
163
sigstatus = (sr & ATMEL_TC_MTIOA);
164
165
*lvl = sigstatus ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
166
167
return 0;
168
}
169
170
static int mchp_tc_count_action_read(struct counter_device *counter,
171
struct counter_count *count,
172
struct counter_synapse *synapse,
173
enum counter_synapse_action *action)
174
{
175
struct mchp_tc_data *const priv = counter_priv(counter);
176
u32 cmr;
177
178
if (priv->qdec_mode) {
179
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
180
return 0;
181
}
182
183
/* Only TIOA signal is evaluated in non-QDEC mode */
184
if (synapse->signal->id != 0) {
185
*action = COUNTER_SYNAPSE_ACTION_NONE;
186
return 0;
187
}
188
189
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
190
191
switch (cmr & ATMEL_TC_ETRGEDG) {
192
default:
193
*action = COUNTER_SYNAPSE_ACTION_NONE;
194
break;
195
case ATMEL_TC_ETRGEDG_RISING:
196
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
197
break;
198
case ATMEL_TC_ETRGEDG_FALLING:
199
*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
200
break;
201
case ATMEL_TC_ETRGEDG_BOTH:
202
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
203
break;
204
}
205
206
return 0;
207
}
208
209
static int mchp_tc_count_action_write(struct counter_device *counter,
210
struct counter_count *count,
211
struct counter_synapse *synapse,
212
enum counter_synapse_action action)
213
{
214
struct mchp_tc_data *const priv = counter_priv(counter);
215
u32 edge = ATMEL_TC_ETRGEDG_NONE;
216
217
/* QDEC mode is rising edge only; only TIOA handled in non-QDEC mode */
218
if (priv->qdec_mode || synapse->signal->id != 0)
219
return -EINVAL;
220
221
switch (action) {
222
case COUNTER_SYNAPSE_ACTION_NONE:
223
edge = ATMEL_TC_ETRGEDG_NONE;
224
break;
225
case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
226
edge = ATMEL_TC_ETRGEDG_RISING;
227
break;
228
case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
229
edge = ATMEL_TC_ETRGEDG_FALLING;
230
break;
231
case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
232
edge = ATMEL_TC_ETRGEDG_BOTH;
233
break;
234
default:
235
/* should never reach this path */
236
return -EINVAL;
237
}
238
239
return regmap_write_bits(priv->regmap,
240
ATMEL_TC_REG(priv->channel[0], CMR),
241
ATMEL_TC_ETRGEDG, edge);
242
}
243
244
static int mchp_tc_count_read(struct counter_device *counter,
245
struct counter_count *count, u64 *val)
246
{
247
struct mchp_tc_data *const priv = counter_priv(counter);
248
u32 cnt;
249
250
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);
251
*val = cnt;
252
253
return 0;
254
}
255
256
static int mchp_tc_count_cap_read(struct counter_device *counter,
257
struct counter_count *count, size_t idx, u64 *val)
258
{
259
struct mchp_tc_data *const priv = counter_priv(counter);
260
u32 cnt;
261
int ret;
262
263
switch (idx) {
264
case COUNTER_MCHP_EXCAP_RA:
265
ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), &cnt);
266
break;
267
case COUNTER_MCHP_EXCAP_RB:
268
ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), &cnt);
269
break;
270
default:
271
return -EINVAL;
272
}
273
274
if (ret < 0)
275
return ret;
276
277
*val = cnt;
278
279
return 0;
280
}
281
282
static int mchp_tc_count_cap_write(struct counter_device *counter,
283
struct counter_count *count, size_t idx, u64 val)
284
{
285
struct mchp_tc_data *const priv = counter_priv(counter);
286
int ret;
287
288
if (val > U32_MAX)
289
return -ERANGE;
290
291
switch (idx) {
292
case COUNTER_MCHP_EXCAP_RA:
293
ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), val);
294
break;
295
case COUNTER_MCHP_EXCAP_RB:
296
ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), val);
297
break;
298
default:
299
return -EINVAL;
300
}
301
302
return ret;
303
}
304
305
static int mchp_tc_count_compare_read(struct counter_device *counter, struct counter_count *count,
306
u64 *val)
307
{
308
struct mchp_tc_data *const priv = counter_priv(counter);
309
u32 cnt;
310
int ret;
311
312
ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), &cnt);
313
if (ret < 0)
314
return ret;
315
316
*val = cnt;
317
318
return 0;
319
}
320
321
static int mchp_tc_count_compare_write(struct counter_device *counter, struct counter_count *count,
322
u64 val)
323
{
324
struct mchp_tc_data *const priv = counter_priv(counter);
325
326
if (val > U32_MAX)
327
return -ERANGE;
328
329
return regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), val);
330
}
331
332
static DEFINE_COUNTER_ARRAY_CAPTURE(mchp_tc_cnt_cap_array, 2);
333
334
static struct counter_comp mchp_tc_count_ext[] = {
335
COUNTER_COMP_ARRAY_CAPTURE(mchp_tc_count_cap_read, mchp_tc_count_cap_write,
336
mchp_tc_cnt_cap_array),
337
COUNTER_COMP_COMPARE(mchp_tc_count_compare_read, mchp_tc_count_compare_write),
338
};
339
340
static int mchp_tc_watch_validate(struct counter_device *counter,
341
const struct counter_watch *watch)
342
{
343
if (watch->channel == COUNTER_MCHP_EVCHN_CV || watch->channel == COUNTER_MCHP_EVCHN_RA)
344
switch (watch->event) {
345
case COUNTER_EVENT_CHANGE_OF_STATE:
346
case COUNTER_EVENT_OVERFLOW:
347
case COUNTER_EVENT_CAPTURE:
348
return 0;
349
default:
350
return -EINVAL;
351
}
352
353
if (watch->channel == COUNTER_MCHP_EVCHN_RB && watch->event == COUNTER_EVENT_CAPTURE)
354
return 0;
355
356
if (watch->channel == COUNTER_MCHP_EVCHN_RC && watch->event == COUNTER_EVENT_THRESHOLD)
357
return 0;
358
359
return -EINVAL;
360
}
361
362
static struct counter_count mchp_tc_counts[] = {
363
{
364
.id = 0,
365
.name = "Timer Counter",
366
.functions_list = mchp_tc_count_functions,
367
.num_functions = ARRAY_SIZE(mchp_tc_count_functions),
368
.synapses = mchp_tc_count_synapses,
369
.num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),
370
.ext = mchp_tc_count_ext,
371
.num_ext = ARRAY_SIZE(mchp_tc_count_ext),
372
},
373
};
374
375
static const struct counter_ops mchp_tc_ops = {
376
.signal_read = mchp_tc_count_signal_read,
377
.count_read = mchp_tc_count_read,
378
.function_read = mchp_tc_count_function_read,
379
.function_write = mchp_tc_count_function_write,
380
.action_read = mchp_tc_count_action_read,
381
.action_write = mchp_tc_count_action_write,
382
.watch_validate = mchp_tc_watch_validate,
383
};
384
385
static const struct atmel_tcb_config tcb_rm9200_config = {
386
.counter_width = 16,
387
};
388
389
static const struct atmel_tcb_config tcb_sam9x5_config = {
390
.counter_width = 32,
391
};
392
393
static const struct atmel_tcb_config tcb_sama5d2_config = {
394
.counter_width = 32,
395
.has_gclk = true,
396
.has_qdec = true,
397
};
398
399
static const struct atmel_tcb_config tcb_sama5d3_config = {
400
.counter_width = 32,
401
.has_qdec = true,
402
};
403
404
static const struct of_device_id atmel_tc_of_match[] = {
405
{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
406
{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
407
{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
408
{ .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },
409
{ /* sentinel */ }
410
};
411
412
static irqreturn_t mchp_tc_isr(int irq, void *dev_id)
413
{
414
struct counter_device *const counter = dev_id;
415
struct mchp_tc_data *const priv = counter_priv(counter);
416
u32 sr, mask;
417
418
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
419
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], IMR), &mask);
420
421
sr &= mask;
422
if (!(sr & ATMEL_TC_ALL_IRQ))
423
return IRQ_NONE;
424
425
if (sr & ATMEL_TC_ETRGS)
426
counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE,
427
COUNTER_MCHP_EVCHN_CV);
428
if (sr & ATMEL_TC_LDRAS)
429
counter_push_event(counter, COUNTER_EVENT_CAPTURE,
430
COUNTER_MCHP_EVCHN_RA);
431
if (sr & ATMEL_TC_LDRBS)
432
counter_push_event(counter, COUNTER_EVENT_CAPTURE,
433
COUNTER_MCHP_EVCHN_RB);
434
if (sr & ATMEL_TC_CPCS)
435
counter_push_event(counter, COUNTER_EVENT_THRESHOLD,
436
COUNTER_MCHP_EVCHN_RC);
437
if (sr & ATMEL_TC_COVFS)
438
counter_push_event(counter, COUNTER_EVENT_OVERFLOW,
439
COUNTER_MCHP_EVCHN_CV);
440
441
return IRQ_HANDLED;
442
}
443
444
static void mchp_tc_irq_remove(void *ptr)
445
{
446
struct mchp_tc_data *priv = ptr;
447
448
regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IDR), ATMEL_TC_DEF_IRQS);
449
}
450
451
static int mchp_tc_irq_enable(struct counter_device *const counter, int irq)
452
{
453
struct mchp_tc_data *const priv = counter_priv(counter);
454
int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, 0,
455
dev_name(counter->parent), counter);
456
457
if (ret < 0)
458
return ret;
459
460
ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IER), ATMEL_TC_DEF_IRQS);
461
if (ret < 0)
462
return ret;
463
464
ret = devm_add_action_or_reset(counter->parent, mchp_tc_irq_remove, priv);
465
if (ret < 0)
466
return ret;
467
468
return 0;
469
}
470
471
static void mchp_tc_clk_remove(void *ptr)
472
{
473
clk_disable_unprepare((struct clk *)ptr);
474
}
475
476
static int mchp_tc_probe(struct platform_device *pdev)
477
{
478
struct device_node *np = pdev->dev.of_node;
479
const struct atmel_tcb_config *tcb_config;
480
const struct of_device_id *match;
481
struct counter_device *counter;
482
struct mchp_tc_data *priv;
483
char clk_name[7];
484
struct regmap *regmap;
485
struct clk *clk[3];
486
int channel;
487
int ret, i;
488
489
counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));
490
if (!counter)
491
return -ENOMEM;
492
priv = counter_priv(counter);
493
494
match = of_match_node(atmel_tc_of_match, np->parent);
495
tcb_config = match->data;
496
if (!tcb_config) {
497
dev_err(&pdev->dev, "No matching parent node found\n");
498
return -ENODEV;
499
}
500
501
regmap = syscon_node_to_regmap(np->parent);
502
if (IS_ERR(regmap))
503
return PTR_ERR(regmap);
504
505
/* max. channels number is 2 when in QDEC mode */
506
priv->num_channels = of_property_count_u32_elems(np, "reg");
507
if (priv->num_channels < 0) {
508
dev_err(&pdev->dev, "Invalid or missing channel\n");
509
return -EINVAL;
510
}
511
512
/* Register channels and initialize clocks */
513
for (i = 0; i < priv->num_channels; i++) {
514
ret = of_property_read_u32_index(np, "reg", i, &channel);
515
if (ret < 0 || channel > 2)
516
return -ENODEV;
517
518
priv->channel[i] = channel;
519
520
snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);
521
522
clk[i] = of_clk_get_by_name(np->parent, clk_name);
523
if (IS_ERR(clk[i])) {
524
/* Fallback to t0_clk */
525
clk[i] = of_clk_get_by_name(np->parent, "t0_clk");
526
if (IS_ERR(clk[i]))
527
return PTR_ERR(clk[i]);
528
}
529
530
ret = clk_prepare_enable(clk[i]);
531
if (ret)
532
return ret;
533
534
ret = devm_add_action_or_reset(&pdev->dev,
535
mchp_tc_clk_remove,
536
clk[i]);
537
if (ret)
538
return ret;
539
540
dev_dbg(&pdev->dev,
541
"Initialized capture mode on channel %d\n",
542
channel);
543
}
544
545
/* Disable Quadrature Decoder and position measure */
546
ret = regmap_update_bits(regmap, ATMEL_TC_BMR, ATMEL_TC_QDEN | ATMEL_TC_POSEN, 0);
547
if (ret)
548
return ret;
549
550
/* Setup the period capture mode */
551
ret = regmap_update_bits(regmap, ATMEL_TC_REG(priv->channel[0], CMR),
552
ATMEL_TC_WAVE | ATMEL_TC_ABETRG | ATMEL_TC_CMR_MASK |
553
ATMEL_TC_TCCLKS,
554
ATMEL_TC_CMR_MASK);
555
if (ret)
556
return ret;
557
558
/* Enable clock and trigger counter */
559
ret = regmap_write(regmap, ATMEL_TC_REG(priv->channel[0], CCR),
560
ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
561
if (ret)
562
return ret;
563
564
priv->tc_cfg = tcb_config;
565
priv->regmap = regmap;
566
counter->name = dev_name(&pdev->dev);
567
counter->parent = &pdev->dev;
568
counter->ops = &mchp_tc_ops;
569
counter->num_counts = ARRAY_SIZE(mchp_tc_counts);
570
counter->counts = mchp_tc_counts;
571
counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals);
572
counter->signals = mchp_tc_count_signals;
573
574
i = of_irq_get(np->parent, 0);
575
if (i == -EPROBE_DEFER)
576
return -EPROBE_DEFER;
577
if (i > 0) {
578
ret = mchp_tc_irq_enable(counter, i);
579
if (ret < 0)
580
return dev_err_probe(&pdev->dev, ret, "Failed to set up IRQ");
581
}
582
583
ret = devm_counter_add(&pdev->dev, counter);
584
if (ret < 0)
585
return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
586
587
return 0;
588
}
589
590
static const struct of_device_id mchp_tc_dt_ids[] = {
591
{ .compatible = "microchip,tcb-capture", },
592
{ /* sentinel */ },
593
};
594
MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);
595
596
static struct platform_driver mchp_tc_driver = {
597
.probe = mchp_tc_probe,
598
.driver = {
599
.name = "microchip-tcb-capture",
600
.of_match_table = mchp_tc_dt_ids,
601
},
602
};
603
module_platform_driver(mchp_tc_driver);
604
605
MODULE_AUTHOR("Kamel Bouhara <[email protected]>");
606
MODULE_DESCRIPTION("Microchip TCB Capture driver");
607
MODULE_LICENSE("GPL v2");
608
MODULE_IMPORT_NS("COUNTER");
609
610