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