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);