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 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                                  struct counter_count_read_value *val)
 182{
 183        struct ftm_quaddec *const ftm = counter->priv;
 184        uint32_t cntval;
 185
 186        ftm_read(ftm, FTM_CNT, &cntval);
 187
 188        counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cntval);
 189
 190        return 0;
 191}
 192
 193static int ftm_quaddec_count_write(struct counter_device *counter,
 194                                   struct counter_count *count,
 195                                   struct counter_count_write_value *val)
 196{
 197        struct ftm_quaddec *const ftm = counter->priv;
 198        u32 cnt;
 199        int err;
 200
 201        err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val);
 202        if (err)
 203                return err;
 204
 205        if (cnt != 0) {
 206                dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
 207                return -EINVAL;
 208        }
 209
 210        ftm_reset_counter(ftm);
 211
 212        return 0;
 213}
 214
 215static int ftm_quaddec_count_function_get(struct counter_device *counter,
 216                                          struct counter_count *count,
 217                                          size_t *function)
 218{
 219        *function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
 220
 221        return 0;
 222}
 223
 224static int ftm_quaddec_action_get(struct counter_device *counter,
 225                                  struct counter_count *count,
 226                                  struct counter_synapse *synapse,
 227                                  size_t *action)
 228{
 229        *action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
 230
 231        return 0;
 232}
 233
 234static const struct counter_ops ftm_quaddec_cnt_ops = {
 235        .count_read = ftm_quaddec_count_read,
 236        .count_write = ftm_quaddec_count_write,
 237        .function_get = ftm_quaddec_count_function_get,
 238        .action_get = ftm_quaddec_action_get,
 239};
 240
 241static struct counter_signal ftm_quaddec_signals[] = {
 242        {
 243                .id = 0,
 244                .name = "Channel 1 Phase A"
 245        },
 246        {
 247                .id = 1,
 248                .name = "Channel 1 Phase B"
 249        }
 250};
 251
 252static struct counter_synapse ftm_quaddec_count_synapses[] = {
 253        {
 254                .actions_list = ftm_quaddec_synapse_actions,
 255                .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 256                .signal = &ftm_quaddec_signals[0]
 257        },
 258        {
 259                .actions_list = ftm_quaddec_synapse_actions,
 260                .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 261                .signal = &ftm_quaddec_signals[1]
 262        }
 263};
 264
 265static const struct counter_count_ext ftm_quaddec_count_ext[] = {
 266        COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
 267        COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
 268};
 269
 270static struct counter_count ftm_quaddec_counts = {
 271        .id = 0,
 272        .name = "Channel 1 Count",
 273        .functions_list = ftm_quaddec_count_functions,
 274        .num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
 275        .synapses = ftm_quaddec_count_synapses,
 276        .num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
 277        .ext = ftm_quaddec_count_ext,
 278        .num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
 279};
 280
 281static int ftm_quaddec_probe(struct platform_device *pdev)
 282{
 283        struct ftm_quaddec *ftm;
 284
 285        struct device_node *node = pdev->dev.of_node;
 286        struct resource *io;
 287        int ret;
 288
 289        ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL);
 290        if (!ftm)
 291                return -ENOMEM;
 292
 293        platform_set_drvdata(pdev, ftm);
 294
 295        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 296        if (!io) {
 297                dev_err(&pdev->dev, "Failed to get memory region\n");
 298                return -ENODEV;
 299        }
 300
 301        ftm->pdev = pdev;
 302        ftm->big_endian = of_property_read_bool(node, "big-endian");
 303        ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
 304
 305        if (!ftm->ftm_base) {
 306                dev_err(&pdev->dev, "Failed to map memory region\n");
 307                return -EINVAL;
 308        }
 309        ftm->counter.name = dev_name(&pdev->dev);
 310        ftm->counter.parent = &pdev->dev;
 311        ftm->counter.ops = &ftm_quaddec_cnt_ops;
 312        ftm->counter.counts = &ftm_quaddec_counts;
 313        ftm->counter.num_counts = 1;
 314        ftm->counter.signals = ftm_quaddec_signals;
 315        ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals);
 316        ftm->counter.priv = ftm;
 317
 318        mutex_init(&ftm->ftm_quaddec_mutex);
 319
 320        ftm_quaddec_init(ftm);
 321
 322        ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
 323        if (ret)
 324                return ret;
 325
 326        ret = devm_counter_register(&pdev->dev, &ftm->counter);
 327        if (ret)
 328                return ret;
 329
 330        return 0;
 331}
 332
 333static const struct of_device_id ftm_quaddec_match[] = {
 334        { .compatible = "fsl,ftm-quaddec" },
 335        {},
 336};
 337
 338static struct platform_driver ftm_quaddec_driver = {
 339        .driver = {
 340                .name = "ftm-quaddec",
 341                .of_match_table = ftm_quaddec_match,
 342        },
 343        .probe = ftm_quaddec_probe,
 344};
 345
 346module_platform_driver(ftm_quaddec_driver);
 347
 348MODULE_LICENSE("GPL");
 349MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
 350MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
 351