David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 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, |
William Breathitt Gray | d49e6ee | 2019-10-06 16:03:09 -0400 | [diff] [blame] | 96 | struct counter_count *count, unsigned long *val) |
David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 97 | { |
| 98 | struct ti_eqep_cnt *priv = counter->priv; |
| 99 | u32 cnt; |
| 100 | |
| 101 | regmap_read(priv->regmap32, QPOSCNT, &cnt); |
William Breathitt Gray | d49e6ee | 2019-10-06 16:03:09 -0400 | [diff] [blame] | 102 | *val = cnt; |
David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 103 | |
| 104 | return 0; |
| 105 | } |
| 106 | |
| 107 | static int ti_eqep_count_write(struct counter_device *counter, |
William Breathitt Gray | d49e6ee | 2019-10-06 16:03:09 -0400 | [diff] [blame] | 108 | struct counter_count *count, unsigned long val) |
David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 109 | { |
| 110 | struct ti_eqep_cnt *priv = counter->priv; |
William Breathitt Gray | d49e6ee | 2019-10-06 16:03:09 -0400 | [diff] [blame] | 111 | u32 max; |
David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 112 | |
| 113 | regmap_read(priv->regmap32, QPOSMAX, &max); |
William Breathitt Gray | d49e6ee | 2019-10-06 16:03:09 -0400 | [diff] [blame] | 114 | if (val > max) |
David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 115 | return -EINVAL; |
| 116 | |
William Breathitt Gray | d49e6ee | 2019-10-06 16:03:09 -0400 | [diff] [blame] | 117 | return regmap_write(priv->regmap32, QPOSCNT, val); |
David Lechner | f213729 | 2019-09-01 17:58:24 -0500 | [diff] [blame] | 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; |
| 160 | break; |
| 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; |
| 168 | break; |
| 169 | default: |
| 170 | *action = TI_EQEP_SYNAPSE_ACTION_NONE; |
| 171 | break; |
| 172 | } |
| 173 | break; |
| 174 | case TI_EQEP_COUNT_FUNC_UP_COUNT: |
| 175 | case TI_EQEP_COUNT_FUNC_DOWN_COUNT: |
| 176 | /* In up/down-count modes only QEPA is counted and QEPB is not |
| 177 | * used. |
| 178 | */ |
| 179 | switch (synapse->signal->id) { |
| 180 | case TI_EQEP_SIGNAL_QEPA: |
| 181 | err = regmap_read(priv->regmap16, QDECCTL, &qdecctl); |
| 182 | if (err) |
| 183 | return err; |
| 184 | |
| 185 | if (qdecctl & QDECCTL_XCR) |
| 186 | *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; |
| 187 | else |
| 188 | *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; |
| 189 | break; |
| 190 | default: |
| 191 | *action = TI_EQEP_SYNAPSE_ACTION_NONE; |
| 192 | break; |
| 193 | } |
| 194 | break; |
| 195 | } |
| 196 | |
| 197 | return 0; |
| 198 | } |
| 199 | |
| 200 | static const struct counter_ops ti_eqep_counter_ops = { |
| 201 | .count_read = ti_eqep_count_read, |
| 202 | .count_write = ti_eqep_count_write, |
| 203 | .function_get = ti_eqep_function_get, |
| 204 | .function_set = ti_eqep_function_set, |
| 205 | .action_get = ti_eqep_action_get, |
| 206 | }; |
| 207 | |
| 208 | static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter, |
| 209 | struct counter_count *count, |
| 210 | void *ext_priv, char *buf) |
| 211 | { |
| 212 | struct ti_eqep_cnt *priv = counter->priv; |
| 213 | u32 qposmax; |
| 214 | |
| 215 | regmap_read(priv->regmap32, QPOSMAX, &qposmax); |
| 216 | |
| 217 | return sprintf(buf, "%u\n", qposmax); |
| 218 | } |
| 219 | |
| 220 | static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter, |
| 221 | struct counter_count *count, |
| 222 | void *ext_priv, const char *buf, |
| 223 | size_t len) |
| 224 | { |
| 225 | struct ti_eqep_cnt *priv = counter->priv; |
| 226 | int err; |
| 227 | u32 res; |
| 228 | |
| 229 | err = kstrtouint(buf, 0, &res); |
| 230 | if (err < 0) |
| 231 | return err; |
| 232 | |
| 233 | regmap_write(priv->regmap32, QPOSMAX, res); |
| 234 | |
| 235 | return len; |
| 236 | } |
| 237 | |
| 238 | static ssize_t ti_eqep_position_floor_read(struct counter_device *counter, |
| 239 | struct counter_count *count, |
| 240 | void *ext_priv, char *buf) |
| 241 | { |
| 242 | struct ti_eqep_cnt *priv = counter->priv; |
| 243 | u32 qposinit; |
| 244 | |
| 245 | regmap_read(priv->regmap32, QPOSINIT, &qposinit); |
| 246 | |
| 247 | return sprintf(buf, "%u\n", qposinit); |
| 248 | } |
| 249 | |
| 250 | static ssize_t ti_eqep_position_floor_write(struct counter_device *counter, |
| 251 | struct counter_count *count, |
| 252 | void *ext_priv, const char *buf, |
| 253 | size_t len) |
| 254 | { |
| 255 | struct ti_eqep_cnt *priv = counter->priv; |
| 256 | int err; |
| 257 | u32 res; |
| 258 | |
| 259 | err = kstrtouint(buf, 0, &res); |
| 260 | if (err < 0) |
| 261 | return err; |
| 262 | |
| 263 | regmap_write(priv->regmap32, QPOSINIT, res); |
| 264 | |
| 265 | return len; |
| 266 | } |
| 267 | |
| 268 | static ssize_t ti_eqep_position_enable_read(struct counter_device *counter, |
| 269 | struct counter_count *count, |
| 270 | void *ext_priv, char *buf) |
| 271 | { |
| 272 | struct ti_eqep_cnt *priv = counter->priv; |
| 273 | u32 qepctl; |
| 274 | |
| 275 | regmap_read(priv->regmap16, QEPCTL, &qepctl); |
| 276 | |
| 277 | return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN)); |
| 278 | } |
| 279 | |
| 280 | static ssize_t ti_eqep_position_enable_write(struct counter_device *counter, |
| 281 | struct counter_count *count, |
| 282 | void *ext_priv, const char *buf, |
| 283 | size_t len) |
| 284 | { |
| 285 | struct ti_eqep_cnt *priv = counter->priv; |
| 286 | int err; |
| 287 | bool res; |
| 288 | |
| 289 | err = kstrtobool(buf, &res); |
| 290 | if (err < 0) |
| 291 | return err; |
| 292 | |
| 293 | regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0); |
| 294 | |
| 295 | return len; |
| 296 | } |
| 297 | |
| 298 | static struct counter_count_ext ti_eqep_position_ext[] = { |
| 299 | { |
| 300 | .name = "ceiling", |
| 301 | .read = ti_eqep_position_ceiling_read, |
| 302 | .write = ti_eqep_position_ceiling_write, |
| 303 | }, |
| 304 | { |
| 305 | .name = "floor", |
| 306 | .read = ti_eqep_position_floor_read, |
| 307 | .write = ti_eqep_position_floor_write, |
| 308 | }, |
| 309 | { |
| 310 | .name = "enable", |
| 311 | .read = ti_eqep_position_enable_read, |
| 312 | .write = ti_eqep_position_enable_write, |
| 313 | }, |
| 314 | }; |
| 315 | |
| 316 | static struct counter_signal ti_eqep_signals[] = { |
| 317 | [TI_EQEP_SIGNAL_QEPA] = { |
| 318 | .id = TI_EQEP_SIGNAL_QEPA, |
| 319 | .name = "QEPA" |
| 320 | }, |
| 321 | [TI_EQEP_SIGNAL_QEPB] = { |
| 322 | .id = TI_EQEP_SIGNAL_QEPB, |
| 323 | .name = "QEPB" |
| 324 | }, |
| 325 | }; |
| 326 | |
| 327 | static const enum counter_count_function ti_eqep_position_functions[] = { |
| 328 | [TI_EQEP_COUNT_FUNC_QUAD_COUNT] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4, |
| 329 | [TI_EQEP_COUNT_FUNC_DIR_COUNT] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION, |
| 330 | [TI_EQEP_COUNT_FUNC_UP_COUNT] = COUNTER_COUNT_FUNCTION_INCREASE, |
| 331 | [TI_EQEP_COUNT_FUNC_DOWN_COUNT] = COUNTER_COUNT_FUNCTION_DECREASE, |
| 332 | }; |
| 333 | |
| 334 | static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = { |
| 335 | [TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES, |
| 336 | [TI_EQEP_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, |
| 337 | [TI_EQEP_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, |
| 338 | }; |
| 339 | |
| 340 | static struct counter_synapse ti_eqep_position_synapses[] = { |
| 341 | { |
| 342 | .actions_list = ti_eqep_position_synapse_actions, |
| 343 | .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), |
| 344 | .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA], |
| 345 | }, |
| 346 | { |
| 347 | .actions_list = ti_eqep_position_synapse_actions, |
| 348 | .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), |
| 349 | .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB], |
| 350 | }, |
| 351 | }; |
| 352 | |
| 353 | static struct counter_count ti_eqep_counts[] = { |
| 354 | { |
| 355 | .id = 0, |
| 356 | .name = "QPOSCNT", |
| 357 | .functions_list = ti_eqep_position_functions, |
| 358 | .num_functions = ARRAY_SIZE(ti_eqep_position_functions), |
| 359 | .synapses = ti_eqep_position_synapses, |
| 360 | .num_synapses = ARRAY_SIZE(ti_eqep_position_synapses), |
| 361 | .ext = ti_eqep_position_ext, |
| 362 | .num_ext = ARRAY_SIZE(ti_eqep_position_ext), |
| 363 | }, |
| 364 | }; |
| 365 | |
| 366 | static const struct regmap_config ti_eqep_regmap32_config = { |
| 367 | .name = "32-bit", |
| 368 | .reg_bits = 32, |
| 369 | .val_bits = 32, |
| 370 | .reg_stride = 4, |
| 371 | .max_register = 0x24, |
| 372 | }; |
| 373 | |
| 374 | static const struct regmap_config ti_eqep_regmap16_config = { |
| 375 | .name = "16-bit", |
| 376 | .reg_bits = 16, |
| 377 | .val_bits = 16, |
| 378 | .reg_stride = 2, |
| 379 | .max_register = 0x1e, |
| 380 | }; |
| 381 | |
| 382 | static int ti_eqep_probe(struct platform_device *pdev) |
| 383 | { |
| 384 | struct device *dev = &pdev->dev; |
| 385 | struct ti_eqep_cnt *priv; |
| 386 | void __iomem *base; |
| 387 | int err; |
| 388 | |
| 389 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| 390 | if (!priv) |
| 391 | return -ENOMEM; |
| 392 | |
| 393 | base = devm_platform_ioremap_resource(pdev, 0); |
| 394 | if (IS_ERR(base)) |
| 395 | return PTR_ERR(base); |
| 396 | |
| 397 | priv->regmap32 = devm_regmap_init_mmio(dev, base, |
| 398 | &ti_eqep_regmap32_config); |
| 399 | if (IS_ERR(priv->regmap32)) |
| 400 | return PTR_ERR(priv->regmap32); |
| 401 | |
| 402 | priv->regmap16 = devm_regmap_init_mmio(dev, base + 0x24, |
| 403 | &ti_eqep_regmap16_config); |
| 404 | if (IS_ERR(priv->regmap16)) |
| 405 | return PTR_ERR(priv->regmap16); |
| 406 | |
| 407 | priv->counter.name = dev_name(dev); |
| 408 | priv->counter.parent = dev; |
| 409 | priv->counter.ops = &ti_eqep_counter_ops; |
| 410 | priv->counter.counts = ti_eqep_counts; |
| 411 | priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts); |
| 412 | priv->counter.signals = ti_eqep_signals; |
| 413 | priv->counter.num_signals = ARRAY_SIZE(ti_eqep_signals); |
| 414 | priv->counter.priv = priv; |
| 415 | |
| 416 | platform_set_drvdata(pdev, priv); |
| 417 | |
| 418 | /* |
| 419 | * Need to make sure power is turned on. On AM33xx, this comes from the |
| 420 | * parent PWMSS bus driver. On AM17xx, this comes from the PSC power |
| 421 | * domain. |
| 422 | */ |
| 423 | pm_runtime_enable(dev); |
| 424 | pm_runtime_get_sync(dev); |
| 425 | |
| 426 | err = counter_register(&priv->counter); |
| 427 | if (err < 0) { |
| 428 | pm_runtime_put_sync(dev); |
| 429 | pm_runtime_disable(dev); |
| 430 | return err; |
| 431 | } |
| 432 | |
| 433 | return 0; |
| 434 | } |
| 435 | |
| 436 | static int ti_eqep_remove(struct platform_device *pdev) |
| 437 | { |
| 438 | struct ti_eqep_cnt *priv = platform_get_drvdata(pdev); |
| 439 | struct device *dev = &pdev->dev; |
| 440 | |
| 441 | counter_unregister(&priv->counter); |
| 442 | pm_runtime_put_sync(dev), |
| 443 | pm_runtime_disable(dev); |
| 444 | |
| 445 | return 0; |
| 446 | } |
| 447 | |
| 448 | static const struct of_device_id ti_eqep_of_match[] = { |
| 449 | { .compatible = "ti,am3352-eqep", }, |
| 450 | { }, |
| 451 | }; |
| 452 | MODULE_DEVICE_TABLE(of, ti_eqep_of_match); |
| 453 | |
| 454 | static struct platform_driver ti_eqep_driver = { |
| 455 | .probe = ti_eqep_probe, |
| 456 | .remove = ti_eqep_remove, |
| 457 | .driver = { |
| 458 | .name = "ti-eqep-cnt", |
| 459 | .of_match_table = ti_eqep_of_match, |
| 460 | }, |
| 461 | }; |
| 462 | module_platform_driver(ti_eqep_driver); |
| 463 | |
| 464 | MODULE_AUTHOR("David Lechner <david@lechnology.com>"); |
| 465 | MODULE_DESCRIPTION("TI eQEP counter driver"); |
| 466 | MODULE_LICENSE("GPL v2"); |