linux/drivers/counter/ftm-quaddec.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Flex Timer Module Quadrature decoder
   4 *
   5 * This module implements a driver for decoding the FTM quadrature
   6 * of ex. a LS1021A
   7 */
   8
   9#include <linux/fsl/ftm.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/of.h>
  13#include <linux/io.h>
  14#include <linux/mutex.h>
  15#include <linux/counter.h>
  16#include <linux/bitfield.h>
  17
  18#define FTM_FIELD_UPDATE(ftm, offset, mask, val)                        \
  19        ({                                                              \
  20                uint32_t flags;                                         \
  21                ftm_read(ftm, offset, &flags);                          \
  22                flags &= ~mask;                                         \
  23                flags |= FIELD_PREP(mask, val);                         \
  24                ftm_write(ftm, offset, flags);                          \
  25        })
  26
  27struct ftm_quaddec {
  28        struct counter_device counter;
  29        struct platform_device *pdev;
  30        void __iomem *ftm_base;
  31        bool big_endian;
  32        struct mutex ftm_quaddec_mutex;
  33};
  34
  35static void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
  36{
  37        if (ftm->big_endian)
  38                *data = ioread32be(ftm->ftm_base + offset);
  39        else
  40                *data = ioread32(ftm->ftm_base + offset);
  41}
  42
  43static void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
  44{
  45        if (ftm->big_endian)
  46                iowrite32be(data, ftm->ftm_base + offset);
  47        else
  48                iowrite32(data, ftm->ftm_base + offset);
  49}
  50
  51/* Hold mutex before modifying write protection state */
  52static void ftm_clear_write_protection(struct ftm_quaddec *ftm)
  53{
  54        uint32_t flag;
  55
  56        /* First see if it is enabled */
  57        ftm_read(ftm, FTM_FMS, &flag);
  58
  59        if (flag & FTM_FMS_WPEN)
  60                FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
  61}
  62
  63static void ftm_set_write_protection(struct ftm_quaddec *ftm)
  64{
  65        FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
  66}
  67
  68static void ftm_reset_counter(struct ftm_quaddec *ftm)
  69{
  70        /* Reset hardware counter to CNTIN */
  71        ftm_write(ftm, FTM_CNT, 0x0);
  72}
  73
  74static void ftm_quaddec_init(struct ftm_quaddec *ftm)
  75{
  76        ftm_clear_write_protection(ftm);
  77
  78        /*
  79         * Do not write in the region from the CNTIN register through the
  80         * PWMLOAD register when FTMEN = 0.
  81         * Also reset other fields to zero
  82         */
  83        ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
  84        ftm_write(ftm, FTM_CNTIN, 0x0000);
  85        ftm_write(ftm, FTM_MOD, 0xffff);
  86        ftm_write(ftm, FTM_CNT, 0x0);
  87        /* Set prescaler, reset other fields to zero */
  88        ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
  89
  90        /* Select quad mode, reset other fields to zero */
  91        ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
  92
  93        /* Unused features and reset to default section */
  94        ftm_write(ftm, FTM_POL, 0x0);
  95        ftm_write(ftm, FTM_FLTCTRL, 0x0);
  96        ftm_write(ftm, FTM_SYNCONF, 0x0);
  97        ftm_write(ftm, FTM_SYNC, 0xffff);
  98
  99        /* Lock the FTM */
 100        ftm_set_write_protection(ftm);
 101}
 102
 103static void ftm_quaddec_disable(void *ftm)
 104{
 105        struct ftm_quaddec *ftm_qua = ftm;
 106
 107        ftm_clear_write_protection(ftm_qua);
 108        ftm_write(ftm_qua, FTM_MODE, 0);
 109        ftm_write(ftm_qua, FTM_QDCTRL, 0);
 110        /*
 111         * This is enough to disable the counter. No clock has been
 112         * selected by writing to FTM_SC in init()
 113         */
 114        ftm_set_write_protection(ftm_qua);
 115}
 116
 117static int ftm_quaddec_get_prescaler(struct counter_device *counter,
 118                                     struct counter_count *count,
 119                                     size_t *cnt_mode)
 120{
 121        struct ftm_quaddec *ftm = counter->priv;
 122        uint32_t scflags;
 123
 124        ftm_read(ftm, FTM_SC, &scflags);
 125
 126        *cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
 127
 128        return 0;
 129}
 130
 131static int ftm_quaddec_set_prescaler(struct counter_device *counter,
 132                                     struct counter_count *count,
 133                                     size_t cnt_mode)
 134{
 135        struct ftm_quaddec *ftm = counter->priv;
 136
 137        mutex_lock(&ftm->ftm_quaddec_mutex);
 138
 139        ftm_clear_write_protection(ftm);
 140        FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
 141        ftm_set_write_protection(ftm);
 142
 143        /* Also resets the counter as it is undefined anyway now */
 144        ftm_reset_counter(ftm);
 145
 146        mutex_unlock(&ftm->ftm_quaddec_mutex);
 147        return 0;
 148}
 149
 150static const char * const ftm_quaddec_prescaler[] = {
 151        "1", "2", "4", "8", "16", "32", "64", "128"
 152};
 153
 154static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
 155        .items = ftm_quaddec_prescaler,
 156        .num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
 157        .get = ftm_quaddec_get_prescaler,
 158        .set = ftm_quaddec_set_prescaler
 159};
 160
 161enum ftm_quaddec_synapse_action {
 162        FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
 163};
 164
 165static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
 166        [FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
 167        COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 168};
 169
 170enum ftm_quaddec_count_function {
 171        FTM_QUADDEC_COUNT_ENCODER_MODE_1,
 172};
 173
 174static const enum counter_count_function ftm_quaddec_count_functions[] = {
 175        [FTM_QUADDEC_COUNT_ENCODER_MODE_1] =
 176        COUNTER_COUNT_FUNCTION_QUADRATURE_X4
 177};
 178
 179static int ftm_quaddec_count_read(struct counter_device *counter,
 180                                  struct counter_count *count,
 181                                  unsigned long *val)
 182{
 183        struct ftm_quaddec *const ftm = counter->priv;
 184        uint32_t cntval;
 185
 186        ftm_read(ftm, FTM_CNT, &cntval);
 187
 188        *val = cntval;
 189
 190        return 0;
 191}
 192
 193static int ftm_quaddec_count_write(struct counter_device *counter,
 194                                   struct counter_count *count,
 195                                   const unsigned long val)
 196{
 197        struct ftm_quaddec *const ftm = counter->priv;
 198
 199        if (val != 0) {
 200                dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
 201                return -EINVAL;
 202        }
 203
 204        ftm_reset_counter(ftm);
 205
 206        return 0;
 207}
 208
 209static int ftm_quaddec_count_function_get(struct counter_device *counter,
 210                                          struct counter_count *count,
 211                                          size_t *function)
 212{
 213        *function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
 214
 215        return 0;
 216}
 217
 218static int ftm_quaddec_action_get(struct counter_device *counter,
 219                                  struct counter_count *count,
 220                                  struct counter_synapse *synapse,
 221                                  size_t *action)
 222{
 223        *action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
 224
 225        return 0;
 226}
 227
 228static const struct counter_ops ftm_quaddec_cnt_ops = {
 229        .count_read = ftm_quaddec_count_read,
 230        .count_write = ftm_quaddec_count_write,
 231        .function_get = ftm_quaddec_count_function_get,
 232        .action_get = ftm_quaddec_action_get,
 233};
 234
 235static struct counter_signal ftm_quaddec_signals[] = {
 236        {
 237                .id = 0,
 238                .name = "Channel 1 Phase A"
 239        },
 240        {
 241                .id = 1,
 242                .name = "Channel 1 Phase B"
 243        }
 244};
 245
 246static struct counter_synapse ftm_quaddec_count_synapses[] = {
 247        {
 248                .actions_list = ftm_quaddec_synapse_actions,
 249                .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 250                .signal = &ftm_quaddec_signals[0]
 251        },
 252        {
 253                .actions_list = ftm_quaddec_synapse_actions,
 254                .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 255                .signal = &ftm_quaddec_signals[1]
 256        }
 257};
 258
 259static const struct counter_count_ext ftm_quaddec_count_ext[] = {
 260        COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
 261        COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
 262};
 263
 264static struct counter_count ftm_quaddec_counts = {
 265        .id = 0,
 266        .name = "Channel 1 Count",
 267        .functions_list = ftm_quaddec_count_functions,
 268        .num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
 269        .synapses = ftm_quaddec_count_synapses,
 270        .num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
 271        .ext = ftm_quaddec_count_ext,
 272        .num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
 273};
 274
 275static int ftm_quaddec_probe(struct platform_device *pdev)
 276{
 277        struct ftm_quaddec *ftm;
 278
 279        struct device_node *node = pdev->dev.of_node;
 280        struct resource *io;
 281        int ret;
 282
 283        ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL);
 284        if (!ftm)
 285                return -ENOMEM;
 286
 287        platform_set_drvdata(pdev, ftm);
 288
 289        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 290        if (!io) {
 291                dev_err(&pdev->dev, "Failed to get memory region\n");
 292                return -ENODEV;
 293        }
 294
 295        ftm->pdev = pdev;
 296        ftm->big_endian = of_property_read_bool(node, "big-endian");
 297        ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
 298
 299        if (!ftm->ftm_base) {
 300                dev_err(&pdev->dev, "Failed to map memory region\n");
 301                return -EINVAL;
 302        }
 303        ftm->counter.name = dev_name(&pdev->dev);
 304        ftm->counter.parent = &pdev->dev;
 305        ftm->counter.ops = &ftm_quaddec_cnt_ops;
 306        ftm->counter.counts = &ftm_quaddec_counts;
 307        ftm->counter.num_counts = 1;
 308        ftm->counter.signals = ftm_quaddec_signals;
 309        ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals);
 310        ftm->counter.priv = ftm;
 311
 312        mutex_init(&ftm->ftm_quaddec_mutex);
 313
 314        ftm_quaddec_init(ftm);
 315
 316        ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
 317        if (ret)
 318                return ret;
 319
 320        ret = devm_counter_register(&pdev->dev, &ftm->counter);
 321        if (ret)
 322                return ret;
 323
 324        return 0;
 325}
 326
 327static const struct of_device_id ftm_quaddec_match[] = {
 328        { .compatible = "fsl,ftm-quaddec" },
 329        {},
 330};
 331
 332static struct platform_driver ftm_quaddec_driver = {
 333        .driver = {
 334                .name = "ftm-quaddec",
 335                .of_match_table = ftm_quaddec_match,
 336        },
 337        .probe = ftm_quaddec_probe,
 338};
 339
 340module_platform_driver(ftm_quaddec_driver);
 341
 342MODULE_LICENSE("GPL");
 343MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
 344MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
 345