linux/drivers/counter/microchip-tcb-capture.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/**
   3 * Copyright (C) 2020 Microchip
   4 *
   5 * Author: Kamel Bouhara <kamel.bouhara@bootlin.com>
   6 */
   7#include <linux/clk.h>
   8#include <linux/counter.h>
   9#include <linux/mfd/syscon.h>
  10#include <linux/module.h>
  11#include <linux/mutex.h>
  12#include <linux/of.h>
  13#include <linux/of_device.h>
  14#include <linux/platform_device.h>
  15#include <linux/regmap.h>
  16#include <soc/at91/atmel_tcb.h>
  17
  18#define ATMEL_TC_CMR_MASK       (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \
  19                                 ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \
  20                                 ATMEL_TC_LDBSTOP)
  21
  22#define ATMEL_TC_QDEN                   BIT(8)
  23#define ATMEL_TC_POSEN                  BIT(9)
  24
  25struct mchp_tc_data {
  26        const struct atmel_tcb_config *tc_cfg;
  27        struct counter_device counter;
  28        struct regmap *regmap;
  29        int qdec_mode;
  30        int num_channels;
  31        int channel[2];
  32        bool trig_inverted;
  33};
  34
  35enum mchp_tc_count_function {
  36        MCHP_TC_FUNCTION_INCREASE,
  37        MCHP_TC_FUNCTION_QUADRATURE,
  38};
  39
  40static const enum counter_count_function mchp_tc_count_functions[] = {
  41        [MCHP_TC_FUNCTION_INCREASE] = COUNTER_COUNT_FUNCTION_INCREASE,
  42        [MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4,
  43};
  44
  45enum mchp_tc_synapse_action {
  46        MCHP_TC_SYNAPSE_ACTION_NONE = 0,
  47        MCHP_TC_SYNAPSE_ACTION_RISING_EDGE,
  48        MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE,
  49        MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE
  50};
  51
  52static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
  53        [MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
  54        [MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
  55        [MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
  56        [MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
  57};
  58
  59static struct counter_signal mchp_tc_count_signals[] = {
  60        {
  61                .id = 0,
  62                .name = "Channel A",
  63        },
  64        {
  65                .id = 1,
  66                .name = "Channel B",
  67        }
  68};
  69
  70static struct counter_synapse mchp_tc_count_synapses[] = {
  71        {
  72                .actions_list = mchp_tc_synapse_actions,
  73                .num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
  74                .signal = &mchp_tc_count_signals[0]
  75        },
  76        {
  77                .actions_list = mchp_tc_synapse_actions,
  78                .num_actions = ARRAY_SIZE(mchp_tc_synapse_actions),
  79                .signal = &mchp_tc_count_signals[1]
  80        }
  81};
  82
  83static int mchp_tc_count_function_get(struct counter_device *counter,
  84                                      struct counter_count *count,
  85                                      size_t *function)
  86{
  87        struct mchp_tc_data *const priv = counter->priv;
  88
  89        if (priv->qdec_mode)
  90                *function = MCHP_TC_FUNCTION_QUADRATURE;
  91        else
  92                *function = MCHP_TC_FUNCTION_INCREASE;
  93
  94        return 0;
  95}
  96
  97static int mchp_tc_count_function_set(struct counter_device *counter,
  98                                      struct counter_count *count,
  99                                      size_t function)
 100{
 101        struct mchp_tc_data *const priv = counter->priv;
 102        u32 bmr, cmr;
 103
 104        regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr);
 105        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
 106
 107        /* Set capture mode */
 108        cmr &= ~ATMEL_TC_WAVE;
 109
 110        switch (function) {
 111        case MCHP_TC_FUNCTION_INCREASE:
 112                priv->qdec_mode = 0;
 113                /* Set highest rate based on whether soc has gclk or not */
 114                bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
 115                if (priv->tc_cfg->has_gclk)
 116                        cmr |= ATMEL_TC_TIMER_CLOCK2;
 117                else
 118                        cmr |= ATMEL_TC_TIMER_CLOCK1;
 119                /* Setup the period capture mode */
 120                cmr |=  ATMEL_TC_CMR_MASK;
 121                cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
 122                break;
 123        case MCHP_TC_FUNCTION_QUADRATURE:
 124                if (!priv->tc_cfg->has_qdec)
 125                        return -EINVAL;
 126                /* In QDEC mode settings both channels 0 and 1 are required */
 127                if (priv->num_channels < 2 || priv->channel[0] != 0 ||
 128                    priv->channel[1] != 1) {
 129                        pr_err("Invalid channels number or id for quadrature mode\n");
 130                        return -EINVAL;
 131                }
 132                priv->qdec_mode = 1;
 133                bmr |= ATMEL_TC_QDEN | ATMEL_TC_POSEN;
 134                cmr |= ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_ABETRG | ATMEL_TC_XC0;
 135                break;
 136        }
 137
 138        regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);
 139        regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);
 140
 141        /* Enable clock and trigger counter */
 142        regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),
 143                     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
 144
 145        if (priv->qdec_mode) {
 146                regmap_write(priv->regmap,
 147                             ATMEL_TC_REG(priv->channel[1], CMR), cmr);
 148                regmap_write(priv->regmap,
 149                             ATMEL_TC_REG(priv->channel[1], CCR),
 150                             ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
 151        }
 152
 153        return 0;
 154}
 155
 156static int mchp_tc_count_signal_read(struct counter_device *counter,
 157                                     struct counter_signal *signal,
 158                                     enum counter_signal_value *val)
 159{
 160        struct mchp_tc_data *const priv = counter->priv;
 161        bool sigstatus;
 162        u32 sr;
 163
 164        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
 165
 166        if (priv->trig_inverted)
 167                sigstatus = (sr & ATMEL_TC_MTIOB);
 168        else
 169                sigstatus = (sr & ATMEL_TC_MTIOA);
 170
 171        *val = sigstatus ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW;
 172
 173        return 0;
 174}
 175
 176static int mchp_tc_count_action_get(struct counter_device *counter,
 177                                    struct counter_count *count,
 178                                    struct counter_synapse *synapse,
 179                                    size_t *action)
 180{
 181        struct mchp_tc_data *const priv = counter->priv;
 182        u32 cmr;
 183
 184        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
 185
 186        switch (cmr & ATMEL_TC_ETRGEDG) {
 187        default:
 188                *action = MCHP_TC_SYNAPSE_ACTION_NONE;
 189                break;
 190        case ATMEL_TC_ETRGEDG_RISING:
 191                *action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
 192                break;
 193        case ATMEL_TC_ETRGEDG_FALLING:
 194                *action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
 195                break;
 196        case ATMEL_TC_ETRGEDG_BOTH:
 197                *action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
 198                break;
 199        }
 200
 201        return 0;
 202}
 203
 204static int mchp_tc_count_action_set(struct counter_device *counter,
 205                                    struct counter_count *count,
 206                                    struct counter_synapse *synapse,
 207                                    size_t action)
 208{
 209        struct mchp_tc_data *const priv = counter->priv;
 210        u32 edge = ATMEL_TC_ETRGEDG_NONE;
 211
 212        /* QDEC mode is rising edge only */
 213        if (priv->qdec_mode)
 214                return -EINVAL;
 215
 216        switch (action) {
 217        case MCHP_TC_SYNAPSE_ACTION_NONE:
 218                edge = ATMEL_TC_ETRGEDG_NONE;
 219                break;
 220        case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
 221                edge = ATMEL_TC_ETRGEDG_RISING;
 222                break;
 223        case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
 224                edge = ATMEL_TC_ETRGEDG_FALLING;
 225                break;
 226        case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
 227                edge = ATMEL_TC_ETRGEDG_BOTH;
 228                break;
 229        }
 230
 231        return regmap_write_bits(priv->regmap,
 232                                ATMEL_TC_REG(priv->channel[0], CMR),
 233                                ATMEL_TC_ETRGEDG, edge);
 234}
 235
 236static int mchp_tc_count_read(struct counter_device *counter,
 237                              struct counter_count *count,
 238                              unsigned long *val)
 239{
 240        struct mchp_tc_data *const priv = counter->priv;
 241        u32 cnt;
 242
 243        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);
 244        *val = cnt;
 245
 246        return 0;
 247}
 248
 249static struct counter_count mchp_tc_counts[] = {
 250        {
 251                .id = 0,
 252                .name = "Timer Counter",
 253                .functions_list = mchp_tc_count_functions,
 254                .num_functions = ARRAY_SIZE(mchp_tc_count_functions),
 255                .synapses = mchp_tc_count_synapses,
 256                .num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),
 257        },
 258};
 259
 260static const struct counter_ops mchp_tc_ops = {
 261        .signal_read  = mchp_tc_count_signal_read,
 262        .count_read   = mchp_tc_count_read,
 263        .function_get = mchp_tc_count_function_get,
 264        .function_set = mchp_tc_count_function_set,
 265        .action_get   = mchp_tc_count_action_get,
 266        .action_set   = mchp_tc_count_action_set
 267};
 268
 269static const struct atmel_tcb_config tcb_rm9200_config = {
 270                .counter_width = 16,
 271};
 272
 273static const struct atmel_tcb_config tcb_sam9x5_config = {
 274                .counter_width = 32,
 275};
 276
 277static const struct atmel_tcb_config tcb_sama5d2_config = {
 278                .counter_width = 32,
 279                .has_gclk = true,
 280                .has_qdec = true,
 281};
 282
 283static const struct atmel_tcb_config tcb_sama5d3_config = {
 284                .counter_width = 32,
 285                .has_qdec = true,
 286};
 287
 288static const struct of_device_id atmel_tc_of_match[] = {
 289        { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
 290        { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
 291        { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
 292        { .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },
 293        { /* sentinel */ }
 294};
 295
 296static void mchp_tc_clk_remove(void *ptr)
 297{
 298        clk_disable_unprepare((struct clk *)ptr);
 299}
 300
 301static int mchp_tc_probe(struct platform_device *pdev)
 302{
 303        struct device_node *np = pdev->dev.of_node;
 304        const struct atmel_tcb_config *tcb_config;
 305        const struct of_device_id *match;
 306        struct mchp_tc_data *priv;
 307        char clk_name[7];
 308        struct regmap *regmap;
 309        struct clk *clk[3];
 310        int channel;
 311        int ret, i;
 312
 313        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 314        if (!priv)
 315                return -ENOMEM;
 316
 317        platform_set_drvdata(pdev, priv);
 318
 319        match = of_match_node(atmel_tc_of_match, np->parent);
 320        tcb_config = match->data;
 321        if (!tcb_config) {
 322                dev_err(&pdev->dev, "No matching parent node found\n");
 323                return -ENODEV;
 324        }
 325
 326        regmap = syscon_node_to_regmap(np->parent);
 327        if (IS_ERR(regmap))
 328                return PTR_ERR(regmap);
 329
 330        /* max. channels number is 2 when in QDEC mode */
 331        priv->num_channels = of_property_count_u32_elems(np, "reg");
 332        if (priv->num_channels < 0) {
 333                dev_err(&pdev->dev, "Invalid or missing channel\n");
 334                return -EINVAL;
 335        }
 336
 337        /* Register channels and initialize clocks */
 338        for (i = 0; i < priv->num_channels; i++) {
 339                ret = of_property_read_u32_index(np, "reg", i, &channel);
 340                if (ret < 0 || channel > 2)
 341                        return -ENODEV;
 342
 343                priv->channel[i] = channel;
 344
 345                snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);
 346
 347                clk[i] = of_clk_get_by_name(np->parent, clk_name);
 348                if (IS_ERR(clk[i])) {
 349                        /* Fallback to t0_clk */
 350                        clk[i] = of_clk_get_by_name(np->parent, "t0_clk");
 351                        if (IS_ERR(clk[i]))
 352                                return PTR_ERR(clk[i]);
 353                }
 354
 355                ret = clk_prepare_enable(clk[i]);
 356                if (ret)
 357                        return ret;
 358
 359                ret = devm_add_action_or_reset(&pdev->dev,
 360                                               mchp_tc_clk_remove,
 361                                               clk[i]);
 362                if (ret)
 363                        return ret;
 364
 365                dev_dbg(&pdev->dev,
 366                        "Initialized capture mode on channel %d\n",
 367                        channel);
 368        }
 369
 370        priv->tc_cfg = tcb_config;
 371        priv->regmap = regmap;
 372        priv->counter.name = dev_name(&pdev->dev);
 373        priv->counter.parent = &pdev->dev;
 374        priv->counter.ops = &mchp_tc_ops;
 375        priv->counter.num_counts = ARRAY_SIZE(mchp_tc_counts);
 376        priv->counter.counts = mchp_tc_counts;
 377        priv->counter.num_signals = ARRAY_SIZE(mchp_tc_count_signals);
 378        priv->counter.signals = mchp_tc_count_signals;
 379        priv->counter.priv = priv;
 380
 381        return devm_counter_register(&pdev->dev, &priv->counter);
 382}
 383
 384static const struct of_device_id mchp_tc_dt_ids[] = {
 385        { .compatible = "microchip,tcb-capture", },
 386        { /* sentinel */ },
 387};
 388MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);
 389
 390static struct platform_driver mchp_tc_driver = {
 391        .probe = mchp_tc_probe,
 392        .driver = {
 393                .name = "microchip-tcb-capture",
 394                .of_match_table = mchp_tc_dt_ids,
 395        },
 396};
 397module_platform_driver(mchp_tc_driver);
 398
 399MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
 400MODULE_DESCRIPTION("Microchip TCB Capture driver");
 401MODULE_LICENSE("GPL v2");
 402