File Index
Files and directories
Sign in
FILE

microchip-tcb-capture.c

File preview with syntax highlighting.

linux-6.19.14/drivers/counter/microchip-tcb-capture.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2020 Microchip
  4 *
  5 * Author: Kamel Bouhara <kamel.bouhara@bootlin.com>
  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
 30struct 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
 38static const enum counter_function mchp_tc_count_functions[] = {
 39	COUNTER_FUNCTION_INCREASE,
 40	COUNTER_FUNCTION_QUADRATURE_X4,
 41};
 42
 43static 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
 50static 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
 61static 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
 74static 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
 88static 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
150static 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
170static 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
209static 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
244static 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
256static 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
282static 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
305static 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
321static 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
332static DEFINE_COUNTER_ARRAY_CAPTURE(mchp_tc_cnt_cap_array, 2);
333
334static 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
340static 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
362static 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
375static 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
385static const struct atmel_tcb_config tcb_rm9200_config = {
386		.counter_width = 16,
387};
388
389static const struct atmel_tcb_config tcb_sam9x5_config = {
390		.counter_width = 32,
391};
392
393static const struct atmel_tcb_config tcb_sama5d2_config = {
394		.counter_width = 32,
395		.has_gclk = true,
396		.has_qdec = true,
397};
398
399static const struct atmel_tcb_config tcb_sama5d3_config = {
400		.counter_width = 32,
401		.has_qdec = true,
402};
403
404static 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
412static 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
444static 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
451static 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, IRQF_SHARED,
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
471static void mchp_tc_clk_remove(void *ptr)
472{
473	clk_disable_unprepare((struct clk *)ptr);
474}
475
476static 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
590static const struct of_device_id mchp_tc_dt_ids[] = {
591	{ .compatible = "microchip,tcb-capture", },
592	{ /* sentinel */ },
593};
594MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);
595
596static 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};
603module_platform_driver(mchp_tc_driver);
604
605MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
606MODULE_DESCRIPTION("Microchip TCB Capture driver");
607MODULE_LICENSE("GPL v2");
608MODULE_IMPORT_NS("COUNTER");
~/ Read-only file preview.