lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 6 Oct 2010 21:29:15 +0530
From:	Arun Murthy <arun.murthy@...ricsson.com>
To:	<lars@...afoo.de>, <akpm@...ux-foundation.org>,
	<kernel@...gutronix.de>, <philipp.zabel@...il.com>,
	<robert.jarzmik@...e.fr>, <marek.vasut@...il.com>,
	<eric.y.miao@...il.com>, <rpurdie@...ys.net>,
	<sameo@...ux.intel.com>, <kgene.kim@...sung.com>,
	<linux-omap@...r.kernel.org>
Cc:	<linux-arm-kernel@...ts.infradead.org>,
	<linux-kernel@...r.kernel.org>, <linux-mips@...ux-mips.org>,
	<STEricsson_nomadik_linux@...t.st.com>,
	<arun.murthy@...ricsson.com>, <bgat@...lgatliff.com>,
	<khilman@...prootsystems.com>
Subject: [PATCHv3 4/7] pwm: Align existing pwm drivers with pwm-core driver

pwm-core: make the driver visible for ARM only
	Align ab8500 pwm with the pwm core driver
	Align twl6030 pwm driver with pwm core driver
	Align Freescale mxc pwm driver with pwm core driver
	Align pxa pwm driver with pwm core driver
	Align samsung(s3c) pwm driver with pwm core driver

mips-jz4740: pwm: Align with new pwm core driver

PWM core driver has been added and has been enabled only for ARM
platform. The same can be utilised for mips also.
Please align with the pwm core driver(drivers/pwm-core.c).

Signed-off-by: Arun Murthy <arun.murthy@...ricsson.com>
Acked-by: Linus Walleij <linus.walleij@...ricsson.com>
---
 arch/arm/plat-mxc/pwm.c     |  165 +++++++++++++-----------------
 arch/arm/plat-pxa/pwm.c     |  209 ++++++++++++++++++--------------------
 arch/arm/plat-samsung/pwm.c |  234 +++++++++++++++++++------------------------
 arch/mips/jz4740/pwm.c      |    2 +-
 drivers/mfd/twl-core.c      |   13 +++
 drivers/mfd/twl6030-pwm.c   |  110 +++++++++++++-------
 drivers/misc/ab8500-pwm.c   |  103 +++++++++-----------
 drivers/pwm/Kconfig         |    1 +
 drivers/pwm/pwm-core.c      |   11 +--
 include/linux/pwm.h         |   38 +++++++-
 10 files changed, 440 insertions(+), 446 deletions(-)

diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c
index c36f263..f74a280 100644
--- a/arch/arm/plat-mxc/pwm.c
+++ b/arch/arm/plat-mxc/pwm.c
@@ -38,22 +38,16 @@
 
 
 
-struct pwm_device {
-	struct list_head	node;
-	struct platform_device *pdev;
-
-	const char	*label;
+struct mxc_pwm_device {
 	struct clk	*clk;
-
 	int		clk_enabled;
 	void __iomem	*mmio_base;
-
-	unsigned int	use_count;
-	unsigned int	pwm_id;
 };
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+static int mxc_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
+	struct mxc_pwm_device *mxc_pwm = pwm->data;
+
 	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
 		return -EINVAL;
 
@@ -62,7 +56,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		unsigned long period_cycles, duty_cycles, prescale;
 		u32 cr;
 
-		c = clk_get_rate(pwm->clk);
+		c = clk_get_rate(mxc_pwm->clk);
 		c = c * period_ns;
 		do_div(c, 1000000000);
 		period_cycles = c;
@@ -74,8 +68,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		do_div(c, period_ns);
 		duty_cycles = c;
 
-		writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
-		writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
+		writel(duty_cycles, mxc_pwm->mmio_base + MX3_PWMSAR);
+		writel(period_cycles, mxc_pwm->mmio_base + MX3_PWMPR);
 
 		cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_EN;
 
@@ -84,7 +78,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		else
 			cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
 
-		writel(cr, pwm->mmio_base + MX3_PWMCR);
+		writel(cr, mxc_pwm->mmio_base + MX3_PWMCR);
 	} else if (cpu_is_mx1() || cpu_is_mx21()) {
 		/* The PWM subsystem allows for exact frequencies. However,
 		 * I cannot connect a scope on my device to the PWM line and
@@ -102,110 +96,76 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 		 * both the prescaler (/1 .. /128) and then by CLKSEL
 		 * (/2 .. /16).
 		 */
-		u32 max = readl(pwm->mmio_base + MX1_PWMP);
+		u32 max = readl(mxc_pwm->mmio_base + MX1_PWMP);
 		u32 p = max * duty_ns / period_ns;
-		writel(max - p, pwm->mmio_base + MX1_PWMS);
+		writel(max - p, mxc_pwm->mmio_base + MX1_PWMS);
 	} else {
 		BUG();
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+static int mxc_pwm_enable(struct pwm_device *pwm)
 {
+	struct mxc_pwm_device *mxc_pwm = pwm->data;
 	int rc = 0;
 
-	if (!pwm->clk_enabled) {
-		rc = clk_enable(pwm->clk);
+	if (!mxc_pwm->clk_enabled) {
+		rc = clk_enable(mxc_pwm->clk);
 		if (!rc)
-			pwm->clk_enabled = 1;
+			mxc_pwm->clk_enabled = 1;
 	}
 	return rc;
 }
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
-	writel(0, pwm->mmio_base + MX3_PWMCR);
-
-	if (pwm->clk_enabled) {
-		clk_disable(pwm->clk);
-		pwm->clk_enabled = 0;
-	}
-}
-EXPORT_SYMBOL(pwm_disable);
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+static int mxc_pwm_disable(struct pwm_device *pwm)
 {
-	struct pwm_device *pwm;
-	int found = 0;
+	struct mxc_pwm_device *mxc_pwm = pwm->data;
 
-	mutex_lock(&pwm_lock);
+	writel(0, mxc_pwm->mmio_base + MX3_PWMCR);
 
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
+	if (mxc_pwm->clk_enabled) {
+		clk_disable(mxc_pwm->clk);
+		mxc_pwm->clk_enabled = 0;
 	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		pr_warning("PWM device already freed\n");
-
-	mutex_unlock(&pwm_lock);
+	return 0;
 }
-EXPORT_SYMBOL(pwm_free);
 
 static int __devinit mxc_pwm_probe(struct platform_device *pdev)
 {
+	struct mxc_pwm_device *mxc_pwm;
 	struct pwm_device *pwm;
+	struct pwm_ops *pops;
 	struct resource *r;
 	int ret = 0;
 
+	mxc_pwm = kzalloc(sizeof(struct mxc_pwm_device), GFP_KERNEL);
+	if (mxc_pwm == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
 	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
 	if (pwm == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto err_free1;
+	}
+	pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL);
+	if (pops == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		ret = -ENOMEM;
+		goto err_free2;
 	}
 
-	pwm->clk = clk_get(&pdev->dev, "pwm");
+	mxc_pwm->clk = clk_get(&pdev->dev, "pwm");
 
-	if (IS_ERR(pwm->clk)) {
-		ret = PTR_ERR(pwm->clk);
-		goto err_free;
+	if (IS_ERR(mxc_pwm->clk)) {
+		ret = PTR_ERR(mxc_pwm->clk);
+		goto err_free3;
 	}
 
-	pwm->clk_enabled = 0;
-
-	pwm->use_count = 0;
-	pwm->pwm_id = pdev->id;
-	pwm->pdev = pdev;
+	mxc_pwm->clk_enabled = 0;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -221,16 +181,26 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev)
 		goto err_free_clk;
 	}
 
-	pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
-	if (pwm->mmio_base == NULL) {
+	mxc_pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
+	if (mxc_pwm->mmio_base == NULL) {
 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
 		ret = -ENODEV;
 		goto err_free_mem;
 	}
 
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
+	pops->pwm_config = mxc_pwm_config;
+	pops->pwm_enable = mxc_pwm_enable;
+	pops->pwm_disable = mxc_pwm_disable;
+
+	pwm->name = pdev->name;
+	pwm->pwm_id = pdev->id;
+	pwm->pops = pops;
+	pwm->data = mxc_pwm;
+	ret = pwm_device_register(pwm);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register pwm device\n");
+		goto err_free_mem;
+	}
 
 	platform_set_drvdata(pdev, pwm);
 	return 0;
@@ -238,33 +208,38 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev)
 err_free_mem:
 	release_mem_region(r->start, r->end - r->start + 1);
 err_free_clk:
-	clk_put(pwm->clk);
-err_free:
+	clk_put(mxc_pwm->clk);
+err_free3:
+	kfree(pops);
+err_free2:
 	kfree(pwm);
+err_free1:
+	kfree(mxc_pwm);
 	return ret;
 }
 
 static int __devexit mxc_pwm_remove(struct platform_device *pdev)
 {
 	struct pwm_device *pwm;
+	struct mxc_pwm_device *mxc_pwm;
 	struct resource *r;
 
 	pwm = platform_get_drvdata(pdev);
 	if (pwm == NULL)
 		return -ENODEV;
+	mxc_pwm = pwm->data;
 
-	mutex_lock(&pwm_lock);
-	list_del(&pwm->node);
-	mutex_unlock(&pwm_lock);
-
-	iounmap(pwm->mmio_base);
+	pwm_device_unregister(pwm);
+	iounmap(mxc_pwm->mmio_base);
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, r->end - r->start + 1);
 
-	clk_put(pwm->clk);
+	clk_put(mxc_pwm->clk);
 
+	kfree(pwm->pops);
 	kfree(pwm);
+	kfree(mxc_pwm);
 	return 0;
 }
 
diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c
index ef32686..cfda98b 100644
--- a/arch/arm/plat-pxa/pwm.c
+++ b/arch/arm/plat-pxa/pwm.c
@@ -43,33 +43,27 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table);
 #define PWMCR_SD	(1 << 6)
 #define PWMDCR_FD	(1 << 10)
 
-struct pwm_device {
-	struct list_head	node;
-	struct pwm_device	*secondary;
-	struct platform_device	*pdev;
-
-	const char	*label;
+struct pxa_pwm_device {
+	struct pxa_pwm_device *sec;
 	struct clk	*clk;
 	int		clk_enabled;
 	void __iomem	*mmio_base;
-
-	unsigned int	use_count;
-	unsigned int	pwm_id;
 };
 
 /*
  * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
  * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
  */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+int pxa_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
 	unsigned long long c;
 	unsigned long period_cycles, prescale, pv, dc;
+	struct pxa_pwm_device *pxa_pwm = pwm->data;
 
 	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
 		return -EINVAL;
 
-	c = clk_get_rate(pwm->clk);
+	c = clk_get_rate(pxa_pwm->clk);
 	c = c * period_ns;
 	do_div(c, 1000000000);
 	period_cycles = c;
@@ -90,94 +84,45 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	/* NOTE: the clock to PWM has to be enabled first
 	 * before writing to the registers
 	 */
-	clk_enable(pwm->clk);
-	__raw_writel(prescale, pwm->mmio_base + PWMCR);
-	__raw_writel(dc, pwm->mmio_base + PWMDCR);
-	__raw_writel(pv, pwm->mmio_base + PWMPCR);
-	clk_disable(pwm->clk);
+	clk_enable(pxa_pwm->clk);
+	__raw_writel(prescale, pxa_pwm->mmio_base + PWMCR);
+	__raw_writel(dc, pxa_pwm->mmio_base + PWMDCR);
+	__raw_writel(pv, pxa_pwm->mmio_base + PWMPCR);
+	clk_disable(pxa_pwm->clk);
 
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+int pxa_pwm_enable(struct pwm_device *pwm)
 {
+	struct pxa_pwm_device *pxa_pwm = pwm->data;
 	int rc = 0;
 
-	if (!pwm->clk_enabled) {
-		rc = clk_enable(pwm->clk);
+	if (!pxa_pwm->clk_enabled) {
+		rc = clk_enable(pxa_pwm->clk);
 		if (!rc)
-			pwm->clk_enabled = 1;
+			pxa_pwm->clk_enabled = 1;
 	}
 	return rc;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+int pxa_pwm_disable(struct pwm_device *pwm)
 {
-	if (pwm->clk_enabled) {
-		clk_disable(pwm->clk);
-		pwm->clk_enabled = 0;
-	}
-}
-EXPORT_SYMBOL(pwm_disable);
-
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
+	struct pxa_pwm_device *pxa_pwm = pwm->data;
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
-
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
+	if (pxa_pwm->clk_enabled) {
+		clk_disable(pxa_pwm->clk);
+		pxa_pwm->clk_enabled = 0;
 	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count++;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		pr_warning("PWM device already freed\n");
-
-	mutex_unlock(&pwm_lock);
-}
-EXPORT_SYMBOL(pwm_free);
-
-static inline void __add_pwm(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->node, &pwm_list);
-	mutex_unlock(&pwm_lock);
+	return 0;
 }
 
 static int __devinit pwm_probe(struct platform_device *pdev)
 {
 	const struct platform_device_id *id = platform_get_device_id(pdev);
+	struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec;
 	struct pwm_device *pwm, *secondary = NULL;
+	struct pwm_ops *pops;
 	struct resource *r;
 	int ret = 0;
 
@@ -186,17 +131,26 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
+	pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL);
+	if (pops == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		kfree(pwm);
+		return -ENOMEM;
+	}
+	pxa_pwm = kzalloc(sizeof(struct pxa_pwm_device), GFP_KERNEL);
+	if (pxa_pwm == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		kfree(pops);
+		kfree(pwm);
+		return -ENOMEM;
+	}
 
-	pwm->clk = clk_get(&pdev->dev, NULL);
-	if (IS_ERR(pwm->clk)) {
-		ret = PTR_ERR(pwm->clk);
+	pxa_pwm->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pxa_pwm->clk)) {
+		ret = PTR_ERR(pxa_pwm->clk);
 		goto err_free;
 	}
-	pwm->clk_enabled = 0;
-
-	pwm->use_count = 0;
-	pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id;
-	pwm->pdev = pdev;
+	pxa_pwm->clk_enabled = 0;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (r == NULL) {
@@ -212,69 +166,104 @@ static int __devinit pwm_probe(struct platform_device *pdev)
 		goto err_free_clk;
 	}
 
-	pwm->mmio_base = ioremap(r->start, resource_size(r));
-	if (pwm->mmio_base == NULL) {
+	pxa_pwm->mmio_base = ioremap(r->start, resource_size(r));
+	if (pxa_pwm->mmio_base == NULL) {
 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
 		ret = -ENODEV;
 		goto err_free_mem;
 	}
 
+	pops->pwm_config = pxa_pwm_config;
+	pops->pwm_enable = pxa_pwm_enable;
+	pops->pwm_disable = pxa_pwm_disable;
+
+	pwm->name = pdev->name;
+	pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id;
+	pwm->pops = pops;
+	pwm->data = pxa_pwm;
+
+	ret = pwm_device_register(pwm);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register pwm device\n");
+		goto err_free_mem;
+	}
+
 	if (id->driver_data & HAS_SECONDARY_PWM) {
 		secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
 		if (secondary == NULL) {
 			ret = -ENOMEM;
-			goto err_free_mem;
+			goto err_pwm;
+		}
+		pxa_pwm_sec = kzalloc(sizeof(struct pxa_pwm_device),
+								GFP_KERNEL);
+		if (pxa_pwm_sec == NULL) {
+			ret = -ENOMEM;
+			goto err_free_mem2;
 		}
 
 		*secondary = *pwm;
-		pwm->secondary = secondary;
+		*pxa_pwm_sec = *pxa_pwm;
+		pxa_pwm->sec = pxa_pwm_sec;
 
 		/* registers for the second PWM has offset of 0x10 */
-		secondary->mmio_base = pwm->mmio_base + 0x10;
+		pxa_pwm_sec->mmio_base = pxa_pwm->mmio_base + 0x10;
 		secondary->pwm_id = pdev->id + 2;
-	}
+		secondary->data = pxa_pwm_sec;
 
-	__add_pwm(pwm);
-	if (secondary)
-		__add_pwm(secondary);
+		ret = pwm_device_register(secondary);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to register pwm device\n");
+			goto err_free_mem3;
+		}
+	}
 
 	platform_set_drvdata(pdev, pwm);
 	return 0;
-
+err_free_mem3:
+	kfree(pxa_pwm_sec);
+err_free_mem2:
+	kfree(secondary);
+err_pwm:
+	pwm_device_unregister(pwm);
 err_free_mem:
 	release_mem_region(r->start, resource_size(r));
 err_free_clk:
-	clk_put(pwm->clk);
+	clk_put(pxa_pwm->clk);
 err_free:
+	kfree(pxa_pwm);
+	kfree(pops);
 	kfree(pwm);
 	return ret;
 }
 
 static int __devexit pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	struct pwm_device *pwm, *secondary;
+	struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec;
 	struct resource *r;
 
 	pwm = platform_get_drvdata(pdev);
 	if (pwm == NULL)
 		return -ENODEV;
-
-	mutex_lock(&pwm_lock);
-
-	if (pwm->secondary) {
-		list_del(&pwm->secondary->node);
-		kfree(pwm->secondary);
+	pxa_pwm = pwm->data;
+	secondary = pwm_request((pdev->id + 2), pdev->name);
+	pxa_pwm_sec = secondary->data;
+
+	pwm_device_unregister(pwm);
+	iounmap(pxa_pwm->mmio_base);
+	if (secondary) {
+		pwm_device_unregister(secondary);
+		iounmap(pxa_pwm->mmio_base);
+		kfree(pxa_pwm_sec);
+		kfree(secondary);
 	}
 
-	list_del(&pwm->node);
-	mutex_unlock(&pwm_lock);
-
-	iounmap(pwm->mmio_base);
-
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(r->start, resource_size(r));
 
-	clk_put(pwm->clk);
+	clk_put(pxa_pwm->clk);
+	kfree(pxa_pwm);
+	kfree(pwm->pops);
 	kfree(pwm);
 	return 0;
 }
diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c
index 2eeb49f..3062537 100644
--- a/arch/arm/plat-samsung/pwm.c
+++ b/arch/arm/plat-samsung/pwm.c
@@ -26,25 +26,19 @@
 #include <plat/devs.h>
 #include <plat/regs-timer.h>
 
-struct pwm_device {
-	struct list_head	 list;
+struct s3c_pwm_device {
 	struct platform_device	*pdev;
 
 	struct clk		*clk_div;
 	struct clk		*clk;
-	const char		*label;
 
 	unsigned int		 period_ns;
 	unsigned int		 duty_ns;
 
 	unsigned char		 tcon_base;
 	unsigned char		 running;
-	unsigned char		 use_count;
-	unsigned char		 pwm_id;
 };
 
-#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
-
 static struct clk *clk_scaler[2];
 
 /* Standard setup for a timer block. */
@@ -78,108 +72,61 @@ struct platform_device s3c_device_timer[] = {
 	[4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) },
 };
 
-static inline int pwm_is_tdiv(struct pwm_device *pwm)
+static inline int pwm_is_tdiv(struct s3c_pwm_device *s3c_pwm)
 {
-	return clk_get_parent(pwm->clk) == pwm->clk_div;
+	return clk_get_parent(s3c_pwm->clk) == s3c_pwm->clk_div;
 }
 
-static DEFINE_MUTEX(pwm_lock);
-static LIST_HEAD(pwm_list);
+#define pwm_tcon_start(s3c_pwm) (1 << (s3c_pwm->tcon_base + 0))
+#define pwm_tcon_invert(s3c_pwm) (1 << (s3c_pwm->tcon_base + 2))
+#define pwm_tcon_autoreload(s3c_pwm) (1 << (s3c_pwm->tcon_base + 3))
+#define pwm_tcon_manulupdate(s3c_pwm) (1 << (s3c_pwm->tcon_base + 1))
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-	int found = 0;
-
-	mutex_lock(&pwm_lock);
-
-	list_for_each_entry(pwm, &pwm_list, list) {
-		if (pwm->pwm_id == pwm_id) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (found) {
-		if (pwm->use_count == 0) {
-			pwm->use_count = 1;
-			pwm->label = label;
-		} else
-			pwm = ERR_PTR(-EBUSY);
-	} else
-		pwm = ERR_PTR(-ENOENT);
-
-	mutex_unlock(&pwm_lock);
-	return pwm;
-}
-
-EXPORT_SYMBOL(pwm_request);
-
-
-void pwm_free(struct pwm_device *pwm)
-{
-	mutex_lock(&pwm_lock);
-
-	if (pwm->use_count) {
-		pwm->use_count--;
-		pwm->label = NULL;
-	} else
-		printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id);
-
-	mutex_unlock(&pwm_lock);
-}
-
-EXPORT_SYMBOL(pwm_free);
-
-#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
-#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
-#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
-#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
-
-int pwm_enable(struct pwm_device *pwm)
+int s3c_pwm_enable(struct pwm_device *pwm)
 {
 	unsigned long flags;
 	unsigned long tcon;
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_start(pwm);
+	tcon |= pwm_tcon_start(s3c_pwm);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
-	pwm->running = 1;
+	s3c_pwm->running = 1;
 	return 0;
 }
 
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
+int s3c_pwm_disable(struct pwm_device *pwm)
 {
 	unsigned long flags;
 	unsigned long tcon;
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon &= ~pwm_tcon_start(pwm);
+	tcon &= ~pwm_tcon_start(s3c_pwm);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
-	pwm->running = 0;
+	s3c_pwm->running = 0;
+	return 0;
 }
 
-EXPORT_SYMBOL(pwm_disable);
-
-static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
+static unsigned long pwm_calc_tin(struct pwm_device *pwm,
+		unsigned long freq)
 {
 	unsigned long tin_parent_rate;
 	unsigned int div;
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 
-	tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div));
-	pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate);
+	tin_parent_rate = clk_get_rate(clk_get_parent(s3c_pwm->clk_div));
+	dev_dbg(pwm->dev, "tin parent at %lu\n", tin_parent_rate);
 
 	for (div = 2; div <= 16; div *= 2) {
 		if ((tin_parent_rate / (div << 16)) < freq)
@@ -191,7 +138,7 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq)
 
 #define NS_IN_HZ (1000000000UL)
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+int s3c_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
 	unsigned long tin_rate;
 	unsigned long tin_ns;
@@ -200,6 +147,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	unsigned long tcon;
 	unsigned long tcnt;
 	long tcmp;
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 
 	/* We currently avoid using 64bit arithmetic by using the
 	 * fact that anything faster than 1Hz is easily representable
@@ -211,8 +159,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (duty_ns > period_ns)
 		return -EINVAL;
 
-	if (period_ns == pwm->period_ns &&
-	    duty_ns == pwm->duty_ns)
+	if (period_ns == s3c_pwm->period_ns &&
+	    duty_ns == s3c_pwm->duty_ns)
 		return 0;
 
 	/* The TCMP and TCNT can be read without a lock, they're not
@@ -223,26 +171,26 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 
 	period = NS_IN_HZ / period_ns;
 
-	pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n",
+	dev_dbg(pwm->dev, "duty_ns=%d, period_ns=%d (%lu)\n",
 		duty_ns, period_ns, period);
 
 	/* Check to see if we are changing the clock rate of the PWM */
 
-	if (pwm->period_ns != period_ns) {
-		if (pwm_is_tdiv(pwm)) {
+	if (s3c_pwm->period_ns != period_ns) {
+		if (pwm_is_tdiv(s3c_pwm)) {
 			tin_rate = pwm_calc_tin(pwm, period);
-			clk_set_rate(pwm->clk_div, tin_rate);
+			clk_set_rate(s3c_pwm->clk_div, tin_rate);
 		} else
-			tin_rate = clk_get_rate(pwm->clk);
+			tin_rate = clk_get_rate(s3c_pwm->clk);
 
-		pwm->period_ns = period_ns;
+		s3c_pwm->period_ns = period_ns;
 
-		pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate);
+		dev_dbg(pwm->dev, "tin_rate=%lu\n", tin_rate);
 
 		tin_ns = NS_IN_HZ / tin_rate;
 		tcnt = period_ns / tin_ns;
 	} else
-		tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk);
+		tin_ns = NS_IN_HZ / clk_get_rate(s3c_pwm->clk);
 
 	/* Note, counters count down */
 
@@ -253,7 +201,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	if (tcmp == tcnt)
 		tcmp--;
 
-	pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
+	dev_dbg(pwm->dev, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
 
 	if (tcmp < 0)
 		tcmp = 0;
@@ -266,11 +214,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	__raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id));
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_manulupdate(pwm);
-	tcon |= pwm_tcon_autoreload(pwm);
+	tcon |= pwm_tcon_manulupdate(s3c_pwm);
+	tcon |= pwm_tcon_autoreload(s3c_pwm);
 	__raw_writel(tcon, S3C2410_TCON);
 
-	tcon &= ~pwm_tcon_manulupdate(pwm);
+	tcon &= ~pwm_tcon_manulupdate(s3c_pwm);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
@@ -278,103 +226,121 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	return 0;
 }
 
-EXPORT_SYMBOL(pwm_config);
-
-static int pwm_register(struct pwm_device *pwm)
-{
-	pwm->duty_ns = -1;
-	pwm->period_ns = -1;
-
-	mutex_lock(&pwm_lock);
-	list_add_tail(&pwm->list, &pwm_list);
-	mutex_unlock(&pwm_lock);
-
-	return 0;
-}
-
 static int s3c_pwm_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
+	struct s3c_pwm_device *s3c_pwm;
 	struct pwm_device *pwm;
+	struct pwm_ops *pops;
 	unsigned long flags;
 	unsigned long tcon;
 	unsigned int id = pdev->id;
-	int ret;
+	int ret = 0;
 
 	if (id == 4) {
 		dev_err(dev, "TIMER4 is currently not supported\n");
 		return -ENXIO;
 	}
 
+	s3c_pwm = kzalloc(sizeof(struct s3c_pwm_device), GFP_KERNEL);
+	if (s3c_pwm == NULL) {
+		dev_err(dev, "failed to allocate pwm_device\n");
+		return -ENOMEM;
+	}
+	s3c_pwm->pdev = pdev;
 	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
 	if (pwm == NULL) {
 		dev_err(dev, "failed to allocate pwm_device\n");
-		return -ENOMEM;
+		goto err_alloc;
+		ret = -ENOMEM;
+	}
+	pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL);
+	if (pops == NULL) {
+		dev_err(dev, "failed to allocate memory\n");
+		goto err_alloc1;
+		ret = -ENOMEM;
 	}
-
-	pwm->pdev = pdev;
-	pwm->pwm_id = id;
 
 	/* calculate base of control bits in TCON */
-	pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+	s3c_pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;
 
-	pwm->clk = clk_get(dev, "pwm-tin");
-	if (IS_ERR(pwm->clk)) {
+	s3c_pwm->clk = clk_get(dev, "pwm-tin");
+	if (IS_ERR(s3c_pwm->clk)) {
 		dev_err(dev, "failed to get pwm tin clk\n");
-		ret = PTR_ERR(pwm->clk);
-		goto err_alloc;
+		ret = PTR_ERR(s3c_pwm->clk);
+		goto err_alloc2;
 	}
 
-	pwm->clk_div = clk_get(dev, "pwm-tdiv");
-	if (IS_ERR(pwm->clk_div)) {
+	s3c_pwm->clk_div = clk_get(dev, "pwm-tdiv");
+	if (IS_ERR(s3c_pwm->clk_div)) {
 		dev_err(dev, "failed to get pwm tdiv clk\n");
-		ret = PTR_ERR(pwm->clk_div);
+		ret = PTR_ERR(s3c_pwm->clk_div);
 		goto err_clk_tin;
 	}
 
 	local_irq_save(flags);
 
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_invert(pwm);
+	tcon |= pwm_tcon_invert(s3c_pwm);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	local_irq_restore(flags);
 
+	pops->pwm_config = s3c_pwm_config;
+	pops->pwm_enable = s3c_pwm_enable;
+	pops->pwm_disable = s3c_pwm_disable;
+
+	pwm->name = pdev->name;
+	pwm->pwm_id = id;
+	pwm->pops = pops;
+	pwm->data = s3c_pwm;
 
-	ret = pwm_register(pwm);
+	s3c_pwm->duty_ns = -1;
+	s3c_pwm->period_ns = -1;
+	ret = pwm_device_register(pwm);
 	if (ret) {
 		dev_err(dev, "failed to register pwm\n");
 		goto err_clk_tdiv;
 	}
 
-	pwm_dbg(pwm, "config bits %02x\n",
-		(__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);
+	dev_dbg(dev, "config bits %02x\n",
+		(__raw_readl(S3C2410_TCON) >> s3c_pwm->tcon_base) & 0x0f);
 
 	dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
-		 clk_get_rate(pwm->clk),
-		 clk_get_rate(pwm->clk_div),
-		 pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);
+		 clk_get_rate(s3c_pwm->clk),
+		 clk_get_rate(s3c_pwm->clk_div),
+		 pwm_is_tdiv(s3c_pwm) ? "div" : "ext", s3c_pwm->tcon_base);
 
 	platform_set_drvdata(pdev, pwm);
 	return 0;
 
- err_clk_tdiv:
-	clk_put(pwm->clk_div);
+err_clk_tdiv:
+	clk_put(s3c_pwm->clk_div);
 
- err_clk_tin:
-	clk_put(pwm->clk);
+err_clk_tin:
+	clk_put(s3c_pwm->clk);
 
- err_alloc:
+err_alloc2:
+	kfree(pops);
+
+err_alloc1:
 	kfree(pwm);
+
+err_alloc:
+	kfree(s3c_pwm);
 	return ret;
 }
 
 static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 {
 	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 
-	clk_put(pwm->clk_div);
-	clk_put(pwm->clk);
+	pwm_device_unregister(pwm);
+	clk_put(s3c_pwm->clk_div);
+	clk_put(s3c_pwm->clk);
+	kfree(s3c_pwm);
+	kfree(pwm->pops);
 	kfree(pwm);
 
 	return 0;
@@ -384,13 +350,14 @@ static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 
 	/* No one preserve these values during suspend so reset them
 	 * Otherwise driver leaves PWM unconfigured if same values
 	 * passed to pwm_config
 	 */
-	pwm->period_ns = 0;
-	pwm->duty_ns = 0;
+	s3c_pwm->period_ns = 0;
+	s3c_pwm->duty_ns = 0;
 
 	return 0;
 }
@@ -398,11 +365,12 @@ static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
 static int s3c_pwm_resume(struct platform_device *pdev)
 {
 	struct pwm_device *pwm = platform_get_drvdata(pdev);
+	struct s3c_pwm_device *s3c_pwm = pwm->data;
 	unsigned long tcon;
 
 	/* Restore invertion */
 	tcon = __raw_readl(S3C2410_TCON);
-	tcon |= pwm_tcon_invert(pwm);
+	tcon |= pwm_tcon_invert(s3c_pwm);
 	__raw_writel(tcon, S3C2410_TCON);
 
 	return 0;
diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c
index a26a6fa..9f46767 100644
--- a/arch/mips/jz4740/pwm.c
+++ b/arch/mips/jz4740/pwm.c
@@ -152,7 +152,7 @@ int pwm_enable(struct pwm_device *pwm)
 	return 0;
 }
 
-void pwm_disable(struct pwm_device *pwm)
+int pwm_disable(struct pwm_device *pwm)
 {
 	uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id);
 
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 5d0fb60..8a98820 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -129,6 +129,12 @@
 #define twl_has_pwrbutton()	false
 #endif
 
+#if defined CONFIG_TWL6030_PWM
+#define twl_has_pwm()	true
+#else
+#define twl_has_pwm()	false
+#endif
+
 #define SUB_CHIP_ID0 0
 #define SUB_CHIP_ID1 1
 #define SUB_CHIP_ID2 2
@@ -831,6 +837,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
 		if (IS_ERR(child))
 			return PTR_ERR(child);
 	}
+	if (twl_has_pwm()) {
+		child = add_child(SUB_CHIP_ID2, "twl6030_pwm",
+				NULL, 0,
+				false, 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
 
 	return 0;
 }
diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
index 5d25bdc..7091df9 100644
--- a/drivers/mfd/twl6030-pwm.c
+++ b/drivers/mfd/twl6030-pwm.c
@@ -20,8 +20,10 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/i2c/twl.h>
 #include <linux/slab.h>
+#include <linux/pwm.h>
+#include <linux/err.h>
+#include <linux/i2c/twl.h>
 
 #define LED_PWM_CTRL1	0xF4
 #define LED_PWM_CTRL2	0xF5
@@ -45,15 +47,10 @@
 
 #define PWM_CTRL2_MODE_MASK	0x3
 
-struct pwm_device {
-	const char *label;
-	unsigned int pwm_id;
-};
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+int twl6030_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
 	u8 duty_cycle;
-	int ret;
+	int ret = 0;
 
 	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
 		return -EINVAL;
@@ -69,12 +66,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 	}
 	return 0;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+int twl6030_pwm_enable(struct pwm_device *pwm)
 {
 	u8 val;
-	int ret;
+	int ret = 0;
 
 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
 	if (ret < 0) {
@@ -95,18 +91,17 @@ int pwm_enable(struct pwm_device *pwm)
 	twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
 	return 0;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+int twl6030_pwm_disable(struct pwm_device *pwm)
 {
 	u8 val;
-	int ret;
+	int ret = 0;
 
 	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
 	if (ret < 0) {
 		pr_err("%s: Failed to disable PWM, Error %d\n",
 			pwm->label, ret);
-		return;
+		return ret;
 	}
 
 	val &= ~PWM_CTRL2_MODE_MASK;
@@ -116,48 +111,85 @@ void pwm_disable(struct pwm_device *pwm)
 	if (ret < 0) {
 		pr_err("%s: Failed to disable PWM, Error %d\n",
 			pwm->label, ret);
-		return;
 	}
-	return;
+	return ret;
 }
-EXPORT_SYMBOL(pwm_disable);
 
-struct pwm_device *pwm_request(int pwm_id, const char *label)
+static int __devinit twl6030_pwm_probe(struct platform_device *pdev)
 {
-	u8 val;
-	int ret;
 	struct pwm_device *pwm;
+	struct pwm_ops *pops;
+	int ret;
+	u8 val;
 
 	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
 	if (pwm == NULL) {
-		pr_err("%s: failed to allocate memory\n", label);
-		return NULL;
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+	pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL);
+	if (pops == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		kfree(pwm);
+		return -ENOMEM;
 	}
 
-	pwm->label = label;
-	pwm->pwm_id = pwm_id;
-
+	pops->pwm_config = twl6030_pwm_config;
+	pops->pwm_enable = twl6030_pwm_enable;
+	pops->pwm_disable = twl6030_pwm_disable;
+	pwm->name = &pdev->name;
+	pwm->pwm_id = pdev->id;
+	pwm->pops = pops;
+	ret = pwm_device_register(pwm);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register pwm device\n");
+		kfree(pwm);
+		kfree(pops);
+		return ret;
+	}
+	platform_set_drvdata(pdev, pwm);
 	/* Configure PWM */
 	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
-		PWM_CTRL2_MODE_HW;
+							PWM_CTRL2_MODE_HW;
 
 	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
-
 	if (ret < 0) {
-		pr_err("%s: Failed to configure PWM, Error %d\n",
-			 pwm->label, ret);
-
-		kfree(pwm);
-		return NULL;
+		dev_err(&pdev->dev, "Failed to configure PWM, Error %d\n", ret);
+		return ret;
 	}
-
-	return pwm;
+	dev_dbg(&pdev->dev, "pwm probe successful\n");
+	return ret;
 }
-EXPORT_SYMBOL(pwm_request);
 
-void pwm_free(struct pwm_device *pwm)
+static int __devexit twl6030_pwm_remove(struct platform_device *pdev)
 {
-	pwm_disable(pwm);
+	struct pwm_device *pwm = platform_get_drvdata(pdev);
+
+	pwm_device_unregister(pwm);
+	kfree(pwm->pops);
 	kfree(pwm);
+	dev_dbg(&pdev->dev, "pwm driver removed\n");
+	return 0;
 }
-EXPORT_SYMBOL(pwm_free);
+
+static struct platform_driver twl6030_pwm_driver = {
+	.driver = {
+		.name = "twl6030_pwm",
+		.owner = THIS_MODULE,
+	},
+	.probe = twl6030_pwm_probe,
+	.remove = __devexit_p(twl6030_pwm_remove),
+};
+
+static int __init twl6030_pwm_init(void)
+{
+	return platform_driver_register(&twl6030_pwm_driver);
+}
+
+static void __exit twl6030_pwm_deinit(void)
+{
+	platform_driver_unregister(&twl6030_pwm_driver);
+}
+
+subsys_initcall(twl6030_pwm_init);
+module_exit(twl6030_pwm_deinit);
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
index 54e3d05..25f9310 100644
--- a/drivers/misc/ab8500-pwm.c
+++ b/drivers/misc/ab8500-pwm.c
@@ -23,16 +23,9 @@
 #define ENABLE_PWM			1
 #define DISABLE_PWM			0
 
-struct pwm_device {
-	struct device *dev;
-	struct list_head node;
-	const char *label;
-	unsigned int pwm_id;
-};
-
-static LIST_HEAD(pwm_list);
+static struct device *parent;
 
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+int ab8500_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
 	int ret = 0;
 	unsigned int higher_val, lower_val;
@@ -51,93 +44,89 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 
 	reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2);
 
-	ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+	ret = abx500_set_register_interruptible(parent, AB8500_MISC,
 			reg, (u8)lower_val);
 	if (ret < 0)
 		return ret;
-	ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+	ret = abx500_set_register_interruptible(parent, AB8500_MISC,
 			(reg + 1), (u8)higher_val);
 
 	return ret;
 }
-EXPORT_SYMBOL(pwm_config);
 
-int pwm_enable(struct pwm_device *pwm)
+int ab8500_pwm_enable(struct pwm_device *pwm)
 {
 	int ret;
 
-	ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+	ret = abx500_mask_and_set_register_interruptible(parent,
 				AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
-				1 << (pwm->pwm_id-1), ENABLE_PWM);
+				1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1));
 	if (ret < 0)
-		dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
-							pwm->label, ret);
+		dev_err(pwm->dev, "%s: Failed to enable PWM, Error %d\n",
+							pwm->name, ret);
 	return ret;
 }
-EXPORT_SYMBOL(pwm_enable);
 
-void pwm_disable(struct pwm_device *pwm)
+int ab8500_pwm_disable(struct pwm_device *pwm)
 {
 	int ret;
 
-	ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+	ret = abx500_mask_and_set_register_interruptible(parent,
 				AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
 				1 << (pwm->pwm_id-1), DISABLE_PWM);
 	if (ret < 0)
 		dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
-							pwm->label, ret);
-	return;
-}
-EXPORT_SYMBOL(pwm_disable);
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-	struct pwm_device *pwm;
-
-	list_for_each_entry(pwm, &pwm_list, node) {
-		if (pwm->pwm_id == pwm_id) {
-			pwm->label = label;
-			pwm->pwm_id = pwm_id;
-			return pwm;
-		}
-	}
-
-	return ERR_PTR(-ENOENT);
-}
-EXPORT_SYMBOL(pwm_request);
-
-void pwm_free(struct pwm_device *pwm)
-{
-	pwm_disable(pwm);
+							pwm->name, ret);
+	return ret;
 }
-EXPORT_SYMBOL(pwm_free);
 
 static int __devinit ab8500_pwm_probe(struct platform_device *pdev)
 {
-	struct pwm_device *pwm;
+	int ret = 0;
+	struct pwm_ops *pops;
+	struct pwm_device *pwm_dev;
 	/*
 	 * Nothing to be done in probe, this is required to get the
 	 * device which is required for ab8500 read and write
 	 */
-	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
-	if (pwm == NULL) {
+	pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL);
+	if (pops == NULL) {
 		dev_err(&pdev->dev, "failed to allocate memory\n");
 		return -ENOMEM;
 	}
-	pwm->dev = &pdev->dev;
-	pwm->pwm_id = pdev->id;
-	list_add_tail(&pwm->node, &pwm_list);
-	platform_set_drvdata(pdev, pwm);
-	dev_dbg(pwm->dev, "pwm probe successful\n");
-	return 0;
+	pwm_dev = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+	if (pwm_dev == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		kfree(pops);
+		return -ENOMEM;
+	}
+	parent = &pdev->dev;
+	pops->pwm_config = ab8500_pwm_config;
+	pops->pwm_enable = ab8500_pwm_enable;
+	pops->pwm_disable = ab8500_pwm_disable;
+	pwm_dev->name = "ab8500";
+	pwm_dev->pwm_id = pdev->id;
+	pwm_dev->pops = pops;
+	ret = pwm_device_register(parent, pwm_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register pwm device\n");
+		kfree(pwm_dev);
+		kfree(pops);
+		return ret;
+	}
+	platform_set_drvdata(pdev, pwm_dev);
+	dev_dbg(&pdev->dev, "pwm probe successful\n");
+	return ret;
 }
 
 static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
 {
-	struct pwm_device *pwm = platform_get_drvdata(pdev);
-	list_del(&pwm->node);
+	struct pwm_device *pwm_dev = platform_get_drvdata(pdev);
+
+	pwm_device_unregister(pwm_dev);
 	dev_dbg(&pdev->dev, "pwm driver removed\n");
-	kfree(pwm);
+	kfree(pwm_dev->pops);
+	kfree(pwm_dev);
 	return 0;
 }
 
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 03a9813..5483b7f 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -4,6 +4,7 @@
 
 menuconfig PWM_DEVICES
 	bool "PWM devices"
+	depends on ARM
 	default y
 	---help---
 	  Say Y to enable pwm core driver and see options for various pwm
diff --git a/drivers/pwm/pwm-core.c b/drivers/pwm/pwm-core.c
index c323969..7826808 100644
--- a/drivers/pwm/pwm-core.c
+++ b/drivers/pwm/pwm-core.c
@@ -11,13 +11,6 @@
 #include <linux/err.h>
 #include <linux/pwm.h>
 
-struct pwm_device {
-	struct pwm_ops *pops;
-	struct device *dev;
-	int pwm_id;
-	const char *name;
-};
-
 static struct class *pwm_class;
 
 /*
@@ -69,11 +62,11 @@ EXPORT_SYMBOL(pwm_enable);
  * This function verifies for the presence of the pops structure and if so
  * calls the pwm device specific disable function.
  */
-void pwm_disable(struct pwm_device *pwm)
+int pwm_disable(struct pwm_device *pwm)
 {
 	if (!pwm->pops)
 		return -EFAULT;
-	pwm->pops->pwm_disable(pwm);
+	return pwm->pops->pwm_disable(pwm);
 }
 EXPORT_SYMBOL(pwm_disable);
 
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index c41e0da..d10ce1d 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -1,14 +1,44 @@
 #ifndef __LINUX_PWM_H
 #define __LINUX_PWM_H
 
-struct pwm_device;
+/*
+ * TODO: #if defined CONFIG_PWM_DEVICES has to be removed after mips jz4740
+ * pwm driver aligning with pwm-core.c driver.
+ */
+#if defined CONFIG_PWM_DEVICES
+/**
+ * struct pwm_device - pwm device properties
+ * @pops:	pointer to the struct pwm_ops
+ * @dev:	pointer to the struct device
+ * @pwm_id:	pwm device id
+ * @data:	pointer to the device specific data may be structure also
+ * @name:	pwm device name
+ */
+struct pwm_device {
+	struct pwm_ops *pops;
+	struct device *dev;
+	unsigned int pwm_id;
+	void *data;
+	const char *name;
+};
 
+/**
+ * struct pwm_ops - operations performed on pwm device
+ * @pwm_config:	function pointer to the device specific config function
+ * @pwm_enable:	function pointer to the device specific enable function
+ * @pwm_disable:function pointer to the device specific disable function
+ *
+ * This struct to be assigned in the pwm driver prior to register with the
+ * pwm core driver.
+ */
 struct pwm_ops {
 	int (*pwm_config)(struct pwm_device *pwm, int duty_ns, int period_ns);
 	int (*pwm_enable)(struct pwm_device *pwm);
 	int (*pwm_disable)(struct pwm_device *pwm);
-	char *name;
 };
+#else
+struct pwm_device;
+#endif
 
 /*
  * pwm_request - request a PWM device
@@ -33,7 +63,11 @@ int pwm_enable(struct pwm_device *pwm);
 /*
  * pwm_disable - stop a PWM output toggling
  */
+#if defined CONFIG_PWM_DEVICES
+int pwm_disable(struct pwm_device *pwm);
+#else
 void pwm_disable(struct pwm_device *pwm);
+#endif
 
 int pwm_device_register(struct device *parent, struct pwm_device *pwm);
 int pwm_device_unregister(struct pwm_device *pwm);
-- 
1.7.2.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ