diff mbox

mfd: Add PWM1 and PWM2 support to twl6030-pwm

Message ID 51632.192.168.10.10.1314687437.squirrel@dbdmail.itg.ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hemanth V Aug. 30, 2011, 6:57 a.m. UTC
From: Hemanth V <hemanthv@ti.com>
Date: Fri, 26 Aug 2011 10:49:29 +0530
Subject: [PATCH] Add PWM1 and PWM2 support to twl6030-pwm driver

This patch adds support for PWM1/PWM2. TWL6030 PWM driver also
supports Indicator LED PWM. Function pointers are defined for
for init, enable, disable and configuration for both Indicator LED
PWM (led_pwm) and PWM1/PWM2 (std_pwm)

Tested-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: Hemanth V <hemanthv@ti.com>
---
 drivers/mfd/twl6030-pwm.c |  324 ++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 309 insertions(+), 15 deletions(-)

Comments

Hemanth V Sept. 6, 2011, 11:28 a.m. UTC | #1
> From: Hemanth V <hemanthv@ti.com>
> Date: Fri, 26 Aug 2011 10:49:29 +0530
> Subject: [PATCH] Add PWM1 and PWM2 support to twl6030-pwm driver

Samuel, any comments on this patch.

Thanks
Hemanth

>
> This patch adds support for PWM1/PWM2. TWL6030 PWM driver also
> supports Indicator LED PWM. Function pointers are defined for
> for init, enable, disable and configuration for both Indicator LED
> PWM (led_pwm) and PWM1/PWM2 (std_pwm)
>
> Tested-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> Signed-off-by: Hemanth V <hemanthv@ti.com>
> ---
>  drivers/mfd/twl6030-pwm.c |  324 ++++++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 309 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
> index e8fee14..8d9c3f5 100644
> --- a/drivers/mfd/twl6030-pwm.c
> +++ b/drivers/mfd/twl6030-pwm.c
> @@ -5,6 +5,9 @@
>   * Copyright (C) 2010 Texas Instruments
>   * Author: Hemanth V <hemanthv@ti.com>
>   *
> + * Added support for PWM1, PWM2
> + * Hemanth V <hemanthv@ti.com>
> + *
>   * This program is free software; you can redistribute it and/or modify it
>   * under the terms of the GNU General Public License version 2 as published by
>   * the Free Software Foundation.
> @@ -36,7 +39,9 @@
>  #define PWM_CTRL2_CURR_02	(2 << 4)
>
>  /* LED supply source */
> +#define PWM_CTRL2_SRC_VBUS	(0 << 2)
>  #define PWM_CTRL2_SRC_VAC	(1 << 2)
> +#define PWM_CTRL2_SRC_EXT	(2 << 2)
>
>  /* LED modes */
>  #define PWM_CTRL2_MODE_HW	(0 << 0)
> @@ -45,12 +50,53 @@
>
>  #define PWM_CTRL2_MODE_MASK	0x3
>
> +/* PWMs supported by driver */
> +#define PWM_ID_LED		1
> +#define PWM_ID_PWM1		2
> +#define PWM_ID_PWM2		3
> +
> +#define LED_PWM1ON		0x00
> +#define LED_PWM1OFF		0x01
> +#define LED_PWM2ON		0x03
> +#define LED_PWM2OFF		0x04
> +#define TWL6030_TOGGLE3		0x92
> +#define PWMSTATUS2		0x94
> +
> +/* Defines for TOGGLE3 register */
> +#define PWM2EN			(1 << 5)
> +#define PWM2S			(1 << 4)
> +#define PWM2R			(1 << 3)
> +#define PWM1EN			(1 << 2)
> +#define PWM1S			(1 << 1)
> +#define PWM1R			(1 << 0)
> +
> +/* Defines for PWMSTATUS2 register */
> +#define PWM1_CLK_EN		(1 << 1)
> +#define PWM2_CLK_EN		(1 << 3)
> +#define	TRUE			1
> +#define	FALSE			0
> +
> +static DEFINE_MUTEX(pwm_lock);
> +static LIST_HEAD(pwm_list);
> +
> +struct pwm_device;
> +
> +struct pwm_ops {
> +	int	(*config)(struct pwm_device *, int, int);
> +	int	(*enable)(struct pwm_device *);
> +	void	(*disable)(struct pwm_device *);
> +	int	(*init)(struct pwm_device *);
> +};
> +
>  struct pwm_device {
> -	const char *label;
> -	unsigned int pwm_id;
> +	struct list_head	node;
> +	const char		*label;
> +	unsigned int		pwm_id;
> +	unsigned int		use_count;
> +	struct pwm_ops		*ops;
>  };
>
> -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
> +int led_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>  {
>  	u8 duty_cycle;
>  	int ret;
> @@ -69,9 +115,8 @@ 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 led_pwm_enable(struct pwm_device *pwm)
>  {
>  	u8 val;
>  	int ret;
> @@ -95,9 +140,8 @@ 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)
> +void led_pwm_disable(struct pwm_device *pwm)
>  {
>  	u8 val;
>  	int ret;
> @@ -120,37 +164,284 @@ void pwm_disable(struct pwm_device *pwm)
>  	}
>  	return;
>  }
> -EXPORT_SYMBOL(pwm_disable);
>
> -struct pwm_device *pwm_request(int pwm_id, const char *label)
> +int led_pwm_init(struct pwm_device *pwm)
>  {
>  	u8 val;
>  	int ret;
> +
> +	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VBUS |
> +		PWM_CTRL2_MODE_HW;
> +
> +	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
> +
> +	return ret;
> +}
> +
> +static struct pwm_ops pwm_led = {
> +	.config = led_pwm_config,
> +	.enable = led_pwm_enable,
> +	.disable = led_pwm_disable,
> +	.init = led_pwm_init,
> +};
> +
> +int std_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
> +{
> +	int ret = 0, level, pwm_id, reg;
> +
> +	level = (duty_ns * PWM_CTRL1_MAX) / period_ns;
> +	pwm_id = pwm->pwm_id;
> +
> +	if (pwm_id == PWM_ID_PWM1)
> +		reg = LED_PWM1ON;
> +	else
> +		reg = LED_PWM2ON;
> +
> +	if (level > 1) {
> +		if (level == 255)
> +			level = 0x7F;
> +		else
> +			level = (~(level/2)) & 0x7F;
> +
> +		ret = twl_i2c_write_u8(TWL_MODULE_PWM, level, reg);
> +	}
> +
> +	return ret;
> +}
> +
> +void std_pwm_disable(struct pwm_device *pwm)
> +{
> +
> +	int ret, pwm_id;
> +	bool pwm1_enabled = FALSE;
> +	bool pwm2_enabled = FALSE;
> +	u8 reg_val, reset_val, dis_val = 0;
> +
> +	pwm_id = pwm->pwm_id;
> +
> +	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &reg_val, PWMSTATUS2);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	if (reg_val & PWM1_CLK_EN)
> +		pwm1_enabled = TRUE;
> +	if (reg_val & PWM2_CLK_EN)
> +		pwm2_enabled = TRUE;
> +
> +	if (pwm_id == PWM_ID_PWM1) {
> +		reset_val = PWM1EN | PWM1R;
> +
> +		/* Donot reset the state for PWM2 */
> +		if (pwm2_enabled) {
> +			dis_val |= PWM2EN | PWM2S;
> +			reset_val |= PWM2EN | PWM2S;
> +		}
> +
> +	} else if (pwm_id == PWM_ID_PWM2) {
> +		reset_val = PWM2EN | PWM2R ;
> +
> +		/* Donot reset the state for PWM1 */
> +		if (pwm1_enabled) {
> +			dis_val |= PWM1EN | PWM1S;
> +			reset_val |= PWM1EN | PWM1S;
> +		}
> +	} else {
> +		ret = -EINVAL;
> +		goto out_err;
> +	}
> +
> +	/* Reset PWM to be disabled */
> +	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, reset_val, TWL6030_TOGGLE3);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	/* Disable PWM */
> +	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, dis_val, TWL6030_TOGGLE3);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	return;
> +
> +out_err:
> +	pr_err("%s: Failed to Enable PWM, Error %d\n",
> +		pwm->label, ret);
> +	return;
> +}
> +
> +int std_pwm_enable(struct pwm_device *pwm)
> +{
> +
> +	int ret, pwm_id;
> +	u8 en_val, reg_val;
> +	bool pwm1_enabled = FALSE;
> +	bool pwm2_enabled = FALSE;
> +
> +	pwm_id = pwm->pwm_id;
> +
> +	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &reg_val, PWMSTATUS2);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	if (reg_val & PWM1_CLK_EN)
> +		pwm1_enabled = TRUE;
> +	if (reg_val & PWM2_CLK_EN)
> +		pwm2_enabled = TRUE;
> +
> +	if (pwm_id == PWM_ID_PWM1) {
> +		en_val = PWM1EN | PWM1S;
> +
> +		/* Maintain PWM2 state as-is*/
> +		if (pwm2_enabled)
> +			en_val |= PWM2EN | PWM2S;
> +
> +	} else if (pwm_id == PWM_ID_PWM2) {
> +		en_val = PWM2EN | PWM2S;
> +
> +		/* Maintain PWM1 state as-is*/
> +		if (pwm1_enabled)
> +			en_val |= PWM1EN | PWM1S;
> +	} else {
> +		ret = -EINVAL;
> +		goto out_err;
> +	}
> +
> +	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, en_val, TWL6030_TOGGLE3);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	return ret;
> +
> +out_err:
> +	pr_err("%s: Failed to Enable PWM, Error %d\n",
> +		pwm->label, ret);
> +	return ret;
> +}
> +
> +int std_pwm_init(struct pwm_device *pwm)
> +{
> +	int ret, reg1, reg2, pwm_id;
> +
> +	pwm_id = pwm->pwm_id;
> +
> +	if (pwm_id == PWM_ID_PWM1) {
> +		reg1 = LED_PWM1ON;
> +		reg2 = LED_PWM1OFF;
> +	} else if (pwm_id == PWM_ID_PWM2) {
> +		reg1 = LED_PWM2ON;
> +		reg2 = LED_PWM2OFF;
> +	} else {
> +		ret = -EINVAL;
> +		goto out_err;
> +	}
> +
> +	ret = twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, reg1);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, reg2);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	twl_i2c_write_u8(TWL6030_MODULE_ID1, 0x0, TWL6030_TOGGLE3);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	return ret;
> +
> +out_err:
> +	pr_err("%s: PWM init failed, Error %d\n",
> +		pwm->label, ret);
> +
> +	return ret;
> +}
> +static struct pwm_ops pwm_std = {
> +	.config = std_pwm_config,
> +	.enable = std_pwm_enable,
> +	.disable = std_pwm_disable,
> +	.init = std_pwm_init,
> +};
> +
> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
> +{
> +	return pwm->ops->config(pwm, duty_ns, period_ns);
> +}
> +EXPORT_SYMBOL(pwm_config);
> +
> +int pwm_enable(struct pwm_device *pwm)
> +{
> +	return pwm->ops->enable(pwm);
> +}
> +EXPORT_SYMBOL(pwm_enable);
> +
> +void pwm_disable(struct pwm_device *pwm)
> +{
> +	pwm->ops->disable(pwm);
> +}
> +EXPORT_SYMBOL(pwm_disable);
> +
> +struct pwm_device *pwm_request(int pwm_id, const char *label)
> +{
> +	int ret, found = 0;
>  	struct pwm_device *pwm;
>
> +	mutex_lock(&pwm_lock);
> +
> +	list_for_each_entry(pwm, &pwm_list, node) {
> +		if (pwm->pwm_id == pwm_id) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	if (found) {
> +		if (pwm->use_count == 0) {
> +			pwm->use_count++;
> +			pwm->label = label;
> +		} else {
> +			pwm = ERR_PTR(-EBUSY);
> +		}
> +
> +		goto out;
> +	}
> +
> +	mutex_unlock(&pwm_lock);
> +
>  	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
>  	if (pwm == NULL) {
>  		pr_err("%s: failed to allocate memory\n", label);
> -		return NULL;
> +		goto out;
>  	}
>
>  	pwm->label = label;
>  	pwm->pwm_id = pwm_id;
> +	pwm->use_count++;
>
> -	/* Configure PWM */
> -	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
> -		PWM_CTRL2_MODE_HW;
> +	if (pwm_id == PWM_ID_LED) {
> +		pwm->ops = &pwm_led;
> +	} else if (pwm_id == PWM_ID_PWM1 || pwm_id == PWM_ID_PWM2) {
> +		pwm->ops = &pwm_std;
> +	} else {
> +		kfree(pwm);
> +		pwm = ERR_PTR(-EINVAL);
> +		goto out;
> +	}
>
> -	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
> +	ret = pwm->ops->init(pwm);
>
>  	if (ret < 0) {
>  		pr_err("%s: Failed to configure PWM, Error %d\n",
>  			 pwm->label, ret);
>
>  		kfree(pwm);
> -		return NULL;
> +		pwm = NULL;
> +		goto out;
>  	}
>
> +	mutex_lock(&pwm_lock);
> +	list_add_tail(&pwm->node, &pwm_list);
> +	mutex_unlock(&pwm_lock);
> +
> +out:
>  	return pwm;
>  }
>  EXPORT_SYMBOL(pwm_request);
> @@ -158,6 +449,9 @@ EXPORT_SYMBOL(pwm_request);
>  void pwm_free(struct pwm_device *pwm)
>  {
>  	pwm_disable(pwm);
> +	mutex_lock(&pwm_lock);
> +	list_del(&pwm->node);
> +	mutex_unlock(&pwm_lock);
>  	kfree(pwm);
>  }
>  EXPORT_SYMBOL(pwm_free);
> --
> 1.7.1
>
>
>


--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
index e8fee14..8d9c3f5 100644
--- a/drivers/mfd/twl6030-pwm.c
+++ b/drivers/mfd/twl6030-pwm.c
@@ -5,6 +5,9 @@ 
  * Copyright (C) 2010 Texas Instruments
  * Author: Hemanth V <hemanthv@ti.com>
  *
+ * Added support for PWM1, PWM2
+ * Hemanth V <hemanthv@ti.com>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published by
  * the Free Software Foundation.
@@ -36,7 +39,9 @@ 
 #define PWM_CTRL2_CURR_02	(2 << 4)

 /* LED supply source */
+#define PWM_CTRL2_SRC_VBUS	(0 << 2)
 #define PWM_CTRL2_SRC_VAC	(1 << 2)
+#define PWM_CTRL2_SRC_EXT	(2 << 2)

 /* LED modes */
 #define PWM_CTRL2_MODE_HW	(0 << 0)
@@ -45,12 +50,53 @@ 

 #define PWM_CTRL2_MODE_MASK	0x3

+/* PWMs supported by driver */
+#define PWM_ID_LED		1
+#define PWM_ID_PWM1		2
+#define PWM_ID_PWM2		3
+
+#define LED_PWM1ON		0x00
+#define LED_PWM1OFF		0x01
+#define LED_PWM2ON		0x03
+#define LED_PWM2OFF		0x04
+#define TWL6030_TOGGLE3		0x92
+#define PWMSTATUS2		0x94
+
+/* Defines for TOGGLE3 register */
+#define PWM2EN			(1 << 5)
+#define PWM2S			(1 << 4)
+#define PWM2R			(1 << 3)
+#define PWM1EN			(1 << 2)
+#define PWM1S			(1 << 1)
+#define PWM1R			(1 << 0)
+
+/* Defines for PWMSTATUS2 register */
+#define PWM1_CLK_EN		(1 << 1)
+#define PWM2_CLK_EN		(1 << 3)
+#define	TRUE			1
+#define	FALSE			0
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device;
+
+struct pwm_ops {
+	int	(*config)(struct pwm_device *, int, int);
+	int	(*enable)(struct pwm_device *);
+	void	(*disable)(struct pwm_device *);
+	int	(*init)(struct pwm_device *);
+};
+
 struct pwm_device {
-	const char *label;
-	unsigned int pwm_id;
+	struct list_head	node;
+	const char		*label;
+	unsigned int		pwm_id;
+	unsigned int		use_count;
+	struct pwm_ops		*ops;
 };

-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+int led_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
 {
 	u8 duty_cycle;
 	int ret;
@@ -69,9 +115,8 @@  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 led_pwm_enable(struct pwm_device *pwm)
 {
 	u8 val;
 	int ret;
@@ -95,9 +140,8 @@  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)
+void led_pwm_disable(struct pwm_device *pwm)
 {
 	u8 val;
 	int ret;
@@ -120,37 +164,284 @@  void pwm_disable(struct pwm_device *pwm)
 	}
 	return;
 }
-EXPORT_SYMBOL(pwm_disable);

-struct pwm_device *pwm_request(int pwm_id, const char *label)
+int led_pwm_init(struct pwm_device *pwm)
 {
 	u8 val;
 	int ret;
+
+	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VBUS |
+		PWM_CTRL2_MODE_HW;
+
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+
+	return ret;
+}
+
+static struct pwm_ops pwm_led = {
+	.config = led_pwm_config,
+	.enable = led_pwm_enable,
+	.disable = led_pwm_disable,
+	.init = led_pwm_init,
+};
+
+int std_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	int ret = 0, level, pwm_id, reg;
+
+	level = (duty_ns * PWM_CTRL1_MAX) / period_ns;
+	pwm_id = pwm->pwm_id;
+
+	if (pwm_id == PWM_ID_PWM1)
+		reg = LED_PWM1ON;
+	else
+		reg = LED_PWM2ON;
+
+	if (level > 1) {
+		if (level == 255)
+			level = 0x7F;
+		else
+			level = (~(level/2)) & 0x7F;
+
+		ret = twl_i2c_write_u8(TWL_MODULE_PWM, level, reg);
+	}
+
+	return ret;
+}
+
+void std_pwm_disable(struct pwm_device *pwm)
+{
+
+	int ret, pwm_id;
+	bool pwm1_enabled = FALSE;
+	bool pwm2_enabled = FALSE;
+	u8 reg_val, reset_val, dis_val = 0;
+
+	pwm_id = pwm->pwm_id;
+
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &reg_val, PWMSTATUS2);
+	if (ret < 0)
+		goto out_err;
+
+	if (reg_val & PWM1_CLK_EN)
+		pwm1_enabled = TRUE;
+	if (reg_val & PWM2_CLK_EN)
+		pwm2_enabled = TRUE;
+
+	if (pwm_id == PWM_ID_PWM1) {
+		reset_val = PWM1EN | PWM1R;
+
+		/* Donot reset the state for PWM2 */
+		if (pwm2_enabled) {
+			dis_val |= PWM2EN | PWM2S;
+			reset_val |= PWM2EN | PWM2S;
+		}
+
+	} else if (pwm_id == PWM_ID_PWM2) {
+		reset_val = PWM2EN | PWM2R ;
+
+		/* Donot reset the state for PWM1 */
+		if (pwm1_enabled) {
+			dis_val |= PWM1EN | PWM1S;
+			reset_val |= PWM1EN | PWM1S;
+		}
+	} else {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Reset PWM to be disabled */
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, reset_val, TWL6030_TOGGLE3);
+	if (ret < 0)
+		goto out_err;
+
+	/* Disable PWM */
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, dis_val, TWL6030_TOGGLE3);
+	if (ret < 0)
+		goto out_err;
+
+	return;
+
+out_err:
+	pr_err("%s: Failed to Enable PWM, Error %d\n",
+		pwm->label, ret);
+	return;
+}
+
+int std_pwm_enable(struct pwm_device *pwm)
+{
+
+	int ret, pwm_id;
+	u8 en_val, reg_val;
+	bool pwm1_enabled = FALSE;
+	bool pwm2_enabled = FALSE;
+
+	pwm_id = pwm->pwm_id;
+
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &reg_val, PWMSTATUS2);
+	if (ret < 0)
+		goto out_err;
+
+	if (reg_val & PWM1_CLK_EN)
+		pwm1_enabled = TRUE;
+	if (reg_val & PWM2_CLK_EN)
+		pwm2_enabled = TRUE;
+
+	if (pwm_id == PWM_ID_PWM1) {
+		en_val = PWM1EN | PWM1S;
+
+		/* Maintain PWM2 state as-is*/
+		if (pwm2_enabled)
+			en_val |= PWM2EN | PWM2S;
+
+	} else if (pwm_id == PWM_ID_PWM2) {
+		en_val = PWM2EN | PWM2S;
+
+		/* Maintain PWM1 state as-is*/
+		if (pwm1_enabled)
+			en_val |= PWM1EN | PWM1S;
+	} else {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, en_val, TWL6030_TOGGLE3);
+	if (ret < 0)
+		goto out_err;
+
+	return ret;
+
+out_err:
+	pr_err("%s: Failed to Enable PWM, Error %d\n",
+		pwm->label, ret);
+	return ret;
+}
+
+int std_pwm_init(struct pwm_device *pwm)
+{
+	int ret, reg1, reg2, pwm_id;
+
+	pwm_id = pwm->pwm_id;
+
+	if (pwm_id == PWM_ID_PWM1) {
+		reg1 = LED_PWM1ON;
+		reg2 = LED_PWM1OFF;
+	} else if (pwm_id == PWM_ID_PWM2) {
+		reg1 = LED_PWM2ON;
+		reg2 = LED_PWM2OFF;
+	} else {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	ret = twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, reg1);
+	if (ret < 0)
+		goto out_err;
+
+	twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, reg2);
+	if (ret < 0)
+		goto out_err;
+
+	twl_i2c_write_u8(TWL6030_MODULE_ID1, 0x0, TWL6030_TOGGLE3);
+	if (ret < 0)
+		goto out_err;
+
+	return ret;
+
+out_err:
+	pr_err("%s: PWM init failed, Error %d\n",
+		pwm->label, ret);
+
+	return ret;
+}
+static struct pwm_ops pwm_std = {
+	.config = std_pwm_config,
+	.enable = std_pwm_enable,
+	.disable = std_pwm_disable,
+	.init = std_pwm_init,
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	return pwm->ops->config(pwm, duty_ns, period_ns);
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	return pwm->ops->enable(pwm);
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	pwm->ops->disable(pwm);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	int ret, found = 0;
 	struct pwm_device *pwm;

+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (pwm->use_count == 0) {
+			pwm->use_count++;
+			pwm->label = label;
+		} else {
+			pwm = ERR_PTR(-EBUSY);
+		}
+
+		goto out;
+	}
+
+	mutex_unlock(&pwm_lock);
+
 	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
 	if (pwm == NULL) {
 		pr_err("%s: failed to allocate memory\n", label);
-		return NULL;
+		goto out;
 	}

 	pwm->label = label;
 	pwm->pwm_id = pwm_id;
+	pwm->use_count++;

-	/* Configure PWM */
-	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
-		PWM_CTRL2_MODE_HW;
+	if (pwm_id == PWM_ID_LED) {
+		pwm->ops = &pwm_led;
+	} else if (pwm_id == PWM_ID_PWM1 || pwm_id == PWM_ID_PWM2) {
+		pwm->ops = &pwm_std;
+	} else {
+		kfree(pwm);
+		pwm = ERR_PTR(-EINVAL);
+		goto out;
+	}

-	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+	ret = pwm->ops->init(pwm);

 	if (ret < 0) {
 		pr_err("%s: Failed to configure PWM, Error %d\n",
 			 pwm->label, ret);

 		kfree(pwm);
-		return NULL;
+		pwm = NULL;
+		goto out;
 	}

+	mutex_lock(&pwm_lock);
+	list_add_tail(&pwm->node, &pwm_list);
+	mutex_unlock(&pwm_lock);
+
+out:
 	return pwm;
 }
 EXPORT_SYMBOL(pwm_request);
@@ -158,6 +449,9 @@  EXPORT_SYMBOL(pwm_request);
 void pwm_free(struct pwm_device *pwm)
 {
 	pwm_disable(pwm);
+	mutex_lock(&pwm_lock);
+	list_del(&pwm->node);
+	mutex_unlock(&pwm_lock);
 	kfree(pwm);
 }
 EXPORT_SYMBOL(pwm_free);