linux/drivers/pwm/pwm-atmel-hlcdc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Free Electrons
   4 * Copyright (C) 2014 Atmel
   5 *
   6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/delay.h>
  11#include <linux/mfd/atmel-hlcdc.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/pwm.h>
  15#include <linux/regmap.h>
  16
  17#define ATMEL_HLCDC_PWMCVAL_MASK        GENMASK(15, 8)
  18#define ATMEL_HLCDC_PWMCVAL(x)          (((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
  19#define ATMEL_HLCDC_PWMPOL              BIT(4)
  20#define ATMEL_HLCDC_PWMPS_MASK          GENMASK(2, 0)
  21#define ATMEL_HLCDC_PWMPS_MAX           0x6
  22#define ATMEL_HLCDC_PWMPS(x)            ((x) & ATMEL_HLCDC_PWMPS_MASK)
  23
  24struct atmel_hlcdc_pwm_errata {
  25        bool slow_clk_erratum;
  26        bool div1_clk_erratum;
  27};
  28
  29struct atmel_hlcdc_pwm {
  30        struct pwm_chip chip;
  31        struct atmel_hlcdc *hlcdc;
  32        struct clk *cur_clk;
  33        const struct atmel_hlcdc_pwm_errata *errata;
  34};
  35
  36static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)
  37{
  38        return container_of(chip, struct atmel_hlcdc_pwm, chip);
  39}
  40
  41static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm,
  42                                 const struct pwm_state *state)
  43{
  44        struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c);
  45        struct atmel_hlcdc *hlcdc = chip->hlcdc;
  46        unsigned int status;
  47        int ret;
  48
  49        if (state->enabled) {
  50                struct clk *new_clk = hlcdc->slow_clk;
  51                u64 pwmcval = state->duty_cycle * 256;
  52                unsigned long clk_freq;
  53                u64 clk_period_ns;
  54                u32 pwmcfg;
  55                int pres;
  56
  57                if (!chip->errata || !chip->errata->slow_clk_erratum) {
  58                        clk_freq = clk_get_rate(new_clk);
  59                        if (!clk_freq)
  60                                return -EINVAL;
  61
  62                        clk_period_ns = (u64)NSEC_PER_SEC * 256;
  63                        do_div(clk_period_ns, clk_freq);
  64                }
  65
  66                /* Errata: cannot use slow clk on some IP revisions */
  67                if ((chip->errata && chip->errata->slow_clk_erratum) ||
  68                    clk_period_ns > state->period) {
  69                        new_clk = hlcdc->sys_clk;
  70                        clk_freq = clk_get_rate(new_clk);
  71                        if (!clk_freq)
  72                                return -EINVAL;
  73
  74                        clk_period_ns = (u64)NSEC_PER_SEC * 256;
  75                        do_div(clk_period_ns, clk_freq);
  76                }
  77
  78                for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
  79                /* Errata: cannot divide by 1 on some IP revisions */
  80                        if (!pres && chip->errata &&
  81                            chip->errata->div1_clk_erratum)
  82                                continue;
  83
  84                        if ((clk_period_ns << pres) >= state->period)
  85                                break;
  86                }
  87
  88                if (pres > ATMEL_HLCDC_PWMPS_MAX)
  89                        return -EINVAL;
  90
  91                pwmcfg = ATMEL_HLCDC_PWMPS(pres);
  92
  93                if (new_clk != chip->cur_clk) {
  94                        u32 gencfg = 0;
  95                        int ret;
  96
  97                        ret = clk_prepare_enable(new_clk);
  98                        if (ret)
  99                                return ret;
 100
 101                        clk_disable_unprepare(chip->cur_clk);
 102                        chip->cur_clk = new_clk;
 103
 104                        if (new_clk == hlcdc->sys_clk)
 105                                gencfg = ATMEL_HLCDC_CLKPWMSEL;
 106
 107                        ret = regmap_update_bits(hlcdc->regmap,
 108                                                 ATMEL_HLCDC_CFG(0),
 109                                                 ATMEL_HLCDC_CLKPWMSEL,
 110                                                 gencfg);
 111                        if (ret)
 112                                return ret;
 113                }
 114
 115                do_div(pwmcval, state->period);
 116
 117                /*
 118                 * The PWM duty cycle is configurable from 0/256 to 255/256 of
 119                 * the period cycle. Hence we can't set a duty cycle occupying
 120                 * the whole period cycle if we're asked to.
 121                 * Set it to 255 if pwmcval is greater than 256.
 122                 */
 123                if (pwmcval > 255)
 124                        pwmcval = 255;
 125
 126                pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
 127
 128                if (state->polarity == PWM_POLARITY_NORMAL)
 129                        pwmcfg |= ATMEL_HLCDC_PWMPOL;
 130
 131                ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
 132                                         ATMEL_HLCDC_PWMCVAL_MASK |
 133                                         ATMEL_HLCDC_PWMPS_MASK |
 134                                         ATMEL_HLCDC_PWMPOL,
 135                                         pwmcfg);
 136                if (ret)
 137                        return ret;
 138
 139                ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN,
 140                                   ATMEL_HLCDC_PWM);
 141                if (ret)
 142                        return ret;
 143
 144                ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
 145                                               status,
 146                                               status & ATMEL_HLCDC_PWM,
 147                                               10, 0);
 148                if (ret)
 149                        return ret;
 150        } else {
 151                ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS,
 152                                   ATMEL_HLCDC_PWM);
 153                if (ret)
 154                        return ret;
 155
 156                ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
 157                                               status,
 158                                               !(status & ATMEL_HLCDC_PWM),
 159                                               10, 0);
 160                if (ret)
 161                        return ret;
 162
 163                clk_disable_unprepare(chip->cur_clk);
 164                chip->cur_clk = NULL;
 165        }
 166
 167        return 0;
 168}
 169
 170static const struct pwm_ops atmel_hlcdc_pwm_ops = {
 171        .apply = atmel_hlcdc_pwm_apply,
 172        .owner = THIS_MODULE,
 173};
 174
 175static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = {
 176        .slow_clk_erratum = true,
 177};
 178
 179static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
 180        .div1_clk_erratum = true,
 181};
 182
 183#ifdef CONFIG_PM_SLEEP
 184static int atmel_hlcdc_pwm_suspend(struct device *dev)
 185{
 186        struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev);
 187
 188        /* Keep the periph clock enabled if the PWM is still running. */
 189        if (pwm_is_enabled(&chip->chip.pwms[0]))
 190                clk_disable_unprepare(chip->hlcdc->periph_clk);
 191
 192        return 0;
 193}
 194
 195static int atmel_hlcdc_pwm_resume(struct device *dev)
 196{
 197        struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev);
 198        struct pwm_state state;
 199        int ret;
 200
 201        pwm_get_state(&chip->chip.pwms[0], &state);
 202
 203        /* Re-enable the periph clock it was stopped during suspend. */
 204        if (!state.enabled) {
 205                ret = clk_prepare_enable(chip->hlcdc->periph_clk);
 206                if (ret)
 207                        return ret;
 208        }
 209
 210        return atmel_hlcdc_pwm_apply(&chip->chip, &chip->chip.pwms[0], &state);
 211}
 212#endif
 213
 214static SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops,
 215                         atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume);
 216
 217static const struct of_device_id atmel_hlcdc_dt_ids[] = {
 218        {
 219                .compatible = "atmel,at91sam9n12-hlcdc",
 220                /* 9n12 has same errata as 9x5 HLCDC PWM */
 221                .data = &atmel_hlcdc_pwm_at91sam9x5_errata,
 222        },
 223        {
 224                .compatible = "atmel,at91sam9x5-hlcdc",
 225                .data = &atmel_hlcdc_pwm_at91sam9x5_errata,
 226        },
 227        {
 228                .compatible = "atmel,sama5d2-hlcdc",
 229        },
 230        {
 231                .compatible = "atmel,sama5d3-hlcdc",
 232                .data = &atmel_hlcdc_pwm_sama5d3_errata,
 233        },
 234        {
 235                .compatible = "atmel,sama5d4-hlcdc",
 236                .data = &atmel_hlcdc_pwm_sama5d3_errata,
 237        },
 238        {       .compatible = "microchip,sam9x60-hlcdc", },
 239        { /* sentinel */ },
 240};
 241MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids);
 242
 243static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
 244{
 245        const struct of_device_id *match;
 246        struct device *dev = &pdev->dev;
 247        struct atmel_hlcdc_pwm *chip;
 248        struct atmel_hlcdc *hlcdc;
 249        int ret;
 250
 251        hlcdc = dev_get_drvdata(dev->parent);
 252
 253        chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 254        if (!chip)
 255                return -ENOMEM;
 256
 257        ret = clk_prepare_enable(hlcdc->periph_clk);
 258        if (ret)
 259                return ret;
 260
 261        match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node);
 262        if (match)
 263                chip->errata = match->data;
 264
 265        chip->hlcdc = hlcdc;
 266        chip->chip.ops = &atmel_hlcdc_pwm_ops;
 267        chip->chip.dev = dev;
 268        chip->chip.base = -1;
 269        chip->chip.npwm = 1;
 270        chip->chip.of_xlate = of_pwm_xlate_with_flags;
 271        chip->chip.of_pwm_n_cells = 3;
 272
 273        ret = pwmchip_add_with_polarity(&chip->chip, PWM_POLARITY_INVERSED);
 274        if (ret) {
 275                clk_disable_unprepare(hlcdc->periph_clk);
 276                return ret;
 277        }
 278
 279        platform_set_drvdata(pdev, chip);
 280
 281        return 0;
 282}
 283
 284static int atmel_hlcdc_pwm_remove(struct platform_device *pdev)
 285{
 286        struct atmel_hlcdc_pwm *chip = platform_get_drvdata(pdev);
 287        int ret;
 288
 289        ret = pwmchip_remove(&chip->chip);
 290        if (ret)
 291                return ret;
 292
 293        clk_disable_unprepare(chip->hlcdc->periph_clk);
 294
 295        return 0;
 296}
 297
 298static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
 299        { .compatible = "atmel,hlcdc-pwm" },
 300        { /* sentinel */ },
 301};
 302
 303static struct platform_driver atmel_hlcdc_pwm_driver = {
 304        .driver = {
 305                .name = "atmel-hlcdc-pwm",
 306                .of_match_table = atmel_hlcdc_pwm_dt_ids,
 307                .pm = &atmel_hlcdc_pwm_pm_ops,
 308        },
 309        .probe = atmel_hlcdc_pwm_probe,
 310        .remove = atmel_hlcdc_pwm_remove,
 311};
 312module_platform_driver(atmel_hlcdc_pwm_driver);
 313
 314MODULE_ALIAS("platform:atmel-hlcdc-pwm");
 315MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
 316MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
 317MODULE_LICENSE("GPL v2");
 318