summaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-qpnp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-qpnp.c')
-rw-r--r--drivers/pwm/pwm-qpnp.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c
index 38b70e4e6edd..fecec1e2738c 100644
--- a/drivers/pwm/pwm-qpnp.c
+++ b/drivers/pwm/pwm-qpnp.c
@@ -1139,6 +1139,88 @@ static int qpnp_lpg_configure_lut_state(struct qpnp_pwm_chip *chip,
return rc;
}
+static int qpnp_lpg_configure_lut_states(struct qpnp_pwm_chip **chips,
+ int num, enum qpnp_lut_state state)
+{
+ struct qpnp_pwm_chip *chip;
+ struct qpnp_lpg_config *lpg_config;
+ u8 value1, value2, mask1, mask2;
+ u8 *reg1, *reg2;
+ u16 addr, addr1;
+ int rc, i;
+ bool test_enable;
+ u8 ramp_en = 0, ramp_mask = 0;
+
+
+ for (i = 0; i < num; i++) {
+ chip = chips[i];
+ lpg_config = &chip->lpg_config;
+ value1 = chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg1 = &chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg2 = &chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
+ mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+ QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK;
+ if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)
+ mask2 |= QPNP_EN_PWM_OUTPUT_MASK;
+
+ if (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE
+ && chip->revision == QPNP_LPG_REVISION_0) {
+ if (state == QPNP_LUT_ENABLE) {
+ QPNP_ENABLE_LUT_V0(value1);
+ value2 = QPNP_ENABLE_LPG_MODE(chip);
+ } else {
+ QPNP_DISABLE_LUT_V0(value1);
+ value2 = QPNP_DISABLE_LPG_MODE(chip);
+ }
+ mask1 = QPNP_RAMP_START_MASK;
+ addr1 = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_CONTROL);
+ } else if ((chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE
+ && chip->revision == QPNP_LPG_REVISION_1)
+ || chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) {
+ if (state == QPNP_LUT_ENABLE) {
+ QPNP_ENABLE_LUT_V1(value1,
+ lpg_config->lut_config.ramp_index);
+ value2 = QPNP_ENABLE_LPG_MODE(chip);
+ } else {
+ value2 = QPNP_DISABLE_LPG_MODE(chip);
+ }
+ mask1 = value1;
+ addr1 = lpg_config->lut_base_addr +
+ SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+ } else {
+ pr_err("Unsupported LPG subtype 0x%02x, revision 0x%02x\n",
+ chip->sub_type, chip->revision);
+ return -EINVAL;
+ }
+
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL);
+
+ if (chip->in_test_mode) {
+ test_enable = (state == QPNP_LUT_ENABLE) ? 1 : 0;
+ rc = qpnp_dtest_config(chip, test_enable);
+ if (rc)
+ pr_err("Failed to configure TEST mode\n");
+ }
+
+ rc = qpnp_lpg_save_and_write(value2, mask2, reg2,
+ addr, 1, chip);
+ if (rc)
+ return rc;
+
+ ramp_en |= value1;
+ ramp_mask |= mask1;
+ }
+
+ if (state == QPNP_LUT_ENABLE
+ || (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE
+ && chip->revision == QPNP_LPG_REVISION_0))
+ rc = qpnp_lpg_save_and_write(ramp_en, ramp_mask, reg1,
+ addr1, 1, chip);
+ return rc;
+}
+
static inline int qpnp_enable_pwm_mode(struct qpnp_pwm_chip *chip)
{
if (chip->pwm_config.supported_sizes == QPNP_PWM_SIZE_7_8_BIT)
@@ -1443,6 +1525,49 @@ static int qpnp_pwm_enable(struct pwm_chip *pwm_chip,
return rc;
}
+int pwm_enable_synchronized(struct pwm_device **pwms, size_t num)
+{
+ unsigned long *flags;
+ struct qpnp_pwm_chip **chips;
+ int rc = 0, i;
+
+ if (pwms == NULL || IS_ERR(pwms) || num == 0) {
+ pr_err("Invalid pwm handle or idx_len=0\n");
+ return -EINVAL;
+ }
+
+ flags = kzalloc(sizeof(unsigned long) * num, GFP_KERNEL);
+ if (!flags)
+ return -ENOMEM;
+
+ chips = kzalloc(sizeof(struct qpnp_pwm_chip *) * num, GFP_KERNEL);
+ if (!chips) {
+ kfree(flags);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ chips[i] = qpnp_pwm_from_pwm_dev(pwms[i]);
+ if (chips[i] == NULL) {
+ rc = -EINVAL;
+ goto err_failed;
+ }
+ spin_lock_irqsave(&chips[i]->lpg_lock, flags[i]);
+ }
+
+ rc = qpnp_lpg_configure_lut_states(chips, num, QPNP_LUT_ENABLE);
+
+err_failed:
+ while(i > 0) {
+ spin_unlock_irqrestore(&chips[i - 1]->lpg_lock, flags[i - 1]);
+ i--;
+ }
+ kfree(flags);
+ kfree(chips);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_enable_synchronized);
+
/**
* qpnp_pwm_disable - stop a PWM output toggling
* @pwm_chip: the PWM chip