diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /arch/arm/plat-samsung/pwm.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'arch/arm/plat-samsung/pwm.c')
-rw-r--r-- | arch/arm/plat-samsung/pwm.c | 103 |
1 files changed, 73 insertions, 30 deletions
diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c index f37457c..bdc892a 100644 --- a/arch/arm/plat-samsung/pwm.c +++ b/arch/arm/plat-samsung/pwm.c @@ -19,10 +19,12 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/pwm.h> +#include <linux/gpio.h> #include <mach/map.h> #include <plat/regs-timer.h> +#include <plat/gpio-cfg.h> struct pwm_device { struct list_head list; @@ -39,11 +41,28 @@ struct pwm_device { unsigned char running; unsigned char use_count; unsigned char pwm_id; + + unsigned long tcfg0; +}; + +struct s3c_pwm_pdata { + /* PWM output port */ + unsigned int gpio_no; + const char *gpio_name; + unsigned int gpio_set_value; }; +struct s3c_pwm_pdata *to_pwm_pdata(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + return (struct s3c_pwm_pdata *)pdev->dev.platform_data; +} + #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) static struct clk *clk_scaler[2]; +static DEFINE_SPINLOCK(pwm_spin_lock); static inline int pwm_is_tdiv(struct pwm_device *pwm) { @@ -108,15 +127,21 @@ int pwm_enable(struct pwm_device *pwm) unsigned long flags; unsigned long tcon; - local_irq_save(flags); + spin_lock_irqsave(&pwm_spin_lock, flags); - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_start(pwm); - __raw_writel(tcon, S3C2410_TCON); + if (!pwm->running) { + clk_enable(pwm->clk); + clk_enable(pwm->clk_div); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_start(pwm); + __raw_writel(tcon, S3C2410_TCON); - local_irq_restore(flags); + pwm->running = 1; + } + + spin_unlock_irqrestore(&pwm_spin_lock, flags); - pwm->running = 1; return 0; } @@ -127,15 +152,19 @@ void pwm_disable(struct pwm_device *pwm) unsigned long flags; unsigned long tcon; - local_irq_save(flags); + spin_lock_irqsave(&pwm_spin_lock, flags); - tcon = __raw_readl(S3C2410_TCON); - tcon &= ~pwm_tcon_start(pwm); - __raw_writel(tcon, S3C2410_TCON); + if (pwm->running) { + tcon = __raw_readl(S3C2410_TCON); + tcon &= ~pwm_tcon_start(pwm); + __raw_writel(tcon, S3C2410_TCON); - local_irq_restore(flags); + clk_disable(pwm->clk); + clk_disable(pwm->clk_div); + pwm->running = 0; + } - pwm->running = 0; + spin_unlock_irqrestore(&pwm_spin_lock, flags); } EXPORT_SYMBOL(pwm_disable); @@ -185,6 +214,9 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) /* The TCMP and TCNT can be read without a lock, they're not * shared between the timers. */ + clk_enable(pwm->clk); + clk_enable(pwm->clk_div); + tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id)); tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id)); @@ -227,12 +259,13 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) /* Update the PWM register block. */ - local_irq_save(flags); + spin_lock_irqsave(&pwm_spin_lock, flags); __raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id)); __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_invert(pwm); tcon |= pwm_tcon_manulupdate(pwm); tcon |= pwm_tcon_autoreload(pwm); __raw_writel(tcon, S3C2410_TCON); @@ -240,7 +273,10 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) tcon &= ~pwm_tcon_manulupdate(pwm); __raw_writel(tcon, S3C2410_TCON); - local_irq_restore(flags); + spin_unlock_irqrestore(&pwm_spin_lock, flags); + + clk_disable(pwm->clk); + clk_disable(pwm->clk_div); return 0; } @@ -263,11 +299,21 @@ static int s3c_pwm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pwm_device *pwm; - unsigned long flags; - unsigned long tcon; + struct s3c_pwm_pdata *pdata = to_pwm_pdata(dev); unsigned int id = pdev->id; int ret; + if (gpio_is_valid(pdata->gpio_no)) { + ret = gpio_request(pdata->gpio_no, pdata->gpio_name); + if (ret) + printk(KERN_ERR "failed to get GPIO for PWM0\n"); + s3c_gpio_cfgpin(pdata->gpio_no, pdata->gpio_set_value); + + /* Inserting the following for commit 2010.02.26: [BACKLIGHT] Fix PWM + driver handling GPIO routine (request but not free)*/ + gpio_free(pdata->gpio_no); + } + if (id == 4) { dev_err(dev, "TIMER4 is currently not supported\n"); return -ENXIO; @@ -299,15 +345,6 @@ static int s3c_pwm_probe(struct platform_device *pdev) goto err_clk_tin; } - local_irq_save(flags); - - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); - __raw_writel(tcon, S3C2410_TCON); - - local_irq_restore(flags); - - ret = pwm_register(pwm); if (ret) { dev_err(dev, "failed to register pwm\n"); @@ -359,18 +396,24 @@ static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) pwm->period_ns = 0; pwm->duty_ns = 0; + clk_enable(pwm->clk); + + pwm->tcfg0 = __raw_readl(S3C2410_TCFG0); + + clk_disable(pwm->clk); + return 0; } static int s3c_pwm_resume(struct platform_device *pdev) { struct pwm_device *pwm = platform_get_drvdata(pdev); - unsigned long tcon; - /* Restore invertion */ - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); - __raw_writel(tcon, S3C2410_TCON); + clk_enable(pwm->clk); + + __raw_writel(pwm->tcfg0, S3C2410_TCFG0); + + clk_disable(pwm->clk); return 0; } |