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_function mchp_tc_count_functions[] = {
  41        [MCHP_TC_FUNCTION_INCREASE] = COUNTER_FUNCTION_INCREASE,
  42        [MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_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        default:
 137                /* should never reach this path */
 138                return -EINVAL;
 139        }
 140
 141        regmap_write(priv->regmap, ATMEL_TC_BMR, bmr);
 142        regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), cmr);
 143
 144        /* Enable clock and trigger counter */
 145        regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], CCR),
 146                     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
 147
 148        if (priv->qdec_mode) {
 149                regmap_write(priv->regmap,
 150                             ATMEL_TC_REG(priv->channel[1], CMR), cmr);
 151                regmap_write(priv->regmap,
 152                             ATMEL_TC_REG(priv->channel[1], CCR),
 153                             ATMEL_TC_CLKEN | ATMEL_TC_SWTRG);
 154        }
 155
 156        return 0;
 157}
 158
 159static int mchp_tc_count_signal_read(struct counter_device *counter,
 160                                     struct counter_signal *signal,
 161                                     enum counter_signal_level *lvl)
 162{
 163        struct mchp_tc_data *const priv = counter->priv;
 164        bool sigstatus;
 165        u32 sr;
 166
 167        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
 168
 169        if (priv->trig_inverted)
 170                sigstatus = (sr & ATMEL_TC_MTIOB);
 171        else
 172                sigstatus = (sr & ATMEL_TC_MTIOA);
 173
 174        *lvl = sigstatus ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
 175
 176        return 0;
 177}
 178
 179static int mchp_tc_count_action_get(struct counter_device *counter,
 180                                    struct counter_count *count,
 181                                    struct counter_synapse *synapse,
 182                                    size_t *action)
 183{
 184        struct mchp_tc_data *const priv = counter->priv;
 185        u32 cmr;
 186
 187        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr);
 188
 189        switch (cmr & ATMEL_TC_ETRGEDG) {
 190        default:
 191                *action = MCHP_TC_SYNAPSE_ACTION_NONE;
 192                break;
 193        case ATMEL_TC_ETRGEDG_RISING:
 194                *action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
 195                break;
 196        case ATMEL_TC_ETRGEDG_FALLING:
 197                *action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
 198                break;
 199        case ATMEL_TC_ETRGEDG_BOTH:
 200                *action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
 201                break;
 202        }
 203
 204        return 0;
 205}
 206
 207static int mchp_tc_count_action_set(struct counter_device *counter,
 208                                    struct counter_count *count,
 209                                    struct counter_synapse *synapse,
 210                                    size_t action)
 211{
 212        struct mchp_tc_data *const priv = counter->priv;
 213        u32 edge = ATMEL_TC_ETRGEDG_NONE;
 214
 215        /* QDEC mode is rising edge only */
 216        if (priv->qdec_mode)
 217                return -EINVAL;
 218
 219        switch (action) {
 220        case MCHP_TC_SYNAPSE_ACTION_NONE:
 221                edge = ATMEL_TC_ETRGEDG_NONE;
 222                break;
 223        case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
 224                edge = ATMEL_TC_ETRGEDG_RISING;
 225                break;
 226        case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
 227                edge = ATMEL_TC_ETRGEDG_FALLING;
 228                break;
 229        case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
 230                edge = ATMEL_TC_ETRGEDG_BOTH;
 231                break;
 232        default:
 233                /* should never reach this path */
 234                return -EINVAL;
 235        }
 236
 237        return regmap_write_bits(priv->regmap,
 238                                ATMEL_TC_REG(priv->channel[0], CMR),
 239                                ATMEL_TC_ETRGEDG, edge);
 240}
 241
 242static int mchp_tc_count_read(struct counter_device *counter,
 243                              struct counter_count *count,
 244                              unsigned long *val)
 245{
 246        struct mchp_tc_data *const priv = counter->priv;
 247        u32 cnt;
 248
 249        regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt);
 250        *val = cnt;
 251
 252        return 0;
 253}
 254
 255static struct counter_count mchp_tc_counts[] = {
 256        {
 257                .id = 0,
 258                .name = "Timer Counter",
 259                .functions_list = mchp_tc_count_functions,
 260                .num_functions = ARRAY_SIZE(mchp_tc_count_functions),
 261                .synapses = mchp_tc_count_synapses,
 262                .num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),
 263        },
 264};
 265
 266static const struct counter_ops mchp_tc_ops = {
 267        .signal_read  = mchp_tc_count_signal_read,
 268        .count_read   = mchp_tc_count_read,
 269        .function_get = mchp_tc_count_function_get,
 270        .function_set = mchp_tc_count_function_set,
 271        .action_get   = mchp_tc_count_action_get,
 272        .action_set   = mchp_tc_count_action_set
 273};
 274
 275static const struct atmel_tcb_config tcb_rm9200_config = {
 276                .counter_width = 16,
 277};
 278
 279static const struct atmel_tcb_config tcb_sam9x5_config = {
 280                .counter_width = 32,
 281};
 282
 283static const struct atmel_tcb_config tcb_sama5d2_config = {
 284                .counter_width = 32,
 285                .has_gclk = true,
 286                .has_qdec = true,
 287};
 288
 289static const struct atmel_tcb_config tcb_sama5d3_config = {
 290                .counter_width = 32,
 291                .has_qdec = true,
 292};
 293
 294static const struct of_device_id atmel_tc_of_match[] = {
 295        { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
 296        { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
 297        { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
 298        { .compatible = "atmel,sama5d3-tcb", .data = &tcb_sama5d3_config, },
 299        { /* sentinel */ }
 300};
 301
 302static void mchp_tc_clk_remove(void *ptr)
 303{
 304        clk_disable_unprepare((struct clk *)ptr);
 305}
 306
 307static int mchp_tc_probe(struct platform_device *pdev)
 308{
 309        struct device_node *np = pdev->dev.of_node;
 310        const struct atmel_tcb_config *tcb_config;
 311        const struct of_device_id *match;
 312        struct mchp_tc_data *priv;
 313        char clk_name[7];
 314        struct regmap *regmap;
 315        struct clk *clk[3];
 316        int channel;
 317        int ret, i;
 318
 319        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 320        if (!priv)
 321                return -ENOMEM;
 322
 323        platform_set_drvdata(pdev, priv);
 324
 325        match = of_match_node(atmel_tc_of_match, np->parent);
 326        tcb_config = match->data;
 327        if (!tcb_config) {
 328                dev_err(&pdev->dev, "No matching parent node found\n");
 329                return -ENODEV;
 330        }
 331
 332        regmap = syscon_node_to_regmap(np->parent);
 333        if (IS_ERR(regmap))
 334                return PTR_ERR(regmap);
 335
 336        /* max. channels number is 2 when in QDEC mode */
 337        priv->num_channels = of_property_count_u32_elems(np, "reg");
 338        if (priv->num_channels < 0) {
 339                dev_err(&pdev->dev, "Invalid or missing channel\n");
 340                return -EINVAL;
 341        }
 342
 343        /* Register channels and initialize clocks */
 344        for (i = 0; i < priv->num_channels; i++) {
 345                ret = of_property_read_u32_index(np, "reg", i, &channel);
 346                if (ret < 0 || channel > 2)
 347                        return -ENODEV;
 348
 349                priv->channel[i] = channel;
 350
 351                snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);
 352
 353                clk[i] = of_clk_get_by_name(np->parent, clk_name);
 354                if (IS_ERR(clk[i])) {
 355                        /* Fallback to t0_clk */
 356                        clk[i] = of_clk_get_by_name(np->parent, "t0_clk");
 357                        if (IS_ERR(clk[i]))
 358                                return PTR_ERR(clk[i]);
 359                }
 360
 361                ret = clk_prepare_enable(clk[i]);
 362                if (ret)
 363                        return ret;
 364
 365                ret = devm_add_action_or_reset(&pdev->dev,
 366                                               mchp_tc_clk_remove,
 367                                               clk[i]);
 368                if (ret)
 369                        return ret;
 370
 371                dev_dbg(&pdev->dev,
 372                        "Initialized capture mode on channel %d\n",
 373                        channel);
 374        }
 375
 376        priv->tc_cfg = tcb_config;
 377        priv->regmap = regmap;
 378        priv->counter.name = dev_name(&pdev->dev);
 379        priv->counter.parent = &pdev->dev;
 380        priv->counter.ops = &mchp_tc_ops;
 381        priv->counter.num_counts = ARRAY_SIZE(mchp_tc_counts);
 382        priv->counter.counts = mchp_tc_counts;
 383        priv->counter.num_signals = ARRAY_SIZE(mchp_tc_count_signals);
 384        priv->counter.signals = mchp_tc_count_signals;
 385        priv->counter.priv = priv;
 386
 387        return devm_counter_register(&pdev->dev, &priv->counter);
 388}
 389
 390static const struct of_device_id mchp_tc_dt_ids[] = {
 391        { .compatible = "microchip,tcb-capture", },
 392        { /* sentinel */ },
 393};
 394MODULE_DEVICE_TABLE(of, mchp_tc_dt_ids);
 395
 396static struct platform_driver mchp_tc_driver = {
 397        .probe = mchp_tc_probe,
 398        .driver = {
 399                .name = "microchip-tcb-capture",
 400                .of_match_table = mchp_tc_dt_ids,
 401        },
 402};
 403module_platform_driver(mchp_tc_driver);
 404
 405MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
 406MODULE_DESCRIPTION("Microchip TCB Capture driver");
 407MODULE_LICENSE("GPL v2");
 408