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