regulator: qpnp-labibb: add support to configure PFM for LAB regulator

As per the hardware documentation, PFM needs to be disabled for
LAB regulator during slow start. When the display is turned off,
PFM needs to be disabled with the default current limit. When the
display is turned on, after VREG_OK interrupt fires, PFM needs to
be enabled after overriding the current limit. Add support for
it. Currently this is required only for pmicobalt.

While at it, fix the current limit configuration for LAB
regulator.

CRs-Fixed: 1024407
Change-Id: Icb3781ca31dd8474cfca077c52593dc69d011127
Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
This commit is contained in:
Subbaraman Narayanamurthy 2016-10-31 20:51:08 -07:00
parent 83dd0b0ca0
commit 08cbd79f44
4 changed files with 183 additions and 29 deletions

View file

@ -93,7 +93,18 @@ LAB subnode required properties:
1130, 1070, 1010, 960, 910.
- qcom,qpnp-lab-limit-maximum-current: The maximum inductor current limit in
mA of LAB regulator. Supported values
are 200, 400, 600 and 800.
are 200, 400, 600, 800, 1000, 1200,
1400 and 1600.
- interrupts: Specify the interrupts as per the interrupt
encoding.
Currently "lab-vreg-ok" is required for
LCD mode in pmicobalt. For AMOLED mode,
"lab-vreg-ok" is required only when SWIRE
control is enabled and skipping 2nd SWIRE
pulse is required in pmi8952/8996.
- interrupt-names: Interrupt names to match up 1-to-1 with
the interrupts specified in 'interrupts'
property.
LAB subnode optional properties:
@ -229,7 +240,8 @@ Example:
reg = <0xde00 0x100>;
reg-names = "lab";
interrupts = <0x3 0xde 0x0>;
interrupts = <0x3 0xde 0x0
IRQ_TYPE_EDGE_RISING>;
interrupt-names = "lab-vreg-ok";
regulator-name = "lab_reg";
@ -249,7 +261,7 @@ Example:
qcom,qpnp-lab-full-pull-down;
qcom,qpnp-lab-pull-down-enable;
qcom,qpnp-lab-switching-clock-frequency = <1600>;
qcom,qpnp-lab-limit-maximum-current = <800>;
qcom,qpnp-lab-limit-maximum-current = <1600>;
qcom,qpnp-lab-limit-max-current-enable;
qcom,qpnp-lab-ps-threshold = <40>;
qcom,qpnp-lab-ps-enable;

View file

@ -497,7 +497,7 @@
qcom,qpnp-lab-pull-down-enable;
qcom,qpnp-lab-switching-clock-frequency =
<1600>;
qcom,qpnp-lab-limit-maximum-current = <800>;
qcom,qpnp-lab-limit-maximum-current = <1600>;
qcom,qpnp-lab-limit-max-current-enable;
qcom,qpnp-lab-ps-threshold = <20>;
qcom,qpnp-lab-ps-enable;

View file

@ -555,6 +555,9 @@
regulator-min-microvolt = <4600000>;
regulator-max-microvolt = <6000000>;
interrupts = <0x3 0xde 0x0
IRQ_TYPE_EDGE_RISING>;
interrupt-names = "lab-vreg-ok";
qcom,qpnp-lab-min-voltage = <4600000>;
qcom,qpnp-lab-step-size = <100000>;
qcom,qpnp-lab-slew-rate = <5000>;
@ -569,7 +572,7 @@
qcom,qpnp-lab-pull-down-enable;
qcom,qpnp-lab-switching-clock-frequency =
<1600>;
qcom,qpnp-lab-limit-maximum-current = <800>;
qcom,qpnp-lab-limit-maximum-current = <1600>;
qcom,qpnp-lab-limit-max-current-enable;
qcom,qpnp-lab-ps-threshold = <20>;
qcom,qpnp-lab-ps-enable;

View file

@ -32,6 +32,7 @@
#define QPNP_LABIBB_REGULATOR_DRIVER_NAME "qcom,qpnp-labibb-regulator"
#define REG_REVISION_2 0x01
#define REG_PERPH_TYPE 0x04
#define QPNP_LAB_TYPE 0x24
@ -60,6 +61,7 @@
#define REG_LAB_PRECHARGE_CTL 0x5E
#define REG_LAB_SOFT_START_CTL 0x5F
#define REG_LAB_SPARE_CTL 0x60
#define REG_LAB_PFM_CTL 0x62
/* LAB register bits definitions */
@ -91,9 +93,9 @@
#define LAB_IBB_EN_RDY_EN BIT(7)
/* REG_LAB_CURRENT_LIMIT */
#define LAB_CURRENT_LIMIT_BITS 3
#define LAB_CURRENT_LIMIT_MASK ((1 << LAB_CURRENT_LIMIT_BITS) - 1)
#define LAB_CURRENT_LIMIT_EN BIT(7)
#define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0)
#define LAB_CURRENT_LIMIT_EN_BIT BIT(7)
#define LAB_OVERRIDE_CURRENT_MAX_BIT BIT(3)
/* REG_LAB_CURRENT_SENSE */
#define LAB_CURRENT_SENSE_GAIN_BITS 2
@ -129,6 +131,9 @@
#define LAB_SPARE_TOUCH_WAKE_BIT BIT(3)
#define LAB_SPARE_DISABLE_SCP_BIT BIT(0)
/* REG_LAB_PFM_CTL */
#define LAB_PFM_EN_BIT BIT(7)
/* IBB register offset definitions */
#define REG_IBB_REVISION4 0x03
#define REG_IBB_STATUS1 0x08
@ -344,6 +349,10 @@ static const int lab_current_limit_plan[] = {
400,
600,
800,
1000,
1200,
1400,
1600,
};
static const char * const lab_current_sense_plan[] = {
@ -471,6 +480,8 @@ struct qpnp_labibb {
struct pmic_revid_data *pmic_rev_id;
u16 lab_base;
u16 ibb_base;
u8 lab_dig_major;
u8 ibb_dig_major;
struct lab_regulator lab_vreg;
struct ibb_regulator ibb_vreg;
enum qpnp_labibb_mode mode;
@ -481,6 +492,7 @@ struct qpnp_labibb {
bool swire_control;
bool ttw_force_lab_on;
bool skip_2nd_swire_cmd;
bool pfm_enable;
u32 swire_2nd_cmd_delay;
u32 swire_ibb_ps_enable_delay;
};
@ -783,7 +795,7 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb,
if (of_property_read_bool(of_node,
"qcom,qpnp-lab-limit-max-current-enable"))
val |= LAB_CURRENT_LIMIT_EN;
val |= LAB_CURRENT_LIMIT_EN_BIT;
rc = qpnp_labibb_write(labibb, labibb->lab_base +
REG_LAB_CURRENT_LIMIT, &val, 1);
@ -936,6 +948,88 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb,
return rc;
}
#define LAB_CURRENT_MAX_1600MA 0x7
#define LAB_CURRENT_MAX_400MA 0x1
static int qpnp_lab_pfm_disable(struct qpnp_labibb *labibb)
{
int rc = 0;
u8 val, mask;
mutex_lock(&(labibb->lab_vreg.lab_mutex));
if (!labibb->pfm_enable) {
pr_debug("PFM already disabled\n");
goto out;
}
val = 0;
mask = LAB_PFM_EN_BIT;
rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
REG_LAB_PFM_CTL, mask, val);
if (rc < 0) {
pr_err("Write register %x failed rc = %d\n",
REG_LAB_PFM_CTL, rc);
goto out;
}
val = LAB_CURRENT_MAX_1600MA;
mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK;
rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
REG_LAB_CURRENT_LIMIT, mask, val);
if (rc < 0) {
pr_err("Write register %x failed rc = %d\n",
REG_LAB_CURRENT_LIMIT, rc);
goto out;
}
labibb->pfm_enable = false;
out:
mutex_unlock(&(labibb->lab_vreg.lab_mutex));
return rc;
}
static int qpnp_lab_pfm_enable(struct qpnp_labibb *labibb)
{
int rc = 0;
u8 val, mask;
mutex_lock(&(labibb->lab_vreg.lab_mutex));
if (labibb->pfm_enable) {
pr_debug("PFM already enabled\n");
goto out;
}
/* Wait for ~100uS */
usleep_range(100, 105);
val = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_MAX_400MA;
mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK;
rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
REG_LAB_CURRENT_LIMIT, mask, val);
if (rc < 0) {
pr_err("Write register %x failed rc = %d\n",
REG_LAB_CURRENT_LIMIT, rc);
goto out;
}
/* Wait for ~100uS */
usleep_range(100, 105);
val = LAB_PFM_EN_BIT;
mask = LAB_PFM_EN_BIT;
rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
REG_LAB_PFM_CTL, mask, val);
if (rc < 0) {
pr_err("Write register %x failed rc = %d\n",
REG_LAB_PFM_CTL, rc);
goto out;
}
labibb->pfm_enable = true;
out:
mutex_unlock(&(labibb->lab_vreg.lab_mutex));
return rc;
}
static int qpnp_labibb_restore_settings(struct qpnp_labibb *labibb)
{
int rc, i;
@ -1435,6 +1529,15 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
return -EINVAL;
}
if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE &&
labibb->mode == QPNP_LABIBB_LCD_MODE) {
rc = qpnp_lab_pfm_disable(labibb);
if (rc < 0) {
pr_err("Error in disabling PFM, rc=%d\n", rc);
return rc;
}
}
labibb->lab_vreg.vreg_enabled = 0;
labibb->ibb_vreg.vreg_enabled = 0;
@ -1644,9 +1747,17 @@ static irqreturn_t lab_vreg_ok_handler(int irq, void *_labibb)
struct qpnp_labibb *labibb = _labibb;
int rc;
rc = qpnp_skip_swire_command(labibb);
if (rc)
pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n", rc);
if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2) {
rc = qpnp_skip_swire_command(labibb);
if (rc < 0)
pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n",
rc);
} else if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE &&
labibb->mode == QPNP_LABIBB_LCD_MODE) {
rc = qpnp_lab_pfm_enable(labibb);
if (rc < 0)
pr_err("Failed to config PFM, rc=%d\n", rc);
}
return IRQ_HANDLED;
}
@ -1661,6 +1772,23 @@ static int qpnp_lab_regulator_get_voltage(struct regulator_dev *rdev)
return labibb->lab_vreg.curr_volt;
}
static bool is_lab_vreg_ok_irq_available(struct qpnp_labibb *labibb)
{
/*
* LAB VREG_OK interrupt is used only to skip 2nd SWIRE command in
* dig_major < 2 targets. For pmicobalt, it is used to enable PFM in
* LCD mode.
*/
if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2)
return true;
if (labibb->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE &&
labibb->mode == QPNP_LABIBB_LCD_MODE)
return true;
return false;
}
static struct regulator_ops qpnp_lab_ops = {
.enable = qpnp_lab_regulator_enable,
.disable = qpnp_lab_regulator_disable,
@ -1809,19 +1937,6 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
}
}
if (labibb->skip_2nd_swire_cmd) {
rc = devm_request_threaded_irq(labibb->dev,
labibb->lab_vreg.lab_vreg_ok_irq, NULL,
lab_vreg_ok_handler,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"lab-vreg-ok", labibb);
if (rc) {
pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n",
rc);
return rc;
}
}
val = (labibb->standalone) ? 0 : LAB_IBB_EN_RDY_EN;
rc = qpnp_labibb_sec_write(labibb, labibb->lab_base,
REG_LAB_IBB_EN_RDY, &val, 1);
@ -1899,6 +2014,19 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
labibb->lab_vreg.vreg_enabled = 1;
}
if (is_lab_vreg_ok_irq_available(labibb)) {
rc = devm_request_threaded_irq(labibb->dev,
labibb->lab_vreg.lab_vreg_ok_irq, NULL,
lab_vreg_ok_handler,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"lab-vreg-ok", labibb);
if (rc) {
pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n",
rc);
return rc;
}
}
rc = qpnp_labibb_read(labibb, &val,
labibb->lab_base + REG_LAB_MODULE_RDY, 1);
if (rc) {
@ -1953,7 +2081,6 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
return -EINVAL;
}
mutex_init(&(labibb->lab_vreg.lab_mutex));
return 0;
}
@ -2683,14 +2810,13 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
return -EINVAL;
}
mutex_init(&(labibb->ibb_vreg.ibb_mutex));
return 0;
}
static int qpnp_lab_register_irq(struct device_node *child,
struct qpnp_labibb *labibb)
{
if (labibb->skip_2nd_swire_cmd) {
if (is_lab_vreg_ok_irq_available(labibb)) {
labibb->lab_vreg.lab_vreg_ok_irq =
of_irq_get_byname(child, "lab-vreg-ok");
if (labibb->lab_vreg.lab_vreg_ok_irq < 0) {
@ -2745,7 +2871,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
unsigned int base;
struct device_node *child, *revid_dev_node;
const char *mode_name;
u8 type;
u8 type, revision;
int rc = 0;
labibb = devm_kzalloc(&pdev->dev,
@ -2763,6 +2889,9 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
labibb->dev = &(pdev->dev);
labibb->pdev = pdev;
mutex_init(&(labibb->lab_vreg.lab_mutex));
mutex_init(&(labibb->ibb_vreg.ibb_mutex));
revid_dev_node = of_parse_phandle(labibb->dev->of_node,
"qcom,pmic-revid", 0);
if (!revid_dev_node) {
@ -2817,6 +2946,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
labibb->skip_2nd_swire_cmd =
of_property_read_bool(labibb->dev->of_node,
"qcom,skip-2nd-swire-cmd");
rc = of_property_read_u32(labibb->dev->of_node,
"qcom,swire-2nd-cmd-delay",
&labibb->swire_2nd_cmd_delay);
@ -2846,6 +2976,13 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
return rc;
}
rc = qpnp_labibb_read(labibb, &revision, base + REG_REVISION_2,
1);
if (rc) {
pr_err("Reading REVISION_2 failed rc=%d\n", rc);
goto fail_registration;
}
rc = qpnp_labibb_read(labibb, &type,
base + REG_PERPH_TYPE, 1);
if (rc) {
@ -2856,6 +2993,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
switch (type) {
case QPNP_LAB_TYPE:
labibb->lab_base = base;
labibb->lab_dig_major = revision;
rc = qpnp_lab_register_irq(child, labibb);
if (rc) {
pr_err("Failed to register LAB IRQ rc=%d\n",
@ -2869,6 +3007,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
case QPNP_IBB_TYPE:
labibb->ibb_base = base;
labibb->ibb_dig_major = revision;
rc = register_qpnp_ibb_regulator(labibb, child);
if (rc)
goto fail_registration;