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