linux/drivers/iio/counter/stm32-lptimer-cnt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * STM32 Low-Power Timer Encoder and Counter driver
   4 *
   5 * Copyright (C) STMicroelectronics 2017
   6 *
   7 * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
   8 *
   9 * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
  10 *
  11 */
  12
  13#include <linux/bitfield.h>
  14#include <linux/iio/iio.h>
  15#include <linux/mfd/stm32-lptimer.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18
  19struct stm32_lptim_cnt {
  20        struct device *dev;
  21        struct regmap *regmap;
  22        struct clk *clk;
  23        u32 preset;
  24        u32 polarity;
  25        u32 quadrature_mode;
  26};
  27
  28static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
  29{
  30        u32 val;
  31        int ret;
  32
  33        ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
  34        if (ret)
  35                return ret;
  36
  37        return FIELD_GET(STM32_LPTIM_ENABLE, val);
  38}
  39
  40static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
  41                                        int enable)
  42{
  43        int ret;
  44        u32 val;
  45
  46        val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
  47        ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
  48        if (ret)
  49                return ret;
  50
  51        if (!enable) {
  52                clk_disable(priv->clk);
  53                return 0;
  54        }
  55
  56        /* LP timer must be enabled before writing CMP & ARR */
  57        ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
  58        if (ret)
  59                return ret;
  60
  61        ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
  62        if (ret)
  63                return ret;
  64
  65        /* ensure CMP & ARR registers are properly written */
  66        ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
  67                                       (val & STM32_LPTIM_CMPOK_ARROK),
  68                                       100, 1000);
  69        if (ret)
  70                return ret;
  71
  72        ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
  73                           STM32_LPTIM_CMPOKCF_ARROKCF);
  74        if (ret)
  75                return ret;
  76
  77        ret = clk_enable(priv->clk);
  78        if (ret) {
  79                regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
  80                return ret;
  81        }
  82
  83        /* Start LP timer in continuous mode */
  84        return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
  85                                  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
  86}
  87
  88static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
  89{
  90        u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
  91                   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
  92        u32 val;
  93
  94        /* Setup LP timer encoder/counter and polarity, without prescaler */
  95        if (priv->quadrature_mode)
  96                val = enable ? STM32_LPTIM_ENC : 0;
  97        else
  98                val = enable ? STM32_LPTIM_COUNTMODE : 0;
  99        val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
 100
 101        return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
 102}
 103
 104static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
 105                                 struct iio_chan_spec const *chan,
 106                                 int val, int val2, long mask)
 107{
 108        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 109        int ret;
 110
 111        switch (mask) {
 112        case IIO_CHAN_INFO_ENABLE:
 113                if (val < 0 || val > 1)
 114                        return -EINVAL;
 115
 116                /* Check nobody uses the timer, or already disabled/enabled */
 117                ret = stm32_lptim_is_enabled(priv);
 118                if ((ret < 0) || (!ret && !val))
 119                        return ret;
 120                if (val && ret)
 121                        return -EBUSY;
 122
 123                ret = stm32_lptim_setup(priv, val);
 124                if (ret)
 125                        return ret;
 126                return stm32_lptim_set_enable_state(priv, val);
 127
 128        default:
 129                return -EINVAL;
 130        }
 131}
 132
 133static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
 134                                struct iio_chan_spec const *chan,
 135                                int *val, int *val2, long mask)
 136{
 137        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 138        u32 dat;
 139        int ret;
 140
 141        switch (mask) {
 142        case IIO_CHAN_INFO_RAW:
 143                ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
 144                if (ret)
 145                        return ret;
 146                *val = dat;
 147                return IIO_VAL_INT;
 148
 149        case IIO_CHAN_INFO_ENABLE:
 150                ret = stm32_lptim_is_enabled(priv);
 151                if (ret < 0)
 152                        return ret;
 153                *val = ret;
 154                return IIO_VAL_INT;
 155
 156        case IIO_CHAN_INFO_SCALE:
 157                /* Non-quadrature mode: scale = 1 */
 158                *val = 1;
 159                *val2 = 0;
 160                if (priv->quadrature_mode) {
 161                        /*
 162                         * Quadrature encoder mode:
 163                         * - both edges, quarter cycle, scale is 0.25
 164                         * - either rising/falling edge scale is 0.5
 165                         */
 166                        if (priv->polarity > 1)
 167                                *val2 = 2;
 168                        else
 169                                *val2 = 1;
 170                }
 171                return IIO_VAL_FRACTIONAL_LOG2;
 172
 173        default:
 174                return -EINVAL;
 175        }
 176}
 177
 178static const struct iio_info stm32_lptim_cnt_iio_info = {
 179        .read_raw = stm32_lptim_read_raw,
 180        .write_raw = stm32_lptim_write_raw,
 181};
 182
 183static const char *const stm32_lptim_quadrature_modes[] = {
 184        "non-quadrature",
 185        "quadrature",
 186};
 187
 188static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
 189                                           const struct iio_chan_spec *chan)
 190{
 191        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 192
 193        return priv->quadrature_mode;
 194}
 195
 196static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
 197                                           const struct iio_chan_spec *chan,
 198                                           unsigned int type)
 199{
 200        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 201
 202        if (stm32_lptim_is_enabled(priv))
 203                return -EBUSY;
 204
 205        priv->quadrature_mode = type;
 206
 207        return 0;
 208}
 209
 210static const struct iio_enum stm32_lptim_quadrature_mode_en = {
 211        .items = stm32_lptim_quadrature_modes,
 212        .num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
 213        .get = stm32_lptim_get_quadrature_mode,
 214        .set = stm32_lptim_set_quadrature_mode,
 215};
 216
 217static const char * const stm32_lptim_cnt_polarity[] = {
 218        "rising-edge", "falling-edge", "both-edges",
 219};
 220
 221static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
 222                                        const struct iio_chan_spec *chan)
 223{
 224        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 225
 226        return priv->polarity;
 227}
 228
 229static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
 230                                        const struct iio_chan_spec *chan,
 231                                        unsigned int type)
 232{
 233        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 234
 235        if (stm32_lptim_is_enabled(priv))
 236                return -EBUSY;
 237
 238        priv->polarity = type;
 239
 240        return 0;
 241}
 242
 243static const struct iio_enum stm32_lptim_cnt_polarity_en = {
 244        .items = stm32_lptim_cnt_polarity,
 245        .num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
 246        .get = stm32_lptim_cnt_get_polarity,
 247        .set = stm32_lptim_cnt_set_polarity,
 248};
 249
 250static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
 251                                          uintptr_t private,
 252                                          const struct iio_chan_spec *chan,
 253                                          char *buf)
 254{
 255        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 256
 257        return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
 258}
 259
 260static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
 261                                          uintptr_t private,
 262                                          const struct iio_chan_spec *chan,
 263                                          const char *buf, size_t len)
 264{
 265        struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
 266        int ret;
 267
 268        if (stm32_lptim_is_enabled(priv))
 269                return -EBUSY;
 270
 271        ret = kstrtouint(buf, 0, &priv->preset);
 272        if (ret)
 273                return ret;
 274
 275        if (priv->preset > STM32_LPTIM_MAX_ARR)
 276                return -EINVAL;
 277
 278        return len;
 279}
 280
 281/* LP timer with encoder */
 282static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
 283        {
 284                .name = "preset",
 285                .shared = IIO_SEPARATE,
 286                .read = stm32_lptim_cnt_get_preset,
 287                .write = stm32_lptim_cnt_set_preset,
 288        },
 289        IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
 290        IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
 291        IIO_ENUM("quadrature_mode", IIO_SEPARATE,
 292                 &stm32_lptim_quadrature_mode_en),
 293        IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
 294        {}
 295};
 296
 297static const struct iio_chan_spec stm32_lptim_enc_channels = {
 298        .type = IIO_COUNT,
 299        .channel = 0,
 300        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 301                              BIT(IIO_CHAN_INFO_ENABLE) |
 302                              BIT(IIO_CHAN_INFO_SCALE),
 303        .ext_info = stm32_lptim_enc_ext_info,
 304        .indexed = 1,
 305};
 306
 307/* LP timer without encoder (counter only) */
 308static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
 309        {
 310                .name = "preset",
 311                .shared = IIO_SEPARATE,
 312                .read = stm32_lptim_cnt_get_preset,
 313                .write = stm32_lptim_cnt_set_preset,
 314        },
 315        IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
 316        IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
 317        {}
 318};
 319
 320static const struct iio_chan_spec stm32_lptim_cnt_channels = {
 321        .type = IIO_COUNT,
 322        .channel = 0,
 323        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 324                              BIT(IIO_CHAN_INFO_ENABLE) |
 325                              BIT(IIO_CHAN_INFO_SCALE),
 326        .ext_info = stm32_lptim_cnt_ext_info,
 327        .indexed = 1,
 328};
 329
 330static int stm32_lptim_cnt_probe(struct platform_device *pdev)
 331{
 332        struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
 333        struct stm32_lptim_cnt *priv;
 334        struct iio_dev *indio_dev;
 335
 336        if (IS_ERR_OR_NULL(ddata))
 337                return -EINVAL;
 338
 339        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
 340        if (!indio_dev)
 341                return -ENOMEM;
 342
 343        priv = iio_priv(indio_dev);
 344        priv->dev = &pdev->dev;
 345        priv->regmap = ddata->regmap;
 346        priv->clk = ddata->clk;
 347        priv->preset = STM32_LPTIM_MAX_ARR;
 348
 349        indio_dev->name = dev_name(&pdev->dev);
 350        indio_dev->dev.parent = &pdev->dev;
 351        indio_dev->dev.of_node = pdev->dev.of_node;
 352        indio_dev->info = &stm32_lptim_cnt_iio_info;
 353        if (ddata->has_encoder)
 354                indio_dev->channels = &stm32_lptim_enc_channels;
 355        else
 356                indio_dev->channels = &stm32_lptim_cnt_channels;
 357        indio_dev->num_channels = 1;
 358
 359        platform_set_drvdata(pdev, priv);
 360
 361        return devm_iio_device_register(&pdev->dev, indio_dev);
 362}
 363
 364static const struct of_device_id stm32_lptim_cnt_of_match[] = {
 365        { .compatible = "st,stm32-lptimer-counter", },
 366        {},
 367};
 368MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
 369
 370static struct platform_driver stm32_lptim_cnt_driver = {
 371        .probe = stm32_lptim_cnt_probe,
 372        .driver = {
 373                .name = "stm32-lptimer-counter",
 374                .of_match_table = stm32_lptim_cnt_of_match,
 375        },
 376};
 377module_platform_driver(stm32_lptim_cnt_driver);
 378
 379MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
 380MODULE_ALIAS("platform:stm32-lptimer-counter");
 381MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
 382MODULE_LICENSE("GPL v2");
 383