mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
pwm: pwm-qti-lpg: Add LUT mode to support modulated PWM output
LPG module supports outputting the pattern programmed in Look-up table (LUT) in addition to supporting PWM output. Add support for configuring LPG to operate in LUT mode and configure LUT patterns per LPG channel. Change-Id: I6352af136c4916f8b6bc17f1b8374ce1d421c10b Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
This commit is contained in:
parent
51d5b4b87d
commit
f1006f3251
@ -11,26 +11,137 @@ device module in Qualcomm Technologies, Inc. PMIC chips.
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: Register base and length for LPG modules. The length
|
||||
varies based on the number of channels available in
|
||||
the PMIC chips.
|
||||
Definition: Register base and length for LPG and LUT modules. LPG size
|
||||
or length available per channel varies depending on the
|
||||
number of channels in PMIC.
|
||||
|
||||
- reg-names:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: The name of the register defined in the reg property.
|
||||
It must be "lpg-base".
|
||||
It must have "lpg-base", "lut-base" is optional but
|
||||
it's required if any LPG channels support LUT mode.
|
||||
|
||||
- #pwm-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: See Documentation/devicetree/bindings/pwm/pwm.txt;
|
||||
Definition: The number of cells in "pwms" property specified in
|
||||
PWM user nodes. It should be 2. The first cell is
|
||||
the PWM channel ID indexed from 0, and the second
|
||||
cell is the PWM default period in nanoseconds.
|
||||
|
||||
- qcom,lut-patterns:
|
||||
Usage: optional
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: Duty ratios in percentages for LPG working at LUT mode.
|
||||
These duty ratios will be translated into PWM values
|
||||
and stored in LUT module. The LUT module has resource
|
||||
to store 47 PWM values at max and shared for all LPG
|
||||
channels. This property is required if any LPG channels
|
||||
support LUT mode.
|
||||
|
||||
Subnode is optional if LUT mode is not required, it's required if any LPG
|
||||
channels expected to be supported in LUT mode.
|
||||
|
||||
Subnode properties:
|
||||
Subnodes for each LPG channel (lpg@X) can be defined if any of the following
|
||||
parameters needs to be configured for that channel.
|
||||
|
||||
- qcom,lpg-chan-id:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: The LPG channel's hardware ID indexed from 1. Allowed
|
||||
range is 1 - 8. Maximum value depends on the number of
|
||||
channels supported on PMIC.
|
||||
|
||||
- qcom,ramp-step-ms:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: The step duration in milliseconds for LPG staying at each
|
||||
duty specified in the LUT pattern. Allowed range is
|
||||
1 - 511.
|
||||
|
||||
- qcom,ramp-high-index:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: The high index of the LUT pattern where LPG ends up
|
||||
ramping to. Allowed range is 1 - 47.
|
||||
|
||||
- qcom,ramp-low-index:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: The low index of the LUT pattern from where LPG begins
|
||||
ramping from. Allowed range is 0 - 46.
|
||||
|
||||
- qcom,ramp-from-low-to-high:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: The flag to specify the LPG ramping direction. The ramping
|
||||
direction is from low index to high index of the LUT
|
||||
pattern if it's specified.
|
||||
|
||||
- qcom,ramp-pattern-repeat:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: The flag to specify if LPG would be ramping with the LUT
|
||||
pattern repeatedly.
|
||||
|
||||
- qcom,ramp-toggle:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: The flag to specify if LPG would toggle the LUT pattern
|
||||
in ramping. If toggling enabled, LPG would return to the
|
||||
low index when high index is reached, or return to the high
|
||||
index when low index is reached.
|
||||
|
||||
- qcom,ramp-pause-hi-count:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: The step count that LPG stop the output when it ramped up
|
||||
to the high index of the LUT.
|
||||
|
||||
- qcom,ramp-pause-lo-count:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: The step count that LPG stop the output when it ramped up
|
||||
to the low index of the LUT.
|
||||
Example:
|
||||
|
||||
pmi8998_lpg: lpg@b100 {
|
||||
compatible = "qcom,pwm-lpg";
|
||||
reg = <0xb100 0x600>;
|
||||
reg-names = "lpg-base";
|
||||
reg = <0xb100 0x600>, <0xb000 0x100>;
|
||||
reg-names = "lpg-base", "lut-base";
|
||||
#pwm-cells = <2>;
|
||||
qcom,lut-patterns = <0 14 28 42 56 70 84 100
|
||||
100 84 70 56 42 28 14 0>;
|
||||
lpg@3 {
|
||||
qcom,lpg-chan-id = <3>;
|
||||
qcom,ramp-step-ms = <200>;
|
||||
qcom,ramp-pause-hi-count = <10>;
|
||||
qcom,ramp-pause-lo-count = <10>;
|
||||
qcom,ramp-low-index = <0>;
|
||||
qcom,ramp-high-index = <15>;
|
||||
qcom,ramp-from-low-to-high;
|
||||
qcom,ramp-pattern-repeat;
|
||||
};
|
||||
lpg@4 {
|
||||
qcom,lpg-chan-id = <4>;
|
||||
qcom,ramp-step-ms = <200>;
|
||||
qcom,ramp-pause-hi-count = <10>;
|
||||
qcom,ramp-pause-lo-count = <10>;
|
||||
qcom,ramp-low-index = <0>;
|
||||
qcom,ramp-high-index = <15>;
|
||||
qcom,ramp-from-low-to-high;
|
||||
qcom,ramp-pattern-repeat;
|
||||
};
|
||||
lpg@5 {
|
||||
qcom,lpg-chan-id = <5>;
|
||||
qcom,ramp-step-ms = <200>;
|
||||
qcom,ramp-pause-hi-count = <10>;
|
||||
qcom,ramp-pause-lo-count = <10>;
|
||||
qcom,ramp-low-index = <0>;
|
||||
qcom,ramp-high-index = <15>;
|
||||
qcom,ramp-from-low-to-high;
|
||||
qcom,ramp-pattern-repeat;
|
||||
};
|
||||
};
|
||||
|
@ -24,11 +24,16 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define REG_SIZE_PER_LPG 0x100
|
||||
#define LPG_BASE "lpg-base"
|
||||
#define LUT_BASE "lut-base"
|
||||
|
||||
/* LPG module registers */
|
||||
#define REG_LPG_PERPH_SUBTYPE 0x05
|
||||
#define REG_LPG_PATTERN_CONFIG 0x40
|
||||
#define REG_LPG_PWM_SIZE_CLK 0x41
|
||||
#define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42
|
||||
#define REG_LPG_PWM_TYPE_CONFIG 0x43
|
||||
@ -36,16 +41,29 @@
|
||||
#define REG_LPG_PWM_VALUE_MSB 0x45
|
||||
#define REG_LPG_ENABLE_CONTROL 0x46
|
||||
#define REG_LPG_PWM_SYNC 0x47
|
||||
#define REG_LPG_RAMP_STEP_DURATION_LSB 0x50
|
||||
#define REG_LPG_RAMP_STEP_DURATION_MSB 0x51
|
||||
#define REG_LPG_PAUSE_HI_MULTIPLIER 0x52
|
||||
#define REG_LPG_PAUSE_LO_MULTIPLIER 0x54
|
||||
#define REG_LPG_HI_INDEX 0x56
|
||||
#define REG_LPG_LO_INDEX 0x57
|
||||
|
||||
/* REG_LPG_PATTERN_CONFIG */
|
||||
#define LPG_PATTERN_EN_PAUSE_LO BIT(0)
|
||||
#define LPG_PATTERN_EN_PAUSE_HI BIT(1)
|
||||
#define LPG_PATTERN_RAMP_TOGGLE BIT(2)
|
||||
#define LPG_PATTERN_REPEAT BIT(3)
|
||||
#define LPG_PATTERN_RAMP_LO_TO_HI BIT(4)
|
||||
|
||||
/* REG_LPG_PERPH_SUBTYPE */
|
||||
#define SUBTYPE_PWM 0x0b
|
||||
#define SUBTYPE_LPG_LITE 0x11
|
||||
|
||||
/* REG_LPG_PWM_SIZE_CLK */
|
||||
#define LPG_PWM_SIZE_MASK_LPG BIT(4)
|
||||
#define LPG_PWM_SIZE_MASK_PWM BIT(2)
|
||||
#define LPG_PWM_SIZE_SHIFT_LPG 4
|
||||
#define LPG_PWM_SIZE_SHIFT_PWM 2
|
||||
#define LPG_PWM_SIZE_LPG_MASK BIT(4)
|
||||
#define LPG_PWM_SIZE_PWM_MASK BIT(2)
|
||||
#define LPG_PWM_SIZE_LPG_SHIFT 4
|
||||
#define LPG_PWM_SIZE_PWM_SHIFT 2
|
||||
#define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0)
|
||||
|
||||
/* REG_LPG_PWM_FREQ_PREDIV_CLK */
|
||||
@ -64,6 +82,7 @@
|
||||
|
||||
/* REG_LPG_ENABLE_CONTROL */
|
||||
#define LPG_EN_LPG_OUT_BIT BIT(7)
|
||||
#define LPG_EN_LPG_OUT_SHIFT 7
|
||||
#define LPG_PWM_SRC_SELECT_MASK BIT(2)
|
||||
#define LPG_PWM_SRC_SELECT_SHIFT 2
|
||||
#define LPG_EN_RAMP_GEN_MASK BIT(1)
|
||||
@ -77,9 +96,18 @@
|
||||
#define NUM_CLK_PREDIV 4
|
||||
#define NUM_PWM_EXP 8
|
||||
|
||||
enum {
|
||||
#define LPG_HI_LO_IDX_MASK GENMASK(5, 0)
|
||||
|
||||
/* LUT module registers */
|
||||
#define REG_LPG_LUT_1_LSB 0x42
|
||||
#define REG_LPG_LUT_RAMP_CONTROL 0xc8
|
||||
|
||||
#define LPG_LUT_VALUE_MSB_MASK BIT(0)
|
||||
#define LPG_LUT_COUNT_MAX 47
|
||||
|
||||
enum lpg_src {
|
||||
LUT_PATTERN = 0,
|
||||
PWM_OUTPUT,
|
||||
PWM_VALUE,
|
||||
};
|
||||
|
||||
static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
|
||||
@ -87,6 +115,19 @@ static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000};
|
||||
static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
|
||||
static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
|
||||
struct lpg_ramp_config {
|
||||
u16 step_ms;
|
||||
u8 pause_hi_count;
|
||||
u8 pause_lo_count;
|
||||
u8 hi_idx;
|
||||
u8 lo_idx;
|
||||
bool ramp_dir_low_to_hi;
|
||||
bool pattern_repeat;
|
||||
bool toggle;
|
||||
u32 *pattern;
|
||||
u32 pattern_length;
|
||||
};
|
||||
|
||||
struct lpg_pwm_config {
|
||||
u32 pwm_size;
|
||||
u32 pwm_clk;
|
||||
@ -96,13 +137,23 @@ struct lpg_pwm_config {
|
||||
u32 best_period_ns;
|
||||
};
|
||||
|
||||
struct qpnp_lpg_lut {
|
||||
struct qpnp_lpg_chip *chip;
|
||||
struct mutex lock;
|
||||
u32 reg_base;
|
||||
u32 *pattern; /* patterns in percentage */
|
||||
};
|
||||
|
||||
struct qpnp_lpg_channel {
|
||||
struct qpnp_lpg_chip *chip;
|
||||
struct lpg_pwm_config pwm_config;
|
||||
struct lpg_ramp_config ramp_config;
|
||||
u32 lpg_idx;
|
||||
u32 reg_base;
|
||||
u32 max_pattern_length;
|
||||
u8 src_sel;
|
||||
u8 subtype;
|
||||
bool lut_written;
|
||||
int current_period_ns;
|
||||
int current_duty_ns;
|
||||
};
|
||||
@ -112,6 +163,7 @@ struct qpnp_lpg_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct qpnp_lpg_channel *lpgs;
|
||||
struct qpnp_lpg_lut *lut;
|
||||
struct mutex bus_lock;
|
||||
u32 num_lpgs;
|
||||
};
|
||||
@ -163,6 +215,36 @@ static int qpnp_lpg_masked_write(struct qpnp_lpg_channel *lpg,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_lut_write(struct qpnp_lpg_lut *lut, u16 addr, u8 val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&lut->chip->bus_lock);
|
||||
rc = regmap_write(lut->chip->regmap, lut->reg_base + addr, val);
|
||||
if (rc < 0)
|
||||
dev_err(lut->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
|
||||
lut->reg_base + addr, val, rc);
|
||||
mutex_unlock(&lut->chip->bus_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_lut_masked_write(struct qpnp_lpg_lut *lut,
|
||||
u16 addr, u8 mask, u8 val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&lut->chip->bus_lock);
|
||||
rc = regmap_update_bits(lut->chip->regmap, lut->reg_base + addr,
|
||||
mask, val);
|
||||
if (rc < 0)
|
||||
dev_err(lut->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
|
||||
lut->reg_base + addr, val, mask, rc);
|
||||
mutex_unlock(&lut->chip->bus_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
|
||||
struct pwm_device *pwm)
|
||||
{
|
||||
@ -228,11 +310,11 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
|
||||
/* pwm_clk_idx is 1 bit lower than the register value */
|
||||
pwm_clk_idx += 1;
|
||||
if (lpg->subtype == SUBTYPE_PWM) {
|
||||
shift = LPG_PWM_SIZE_SHIFT_PWM;
|
||||
mask = LPG_PWM_SIZE_MASK_PWM;
|
||||
shift = LPG_PWM_SIZE_PWM_SHIFT;
|
||||
mask = LPG_PWM_SIZE_PWM_MASK;
|
||||
} else {
|
||||
shift = LPG_PWM_SIZE_SHIFT_LPG;
|
||||
mask = LPG_PWM_SIZE_MASK_LPG;
|
||||
shift = LPG_PWM_SIZE_LPG_SHIFT;
|
||||
mask = LPG_PWM_SIZE_LPG_MASK;
|
||||
}
|
||||
|
||||
val = pwm_size_idx << shift | pwm_clk_idx;
|
||||
@ -253,6 +335,9 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (lpg->src_sel == LUT_PATTERN)
|
||||
return 0;
|
||||
|
||||
val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
|
||||
rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
|
||||
if (rc < 0) {
|
||||
@ -281,6 +366,145 @@ static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
|
||||
unsigned int *pattern, unsigned int length)
|
||||
{
|
||||
struct qpnp_lpg_lut *lut = lpg->chip->lut;
|
||||
int i, rc = 0;
|
||||
u16 full_duty_value, pwm_values[LPG_LUT_COUNT_MAX + 1] = {0};
|
||||
u8 lsb, msb, addr;
|
||||
|
||||
if (length > lpg->max_pattern_length) {
|
||||
dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
|
||||
length, lpg->max_pattern_length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Program LUT pattern */
|
||||
mutex_lock(&lut->lock);
|
||||
addr = REG_LPG_LUT_1_LSB + lpg->ramp_config.lo_idx * 2;
|
||||
for (i = 0; i < length; i++) {
|
||||
full_duty_value = 1 << lpg->pwm_config.pwm_size;
|
||||
pwm_values[i] = pattern[i] * full_duty_value / 100;
|
||||
|
||||
if (unlikely(pwm_values[i] > full_duty_value)) {
|
||||
dev_err(lpg->chip->dev, "PWM value %d exceed the max %d\n",
|
||||
pwm_values[i], full_duty_value);
|
||||
rc = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (pwm_values[i] == full_duty_value)
|
||||
pwm_values[i] = full_duty_value - 1;
|
||||
|
||||
lsb = pwm_values[i] & 0xff;
|
||||
msb = pwm_values[i] >> 8;
|
||||
rc = qpnp_lut_write(lut, addr++, lsb);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write NO.%d LUT pattern LSB (%d) failed, rc=%d",
|
||||
i, lsb, rc);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rc = qpnp_lut_masked_write(lut, addr++,
|
||||
LPG_LUT_VALUE_MSB_MASK, msb);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write NO.%d LUT pattern MSB (%d) failed, rc=%d",
|
||||
i, msb, rc);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
lpg->ramp_config.pattern_length = length;
|
||||
unlock:
|
||||
mutex_unlock(&lut->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_lpg_set_ramp_config(struct qpnp_lpg_channel *lpg)
|
||||
{
|
||||
struct lpg_ramp_config *ramp = &lpg->ramp_config;
|
||||
u8 lsb, msb, addr, mask, val;
|
||||
int rc = 0;
|
||||
|
||||
/* Set ramp step duration */
|
||||
lsb = ramp->step_ms & 0xff;
|
||||
msb = ramp->step_ms >> 8;
|
||||
addr = REG_LPG_RAMP_STEP_DURATION_LSB;
|
||||
rc = qpnp_lpg_write(lpg, addr, lsb);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_LSB failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
rc = qpnp_lpg_write(lpg, addr + 1, msb);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_MSB failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set hi_idx and lo_idx */
|
||||
rc = qpnp_lpg_masked_write(lpg, REG_LPG_HI_INDEX,
|
||||
LPG_HI_LO_IDX_MASK, ramp->hi_idx);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write LPG_HI_IDX failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = qpnp_lpg_masked_write(lpg, REG_LPG_LO_INDEX,
|
||||
LPG_HI_LO_IDX_MASK, ramp->lo_idx);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write LPG_LO_IDX failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set pause_hi/lo_count */
|
||||
rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_HI_MULTIPLIER,
|
||||
ramp->pause_hi_count);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write LPG_PAUSE_HI_MULTIPLIER failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_LO_MULTIPLIER,
|
||||
ramp->pause_lo_count);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write LPG_PAUSE_LO_MULTIPLIER failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set LPG_PATTERN_CONFIG */
|
||||
addr = REG_LPG_PATTERN_CONFIG;
|
||||
mask = LPG_PATTERN_EN_PAUSE_LO | LPG_PATTERN_EN_PAUSE_HI
|
||||
| LPG_PATTERN_RAMP_TOGGLE | LPG_PATTERN_REPEAT
|
||||
| LPG_PATTERN_RAMP_LO_TO_HI;
|
||||
val = 0;
|
||||
if (ramp->pause_lo_count != 0)
|
||||
val |= LPG_PATTERN_EN_PAUSE_LO;
|
||||
if (ramp->pause_hi_count != 0)
|
||||
val |= LPG_PATTERN_EN_PAUSE_HI;
|
||||
if (ramp->ramp_dir_low_to_hi)
|
||||
val |= LPG_PATTERN_RAMP_LO_TO_HI;
|
||||
if (ramp->pattern_repeat)
|
||||
val |= LPG_PATTERN_REPEAT;
|
||||
if (ramp->toggle)
|
||||
val |= LPG_PATTERN_RAMP_TOGGLE;
|
||||
|
||||
rc = qpnp_lpg_masked_write(lpg, addr, mask, val);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Write LPG_PATTERN_CONFIG failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __qpnp_lpg_calc_pwm_period(int period_ns,
|
||||
struct lpg_pwm_config *pwm_config)
|
||||
{
|
||||
@ -397,17 +621,202 @@ static int qpnp_lpg_pwm_config(struct pwm_chip *pwm_chip,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (period_ns != lpg->current_period_ns)
|
||||
if (period_ns != lpg->current_period_ns) {
|
||||
__qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
|
||||
|
||||
/* program LUT if PWM period is changed */
|
||||
if (lpg->src_sel == LUT_PATTERN) {
|
||||
rc = qpnp_lpg_set_lut_pattern(lpg,
|
||||
lpg->ramp_config.pattern,
|
||||
lpg->ramp_config.pattern_length);
|
||||
if (rc < 0) {
|
||||
dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
return rc;
|
||||
}
|
||||
lpg->lut_written = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (period_ns != lpg->current_period_ns ||
|
||||
duty_ns != lpg->current_duty_ns)
|
||||
__qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
|
||||
|
||||
rc = qpnp_lpg_set_pwm_config(lpg);
|
||||
if (rc < 0)
|
||||
if (rc < 0) {
|
||||
dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
lpg->current_period_ns = period_ns;
|
||||
lpg->current_duty_ns = duty_ns;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
|
||||
{
|
||||
struct qpnp_lpg_chip *chip = lpg->chip;
|
||||
struct qpnp_lpg_lut *lut = chip->lut;
|
||||
u8 mask, val;
|
||||
int rc;
|
||||
|
||||
mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT |
|
||||
LPG_EN_RAMP_GEN_MASK;
|
||||
val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
|
||||
|
||||
if (lpg->src_sel == LUT_PATTERN)
|
||||
val |= 1 << LPG_EN_RAMP_GEN_SHIFT;
|
||||
|
||||
if (en)
|
||||
val |= 1 << LPG_EN_LPG_OUT_SHIFT;
|
||||
|
||||
rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Write LPG_ENABLE_CONTROL failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (lpg->src_sel == LUT_PATTERN && en) {
|
||||
mutex_lock(&lut->lock);
|
||||
val = 1 << lpg->lpg_idx;
|
||||
rc = qpnp_lut_write(lut, REG_LPG_LUT_RAMP_CONTROL, val);
|
||||
if (rc < 0)
|
||||
dev_err(chip->dev, "Write LPG_LUT_RAMP_CONTROL failed, rc=%d\n",
|
||||
rc);
|
||||
mutex_unlock(&lut->lock);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
|
||||
struct pwm_device *pwm, enum pwm_output_type output_type)
|
||||
{
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
enum lpg_src src_sel;
|
||||
int rc;
|
||||
|
||||
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
|
||||
if (lpg == NULL) {
|
||||
dev_err(pwm_chip->dev, "lpg not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (lpg->chip->lut == NULL) {
|
||||
pr_debug("lpg%d only support PWM mode\n", lpg->lpg_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
src_sel = (output_type == PWM_OUTPUT_MODULATED) ?
|
||||
LUT_PATTERN : PWM_VALUE;
|
||||
if (src_sel == lpg->src_sel)
|
||||
return 0;
|
||||
|
||||
if (src_sel == LUT_PATTERN) {
|
||||
/* program LUT if it's never been programmed */
|
||||
if (!lpg->lut_written) {
|
||||
rc = qpnp_lpg_set_lut_pattern(lpg,
|
||||
lpg->ramp_config.pattern,
|
||||
lpg->ramp_config.pattern_length);
|
||||
if (rc < 0) {
|
||||
dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
return rc;
|
||||
}
|
||||
lpg->lut_written = true;
|
||||
}
|
||||
|
||||
rc = qpnp_lpg_set_ramp_config(lpg);
|
||||
if (rc < 0) {
|
||||
dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
lpg->src_sel = src_sel;
|
||||
|
||||
if (pwm_is_enabled(pwm)) {
|
||||
rc = qpnp_lpg_pwm_src_enable(lpg, true);
|
||||
if (rc < 0) {
|
||||
dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qpnp_lpg_pwm_set_output_pattern(struct pwm_chip *pwm_chip,
|
||||
struct pwm_device *pwm, struct pwm_output_pattern *output_pattern)
|
||||
{
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
int rc = 0, i, period_ns, duty_ns;
|
||||
u32 *percentages;
|
||||
|
||||
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
|
||||
if (lpg == NULL) {
|
||||
dev_err(pwm_chip->dev, "lpg not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (output_pattern->num_entries > lpg->max_pattern_length) {
|
||||
dev_err(lpg->chip->dev, "pattern length %d shouldn't exceed %d\n",
|
||||
output_pattern->num_entries,
|
||||
lpg->max_pattern_length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
percentages = kcalloc(output_pattern->num_entries,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!percentages)
|
||||
return -ENOMEM;
|
||||
|
||||
period_ns = pwm_get_period(pwm);
|
||||
for (i = 0; i < output_pattern->num_entries; i++) {
|
||||
duty_ns = output_pattern->duty_pattern[i];
|
||||
if (duty_ns > period_ns) {
|
||||
dev_err(lpg->chip->dev, "duty %dns is larger than period %dns\n",
|
||||
duty_ns, period_ns);
|
||||
goto err;
|
||||
}
|
||||
/* Translate the pattern in duty_ns to percentage */
|
||||
if ((INT_MAX / duty_ns) < 100)
|
||||
percentages[i] = duty_ns / (period_ns / 100);
|
||||
else
|
||||
percentages[i] = (duty_ns * 100) / period_ns;
|
||||
}
|
||||
|
||||
rc = qpnp_lpg_set_lut_pattern(lpg, percentages,
|
||||
output_pattern->num_entries);
|
||||
if (rc < 0) {
|
||||
dev_err(lpg->chip->dev, "Set LUT pattern failed for LPG%d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
lpg->lut_written = true;
|
||||
memcpy(lpg->ramp_config.pattern, percentages,
|
||||
output_pattern->num_entries);
|
||||
lpg->ramp_config.hi_idx = lpg->ramp_config.lo_idx +
|
||||
output_pattern->num_entries - 1;
|
||||
if ((INT_MAX / period_ns) > output_pattern->cycles_per_duty)
|
||||
lpg->ramp_config.step_ms = output_pattern->cycles_per_duty *
|
||||
period_ns / NSEC_PER_MSEC;
|
||||
else
|
||||
lpg->ramp_config.step_ms = (period_ns / NSEC_PER_MSEC) *
|
||||
output_pattern->cycles_per_duty;
|
||||
|
||||
rc = qpnp_lpg_set_ramp_config(lpg);
|
||||
if (rc < 0)
|
||||
dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
err:
|
||||
kfree(percentages);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -417,7 +826,6 @@ static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip,
|
||||
{
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
int rc = 0;
|
||||
u8 mask, val;
|
||||
|
||||
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
|
||||
if (lpg == NULL) {
|
||||
@ -432,10 +840,7 @@ static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip,
|
||||
return rc;
|
||||
}
|
||||
|
||||
mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
|
||||
val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
|
||||
|
||||
rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
|
||||
rc = qpnp_lpg_pwm_src_enable(lpg, true);
|
||||
if (rc < 0)
|
||||
dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
@ -448,7 +853,6 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
|
||||
{
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
int rc;
|
||||
u8 mask, val;
|
||||
|
||||
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
|
||||
if (lpg == NULL) {
|
||||
@ -456,10 +860,7 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
|
||||
return;
|
||||
}
|
||||
|
||||
mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
|
||||
val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
|
||||
|
||||
rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
|
||||
rc = qpnp_lpg_pwm_src_enable(lpg, false);
|
||||
if (rc < 0) {
|
||||
dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
|
||||
lpg->lpg_idx, rc);
|
||||
@ -472,13 +873,32 @@ static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
|
||||
rc);
|
||||
}
|
||||
|
||||
static int qpnp_lpg_pwm_output_types_supported(struct pwm_chip *pwm_chip,
|
||||
struct pwm_device *pwm)
|
||||
{
|
||||
enum pwm_output_type type = PWM_OUTPUT_FIXED;
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
|
||||
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
|
||||
if (lpg == NULL) {
|
||||
dev_err(pwm_chip->dev, "lpg not found\n");
|
||||
return type;
|
||||
}
|
||||
|
||||
if (lpg->chip->lut != NULL)
|
||||
type |= PWM_OUTPUT_MODULATED;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
|
||||
{
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
struct lpg_pwm_config *cfg;
|
||||
struct lpg_ramp_config *ramp;
|
||||
struct pwm_device *pwm;
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < pwm_chip->npwm; i++) {
|
||||
pwm = &pwm_chip->pwms[i];
|
||||
@ -513,12 +933,39 @@ static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
|
||||
seq_printf(s, " pwm_value = %d\n", cfg->pwm_value);
|
||||
seq_printf(s, " Requested period: %dns, best period = %dns\n",
|
||||
pwm_get_period(pwm), cfg->best_period_ns);
|
||||
|
||||
ramp = &lpg->ramp_config;
|
||||
if (pwm_get_output_type(pwm) == PWM_OUTPUT_MODULATED) {
|
||||
seq_puts(s, " ramping duty percentages:");
|
||||
for (j = 0; j < ramp->pattern_length; j++)
|
||||
seq_printf(s, " %d", ramp->pattern[j]);
|
||||
seq_puts(s, "\n");
|
||||
seq_printf(s, " ramping time per step: %dms\n",
|
||||
ramp->step_ms);
|
||||
seq_printf(s, " ramping low index: %d\n",
|
||||
ramp->lo_idx);
|
||||
seq_printf(s, " ramping high index: %d\n",
|
||||
ramp->hi_idx);
|
||||
seq_printf(s, " ramping from low to high: %d\n",
|
||||
ramp->ramp_dir_low_to_hi);
|
||||
seq_printf(s, " ramping pattern repeat: %d\n",
|
||||
ramp->pattern_repeat);
|
||||
seq_printf(s, " ramping toggle: %d\n",
|
||||
ramp->toggle);
|
||||
seq_printf(s, " ramping pause count at low index: %d\n",
|
||||
ramp->pause_lo_count);
|
||||
seq_printf(s, " ramping pause count at high index: %d\n",
|
||||
ramp->pause_hi_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct pwm_ops qpnp_lpg_pwm_ops = {
|
||||
.config = qpnp_lpg_pwm_config,
|
||||
.get_output_type_supported = qpnp_lpg_pwm_output_types_supported,
|
||||
.set_output_type = qpnp_lpg_pwm_set_output_type,
|
||||
.set_output_pattern = qpnp_lpg_pwm_set_output_pattern,
|
||||
.enable = qpnp_lpg_pwm_enable,
|
||||
.disable = qpnp_lpg_pwm_disable,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
@ -529,15 +976,19 @@ static const struct pwm_ops qpnp_lpg_pwm_ops = {
|
||||
|
||||
static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct qpnp_lpg_channel *lpg;
|
||||
struct lpg_ramp_config *ramp;
|
||||
int rc = 0, i;
|
||||
u64 base, length;
|
||||
u32 base, length, lpg_chan_id, tmp;
|
||||
const __be32 *addr;
|
||||
|
||||
addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
|
||||
if (!addr) {
|
||||
dev_err(chip->dev, "Getting address failed\n");
|
||||
dev_err(chip->dev, "Get %s address failed\n", LPG_BASE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = be32_to_cpu(addr[0]);
|
||||
length = be32_to_cpu(addr[1]);
|
||||
|
||||
@ -551,7 +1002,7 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
|
||||
chip->lpgs[i].chip = chip;
|
||||
chip->lpgs[i].lpg_idx = i;
|
||||
chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
|
||||
chip->lpgs[i].src_sel = PWM_OUTPUT;
|
||||
chip->lpgs[i].src_sel = PWM_VALUE;
|
||||
rc = qpnp_lpg_read(&chip->lpgs[i], REG_LPG_PERPH_SUBTYPE,
|
||||
&chip->lpgs[i].subtype);
|
||||
if (rc < 0) {
|
||||
@ -560,7 +1011,142 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
|
||||
if (!addr) {
|
||||
pr_debug("NO LUT address assigned\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL);
|
||||
if (!chip->lut)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->lut->chip = chip;
|
||||
chip->lut->reg_base = be32_to_cpu(*addr);
|
||||
mutex_init(&chip->lut->lock);
|
||||
|
||||
rc = of_property_count_elems_of_size(chip->dev->of_node,
|
||||
"qcom,lut-patterns", sizeof(u32));
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Read qcom,lut-patterns failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
length = rc;
|
||||
if (length > LPG_LUT_COUNT_MAX) {
|
||||
dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n",
|
||||
length, LPG_LUT_COUNT_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->lut->pattern = devm_kcalloc(chip->dev, LPG_LUT_COUNT_MAX,
|
||||
sizeof(*chip->lut->pattern), GFP_KERNEL);
|
||||
if (!chip->lut->pattern)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32_array(chip->dev->of_node, "qcom,lut-patterns",
|
||||
chip->lut->pattern, length);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Get qcom,lut-patterns failed, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (of_get_available_child_count(chip->dev->of_node) == 0) {
|
||||
dev_err(chip->dev, "No ramp configuration for any LPG\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(chip->dev->of_node, child) {
|
||||
rc = of_property_read_u32(child, "qcom,lpg-chan-id",
|
||||
&lpg_chan_id);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Get qcom,lpg-chan-id failed for node %s, rc=%d\n",
|
||||
child->name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (lpg_chan_id > chip->num_lpgs) {
|
||||
dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n",
|
||||
lpg_chan_id, chip->num_lpgs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* lpg channel id is indexed from 1 in hardware */
|
||||
lpg = &chip->lpgs[lpg_chan_id - 1];
|
||||
ramp = &lpg->ramp_config;
|
||||
|
||||
rc = of_property_read_u32(child, "qcom,ramp-step-ms", &tmp);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "get qcom,ramp-step-ms failed for lpg%d, rc=%d\n",
|
||||
lpg_chan_id, rc);
|
||||
return rc;
|
||||
}
|
||||
ramp->step_ms = (u16)tmp;
|
||||
|
||||
rc = of_property_read_u32(child, "qcom,ramp-low-index", &tmp);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "get qcom,ramp-low-index failed for lpg%d, rc=%d\n",
|
||||
lpg_chan_id, rc);
|
||||
return rc;
|
||||
}
|
||||
ramp->lo_idx = (u8)tmp;
|
||||
if (ramp->lo_idx >= LPG_LUT_COUNT_MAX) {
|
||||
dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n",
|
||||
LPG_LUT_COUNT_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(child, "qcom,ramp-high-index", &tmp);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "get qcom,ramp-high-index failed for lpg%d, rc=%d\n",
|
||||
lpg_chan_id, rc);
|
||||
return rc;
|
||||
}
|
||||
ramp->hi_idx = (u8)tmp;
|
||||
|
||||
if (ramp->hi_idx > LPG_LUT_COUNT_MAX) {
|
||||
dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n",
|
||||
LPG_LUT_COUNT_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ramp->hi_idx <= ramp->lo_idx) {
|
||||
dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d)\n",
|
||||
ramp->hi_idx, ramp->lo_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ramp->pattern_length = ramp->hi_idx - ramp->lo_idx + 1;
|
||||
ramp->pattern = &chip->lut->pattern[ramp->lo_idx];
|
||||
lpg->max_pattern_length = ramp->pattern_length;
|
||||
|
||||
rc = of_property_read_u32(child,
|
||||
"qcom,ramp-pause-hi-count", &tmp);
|
||||
if (rc < 0)
|
||||
ramp->pause_hi_count = 0;
|
||||
else
|
||||
ramp->pause_hi_count = (u8)tmp;
|
||||
|
||||
rc = of_property_read_u32(child,
|
||||
"qcom,ramp-pause-lo-count", &tmp);
|
||||
if (rc < 0)
|
||||
ramp->pause_lo_count = 0;
|
||||
else
|
||||
ramp->pause_lo_count = (u8)tmp;
|
||||
|
||||
ramp->ramp_dir_low_to_hi = of_property_read_bool(child,
|
||||
"qcom,ramp-from-low-to-high");
|
||||
|
||||
ramp->pattern_repeat = of_property_read_bool(child,
|
||||
"qcom,ramp-pattern-repeat");
|
||||
|
||||
ramp->toggle = of_property_read_bool(child,
|
||||
"qcom,ramp-toggle");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qpnp_lpg_probe(struct platform_device *pdev)
|
||||
@ -584,7 +1170,7 @@ static int qpnp_lpg_probe(struct platform_device *pdev)
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
|
||||
rc);
|
||||
goto destroy;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
@ -596,11 +1182,11 @@ static int qpnp_lpg_probe(struct platform_device *pdev)
|
||||
rc = pwmchip_add(&chip->pwm_chip);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
|
||||
goto destroy;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
destroy:
|
||||
err_out:
|
||||
mutex_destroy(&chip->bus_lock);
|
||||
return rc;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user