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#include <linux/types.h>
  18
  19#define FTM_FIELD_UPDATE(ftm, offset, mask, val)                        \
  20        ({                                                              \
  21                uint32_t flags;                                         \
  22                ftm_read(ftm, offset, &flags);                          \
  23                flags &= ~mask;                                         \
  24                flags |= FIELD_PREP(mask, val);                         \
  25                ftm_write(ftm, offset, flags);                          \
  26        })
  27
  28struct ftm_quaddec {
  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, u32 *cnt_mode)
 119{
 120        struct ftm_quaddec *ftm = counter_priv(counter);
 121        uint32_t scflags;
 122
 123        ftm_read(ftm, FTM_SC, &scflags);
 124
 125        *cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
 126
 127        return 0;
 128}
 129
 130static int ftm_quaddec_set_prescaler(struct counter_device *counter,
 131                                     struct counter_count *count, u32 cnt_mode)
 132{
 133        struct ftm_quaddec *ftm = counter_priv(counter);
 134
 135        mutex_lock(&ftm->ftm_quaddec_mutex);
 136
 137        ftm_clear_write_protection(ftm);
 138        FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
 139        ftm_set_write_protection(ftm);
 140
 141        /* Also resets the counter as it is undefined anyway now */
 142        ftm_reset_counter(ftm);
 143
 144        mutex_unlock(&ftm->ftm_quaddec_mutex);
 145        return 0;
 146}
 147
 148static const char * const ftm_quaddec_prescaler[] = {
 149        "1", "2", "4", "8", "16", "32", "64", "128"
 150};
 151
 152static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
 153        COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 154};
 155
 156static const enum counter_function ftm_quaddec_count_functions[] = {
 157        COUNTER_FUNCTION_QUADRATURE_X4
 158};
 159
 160static int ftm_quaddec_count_read(struct counter_device *counter,
 161                                  struct counter_count *count,
 162                                  u64 *val)
 163{
 164        struct ftm_quaddec *const ftm = counter_priv(counter);
 165        uint32_t cntval;
 166
 167        ftm_read(ftm, FTM_CNT, &cntval);
 168
 169        *val = cntval;
 170
 171        return 0;
 172}
 173
 174static int ftm_quaddec_count_write(struct counter_device *counter,
 175                                   struct counter_count *count,
 176                                   const u64 val)
 177{
 178        struct ftm_quaddec *const ftm = counter_priv(counter);
 179
 180        if (val != 0) {
 181                dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
 182                return -EINVAL;
 183        }
 184
 185        ftm_reset_counter(ftm);
 186
 187        return 0;
 188}
 189
 190static int ftm_quaddec_count_function_read(struct counter_device *counter,
 191                                           struct counter_count *count,
 192                                           enum counter_function *function)
 193{
 194        *function = COUNTER_FUNCTION_QUADRATURE_X4;
 195
 196        return 0;
 197}
 198
 199static int ftm_quaddec_action_read(struct counter_device *counter,
 200                                   struct counter_count *count,
 201                                   struct counter_synapse *synapse,
 202                                   enum counter_synapse_action *action)
 203{
 204        *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
 205
 206        return 0;
 207}
 208
 209static const struct counter_ops ftm_quaddec_cnt_ops = {
 210        .count_read = ftm_quaddec_count_read,
 211        .count_write = ftm_quaddec_count_write,
 212        .function_read = ftm_quaddec_count_function_read,
 213        .action_read = ftm_quaddec_action_read,
 214};
 215
 216static struct counter_signal ftm_quaddec_signals[] = {
 217        {
 218                .id = 0,
 219                .name = "Channel 1 Phase A"
 220        },
 221        {
 222                .id = 1,
 223                .name = "Channel 1 Phase B"
 224        }
 225};
 226
 227static struct counter_synapse ftm_quaddec_count_synapses[] = {
 228        {
 229                .actions_list = ftm_quaddec_synapse_actions,
 230                .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 231                .signal = &ftm_quaddec_signals[0]
 232        },
 233        {
 234                .actions_list = ftm_quaddec_synapse_actions,
 235                .num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
 236                .signal = &ftm_quaddec_signals[1]
 237        }
 238};
 239
 240static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
 241
 242static struct counter_comp ftm_quaddec_count_ext[] = {
 243        COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
 244                                ftm_quaddec_set_prescaler,
 245                                ftm_quaddec_prescaler_enum),
 246};
 247
 248static struct counter_count ftm_quaddec_counts = {
 249        .id = 0,
 250        .name = "Channel 1 Count",
 251        .functions_list = ftm_quaddec_count_functions,
 252        .num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
 253        .synapses = ftm_quaddec_count_synapses,
 254        .num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
 255        .ext = ftm_quaddec_count_ext,
 256        .num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
 257};
 258
 259static int ftm_quaddec_probe(struct platform_device *pdev)
 260{
 261        struct counter_device *counter;
 262        struct ftm_quaddec *ftm;
 263
 264        struct device_node *node = pdev->dev.of_node;
 265        struct resource *io;
 266        int ret;
 267
 268        counter = devm_counter_alloc(&pdev->dev, sizeof(*ftm));
 269        if (!counter)
 270                return -ENOMEM;
 271        ftm = counter_priv(counter);
 272
 273        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 274        if (!io) {
 275                dev_err(&pdev->dev, "Failed to get memory region\n");
 276                return -ENODEV;
 277        }
 278
 279        ftm->pdev = pdev;
 280        ftm->big_endian = of_property_read_bool(node, "big-endian");
 281        ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
 282
 283        if (!ftm->ftm_base) {
 284                dev_err(&pdev->dev, "Failed to map memory region\n");
 285                return -EINVAL;
 286        }
 287        counter->name = dev_name(&pdev->dev);
 288        counter->parent = &pdev->dev;
 289        counter->ops = &ftm_quaddec_cnt_ops;
 290        counter->counts = &ftm_quaddec_counts;
 291        counter->num_counts = 1;
 292        counter->signals = ftm_quaddec_signals;
 293        counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals);
 294
 295        mutex_init(&ftm->ftm_quaddec_mutex);
 296
 297        ftm_quaddec_init(ftm);
 298
 299        ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
 300        if (ret)
 301                return ret;
 302
 303        ret = devm_counter_add(&pdev->dev, counter);
 304        if (ret)
 305                return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
 306
 307        return 0;
 308}
 309
 310static const struct of_device_id ftm_quaddec_match[] = {
 311        { .compatible = "fsl,ftm-quaddec" },
 312        {},
 313};
 314
 315static struct platform_driver ftm_quaddec_driver = {
 316        .driver = {
 317                .name = "ftm-quaddec",
 318                .of_match_table = ftm_quaddec_match,
 319        },
 320        .probe = ftm_quaddec_probe,
 321};
 322
 323module_platform_driver(ftm_quaddec_driver);
 324
 325MODULE_LICENSE("GPL");
 326MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
 327MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
 328