blob: 00844445143b65d2e753e0dc8f6c7b77b40caebb [file] [log] [blame]
Kamel Bouhara106b1042020-07-06 13:43:47 +02001// SPDX-License-Identifier: GPL-2.0-only
Jonathan Cameronfe90fca2021-09-30 18:03:47 +01002/*
Kamel Bouhara106b1042020-07-06 13:43:47 +02003 * 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/mfd/syscon.h>
10#include <linux/module.h>
11#include <linux/mutex.h>
12#include <linux/of.h>
13#include <linux/of_device.h>
14#include <linux/platform_device.h>
15#include <linux/regmap.h>
16#include <soc/at91/atmel_tcb.h>
17
18#define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \
19 ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \
20 ATMEL_TC_LDBSTOP)
21
22#define ATMEL_TC_QDEN BIT(8)
23#define ATMEL_TC_POSEN BIT(9)
24
25struct mchp_tc_data {
26 const struct atmel_tcb_config *tc_cfg;
Kamel Bouhara106b1042020-07-06 13:43:47 +020027 struct regmap *regmap;
28 int qdec_mode;
29 int num_channels;
30 int channel[2];
31 bool trig_inverted;
32};
33
William Breathitt Gray394a0152021-08-03 21:06:15 +090034static const enum counter_function mchp_tc_count_functions[] = {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090035 COUNTER_FUNCTION_INCREASE,
36 COUNTER_FUNCTION_QUADRATURE_X4,
Kamel Bouhara106b1042020-07-06 13:43:47 +020037};
38
William Breathitt Gray0056a402021-06-09 10:31:18 +090039static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090040 COUNTER_SYNAPSE_ACTION_NONE,
41 COUNTER_SYNAPSE_ACTION_RISING_EDGE,
42 COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
43 COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
Kamel Bouhara106b1042020-07-06 13:43:47 +020044};
45
46static struct counter_signal mchp_tc_count_signals[] = {
47 {
48 .id = 0,
49 .name = "Channel A",
50 },
51 {
52 .id = 1,
53 .name = "Channel B",
54 }
55};
56
57static struct counter_synapse mchp_tc_count_synapses[] = {
58 {
59 .actions_list = mchp_tc_synapse_actions,
60 .num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
61 .signal = &mchp_tc_count_signals[0]
62 },
63 {
64 .actions_list = mchp_tc_synapse_actions,
65 .num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
66 .signal = &mchp_tc_count_signals[1]
67 }
68};
69
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090070static int mchp_tc_count_function_read(struct counter_device *counter,
71 struct counter_count *count,
72 enum counter_function *function)
Kamel Bouhara106b1042020-07-06 13:43:47 +020073{
Uwe Kleine-Königa49ede82021-12-30 16:02:44 +010074 struct mchp_tc_data *const priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +020075
76 if (priv->qdec_mode)
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090077 *function = COUNTER_FUNCTION_QUADRATURE_X4;
Kamel Bouhara106b1042020-07-06 13:43:47 +020078 else
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090079 *function = COUNTER_FUNCTION_INCREASE;
Kamel Bouhara106b1042020-07-06 13:43:47 +020080
81 return 0;
82}
83
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090084static int mchp_tc_count_function_write(struct counter_device *counter,
85 struct counter_count *count,
86 enum counter_function function)
Kamel Bouhara106b1042020-07-06 13:43:47 +020087{
Uwe Kleine-Königa49ede82021-12-30 16:02:44 +010088 struct mchp_tc_data *const priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +020089 u32 bmr, cmr;
90
91 regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);
92 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
93
94 /* Set capture mode */
95 cmr &= ~ATMEL_TC_WAVE;
96
97 switch (function) {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090098 case COUNTER_FUNCTION_INCREASE:
Kamel Bouhara106b1042020-07-06 13:43:47 +020099 priv->qdec_mode = 0;
100 /* Set highest rate based on whether soc has gclk or not */
101 bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
102 if (priv->tc_cfg->has_gclk)
103 cmr |= ATMEL_TC_TIMER_CLOCK2;
104 else
105 cmr |= ATMEL_TC_TIMER_CLOCK1;
106 /* Setup the period capture mode */
107 cmr |= ATMEL_TC_CMR_MASK;
108 cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
109 break;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900110 case COUNTER_FUNCTION_QUADRATURE_X4:
Kamel Bouhara106b1042020-07-06 13:43:47 +0200111 if (!priv->tc_cfg->has_qdec)
112 return -EINVAL;
113 /* In QDEC mode settings both channels 0 and 1 are required */
114 if (priv->num_channels < 2 || priv->channel[0] != 0 ||
115 priv->channel[1] != 1) {
116 pr_err("Invalid channels number or id for quadrature mode\n");
117 return -EINVAL;
118 }
119 priv->qdec_mode = 1;
120 bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;
121 cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;
122 break;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900123 default:
124 /* should never reach this path */
125 return -EINVAL;
Kamel Bouhara106b1042020-07-06 13:43:47 +0200126 }
127
128 regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);
129 regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);
130
131 /* Enable clock and trigger counter */
132 regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),
133 ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
134
135 if (priv->qdec_mode) {
136 regmap_write(priv->regmap,
137 ATMEL_TC_REG(priv->channel[1], CMR), cmr);
138 regmap_write(priv->regmap,
139 ATMEL_TC_REG(priv->channel[1], CCR),
140 ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
141 }
142
143 return 0;
144}
145
146static int mchp_tc_count_signal_read(struct counter_device *counter,
147 struct counter_signal *signal,
William Breathitt Gray493b9382021-08-03 21:06:14 +0900148 enum counter_signal_level *lvl)
Kamel Bouhara106b1042020-07-06 13:43:47 +0200149{
Uwe Kleine-Königa49ede82021-12-30 16:02:44 +0100150 struct mchp_tc_data *const priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +0200151 bool sigstatus;
152 u32 sr;
153
154 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
155
156 if (priv->trig_inverted)
157 sigstatus = (sr & ATMEL_TC_MTIOB);
158 else
159 sigstatus = (sr & ATMEL_TC_MTIOA);
160
William Breathitt Gray493b9382021-08-03 21:06:14 +0900161 *lvl = sigstatus ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
Kamel Bouhara106b1042020-07-06 13:43:47 +0200162
163 return 0;
164}
165
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900166static int mchp_tc_count_action_read(struct counter_device *counter,
167 struct counter_count *count,
168 struct counter_synapse *synapse,
169 enum counter_synapse_action *action)
Kamel Bouhara106b1042020-07-06 13:43:47 +0200170{
Uwe Kleine-Königa49ede82021-12-30 16:02:44 +0100171 struct mchp_tc_data *const priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +0200172 u32 cmr;
173
174 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
175
William Breathitt Gray3418bd72020-11-14 18:28:05 -0500176 switch (cmr & ATMEL_TC_ETRGEDG) {
177 default:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900178 *action = COUNTER_SYNAPSE_ACTION_NONE;
William Breathitt Gray3418bd72020-11-14 18:28:05 -0500179 break;
180 case ATMEL_TC_ETRGEDG_RISING:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900181 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
William Breathitt Gray3418bd72020-11-14 18:28:05 -0500182 break;
183 case ATMEL_TC_ETRGEDG_FALLING:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900184 *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
William Breathitt Gray3418bd72020-11-14 18:28:05 -0500185 break;
186 case ATMEL_TC_ETRGEDG_BOTH:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900187 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
William Breathitt Gray3418bd72020-11-14 18:28:05 -0500188 break;
189 }
Kamel Bouhara106b1042020-07-06 13:43:47 +0200190
191 return 0;
192}
193
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900194static int mchp_tc_count_action_write(struct counter_device *counter,
195 struct counter_count *count,
196 struct counter_synapse *synapse,
197 enum counter_synapse_action action)
Kamel Bouhara106b1042020-07-06 13:43:47 +0200198{
Uwe Kleine-Königa49ede82021-12-30 16:02:44 +0100199 struct mchp_tc_data *const priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +0200200 u32 edge = ATMEL_TC_ETRGEDG_NONE;
201
202 /* QDEC mode is rising edge only */
203 if (priv->qdec_mode)
204 return -EINVAL;
205
206 switch (action) {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900207 case COUNTER_SYNAPSE_ACTION_NONE:
Kamel Bouhara106b1042020-07-06 13:43:47 +0200208 edge = ATMEL_TC_ETRGEDG_NONE;
209 break;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900210 case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
Kamel Bouhara106b1042020-07-06 13:43:47 +0200211 edge = ATMEL_TC_ETRGEDG_RISING;
212 break;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900213 case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
Kamel Bouhara106b1042020-07-06 13:43:47 +0200214 edge = ATMEL_TC_ETRGEDG_FALLING;
215 break;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900216 case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
Kamel Bouhara106b1042020-07-06 13:43:47 +0200217 edge = ATMEL_TC_ETRGEDG_BOTH;
218 break;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900219 default:
220 /* should never reach this path */
221 return -EINVAL;
Kamel Bouhara106b1042020-07-06 13:43:47 +0200222 }
223
224 return regmap_write_bits(priv->regmap,
225 ATMEL_TC_REG(priv->channel[0], CMR),
226 ATMEL_TC_ETRGEDG, edge);
227}
228
229static int mchp_tc_count_read(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900230 struct counter_count *count, u64 *val)
Kamel Bouhara106b1042020-07-06 13:43:47 +0200231{
Uwe Kleine-Königa49ede82021-12-30 16:02:44 +0100232 struct mchp_tc_data *const priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +0200233 u32 cnt;
234
235 regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);
236 *val = cnt;
237
238 return 0;
239}
240
241static struct counter_count mchp_tc_counts[] = {
242 {
243 .id = 0,
244 .name = "Timer Counter",
245 .functions_list = mchp_tc_count_functions,
246 .num_functions = ARRAY_SIZE(mchp_tc_count_functions),
247 .synapses = mchp_tc_count_synapses,
248 .num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),
249 },
250};
251
Rikard Falkeborn0854fa22020-09-22 22:19:41 +0200252static const struct counter_ops mchp_tc_ops = {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900253 .signal_read = mchp_tc_count_signal_read,
254 .count_read = mchp_tc_count_read,
255 .function_read = mchp_tc_count_function_read,
256 .function_write = mchp_tc_count_function_write,
257 .action_read = mchp_tc_count_action_read,
258 .action_write = mchp_tc_count_action_write
Kamel Bouhara106b1042020-07-06 13:43:47 +0200259};
260
261static const struct atmel_tcb_config tcb_rm9200_config = {
262 .counter_width = 16,
263};
264
265static const struct atmel_tcb_config tcb_sam9x5_config = {
266 .counter_width = 32,
267};
268
269static const struct atmel_tcb_config tcb_sama5d2_config = {
270 .counter_width = 32,
271 .has_gclk = true,
272 .has_qdec = true,
273};
274
275static const struct atmel_tcb_config tcb_sama5d3_config = {
276 .counter_width = 32,
277 .has_qdec = true,
278};
279
280static const struct of_device_id atmel_tc_of_match[] = {
281 { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
282 { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
283 { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
284 { .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },
285 { /* sentinel */ }
286};
287
288static void mchp_tc_clk_remove(void *ptr)
289{
290 clk_disable_unprepare((struct clk *)ptr);
291}
292
293static int mchp_tc_probe(struct platform_device *pdev)
294{
295 struct device_node *np = pdev->dev.of_node;
296 const struct atmel_tcb_config *tcb_config;
297 const struct of_device_id *match;
Uwe Kleine-König5998ea62021-12-30 16:02:56 +0100298 struct counter_device *counter;
Kamel Bouhara106b1042020-07-06 13:43:47 +0200299 struct mchp_tc_data *priv;
300 char clk_name[7];
301 struct regmap *regmap;
302 struct clk *clk[3];
303 int channel;
304 int ret, i;
305
Uwe Kleine-König5998ea62021-12-30 16:02:56 +0100306 counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));
307 if (!counter)
Kamel Bouhara106b1042020-07-06 13:43:47 +0200308 return -ENOMEM;
Uwe Kleine-König5998ea62021-12-30 16:02:56 +0100309 priv = counter_priv(counter);
Kamel Bouhara106b1042020-07-06 13:43:47 +0200310
Kamel Bouhara106b1042020-07-06 13:43:47 +0200311 match = of_match_node(atmel_tc_of_match, np->parent);
312 tcb_config = match->data;
313 if (!tcb_config) {
314 dev_err(&pdev->dev, "No matching parent node found\n");
315 return -ENODEV;
316 }
317
318 regmap = syscon_node_to_regmap(np->parent);
Dan Carpenterab3300d2020-07-27 14:23:16 +0300319 if (IS_ERR(regmap))
320 return PTR_ERR(regmap);
Kamel Bouhara106b1042020-07-06 13:43:47 +0200321
322 /* max. channels number is 2 when in QDEC mode */
323 priv->num_channels = of_property_count_u32_elems(np, "reg");
324 if (priv->num_channels < 0) {
325 dev_err(&pdev->dev, "Invalid or missing channel\n");
326 return -EINVAL;
327 }
328
329 /* Register channels and initialize clocks */
330 for (i = 0; i < priv->num_channels; i++) {
331 ret = of_property_read_u32_index(np, "reg", i, &channel);
332 if (ret < 0 || channel > 2)
333 return -ENODEV;
334
335 priv->channel[i] = channel;
336
337 snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);
338
339 clk[i] = of_clk_get_by_name(np->parent, clk_name);
340 if (IS_ERR(clk[i])) {
341 /* Fallback to t0_clk */
342 clk[i] = of_clk_get_by_name(np->parent, "t0_clk");
343 if (IS_ERR(clk[i]))
344 return PTR_ERR(clk[i]);
345 }
346
347 ret = clk_prepare_enable(clk[i]);
348 if (ret)
349 return ret;
350
351 ret = devm_add_action_or_reset(&pdev->dev,
352 mchp_tc_clk_remove,
353 clk[i]);
354 if (ret)
355 return ret;
356
357 dev_dbg(&pdev->dev,
358 "Initialized capture mode on channel %d\n",
359 channel);
360 }
361
362 priv->tc_cfg = tcb_config;
363 priv->regmap = regmap;
Uwe Kleine-König5998ea62021-12-30 16:02:56 +0100364 counter->name = dev_name(&pdev->dev);
365 counter->parent = &pdev->dev;
366 counter->ops = &mchp_tc_ops;
367 counter->num_counts = ARRAY_SIZE(mchp_tc_counts);
368 counter->counts = mchp_tc_counts;
369 counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals);
370 counter->signals = mchp_tc_count_signals;
Kamel Bouhara106b1042020-07-06 13:43:47 +0200371
Uwe Kleine-König5998ea62021-12-30 16:02:56 +0100372 ret = devm_counter_add(&pdev->dev, counter);
373 if (ret < 0)
374 return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
375
376 return 0;
Kamel Bouhara106b1042020-07-06 13:43:47 +0200377}
378
379static const struct of_device_id mchp_tc_dt_ids[] = {
380 { .compatible = "microchip,tcb-capture", },
381 { /* sentinel */ },
382};
383MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);
384
385static struct platform_driver mchp_tc_driver = {
386 .probe = mchp_tc_probe,
387 .driver = {
388 .name = "microchip-tcb-capture",
389 .of_match_table = mchp_tc_dt_ids,
390 },
391};
392module_platform_driver(mchp_tc_driver);
393
394MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
395MODULE_DESCRIPTION("Microchip TCB Capture driver");
396MODULE_LICENSE("GPL v2");