aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-samsung/pwm.c
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /arch/arm/plat-samsung/pwm.c
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_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.c103
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;
}