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
  29/* ECAP registers and bits definitions */
  30#define CAP1                    0x08
  31#define CAP2                    0x0C
  32#define CAP3                    0x10
  33#define CAP4                    0x14
  34#define ECCTL2                  0x2A
  35#define ECCTL2_APWM_POL_LOW     BIT(10)
  36#define ECCTL2_APWM_MODE        BIT(9)
  37#define ECCTL2_SYNC_SEL_DISA    (BIT(7) | BIT(6))
  38#define ECCTL2_TSCTR_FREERUN    BIT(4)
  39
  40struct ecap_pwm_chip {
  41        struct pwm_chip chip;
  42        unsigned int    clk_rate;
  43        void __iomem    *mmio_base;
  44};
  45
  46static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
  47{
  48        return container_of(chip, struct ecap_pwm_chip, chip);
  49}
  50
  51/*
  52 * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
  53 * duty_ns   = 10^9 * duty_cycles / PWM_CLK_RATE
  54 */
  55static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  56                int duty_ns, int period_ns)
  57{
  58        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
  59        unsigned long long c;
  60        unsigned long period_cycles, duty_cycles;
  61        unsigned int reg_val;
  62
  63        if (period_ns > NSEC_PER_SEC)
  64                return -ERANGE;
  65
  66        c = pc->clk_rate;
  67        c = c * period_ns;
  68        do_div(c, NSEC_PER_SEC);
  69        period_cycles = (unsigned long)c;
  70
  71        if (period_cycles < 1) {
  72                period_cycles = 1;
  73                duty_cycles = 1;
  74        } else {
  75                c = pc->clk_rate;
  76                c = c * duty_ns;
  77                do_div(c, NSEC_PER_SEC);
  78                duty_cycles = (unsigned long)c;
  79        }
  80
  81        pm_runtime_get_sync(pc->chip.dev);
  82
  83        reg_val = readw(pc->mmio_base + ECCTL2);
  84
  85        /* Configure APWM mode & disable sync option */
  86        reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
  87
  88        writew(reg_val, pc->mmio_base + ECCTL2);
  89
  90        if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
  91                /* Update active registers if not running */
  92                writel(duty_cycles, pc->mmio_base + CAP2);
  93                writel(period_cycles, pc->mmio_base + CAP1);
  94        } else {
  95                /*
  96                 * Update shadow registers to configure period and
  97                 * compare values. This helps current PWM period to
  98                 * complete on reconfiguring
  99                 */
 100                writel(duty_cycles, pc->mmio_base + CAP4);
 101                writel(period_cycles, pc->mmio_base + CAP3);
 102        }
 103
 104        if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
 105                reg_val = readw(pc->mmio_base + ECCTL2);
 106                /* Disable APWM mode to put APWM output Low */
 107                reg_val &= ~ECCTL2_APWM_MODE;
 108                writew(reg_val, pc->mmio_base + ECCTL2);
 109        }
 110
 111        pm_runtime_put_sync(pc->chip.dev);
 112        return 0;
 113}
 114
 115static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
 116                enum pwm_polarity polarity)
 117{
 118        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
 119        unsigned short reg_val;
 120
 121        pm_runtime_get_sync(pc->chip.dev);
 122        reg_val = readw(pc->mmio_base + ECCTL2);
 123        if (polarity == PWM_POLARITY_INVERSED)
 124                /* Duty cycle defines LOW period of PWM */
 125                reg_val |= ECCTL2_APWM_POL_LOW;
 126        else
 127                /* Duty cycle defines HIGH period of PWM */
 128                reg_val &= ~ECCTL2_APWM_POL_LOW;
 129
 130        writew(reg_val, pc->mmio_base + ECCTL2);
 131        pm_runtime_put_sync(pc->chip.dev);
 132        return 0;
 133}
 134
 135static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 136{
 137        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
 138        unsigned int reg_val;
 139
 140        /* Leave clock enabled on enabling PWM */
 141        pm_runtime_get_sync(pc->chip.dev);
 142
 143        /*
 144         * Enable 'Free run Time stamp counter mode' to start counter
 145         * and  'APWM mode' to enable APWM output
 146         */
 147        reg_val = readw(pc->mmio_base + ECCTL2);
 148        reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
 149        writew(reg_val, pc->mmio_base + ECCTL2);
 150        return 0;
 151}
 152
 153static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 154{
 155        struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
 156        unsigned int reg_val;
 157
 158        /*
 159         * Disable 'Free run Time stamp counter mode' to stop counter
 160         * and 'APWM mode' to put APWM output to low
 161         */
 162        reg_val = readw(pc->mmio_base + ECCTL2);
 163        reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
 164        writew(reg_val, pc->mmio_base + ECCTL2);
 165
 166        /* Disable clock on PWM disable */
 167        pm_runtime_put_sync(pc->chip.dev);
 168}
 169
 170static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 171{
 172        if (test_bit(PWMF_ENABLED, &pwm->flags)) {
 173                dev_warn(chip->dev, "Removing PWM device without disabling\n");
 174                pm_runtime_put_sync(chip->dev);
 175        }
 176}
 177
 178static const struct pwm_ops ecap_pwm_ops = {
 179        .free           = ecap_pwm_free,
 180        .config         = ecap_pwm_config,
 181        .set_polarity   = ecap_pwm_set_polarity,
 182        .enable         = ecap_pwm_enable,
 183        .disable        = ecap_pwm_disable,
 184        .owner          = THIS_MODULE,
 185};
 186
 187static int __devinit ecap_pwm_probe(struct platform_device *pdev)
 188{
 189        int ret;
 190        struct resource *r;
 191        struct clk *clk;
 192        struct ecap_pwm_chip *pc;
 193
 194        pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
 195        if (!pc) {
 196                dev_err(&pdev->dev, "failed to allocate memory\n");
 197                return -ENOMEM;
 198        }
 199
 200        clk = devm_clk_get(&pdev->dev, "fck");
 201        if (IS_ERR(clk)) {
 202                dev_err(&pdev->dev, "failed to get clock\n");
 203                return PTR_ERR(clk);
 204        }
 205
 206        pc->clk_rate = clk_get_rate(clk);
 207        if (!pc->clk_rate) {
 208                dev_err(&pdev->dev, "failed to get clock rate\n");
 209                return -EINVAL;
 210        }
 211
 212        pc->chip.dev = &pdev->dev;
 213        pc->chip.ops = &ecap_pwm_ops;
 214        pc->chip.base = -1;
 215        pc->chip.npwm = 1;
 216
 217        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 218        if (!r) {
 219                dev_err(&pdev->dev, "no memory resource defined\n");
 220                return -ENODEV;
 221        }
 222
 223        pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
 224        if (!pc->mmio_base)
 225                return -EADDRNOTAVAIL;
 226
 227        ret = pwmchip_add(&pc->chip);
 228        if (ret < 0) {
 229                dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
 230                return ret;
 231        }
 232
 233        pm_runtime_enable(&pdev->dev);
 234        platform_set_drvdata(pdev, pc);
 235        return 0;
 236}
 237
 238static int __devexit ecap_pwm_remove(struct platform_device *pdev)
 239{
 240        struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
 241
 242        pm_runtime_put_sync(&pdev->dev);
 243        pm_runtime_disable(&pdev->dev);
 244        return pwmchip_remove(&pc->chip);
 245}
 246
 247static struct platform_driver ecap_pwm_driver = {
 248        .driver = {
 249                .name = "ecap",
 250        },
 251        .probe = ecap_pwm_probe,
 252        .remove = __devexit_p(ecap_pwm_remove),
 253};
 254
 255module_platform_driver(ecap_pwm_driver);
 256
 257MODULE_DESCRIPTION("ECAP PWM driver");
 258MODULE_AUTHOR("Texas Instruments");
 259MODULE_LICENSE("GPL");
 260
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.