1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2019 David Lechner <david@lechnology.com> |
4 | * |
5 | * Counter driver for Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) |
6 | */ |
7 | |
8 | #include <linux/bitops.h> |
9 | #include <linux/counter.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/module.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/types.h> |
17 | |
18 | /* 32-bit registers */ |
19 | #define QPOSCNT 0x0 |
20 | #define QPOSINIT 0x4 |
21 | #define QPOSMAX 0x8 |
22 | #define QPOSCMP 0xc |
23 | #define QPOSILAT 0x10 |
24 | #define QPOSSLAT 0x14 |
25 | #define QPOSLAT 0x18 |
26 | #define QUTMR 0x1c |
27 | #define QUPRD 0x20 |
28 | |
29 | /* 16-bit registers */ |
30 | #define QWDTMR 0x0 /* 0x24 */ |
31 | #define QWDPRD 0x2 /* 0x26 */ |
32 | #define QDECCTL 0x4 /* 0x28 */ |
33 | #define QEPCTL 0x6 /* 0x2a */ |
34 | #define QCAPCTL 0x8 /* 0x2c */ |
35 | #define QPOSCTL 0xa /* 0x2e */ |
36 | #define QEINT 0xc /* 0x30 */ |
37 | #define QFLG 0xe /* 0x32 */ |
38 | #define QCLR 0x10 /* 0x34 */ |
39 | #define QFRC 0x12 /* 0x36 */ |
40 | #define QEPSTS 0x14 /* 0x38 */ |
41 | #define QCTMR 0x16 /* 0x3a */ |
42 | #define QCPRD 0x18 /* 0x3c */ |
43 | #define QCTMRLAT 0x1a /* 0x3e */ |
44 | #define QCPRDLAT 0x1c /* 0x40 */ |
45 | |
46 | #define QDECCTL_QSRC_SHIFT 14 |
47 | #define QDECCTL_QSRC GENMASK(15, 14) |
48 | #define QDECCTL_SOEN BIT(13) |
49 | #define QDECCTL_SPSEL BIT(12) |
50 | #define QDECCTL_XCR BIT(11) |
51 | #define QDECCTL_SWAP BIT(10) |
52 | #define QDECCTL_IGATE BIT(9) |
53 | #define QDECCTL_QAP BIT(8) |
54 | #define QDECCTL_QBP BIT(7) |
55 | #define QDECCTL_QIP BIT(6) |
56 | #define QDECCTL_QSP BIT(5) |
57 | |
58 | #define QEPCTL_FREE_SOFT GENMASK(15, 14) |
59 | #define QEPCTL_PCRM GENMASK(13, 12) |
60 | #define QEPCTL_SEI GENMASK(11, 10) |
61 | #define QEPCTL_IEI GENMASK(9, 8) |
62 | #define QEPCTL_SWI BIT(7) |
63 | #define QEPCTL_SEL BIT(6) |
64 | #define QEPCTL_IEL GENMASK(5, 4) |
65 | #define QEPCTL_PHEN BIT(3) |
66 | #define QEPCTL_QCLM BIT(2) |
67 | #define QEPCTL_UTE BIT(1) |
68 | #define QEPCTL_WDE BIT(0) |
69 | |
70 | /* EQEP Inputs */ |
71 | enum { |
72 | TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */ |
73 | TI_EQEP_SIGNAL_QEPB, /* QEPB/XDIR */ |
74 | }; |
75 | |
76 | /* Position Counter Input Modes */ |
77 | enum ti_eqep_count_func { |
78 | TI_EQEP_COUNT_FUNC_QUAD_COUNT, |
79 | TI_EQEP_COUNT_FUNC_DIR_COUNT, |
80 | TI_EQEP_COUNT_FUNC_UP_COUNT, |
81 | TI_EQEP_COUNT_FUNC_DOWN_COUNT, |
82 | }; |
83 | |
84 | struct ti_eqep_cnt { |
85 | struct counter_device counter; |
86 | struct regmap *regmap32; |
87 | struct regmap *regmap16; |
88 | }; |
89 | |
90 | static struct ti_eqep_cnt *ti_eqep_count_from_counter(struct counter_device *counter) |
91 | { |
92 | return counter_priv(counter); |
93 | } |
94 | |
95 | static int ti_eqep_count_read(struct counter_device *counter, |
96 | struct counter_count *count, u64 *val) |
97 | { |
98 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
99 | u32 cnt; |
100 | |
101 | regmap_read(map: priv->regmap32, QPOSCNT, val: &cnt); |
102 | *val = cnt; |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static int ti_eqep_count_write(struct counter_device *counter, |
108 | struct counter_count *count, u64 val) |
109 | { |
110 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
111 | u32 max; |
112 | |
113 | regmap_read(map: priv->regmap32, QPOSMAX, val: &max); |
114 | if (val > max) |
115 | return -EINVAL; |
116 | |
117 | return regmap_write(map: priv->regmap32, QPOSCNT, val); |
118 | } |
119 | |
120 | static int ti_eqep_function_read(struct counter_device *counter, |
121 | struct counter_count *count, |
122 | enum counter_function *function) |
123 | { |
124 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
125 | u32 qdecctl; |
126 | |
127 | regmap_read(map: priv->regmap16, QDECCTL, val: &qdecctl); |
128 | |
129 | switch ((qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT) { |
130 | case TI_EQEP_COUNT_FUNC_QUAD_COUNT: |
131 | *function = COUNTER_FUNCTION_QUADRATURE_X4; |
132 | break; |
133 | case TI_EQEP_COUNT_FUNC_DIR_COUNT: |
134 | *function = COUNTER_FUNCTION_PULSE_DIRECTION; |
135 | break; |
136 | case TI_EQEP_COUNT_FUNC_UP_COUNT: |
137 | *function = COUNTER_FUNCTION_INCREASE; |
138 | break; |
139 | case TI_EQEP_COUNT_FUNC_DOWN_COUNT: |
140 | *function = COUNTER_FUNCTION_DECREASE; |
141 | break; |
142 | } |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | static int ti_eqep_function_write(struct counter_device *counter, |
148 | struct counter_count *count, |
149 | enum counter_function function) |
150 | { |
151 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
152 | enum ti_eqep_count_func qsrc; |
153 | |
154 | switch (function) { |
155 | case COUNTER_FUNCTION_QUADRATURE_X4: |
156 | qsrc = TI_EQEP_COUNT_FUNC_QUAD_COUNT; |
157 | break; |
158 | case COUNTER_FUNCTION_PULSE_DIRECTION: |
159 | qsrc = TI_EQEP_COUNT_FUNC_DIR_COUNT; |
160 | break; |
161 | case COUNTER_FUNCTION_INCREASE: |
162 | qsrc = TI_EQEP_COUNT_FUNC_UP_COUNT; |
163 | break; |
164 | case COUNTER_FUNCTION_DECREASE: |
165 | qsrc = TI_EQEP_COUNT_FUNC_DOWN_COUNT; |
166 | break; |
167 | default: |
168 | /* should never reach this path */ |
169 | return -EINVAL; |
170 | } |
171 | |
172 | return regmap_write_bits(map: priv->regmap16, QDECCTL, QDECCTL_QSRC, |
173 | val: qsrc << QDECCTL_QSRC_SHIFT); |
174 | } |
175 | |
176 | static int ti_eqep_action_read(struct counter_device *counter, |
177 | struct counter_count *count, |
178 | struct counter_synapse *synapse, |
179 | enum counter_synapse_action *action) |
180 | { |
181 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
182 | enum counter_function function; |
183 | u32 qdecctl; |
184 | int err; |
185 | |
186 | err = ti_eqep_function_read(counter, count, function: &function); |
187 | if (err) |
188 | return err; |
189 | |
190 | switch (function) { |
191 | case COUNTER_FUNCTION_QUADRATURE_X4: |
192 | /* In quadrature mode, the rising and falling edge of both |
193 | * QEPA and QEPB trigger QCLK. |
194 | */ |
195 | *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; |
196 | return 0; |
197 | case COUNTER_FUNCTION_PULSE_DIRECTION: |
198 | /* In direction-count mode only rising edge of QEPA is counted |
199 | * and QEPB gives direction. |
200 | */ |
201 | switch (synapse->signal->id) { |
202 | case TI_EQEP_SIGNAL_QEPA: |
203 | *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; |
204 | return 0; |
205 | case TI_EQEP_SIGNAL_QEPB: |
206 | *action = COUNTER_SYNAPSE_ACTION_NONE; |
207 | return 0; |
208 | default: |
209 | /* should never reach this path */ |
210 | return -EINVAL; |
211 | } |
212 | case COUNTER_FUNCTION_INCREASE: |
213 | case COUNTER_FUNCTION_DECREASE: |
214 | /* In up/down-count modes only QEPA is counted and QEPB is not |
215 | * used. |
216 | */ |
217 | switch (synapse->signal->id) { |
218 | case TI_EQEP_SIGNAL_QEPA: |
219 | err = regmap_read(map: priv->regmap16, QDECCTL, val: &qdecctl); |
220 | if (err) |
221 | return err; |
222 | |
223 | if (qdecctl & QDECCTL_XCR) |
224 | *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; |
225 | else |
226 | *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; |
227 | return 0; |
228 | case TI_EQEP_SIGNAL_QEPB: |
229 | *action = COUNTER_SYNAPSE_ACTION_NONE; |
230 | return 0; |
231 | default: |
232 | /* should never reach this path */ |
233 | return -EINVAL; |
234 | } |
235 | default: |
236 | /* should never reach this path */ |
237 | return -EINVAL; |
238 | } |
239 | } |
240 | |
241 | static const struct counter_ops ti_eqep_counter_ops = { |
242 | .count_read = ti_eqep_count_read, |
243 | .count_write = ti_eqep_count_write, |
244 | .function_read = ti_eqep_function_read, |
245 | .function_write = ti_eqep_function_write, |
246 | .action_read = ti_eqep_action_read, |
247 | }; |
248 | |
249 | static int ti_eqep_position_ceiling_read(struct counter_device *counter, |
250 | struct counter_count *count, |
251 | u64 *ceiling) |
252 | { |
253 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
254 | u32 qposmax; |
255 | |
256 | regmap_read(map: priv->regmap32, QPOSMAX, val: &qposmax); |
257 | |
258 | *ceiling = qposmax; |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static int ti_eqep_position_ceiling_write(struct counter_device *counter, |
264 | struct counter_count *count, |
265 | u64 ceiling) |
266 | { |
267 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
268 | |
269 | if (ceiling != (u32)ceiling) |
270 | return -ERANGE; |
271 | |
272 | regmap_write(map: priv->regmap32, QPOSMAX, val: ceiling); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | static int ti_eqep_position_enable_read(struct counter_device *counter, |
278 | struct counter_count *count, u8 *enable) |
279 | { |
280 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
281 | u32 qepctl; |
282 | |
283 | regmap_read(map: priv->regmap16, QEPCTL, val: &qepctl); |
284 | |
285 | *enable = !!(qepctl & QEPCTL_PHEN); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static int ti_eqep_position_enable_write(struct counter_device *counter, |
291 | struct counter_count *count, u8 enable) |
292 | { |
293 | struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); |
294 | |
295 | regmap_write_bits(map: priv->regmap16, QEPCTL, QEPCTL_PHEN, val: enable ? -1 : 0); |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | static struct counter_comp ti_eqep_position_ext[] = { |
301 | COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read, |
302 | ti_eqep_position_ceiling_write), |
303 | COUNTER_COMP_ENABLE(ti_eqep_position_enable_read, |
304 | ti_eqep_position_enable_write), |
305 | }; |
306 | |
307 | static struct counter_signal ti_eqep_signals[] = { |
308 | [TI_EQEP_SIGNAL_QEPA] = { |
309 | .id = TI_EQEP_SIGNAL_QEPA, |
310 | .name = "QEPA" |
311 | }, |
312 | [TI_EQEP_SIGNAL_QEPB] = { |
313 | .id = TI_EQEP_SIGNAL_QEPB, |
314 | .name = "QEPB" |
315 | }, |
316 | }; |
317 | |
318 | static const enum counter_function ti_eqep_position_functions[] = { |
319 | COUNTER_FUNCTION_QUADRATURE_X4, |
320 | COUNTER_FUNCTION_PULSE_DIRECTION, |
321 | COUNTER_FUNCTION_INCREASE, |
322 | COUNTER_FUNCTION_DECREASE, |
323 | }; |
324 | |
325 | static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = { |
326 | COUNTER_SYNAPSE_ACTION_BOTH_EDGES, |
327 | COUNTER_SYNAPSE_ACTION_RISING_EDGE, |
328 | COUNTER_SYNAPSE_ACTION_NONE, |
329 | }; |
330 | |
331 | static struct counter_synapse ti_eqep_position_synapses[] = { |
332 | { |
333 | .actions_list = ti_eqep_position_synapse_actions, |
334 | .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), |
335 | .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA], |
336 | }, |
337 | { |
338 | .actions_list = ti_eqep_position_synapse_actions, |
339 | .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), |
340 | .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB], |
341 | }, |
342 | }; |
343 | |
344 | static struct counter_count ti_eqep_counts[] = { |
345 | { |
346 | .id = 0, |
347 | .name = "QPOSCNT" , |
348 | .functions_list = ti_eqep_position_functions, |
349 | .num_functions = ARRAY_SIZE(ti_eqep_position_functions), |
350 | .synapses = ti_eqep_position_synapses, |
351 | .num_synapses = ARRAY_SIZE(ti_eqep_position_synapses), |
352 | .ext = ti_eqep_position_ext, |
353 | .num_ext = ARRAY_SIZE(ti_eqep_position_ext), |
354 | }, |
355 | }; |
356 | |
357 | static const struct regmap_config ti_eqep_regmap32_config = { |
358 | .name = "32-bit" , |
359 | .reg_bits = 32, |
360 | .val_bits = 32, |
361 | .reg_stride = 4, |
362 | .max_register = QUPRD, |
363 | }; |
364 | |
365 | static const struct regmap_config ti_eqep_regmap16_config = { |
366 | .name = "16-bit" , |
367 | .reg_bits = 16, |
368 | .val_bits = 16, |
369 | .reg_stride = 2, |
370 | .max_register = QCPRDLAT, |
371 | }; |
372 | |
373 | static int ti_eqep_probe(struct platform_device *pdev) |
374 | { |
375 | struct device *dev = &pdev->dev; |
376 | struct counter_device *counter; |
377 | struct ti_eqep_cnt *priv; |
378 | void __iomem *base; |
379 | int err; |
380 | |
381 | counter = devm_counter_alloc(dev, sizeof_priv: sizeof(*priv)); |
382 | if (!counter) |
383 | return -ENOMEM; |
384 | priv = counter_priv(counter); |
385 | |
386 | base = devm_platform_ioremap_resource(pdev, index: 0); |
387 | if (IS_ERR(ptr: base)) |
388 | return PTR_ERR(ptr: base); |
389 | |
390 | priv->regmap32 = devm_regmap_init_mmio(dev, base, |
391 | &ti_eqep_regmap32_config); |
392 | if (IS_ERR(ptr: priv->regmap32)) |
393 | return PTR_ERR(ptr: priv->regmap32); |
394 | |
395 | priv->regmap16 = devm_regmap_init_mmio(dev, base + 0x24, |
396 | &ti_eqep_regmap16_config); |
397 | if (IS_ERR(ptr: priv->regmap16)) |
398 | return PTR_ERR(ptr: priv->regmap16); |
399 | |
400 | counter->name = dev_name(dev); |
401 | counter->parent = dev; |
402 | counter->ops = &ti_eqep_counter_ops; |
403 | counter->counts = ti_eqep_counts; |
404 | counter->num_counts = ARRAY_SIZE(ti_eqep_counts); |
405 | counter->signals = ti_eqep_signals; |
406 | counter->num_signals = ARRAY_SIZE(ti_eqep_signals); |
407 | |
408 | platform_set_drvdata(pdev, data: counter); |
409 | |
410 | /* |
411 | * Need to make sure power is turned on. On AM33xx, this comes from the |
412 | * parent PWMSS bus driver. On AM17xx, this comes from the PSC power |
413 | * domain. |
414 | */ |
415 | pm_runtime_enable(dev); |
416 | pm_runtime_get_sync(dev); |
417 | |
418 | err = counter_add(counter); |
419 | if (err < 0) { |
420 | pm_runtime_put_sync(dev); |
421 | pm_runtime_disable(dev); |
422 | return err; |
423 | } |
424 | |
425 | return 0; |
426 | } |
427 | |
428 | static int ti_eqep_remove(struct platform_device *pdev) |
429 | { |
430 | struct counter_device *counter = platform_get_drvdata(pdev); |
431 | struct device *dev = &pdev->dev; |
432 | |
433 | counter_unregister(counter); |
434 | pm_runtime_put_sync(dev); |
435 | pm_runtime_disable(dev); |
436 | |
437 | return 0; |
438 | } |
439 | |
440 | static const struct of_device_id ti_eqep_of_match[] = { |
441 | { .compatible = "ti,am3352-eqep" , }, |
442 | { }, |
443 | }; |
444 | MODULE_DEVICE_TABLE(of, ti_eqep_of_match); |
445 | |
446 | static struct platform_driver ti_eqep_driver = { |
447 | .probe = ti_eqep_probe, |
448 | .remove = ti_eqep_remove, |
449 | .driver = { |
450 | .name = "ti-eqep-cnt" , |
451 | .of_match_table = ti_eqep_of_match, |
452 | }, |
453 | }; |
454 | module_platform_driver(ti_eqep_driver); |
455 | |
456 | MODULE_AUTHOR("David Lechner <david@lechnology.com>" ); |
457 | MODULE_DESCRIPTION("TI eQEP counter driver" ); |
458 | MODULE_LICENSE("GPL v2" ); |
459 | MODULE_IMPORT_NS(COUNTER); |
460 | |