linux/arch/arm/plat-mxc/pwm.c
<<
>>
Prefs
   1/*
   2 * simple driver for PWM (Pulse Width Modulator) controller
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/platform_device.h>
  14#include <linux/err.h>
  15#include <linux/clk.h>
  16#include <linux/io.h>
  17#include <linux/pwm.h>
  18
  19#if defined CONFIG_ARCH_MX1 || defined CONFIG_ARCH_MX21
  20#define PWM_VER_1
  21
  22#define PWMCR   0x00    /* PWM Control Register         */
  23#define PWMSR   0x04    /* PWM Sample Register          */
  24#define PWMPR   0x08    /* PWM Period Register          */
  25#define PWMCNR  0x0C    /* PWM Counter Register         */
  26
  27#define PWMCR_HCTR              (1 << 18)               /* Halfword FIFO Data Swapping  */
  28#define PWMCR_BCTR              (1 << 17)               /* Byte FIFO Data Swapping      */
  29#define PWMCR_SWR               (1 << 16)               /* Software Reset               */
  30#define PWMCR_CLKSRC_PERCLK     (0 << 15)               /* PERCLK Clock Source          */
  31#define PWMCR_CLKSRC_CLK32      (1 << 15)               /* 32KHz Clock Source           */
  32#define PWMCR_PRESCALER(x)      (((x - 1) & 0x7F) << 8) /* PRESCALER                    */
  33#define PWMCR_IRQ               (1 << 7)                /* Interrupt Request            */
  34#define PWMCR_IRQEN             (1 << 6)                /* Interrupt Request Enable     */
  35#define PWMCR_FIFOAV            (1 << 5)                /* FIFO Available               */
  36#define PWMCR_EN                (1 << 4)                /* Enables/Disables the PWM     */
  37#define PWMCR_REPEAT(x)         (((x) & 0x03) << 2)     /* Sample Repeats               */
  38#define PWMCR_DIV(x)            (((x) & 0x03) << 0)     /* Clock divider 2/4/8/16       */
  39
  40#define MAX_DIV                 (128 * 16)
  41#endif
  42
  43#if defined CONFIG_MACH_MX27 || defined CONFIG_ARCH_MX31
  44#define PWM_VER_2
  45
  46#define PWMCR   0x00    /* PWM Control Register         */
  47#define PWMSR   0x04    /* PWM Status Register          */
  48#define PWMIR   0x08    /* PWM Interrupt Register       */
  49#define PWMSAR  0x0C    /* PWM Sample Register          */
  50#define PWMPR   0x10    /* PWM Period Register          */
  51#define PWMCNR  0x14    /* PWM Counter Register         */
  52
  53#define PWMCR_EN                (1 << 0)                /* Enables/Disables the PWM     */
  54#define PWMCR_REPEAT(x)         (((x) & 0x03) << 1)     /* Sample Repeats               */
  55#define PWMCR_SWR               (1 << 3)                /* Software Reset               */
  56#define PWMCR_PRESCALER(x)      (((x - 1) & 0xFFF) << 4)/* PRESCALER                    */
  57#define PWMCR_CLKSRC(x)         (((x) & 0x3) << 16)
  58#define PWMCR_CLKSRC_OFF        (0 << 16)
  59#define PWMCR_CLKSRC_IPG        (1 << 16)
  60#define PWMCR_CLKSRC_IPG_HIGH   (2 << 16)
  61#define PWMCR_CLKSRC_CLK32      (3 << 16)
  62#define PWMCR_POUTC
  63#define PWMCR_HCTR              (1 << 20)               /* Halfword FIFO Data Swapping  */
  64#define PWMCR_BCTR              (1 << 21)               /* Byte FIFO Data Swapping      */
  65#define PWMCR_DBGEN             (1 << 22)               /* Debug Mode                   */
  66#define PWMCR_WAITEN            (1 << 23)               /* Wait Mode                    */
  67#define PWMCR_DOZEN             (1 << 24)               /* Doze Mode                    */
  68#define PWMCR_STOPEN            (1 << 25)               /* Stop Mode                    */
  69#define PWMCR_FWM(x)            (((x) & 0x3) << 26)     /* FIFO Water Mark              */
  70
  71#define MAX_DIV 4096
  72#endif
  73
  74#define PWMS_SAMPLE(x)          ((x) & 0xFFFF)          /* Contains a two-sample word   */
  75#define PWMP_PERIOD(x)          ((x) & 0xFFFF)          /* Represents the PWM's period  */
  76#define PWMC_COUNTER(x)         ((x) & 0xFFFF)          /* Represents the current count value   */
  77
  78struct pwm_device {
  79        struct list_head        node;
  80        struct platform_device *pdev;
  81
  82        const char      *label;
  83        struct clk      *clk;
  84
  85        int             clk_enabled;
  86        void __iomem    *mmio_base;
  87
  88        unsigned int    use_count;
  89        unsigned int    pwm_id;
  90};
  91
  92int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
  93{
  94        unsigned long long c;
  95        unsigned long period_cycles, duty_cycles, prescale;
  96
  97        if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
  98                return -EINVAL;
  99
 100        c = clk_get_rate(pwm->clk);
 101        c = c * period_ns;
 102        do_div(c, 1000000000);
 103        period_cycles = c;
 104
 105        prescale = period_cycles / 0x10000 + 1;
 106
 107        period_cycles /= prescale;
 108        c = (unsigned long long)period_cycles * duty_ns;
 109        do_div(c, period_ns);
 110        duty_cycles = c;
 111
 112#ifdef PWM_VER_2
 113        writel(duty_cycles, pwm->mmio_base + PWMSAR);
 114        writel(period_cycles, pwm->mmio_base + PWMPR);
 115        writel(PWMCR_PRESCALER(prescale - 1) | PWMCR_CLKSRC_IPG_HIGH | PWMCR_EN,
 116                        pwm->mmio_base + PWMCR);
 117#elif defined PWM_VER_1
 118#error PWM not yet working on MX1 / MX21
 119#endif
 120
 121        return 0;
 122}
 123EXPORT_SYMBOL(pwm_config);
 124
 125int pwm_enable(struct pwm_device *pwm)
 126{
 127        int rc = 0;
 128
 129        if (!pwm->clk_enabled) {
 130                rc = clk_enable(pwm->clk);
 131                if (!rc)
 132                        pwm->clk_enabled = 1;
 133        }
 134        return rc;
 135}
 136EXPORT_SYMBOL(pwm_enable);
 137
 138void pwm_disable(struct pwm_device *pwm)
 139{
 140        if (pwm->clk_enabled) {
 141                clk_disable(pwm->clk);
 142                pwm->clk_enabled = 0;
 143        }
 144}
 145EXPORT_SYMBOL(pwm_disable);
 146
 147static DEFINE_MUTEX(pwm_lock);
 148static LIST_HEAD(pwm_list);
 149
 150struct pwm_device *pwm_request(int pwm_id, const char *label)
 151{
 152        struct pwm_device *pwm;
 153        int found = 0;
 154
 155        mutex_lock(&pwm_lock);
 156
 157        list_for_each_entry(pwm, &pwm_list, node) {
 158                if (pwm->pwm_id == pwm_id) {
 159                        found = 1;
 160                        break;
 161                }
 162        }
 163
 164        if (found) {
 165                if (pwm->use_count == 0) {
 166                        pwm->use_count++;
 167                        pwm->label = label;
 168                } else
 169                        pwm = ERR_PTR(-EBUSY);
 170        } else
 171                pwm = ERR_PTR(-ENOENT);
 172
 173        mutex_unlock(&pwm_lock);
 174        return pwm;
 175}
 176EXPORT_SYMBOL(pwm_request);
 177
 178void pwm_free(struct pwm_device *pwm)
 179{
 180        mutex_lock(&pwm_lock);
 181
 182        if (pwm->use_count) {
 183                pwm->use_count--;
 184                pwm->label = NULL;
 185        } else
 186                pr_warning("PWM device already freed\n");
 187
 188        mutex_unlock(&pwm_lock);
 189}
 190EXPORT_SYMBOL(pwm_free);
 191
 192static int __devinit mxc_pwm_probe(struct platform_device *pdev)
 193{
 194        struct pwm_device *pwm;
 195        struct resource *r;
 196        int ret = 0;
 197
 198        pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
 199        if (pwm == NULL) {
 200                dev_err(&pdev->dev, "failed to allocate memory\n");
 201                return -ENOMEM;
 202        }
 203
 204        pwm->clk = clk_get(&pdev->dev, "pwm");
 205
 206        if (IS_ERR(pwm->clk)) {
 207                ret = PTR_ERR(pwm->clk);
 208                goto err_free;
 209        }
 210
 211        pwm->clk_enabled = 0;
 212
 213        pwm->use_count = 0;
 214        pwm->pwm_id = pdev->id;
 215        pwm->pdev = pdev;
 216
 217        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 218        if (r == NULL) {
 219                dev_err(&pdev->dev, "no memory resource defined\n");
 220                ret = -ENODEV;
 221                goto err_free_clk;
 222        }
 223
 224        r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
 225        if (r == NULL) {
 226                dev_err(&pdev->dev, "failed to request memory resource\n");
 227                ret = -EBUSY;
 228                goto err_free_clk;
 229        }
 230
 231        pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
 232        if (pwm->mmio_base == NULL) {
 233                dev_err(&pdev->dev, "failed to ioremap() registers\n");
 234                ret = -ENODEV;
 235                goto err_free_mem;
 236        }
 237
 238        mutex_lock(&pwm_lock);
 239        list_add_tail(&pwm->node, &pwm_list);
 240        mutex_unlock(&pwm_lock);
 241
 242        platform_set_drvdata(pdev, pwm);
 243        return 0;
 244
 245err_free_mem:
 246        release_mem_region(r->start, r->end - r->start + 1);
 247err_free_clk:
 248        clk_put(pwm->clk);
 249err_free:
 250        kfree(pwm);
 251        return ret;
 252}
 253
 254static int __devexit mxc_pwm_remove(struct platform_device *pdev)
 255{
 256        struct pwm_device *pwm;
 257        struct resource *r;
 258
 259        pwm = platform_get_drvdata(pdev);
 260        if (pwm == NULL)
 261                return -ENODEV;
 262
 263        mutex_lock(&pwm_lock);
 264        list_del(&pwm->node);
 265        mutex_unlock(&pwm_lock);
 266
 267        iounmap(pwm->mmio_base);
 268
 269        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 270        release_mem_region(r->start, r->end - r->start + 1);
 271
 272        clk_put(pwm->clk);
 273
 274        kfree(pwm);
 275        return 0;
 276}
 277
 278static struct platform_driver mxc_pwm_driver = {
 279        .driver         = {
 280                .name   = "mxc_pwm",
 281        },
 282        .probe          = mxc_pwm_probe,
 283        .remove         = __devexit_p(mxc_pwm_remove),
 284};
 285
 286static int __init mxc_pwm_init(void)
 287{
 288        return platform_driver_register(&mxc_pwm_driver);
 289}
 290arch_initcall(mxc_pwm_init);
 291
 292static void __exit mxc_pwm_exit(void)
 293{
 294        platform_driver_unregister(&mxc_pwm_driver);
 295}
 296module_exit(mxc_pwm_exit);
 297
 298MODULE_LICENSE("GPL v2");
 299MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 300
 301
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.