linux/drivers/pwm/pwm-stm32-lp.c
<<
>>
Prefs
   1/*
   2 * STM32 Low-Power Timer PWM driver
   3 *
   4 * Copyright (C) STMicroelectronics 2017
   5 *
   6 * Author: Gerald Baeza <gerald.baeza@st.com>
   7 *
   8 * License terms: GNU General Public License (GPL), version 2
   9 *
  10 * Inspired by Gerald Baeza's pwm-stm32 driver
  11 */
  12
  13#include <linux/bitfield.h>
  14#include <linux/mfd/stm32-lptimer.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/platform_device.h>
  18#include <linux/pwm.h>
  19
  20struct stm32_pwm_lp {
  21        struct pwm_chip chip;
  22        struct clk *clk;
  23        struct regmap *regmap;
  24};
  25
  26static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
  27{
  28        return container_of(chip, struct stm32_pwm_lp, chip);
  29}
  30
  31/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
  32#define STM32_LPTIM_MAX_PRESCALER       128
  33
  34static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
  35                              struct pwm_state *state)
  36{
  37        struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
  38        unsigned long long prd, div, dty;
  39        struct pwm_state cstate;
  40        u32 val, mask, cfgr, presc = 0;
  41        bool reenable;
  42        int ret;
  43
  44        pwm_get_state(pwm, &cstate);
  45        reenable = !cstate.enabled;
  46
  47        if (!state->enabled) {
  48                if (cstate.enabled) {
  49                        /* Disable LP timer */
  50                        ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
  51                        if (ret)
  52                                return ret;
  53                        /* disable clock to PWM counter */
  54                        clk_disable(priv->clk);
  55                }
  56                return 0;
  57        }
  58
  59        /* Calculate the period and prescaler value */
  60        div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
  61        do_div(div, NSEC_PER_SEC);
  62        prd = div;
  63        while (div > STM32_LPTIM_MAX_ARR) {
  64                presc++;
  65                if ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
  66                        dev_err(priv->chip.dev, "max prescaler exceeded\n");
  67                        return -EINVAL;
  68                }
  69                div = prd >> presc;
  70        }
  71        prd = div;
  72
  73        /* Calculate the duty cycle */
  74        dty = prd * state->duty_cycle;
  75        do_div(dty, state->period);
  76
  77        if (!cstate.enabled) {
  78                /* enable clock to drive PWM counter */
  79                ret = clk_enable(priv->clk);
  80                if (ret)
  81                        return ret;
  82        }
  83
  84        ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
  85        if (ret)
  86                goto err;
  87
  88        if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
  89            (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
  90                val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
  91                val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
  92                mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
  93
  94                /* Must disable LP timer to modify CFGR */
  95                reenable = true;
  96                ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
  97                if (ret)
  98                        goto err;
  99
 100                ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
 101                                         val);
 102                if (ret)
 103                        goto err;
 104        }
 105
 106        if (reenable) {
 107                /* Must (re)enable LP timer to modify CMP & ARR */
 108                ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
 109                                   STM32_LPTIM_ENABLE);
 110                if (ret)
 111                        goto err;
 112        }
 113
 114        ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
 115        if (ret)
 116                goto err;
 117
 118        ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
 119        if (ret)
 120                goto err;
 121
 122        /* ensure CMP & ARR registers are properly written */
 123        ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
 124                                       (val & STM32_LPTIM_CMPOK_ARROK),
 125                                       100, 1000);
 126        if (ret) {
 127                dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");
 128                goto err;
 129        }
 130        ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
 131                           STM32_LPTIM_CMPOKCF_ARROKCF);
 132        if (ret)
 133                goto err;
 134
 135        if (reenable) {
 136                /* Start LP timer in continuous mode */
 137                ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
 138                                         STM32_LPTIM_CNTSTRT,
 139                                         STM32_LPTIM_CNTSTRT);
 140                if (ret) {
 141                        regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
 142                        goto err;
 143                }
 144        }
 145
 146        return 0;
 147err:
 148        if (!cstate.enabled)
 149                clk_disable(priv->clk);
 150
 151        return ret;
 152}
 153
 154static void stm32_pwm_lp_get_state(struct pwm_chip *chip,
 155                                   struct pwm_device *pwm,
 156                                   struct pwm_state *state)
 157{
 158        struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
 159        unsigned long rate = clk_get_rate(priv->clk);
 160        u32 val, presc, prd;
 161        u64 tmp;
 162
 163        regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
 164        state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
 165        /* Keep PWM counter clock refcount in sync with PWM initial state */
 166        if (state->enabled)
 167                clk_enable(priv->clk);
 168
 169        regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
 170        presc = FIELD_GET(STM32_LPTIM_PRESC, val);
 171        state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
 172
 173        regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
 174        tmp = prd + 1;
 175        tmp = (tmp << presc) * NSEC_PER_SEC;
 176        state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
 177
 178        regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
 179        tmp = prd - val;
 180        tmp = (tmp << presc) * NSEC_PER_SEC;
 181        state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
 182}
 183
 184static const struct pwm_ops stm32_pwm_lp_ops = {
 185        .owner = THIS_MODULE,
 186        .apply = stm32_pwm_lp_apply,
 187        .get_state = stm32_pwm_lp_get_state,
 188};
 189
 190static int stm32_pwm_lp_probe(struct platform_device *pdev)
 191{
 192        struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
 193        struct stm32_pwm_lp *priv;
 194        int ret;
 195
 196        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 197        if (!priv)
 198                return -ENOMEM;
 199
 200        priv->regmap = ddata->regmap;
 201        priv->clk = ddata->clk;
 202        priv->chip.base = -1;
 203        priv->chip.dev = &pdev->dev;
 204        priv->chip.ops = &stm32_pwm_lp_ops;
 205        priv->chip.npwm = 1;
 206
 207        ret = pwmchip_add(&priv->chip);
 208        if (ret < 0)
 209                return ret;
 210
 211        platform_set_drvdata(pdev, priv);
 212
 213        return 0;
 214}
 215
 216static int stm32_pwm_lp_remove(struct platform_device *pdev)
 217{
 218        struct stm32_pwm_lp *priv = platform_get_drvdata(pdev);
 219        unsigned int i;
 220
 221        for (i = 0; i < priv->chip.npwm; i++)
 222                pwm_disable(&priv->chip.pwms[i]);
 223
 224        return pwmchip_remove(&priv->chip);
 225}
 226
 227static const struct of_device_id stm32_pwm_lp_of_match[] = {
 228        { .compatible = "st,stm32-pwm-lp", },
 229        {},
 230};
 231MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
 232
 233static struct platform_driver stm32_pwm_lp_driver = {
 234        .probe  = stm32_pwm_lp_probe,
 235        .remove = stm32_pwm_lp_remove,
 236        .driver = {
 237                .name = "stm32-pwm-lp",
 238                .of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
 239        },
 240};
 241module_platform_driver(stm32_pwm_lp_driver);
 242
 243MODULE_ALIAS("platform:stm32-pwm-lp");
 244MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
 245MODULE_LICENSE("GPL v2");
 246