linux/drivers/pwm/pwm-tiecap.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * ECAP PWM driver
   4 *
   5 * Copyright (C) 2012 Texas Instruments, Inc. - https://www.ti.com/
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/platform_device.h>
  10#include <linux/io.h>
  11#include <linux/err.h>
  12#include <linux/clk.h>
  13#include <linux/pm_runtime.h>
  14#include <linux/pwm.h>
  15#include <linux/of_device.h>
  16
  17/* ECAP registers and bits definitions */
  18#define CAP1                    0x08
  19#define CAP2                    0x0C
  20#define CAP3                    0x10
  21#define CAP4                    0x14
  22#define ECCTL2                  0x2A
  23#define ECCTL2_APWM_POL_LOW     BIT(10)
  24#define ECCTL2_APWM_MODE        BIT(9)
  25#define ECCTL2_SYNC_SEL_DISA    (BIT(7) | BIT(6))
  26#define ECCTL2_TSCTR_FREERUN    BIT(4)
  27
  28struct ecap_context {
  29        u32 cap3;
  30        u32 cap4;
  31        u16 ecctl2;
  32};
  33
  34struct ecap_pwm_chip {
  35        struct pwm_chip chip;
  36        unsigned int clk_rate;
  37        void __iomem *mmio_base;
  38        struct ecap_context ctx;
  39};
  40
  41static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
  42{
  43        return container_of(chip, struct ecap_pwm_chip, chip);
  44}
  45
  46/*
  47 * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
  48 * duty_ns   = 10^9 * duty_cycles / PWM_CLK_RATE
  49 */
  50static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  51                           int duty_ns, int period_ns, int enabled)
  52{
  53        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
  54        u32 period_cycles, duty_cycles;
  55        unsigned long long c;
  56        u16 value;
  57
  58        c = pc->clk_rate;
  59        c = c * period_ns;
  60        do_div(c, NSEC_PER_SEC);
  61        period_cycles = (u32)c;
  62
  63        if (period_cycles < 1) {
  64                period_cycles = 1;
  65                duty_cycles = 1;
  66        } else {
  67                c = pc->clk_rate;
  68                c = c * duty_ns;
  69                do_div(c, NSEC_PER_SEC);
  70                duty_cycles = (u32)c;
  71        }
  72
  73        pm_runtime_get_sync(pc->chip.dev);
  74
  75        value = readw(pc->mmio_base + ECCTL2);
  76
  77        /* Configure APWM mode & disable sync option */
  78        value |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
  79
  80        writew(value, pc->mmio_base + ECCTL2);
  81
  82        if (!enabled) {
  83                /* Update active registers if not running */
  84                writel(duty_cycles, pc->mmio_base + CAP2);
  85                writel(period_cycles, pc->mmio_base + CAP1);
  86        } else {
  87                /*
  88                 * Update shadow registers to configure period and
  89                 * compare values. This helps current PWM period to
  90                 * complete on reconfiguring
  91                 */
  92                writel(duty_cycles, pc->mmio_base + CAP4);
  93                writel(period_cycles, pc->mmio_base + CAP3);
  94        }
  95
  96        if (!enabled) {
  97                value = readw(pc->mmio_base + ECCTL2);
  98                /* Disable APWM mode to put APWM output Low */
  99                value &= ~ECCTL2_APWM_MODE;
 100                writew(value, pc->mmio_base + ECCTL2);
 101        }
 102
 103        pm_runtime_put_sync(pc->chip.dev);
 104
 105        return 0;
 106}
 107
 108static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
 109                                 enum pwm_polarity polarity)
 110{
 111        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
 112        u16 value;
 113
 114        pm_runtime_get_sync(pc->chip.dev);
 115
 116        value = readw(pc->mmio_base + ECCTL2);
 117
 118        if (polarity == PWM_POLARITY_INVERSED)
 119                /* Duty cycle defines LOW period of PWM */
 120                value |= ECCTL2_APWM_POL_LOW;
 121        else
 122                /* Duty cycle defines HIGH period of PWM */
 123                value &= ~ECCTL2_APWM_POL_LOW;
 124
 125        writew(value, pc->mmio_base + ECCTL2);
 126
 127        pm_runtime_put_sync(pc->chip.dev);
 128
 129        return 0;
 130}
 131
 132static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 133{
 134        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
 135        u16 value;
 136
 137        /* Leave clock enabled on enabling PWM */
 138        pm_runtime_get_sync(pc->chip.dev);
 139
 140        /*
 141         * Enable 'Free run Time stamp counter mode' to start counter
 142         * and  'APWM mode' to enable APWM output
 143         */
 144        value = readw(pc->mmio_base + ECCTL2);
 145        value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
 146        writew(value, pc->mmio_base + ECCTL2);
 147
 148        return 0;
 149}
 150
 151static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 152{
 153        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
 154        u16 value;
 155
 156        /*
 157         * Disable 'Free run Time stamp counter mode' to stop counter
 158         * and 'APWM mode' to put APWM output to low
 159         */
 160        value = readw(pc->mmio_base + ECCTL2);
 161        value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
 162        writew(value, pc->mmio_base + ECCTL2);
 163
 164        /* Disable clock on PWM disable */
 165        pm_runtime_put_sync(pc->chip.dev);
 166}
 167
 168static int ecap_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 169                          const struct pwm_state *state)
 170{
 171        int err;
 172        int enabled = pwm->state.enabled;
 173
 174        if (state->polarity != pwm->state.polarity) {
 175
 176                if (enabled) {
 177                        ecap_pwm_disable(chip, pwm);
 178                        enabled = false;
 179                }
 180
 181                err = ecap_pwm_set_polarity(chip, pwm, state->polarity);
 182                if (err)
 183                        return err;
 184        }
 185
 186        if (!state->enabled) {
 187                if (enabled)
 188                        ecap_pwm_disable(chip, pwm);
 189                return 0;
 190        }
 191
 192        if (state->period > NSEC_PER_SEC)
 193                return -ERANGE;
 194
 195        err = ecap_pwm_config(chip, pwm, state->duty_cycle,
 196                              state->period, enabled);
 197        if (err)
 198                return err;
 199
 200        if (!enabled)
 201                return ecap_pwm_enable(chip, pwm);
 202
 203        return 0;
 204}
 205
 206static const struct pwm_ops ecap_pwm_ops = {
 207        .apply = ecap_pwm_apply,
 208        .owner = THIS_MODULE,
 209};
 210
 211static const struct of_device_id ecap_of_match[] = {
 212        { .compatible   = "ti,am3352-ecap" },
 213        { .compatible   = "ti,am33xx-ecap" },
 214        {},
 215};
 216MODULE_DEVICE_TABLE(of, ecap_of_match);
 217
 218static int ecap_pwm_probe(struct platform_device *pdev)
 219{
 220        struct device_node *np = pdev->dev.of_node;
 221        struct ecap_pwm_chip *pc;
 222        struct clk *clk;
 223        int ret;
 224
 225        pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
 226        if (!pc)
 227                return -ENOMEM;
 228
 229        clk = devm_clk_get(&pdev->dev, "fck");
 230        if (IS_ERR(clk)) {
 231                if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
 232                        dev_warn(&pdev->dev, "Binding is obsolete.\n");
 233                        clk = devm_clk_get(pdev->dev.parent, "fck");
 234                }
 235        }
 236
 237        if (IS_ERR(clk)) {
 238                dev_err(&pdev->dev, "failed to get clock\n");
 239                return PTR_ERR(clk);
 240        }
 241
 242        pc->clk_rate = clk_get_rate(clk);
 243        if (!pc->clk_rate) {
 244                dev_err(&pdev->dev, "failed to get clock rate\n");
 245                return -EINVAL;
 246        }
 247
 248        pc->chip.dev = &pdev->dev;
 249        pc->chip.ops = &ecap_pwm_ops;
 250        pc->chip.npwm = 1;
 251
 252        pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
 253        if (IS_ERR(pc->mmio_base))
 254                return PTR_ERR(pc->mmio_base);
 255
 256        ret = pwmchip_add(&pc->chip);
 257        if (ret < 0) {
 258                dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
 259                return ret;
 260        }
 261
 262        platform_set_drvdata(pdev, pc);
 263        pm_runtime_enable(&pdev->dev);
 264
 265        return 0;
 266}
 267
 268static int ecap_pwm_remove(struct platform_device *pdev)
 269{
 270        struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
 271
 272        pm_runtime_disable(&pdev->dev);
 273
 274        return pwmchip_remove(&pc->chip);
 275}
 276
 277#ifdef CONFIG_PM_SLEEP
 278static void ecap_pwm_save_context(struct ecap_pwm_chip *pc)
 279{
 280        pm_runtime_get_sync(pc->chip.dev);
 281        pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2);
 282        pc->ctx.cap4 = readl(pc->mmio_base + CAP4);
 283        pc->ctx.cap3 = readl(pc->mmio_base + CAP3);
 284        pm_runtime_put_sync(pc->chip.dev);
 285}
 286
 287static void ecap_pwm_restore_context(struct ecap_pwm_chip *pc)
 288{
 289        writel(pc->ctx.cap3, pc->mmio_base + CAP3);
 290        writel(pc->ctx.cap4, pc->mmio_base + CAP4);
 291        writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2);
 292}
 293
 294static int ecap_pwm_suspend(struct device *dev)
 295{
 296        struct ecap_pwm_chip *pc = dev_get_drvdata(dev);
 297        struct pwm_device *pwm = pc->chip.pwms;
 298
 299        ecap_pwm_save_context(pc);
 300
 301        /* Disable explicitly if PWM is running */
 302        if (pwm_is_enabled(pwm))
 303                pm_runtime_put_sync(dev);
 304
 305        return 0;
 306}
 307
 308static int ecap_pwm_resume(struct device *dev)
 309{
 310        struct ecap_pwm_chip *pc = dev_get_drvdata(dev);
 311        struct pwm_device *pwm = pc->chip.pwms;
 312
 313        /* Enable explicitly if PWM was running */
 314        if (pwm_is_enabled(pwm))
 315                pm_runtime_get_sync(dev);
 316
 317        ecap_pwm_restore_context(pc);
 318        return 0;
 319}
 320#endif
 321
 322static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume);
 323
 324static struct platform_driver ecap_pwm_driver = {
 325        .driver = {
 326                .name = "ecap",
 327                .of_match_table = ecap_of_match,
 328                .pm = &ecap_pwm_pm_ops,
 329        },
 330        .probe = ecap_pwm_probe,
 331        .remove = ecap_pwm_remove,
 332};
 333module_platform_driver(ecap_pwm_driver);
 334
 335MODULE_DESCRIPTION("ECAP PWM driver");
 336MODULE_AUTHOR("Texas Instruments");
 337MODULE_LICENSE("GPL");
 338