mfd: pm8xxx-pwm: Add suppport for stand-alone PWM chip

Update SSBI register definitions based on PMIC type, and
handle cases where LPG module is not suppoted to drive
PWM.

Change-Id: Ife8312fcf6fc140f6c39c2b24827a5121d298c80
Signed-off-by: Jay Chokshi <jchokshi@codeaurora.org>
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
index ca9eebb..fa4b130 100644
--- a/drivers/mfd/pm8xxx-pwm.c
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -25,12 +25,33 @@
 #include <linux/mfd/pm8xxx/core.h>
 #include <linux/mfd/pm8xxx/pwm.h>
 
-#define PM8XXX_LPG_BANKS		8
-#define PM8XXX_PWM_CHANNELS		PM8XXX_LPG_BANKS
+#define PM8XXX_PWM_CHANNELS		3
 
+#define PM8XXX_LPG_BANKS                8
+#define PM8XXX_LPG_PWM_CHANNELS         PM8XXX_LPG_BANKS
 #define PM8XXX_LPG_CTL_REGS		7
 
 /* PM8XXX PWM */
+#define SSBI_REG_ADDR_PWM1_CTRL1	0x88
+#define SSBI_REG_ADDR_PWM1_CTRL2	0x89
+#define SSBI_REG_ADDR_PWM_CTL(id, base) (id == 0 ? base : (base + (id << 1)))
+#define SSBI_REG_ADDR_PWM_CTL1(id)	SSBI_REG_ADDR_PWM_CTL(id, \
+						SSBI_REG_ADDR_PWM1_CTRL1)
+#define SSBI_REG_ADDR_PWM_CTL2(id)	SSBI_REG_ADDR_PWM_CTL(id, \
+						SSBI_REG_ADDR_PWM1_CTRL2)
+
+#define PM8XXX_PWM_CLK_SEL_SHIFT	6
+#define PM8XXX_PWM_CLK_SEL_MASK		0xC0
+#define PM8XXX_PWM_PREDIVIDE_SHIFT	5
+#define PM8XXX_PWM_PREDIVIDE_MASK	0x20
+#define PM8XXX_PWM_M_SHIFT		2
+#define PM8XXX_PWM_M_MASK		0x1C
+#define PM8XXX_PWM_SIZE_SHIFT		1
+#define PM8XXX_PWM_SIZE_MASK		0x02
+#define PM8XXX_PWM_VALUE_BIT0		0x01
+#define PM8XXX_PWM_DISABLE		0x3F
+
+/* PM8XXX LPG PWM */
 #define SSBI_REG_ADDR_LPG_CTL_BASE	0x13C
 #define SSBI_REG_ADDR_LPG_CTL(n)	(SSBI_REG_ADDR_LPG_CTL_BASE + (n))
 #define SSBI_REG_ADDR_LPG_BANK_SEL	0x143
@@ -38,7 +59,7 @@
 #define SSBI_REG_ADDR_LPG_LUT_CFG0	0x145
 #define SSBI_REG_ADDR_LPG_LUT_CFG1	0x146
 
-/* Control 0 */
+/* LPG Control 0 */
 #define PM8XXX_PWM_1KHZ_COUNT_MASK	0xF0
 #define PM8XXX_PWM_1KHZ_COUNT_SHIFT	4
 
@@ -54,51 +75,51 @@
 #define PM8XXX_PWM_RAMP_GEN_START	(PM8XXX_PWM_RAMP_GEN_EN \
 					| PM8XXX_PWM_RAMP_START)
 
-/* Control 1 */
+/* LPG Control 1 */
 #define PM8XXX_PWM_REVERSE_EN		0x80
 #define PM8XXX_PWM_BYPASS_LUT		0x40
 #define PM8XXX_PWM_HIGH_INDEX_MASK	0x3F
 
-/* Control 2 */
+/* LPG Control 2 */
 #define PM8XXX_PWM_LOOP_EN		0x80
 #define PM8XXX_PWM_RAMP_UP		0x40
 #define PM8XXX_PWM_LOW_INDEX_MASK	0x3F
 
-/* Control 3 */
+/* LPG Control 3 */
 #define PM8XXX_PWM_VALUE_BIT7_0		0xFF
 #define PM8XXX_PWM_VALUE_BIT5_0		0x3F
 
-/* Control 4 */
+/* LPG Control 4 */
 #define PM8XXX_PWM_VALUE_BIT8		0x80
 
-#define PM8XXX_PWM_CLK_SEL_MASK		0x60
-#define PM8XXX_PWM_CLK_SEL_SHIFT	5
+#define PM8XXX_LPG_PWM_CLK_SEL_MASK	0x60
+#define PM8XXX_LPG_PWM_CLK_SEL_SHIFT	5
 
 #define PM8XXX_PWM_CLK_SEL_NO		0
 #define PM8XXX_PWM_CLK_SEL_1KHZ		1
 #define PM8XXX_PWM_CLK_SEL_32KHZ	2
 #define PM8XXX_PWM_CLK_SEL_19P2MHZ	3
 
-#define PM8XXX_PWM_PREDIVIDE_MASK	0x18
-#define PM8XXX_PWM_PREDIVIDE_SHIFT	3
+#define PM8XXX_LPG_PWM_PREDIVIDE_MASK	0x18
+#define PM8XXX_LPG_PWM_PREDIVIDE_SHIFT	3
 
 #define PM8XXX_PWM_PREDIVIDE_2		0
 #define PM8XXX_PWM_PREDIVIDE_3		1
 #define PM8XXX_PWM_PREDIVIDE_5		2
 #define PM8XXX_PWM_PREDIVIDE_6		3
 
-#define PM8XXX_PWM_M_MASK		0x07
+#define PM8XXX_LPG_PWM_M_MASK		0x07
 #define PM8XXX_PWM_M_MIN		0
 #define PM8XXX_PWM_M_MAX		7
 
-/* Control 5 */
+/* LPG Control 5 */
 #define PM8XXX_PWM_PAUSE_COUNT_HI_MASK		0xFC
 #define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT		2
 
 #define PM8XXX_PWM_PAUSE_ENABLE_HIGH		0x02
 #define PM8XXX_PWM_SIZE_9_BIT			0x01
 
-/* Control 6 */
+/* LPG Control 6 */
 #define PM8XXX_PWM_PAUSE_COUNT_LO_MASK		0xFC
 #define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT		2
 
@@ -107,11 +128,10 @@
 
 #define PM8XXX_PWM_PAUSE_COUNT_MAX		56 /* < 2^6 = 64 */
 
-/* LUT_CFG1 */
+/* LPG LUT_CFG1 */
 #define PM8XXX_PWM_LUT_READ			0x40
 
 
-
 /*
  * PWM Frequency = Clock Frequency / (N * T)
  *	or
@@ -132,7 +152,8 @@
 #define CLK_PERIOD_MIN	NSEC_19P2MHZ
 #define CLK_PERIOD_MAX	NSEC_1000HZ
 
-#define NUM_PRE_DIVIDE	3	/* No default support for pre-divide = 6 */
+#define NUM_LPG_PRE_DIVIDE	3  /* No default support for pre-divide = 6 */
+#define NUM_PWM_PRE_DIVIDE	2
 
 #define PRE_DIVIDE_0		2
 #define PRE_DIVIDE_1		3
@@ -141,7 +162,7 @@
 #define PRE_DIVIDE_MIN		PRE_DIVIDE_0
 #define PRE_DIVIDE_MAX		PRE_DIVIDE_2
 
-static unsigned int pt_t[NUM_PRE_DIVIDE][NUM_CLOCKS] = {
+static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
 	{	PRE_DIVIDE_0 * NSEC_1000HZ,
 		PRE_DIVIDE_0 * NSEC_32768HZ,
 		PRE_DIVIDE_0 * NSEC_19P2MHZ,
@@ -170,17 +191,22 @@
 	int			pwm_value;
 	int			pwm_period;
 	int			pwm_duty;
-	u8			pwm_ctl[PM8XXX_LPG_CTL_REGS];
+	u8			pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
+	u8			pwm_ctl1;
+	u8			pwm_ctl2;
 	int			irq;
 	struct pm8xxx_pwm_chip	*chip;
 	int			bypass_lut;
 };
 
 struct pm8xxx_pwm_chip {
-	struct pwm_device		pwm_dev[PM8XXX_PWM_CHANNELS];
+	struct pwm_device		*pwm_dev;
+	u8				pwm_channels;
+	u8				pwm_total_pre_divs;
 	u8				bank_mask;
 	struct mutex			pwm_mutex;
 	struct device			*dev;
+	bool				is_lpg_supported;
 };
 
 static struct pm8xxx_pwm_chip	*pwm_chip;
@@ -255,13 +281,13 @@
 	u8	reg;
 
 	if (start) {
-		reg = pwm->pwm_ctl[0] | PM8XXX_PWM_PWM_START;
+		reg = pwm->pwm_lpg_ctl[0] | PM8XXX_PWM_PWM_START;
 		if (ramp_start)
 			reg |= PM8XXX_PWM_RAMP_GEN_START;
 		else
 			reg &= ~PM8XXX_PWM_RAMP_GEN_START;
 	} else {
-		reg = pwm->pwm_ctl[0] & ~PM8XXX_PWM_PWM_START;
+		reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
 		reg &= ~PM8XXX_PWM_RAMP_GEN_START;
 	}
 
@@ -270,7 +296,40 @@
 	if (rc)
 		pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
 	else
-		pwm->pwm_ctl[0] = reg;
+		pwm->pwm_lpg_ctl[0] = reg;
+	return rc;
+}
+
+static int pm8xxx_pwm_disable(struct pwm_device *pwm)
+{
+	int	rc;
+	u8	reg;
+
+	reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
+
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
+								pwm->pwm_id);
+	return rc;
+}
+
+static int pm8xxx_pwm_enable(struct pwm_device *pwm)
+{
+	/**
+	 * A kind of best Effort: Just write the clock information that
+	 * we have in the register.
+	 */
+	int	rc;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
+
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
+								pwm->pwm_id);
 	return rc;
 }
 
@@ -304,7 +363,7 @@
 	best_clk = 0;
 	best_div = 0;
 	for (clk = 0; clk < NUM_CLOCKS; clk++) {
-		for (div = 0; div < NUM_PRE_DIVIDE; div++) {
+		for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
 			tmp_p = period_n;
 			last_p = tmp_p;
 			for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
@@ -384,7 +443,7 @@
 	int	i, pwm_size;
 	int	rc = 0;
 
-	pwm_size = (pwm->pwm_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
+	pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
 	max_pwm_value = (1 << pwm_size) - 1;
 	for (i = 0; i < len; i++) {
 		if (raw_value)
@@ -414,44 +473,65 @@
 static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
 				   int low_idx, int high_idx, int flags)
 {
-	pwm->pwm_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
-	pwm->pwm_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
+	pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
+	pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
 
 	if (flags & PM_PWM_LUT_REVERSE)
-		pwm->pwm_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
+		pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
 	if (flags & PM_PWM_LUT_RAMP_UP)
-		pwm->pwm_ctl[2] |= PM8XXX_PWM_RAMP_UP;
+		pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
 	if (flags & PM_PWM_LUT_LOOP)
-		pwm->pwm_ctl[2] |= PM8XXX_PWM_LOOP_EN;
+		pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
 }
 
 static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
 {
 	u8	mask, val;
 
-	val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
-		& PM8XXX_PWM_CLK_SEL_MASK;
-	val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
-		& PM8XXX_PWM_PREDIVIDE_MASK;
-	val |= pwm->period.pre_div_exp & PM8XXX_PWM_M_MASK;
-	mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
-		PM8XXX_PWM_M_MASK;
-	pm8xxx_pwm_save(&pwm->pwm_ctl[4], mask, val);
+	if (pwm_chip->is_lpg_supported) {
+		val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
+			& PM8XXX_LPG_PWM_CLK_SEL_MASK;
+		val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
+			& PM8XXX_LPG_PWM_PREDIVIDE_MASK;
+		val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
+		mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
+			PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
 
-	val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
-	mask = PM8XXX_PWM_SIZE_9_BIT;
-	pm8xxx_pwm_save(&pwm->pwm_ctl[5], mask, val);
+		val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
+		mask = PM8XXX_PWM_SIZE_9_BIT;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+	} else {
+		val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
+			& PM8XXX_PWM_CLK_SEL_MASK;
+		val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
+			& PM8XXX_PWM_PREDIVIDE_MASK;
+		val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
+				& PM8XXX_PWM_M_MASK;
+		val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
+			<< PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
+
+		mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
+			PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
+		pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+	}
 }
 
 static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
 {
 	u8	mask, val;
 
-	pwm->pwm_ctl[3] = pwm->pwm_value;
-
-	val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
-	mask = PM8XXX_PWM_VALUE_BIT8;
-	pm8xxx_pwm_save(&pwm->pwm_ctl[4], mask, val);
+	if (pwm_chip->is_lpg_supported) {
+		val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
+		pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
+		mask = PM8XXX_PWM_VALUE_BIT8;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+	} else {
+		val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
+		pwm->pwm_ctl2 = pwm->pwm_value;
+		mask = PM8XXX_PWM_VALUE_BIT0;
+		pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+	}
 }
 
 static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
@@ -468,7 +548,7 @@
 	val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
 
 	mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
-	pm8xxx_pwm_save(&pwm->pwm_ctl[0], mask, val);
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
 }
 
 static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
@@ -477,7 +557,7 @@
 	int	i, pause_cnt, time_cnt;
 	u8	mask, val;
 
-	time_cnt = (pwm->pwm_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
+	time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
 				>> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
 	if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
 		pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
@@ -495,7 +575,7 @@
 	}
 
 	mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
-	pm8xxx_pwm_save(&pwm->pwm_ctl[5], mask, val);
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
 
 	if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
 		/* Linear search for pause time */
@@ -513,10 +593,35 @@
 	}
 
 	mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
-	pm8xxx_pwm_save(&pwm->pwm_ctl[6], mask, val);
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
 }
 
-static int pm8xxx_pwm_write(struct pwm_device *pwm, int start, int end)
+static int pm8xxx_pwm_write(struct pwm_device *pwm)
+{
+	int rc = 0;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			   SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
+			   pwm->pwm_ctl1);
+	if (rc) {
+		pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
+							rc, pwm->pwm_id);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			   SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
+			   pwm->pwm_ctl2);
+	if (rc) {
+		pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
+							rc, pwm->pwm_id);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
 {
 	int	i, rc;
 
@@ -524,7 +629,7 @@
 	for (i = end - 1; i >= start; i--) {
 		rc = pm8xxx_writeb(pwm->chip->dev->parent,
 				   SSBI_REG_ADDR_LPG_CTL(i),
-				   pwm->pwm_ctl[i]);
+				   pwm->pwm_lpg_ctl[i]);
 		if (rc) {
 			pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
 			return rc;
@@ -543,10 +648,10 @@
 			     lut->lut_hi_index, lut->flags);
 	pm8xxx_pwm_save_duty_time(pwm, lut);
 	pm8xxx_pwm_save_pause(pwm, lut);
-	pm8xxx_pwm_save(&pwm->pwm_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
 
 	pm8xxx_pwm_bank_sel(pwm);
-	rc = pm8xxx_pwm_write(pwm, 0, 7);
+	rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
 
 	return rc;
 }
@@ -561,16 +666,17 @@
 {
 	struct pwm_device	*pwm;
 
-	if (pwm_id > PM8XXX_PWM_CHANNELS || pwm_id < 0) {
-		pr_err("Invalid pwm_id: %d with %s\n",
-		       pwm_id, label ? label : ".");
-		return ERR_PTR(-EINVAL);
-	}
 	if (pwm_chip == NULL) {
 		pr_err("No pwm_chip\n");
 		return ERR_PTR(-ENODEV);
 	}
 
+	if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
+		pr_err("Invalid pwm_id: %d with %s\n",
+		       pwm_id, label ? label : ".");
+		return ERR_PTR(-EINVAL);
+	}
+
 	mutex_lock(&pwm_chip->pwm_mutex);
 	pwm = &pwm_chip->pwm_dev[pwm_id];
 	if (!pwm->in_use) {
@@ -598,13 +704,17 @@
 
 	mutex_lock(&pwm->chip->pwm_mutex);
 	if (pwm->in_use) {
-		pm8xxx_pwm_bank_sel(pwm);
-		pm8xxx_pwm_start(pwm, 0, 0);
-
+		if (pwm_chip->is_lpg_supported) {
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 0, 0);
+		} else {
+			pm8xxx_pwm_disable(pwm);
+		}
 		pwm->in_use = 0;
 		pwm->label = NULL;
 	}
-	pm8xxx_pwm_bank_enable(pwm, 0);
+	if (pwm_chip->is_lpg_supported)
+		pm8xxx_pwm_bank_enable(pwm, 0);
 	mutex_unlock(&pwm->chip->pwm_mutex);
 }
 EXPORT_SYMBOL_GPL(pwm_free);
@@ -618,7 +728,7 @@
 int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
 {
 	struct pm8xxx_pwm_period *period;
-	int	rc;
+	int	rc = 0;
 
 	if (pwm == NULL || IS_ERR(pwm) ||
 		duty_us > period_us ||
@@ -649,11 +759,16 @@
 
 	pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
 	pm8xxx_pwm_save_pwm_value(pwm);
-	pm8xxx_pwm_save(&pwm->pwm_ctl[1],
-			PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
 
-	pm8xxx_pwm_bank_sel(pwm);
-	rc = pm8xxx_pwm_write(pwm, 1, 6);
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+				PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
 
 	pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
 		 (unsigned)duty_us, (unsigned)period_us,
@@ -671,7 +786,7 @@
  */
 int pwm_enable(struct pwm_device *pwm)
 {
-	int	rc;
+	int	rc = 0;
 
 	if (pwm == NULL || IS_ERR(pwm)) {
 		pr_err("Invalid pwm handle\n");
@@ -687,10 +802,13 @@
 		pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
 		rc = -EINVAL;
 	} else {
-		rc = pm8xxx_pwm_bank_enable(pwm, 1);
-
-		pm8xxx_pwm_bank_sel(pwm);
-		pm8xxx_pwm_start(pwm, 1, 0);
+		if (pwm_chip->is_lpg_supported) {
+			rc = pm8xxx_pwm_bank_enable(pwm, 1);
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 1, 0);
+		} else {
+			pm8xxx_pwm_enable(pwm);
+		}
 	}
 	mutex_unlock(&pwm->chip->pwm_mutex);
 	return rc;
@@ -710,10 +828,13 @@
 
 	mutex_lock(&pwm->chip->pwm_mutex);
 	if (pwm->in_use) {
-		pm8xxx_pwm_bank_sel(pwm);
-		pm8xxx_pwm_start(pwm, 0, 0);
-
-		pm8xxx_pwm_bank_enable(pwm, 0);
+		if (pwm_chip->is_lpg_supported) {
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 0, 0);
+			pm8xxx_pwm_bank_enable(pwm, 0);
+		} else {
+			pm8xxx_pwm_disable(pwm);
+		}
 	}
 	mutex_unlock(&pwm->chip->pwm_mutex);
 }
@@ -748,8 +869,14 @@
 	pwm->period.pre_div_exp = period->pre_div_exp;
 
 	pm8xxx_pwm_save_period(pwm);
-	pm8xxx_pwm_bank_sel(pwm);
-	rc = pm8xxx_pwm_write(pwm, 4, 6);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
 
 out_unlock:
 	mutex_unlock(&pwm->chip->pwm_mutex);
@@ -784,11 +911,15 @@
 	pwm->pwm_value = pwm_value;
 
 	pm8xxx_pwm_save_pwm_value(pwm);
-	pm8xxx_pwm_save(&pwm->pwm_ctl[1],
-			PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
 
-	pm8xxx_pwm_bank_sel(pwm);
-	rc = pm8xxx_pwm_write(pwm, 1, 6);
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+				PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
 
 	if (rc)
 		pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
@@ -832,6 +963,12 @@
 		pr_err("No pwm_chip\n");
 		return -ENODEV;
 	}
+
+	if (pwm->chip->is_lpg_supported == 0) {
+		pr_err("LPG module isn't supported\n");
+		return -EINVAL;
+	}
+
 	if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
 		pr_err("Wrong LUT size or index\n");
 		return -EINVAL;
@@ -904,6 +1041,10 @@
 		pr_err("No pwm_chip\n");
 		return -ENODEV;
 	}
+	if (pwm->chip->is_lpg_supported == 0) {
+		pr_err("LPG module isn't supported\n");
+		return -EINVAL;
+	}
 
 	mutex_lock(&pwm->chip->pwm_mutex);
 	if (start) {
@@ -940,7 +1081,7 @@
 	struct device		*dev;
 	struct dentry		*dent;
 
-	struct pm8xxx_pwm_user	user[PM8XXX_PWM_CHANNELS];
+	struct pm8xxx_pwm_user	*user;
 };
 
 static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
@@ -1101,6 +1242,14 @@
 		return -ENOMEM;
 	}
 
+	dbgdev->user = kcalloc(pwm_chip->pwm_channels,
+				sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
+	if (dbgdev->user == NULL) {
+		pr_err("kcalloc() failed.\n");
+		kfree(dbgdev);
+		return -ENOMEM;
+	}
+
 	mutex_init(&dbgdev->dbg_mutex);
 
 	dbgdev->dev = dev;
@@ -1113,7 +1262,7 @@
 
 	dbgdev->dent = dent;
 
-	for (i = 0; i < PM8XXX_PWM_CHANNELS; i++) {
+	for (i = 0; i < pwm_chip->pwm_channels; i++) {
 		char pwm_ch[] = "0";
 
 		pwm_ch[0] = '0' + i;
@@ -1160,6 +1309,7 @@
 static int __devexit pm8xxx_pwm_dbg_remove(void)
 {
 	if (pmic_dbg_device) {
+		kfree(pmic_dbg_device->user);
 		debugfs_remove_recursive(pmic_dbg_device->dent);
 		kfree(pmic_dbg_device);
 	}
@@ -1184,6 +1334,7 @@
 {
 	struct pm8xxx_pwm_chip	*chip;
 	int	i;
+	enum pm8xxx_version version;
 
 	chip = kzalloc(sizeof *chip, GFP_KERNEL);
 	if (chip == NULL) {
@@ -1191,15 +1342,39 @@
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < PM8XXX_PWM_CHANNELS; i++) {
-		chip->pwm_dev[i].pwm_id = i;
-		chip->pwm_dev[i].chip = chip;
-	}
-
 	mutex_init(&chip->pwm_mutex);
 
 	chip->dev = &pdev->dev;
 	pwm_chip = chip;
+
+	version = pm8xxx_get_version(chip->dev->parent);
+
+	if (version == PM8XXX_VERSION_8921 ||
+			version == PM8XXX_VERSION_8058) {
+		chip->is_lpg_supported = 1;
+	}
+	if (chip->is_lpg_supported) {
+		chip->pwm_channels = PM8XXX_LPG_PWM_CHANNELS;
+		chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
+	} else {
+		chip->pwm_channels = PM8XXX_PWM_CHANNELS;
+		chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
+	}
+
+	chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
+								GFP_KERNEL);
+	if (chip->pwm_dev == NULL) {
+		pr_err("kcalloc() failed.\n");
+		mutex_destroy(&chip->pwm_mutex);
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < chip->pwm_channels; i++) {
+		chip->pwm_dev[i].pwm_id = i;
+		chip->pwm_dev[i].chip = chip;
+	}
+
 	platform_set_drvdata(pdev, chip);
 
 	if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
@@ -1214,6 +1389,7 @@
 	struct pm8xxx_pwm_chip	*chip = dev_get_drvdata(pdev->dev.parent);
 
 	pm8xxx_pwm_dbg_remove();
+	kfree(chip->pwm_dev);
 	mutex_destroy(&chip->pwm_mutex);
 	platform_set_drvdata(pdev, NULL);
 	kfree(chip);