Skip to content

Commit

Permalink
regulator: qpnp-amoled: remove AB ldo bypass
Browse files Browse the repository at this point in the history
The AB LDO bypass could lead to a race condition during AOD->Normal
transition, that could lead to PMIC issue. Disable the bypass to avoid
this.

Bug: 137055672
Change-Id: I1d94379b51095740635ea7be034cbb826411e0f7
Signed-off-by: Adrian Salido <salidoa@google.com>
  • Loading branch information
adriansm committed Jul 16, 2019
1 parent fcefc0e commit 543dc23
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 234 deletions.
Expand Up @@ -67,15 +67,6 @@ Subnode common properties for OLEDB and AB/IBB regulator devices.
Definition: A boolean property to specify that the pull down control
for AB/IBB needs to be configured during AOD mode.

Subnode properties for AB regulator device.

- qcom,aod-entry-poll-time-ms:
Usage: optional
Value type: <u32>
Definition: Poll timeout (in ms) for AB VREG_OK to go low during AOD
entry. If this is not specified, a default value of 100 ms
is used.

Subnode properties for OLEDB regulator device.

- qcom,default-vout-override:
Expand Down
247 changes: 22 additions & 225 deletions drivers/regulator/qpnp-amoled-regulator.c
Expand Up @@ -79,7 +79,6 @@ struct ab_regulator {
/* DT params */
bool swire_control;
bool pd_control;
u32 aod_entry_poll_time_ms;
};

struct ibb_regulator {
Expand All @@ -97,7 +96,6 @@ struct qpnp_amoled {
struct ab_regulator ab;
struct ibb_regulator ibb;
struct mutex reg_lock;
struct work_struct aod_work;
struct delayed_work vout_work;
struct workqueue_struct *wq;

Expand Down Expand Up @@ -232,102 +230,6 @@ static int qpnp_ab_pd_control(struct qpnp_amoled *chip, bool en)
return qpnp_amoled_write(chip, AB_LDO_PD_CTL(chip), &val, 1);
}

#define AB_VREG_OK_POLL_TIME_US 2000
#define AB_VREG_OK_POLL_HIGH_TRIES 8
#define AB_VREG_OK_POLL_HIGH_TIME_US 10000
#define AB_VREG_OK_POLL_AGAIN_TRIES 10

static int qpnp_ab_poll_vreg_ok(struct qpnp_amoled *chip, bool status,
u32 poll_time_us)
{
u32 i, poll_us = AB_VREG_OK_POLL_TIME_US, wait_time_us = 0;
bool swire_high = false, poll_again = false, monitor = false;
int rc;
u8 val;

if (poll_time_us < AB_VREG_OK_POLL_TIME_US)
return -EINVAL;

i = poll_time_us / AB_VREG_OK_POLL_TIME_US;
loop:
while (i--) {
/* Write a dummy value before reading AB_STATUS1 */
rc = qpnp_amoled_write(chip, AB_STATUS1(chip), &val, 1);
if (rc < 0)
return rc;

rc = qpnp_amoled_read(chip, AB_STATUS1(chip), &val, 1);
if (rc < 0)
return rc;

wait_time_us += poll_us;
if (((val & VREG_OK_BIT) >> VREG_OK_SHIFT) == status) {
pr_debug("Waited for %d us\n", wait_time_us);

/*
* Return if we're polling for VREG_OK low. Else, poll
* for VREG_OK high for at least 80 ms. IF VREG_OK stays
* high, then consider it as a valid SWIRE pulse.
*/

if (status) {
swire_high = true;
if (!poll_again && !monitor) {
pr_debug("SWIRE is high, start monitoring\n");
i = AB_VREG_OK_POLL_HIGH_TRIES;
poll_us = AB_VREG_OK_POLL_HIGH_TIME_US;
wait_time_us = 0;
monitor = true;
}

if (poll_again)
poll_again = false;
} else {
return 0;
}
} else {
/*
* If we're here when polling for VREG_OK high, then it
* is possibly because of an intermittent SWIRE pulse.
* Ignore it and poll for valid SWIRE pulse again.
*/
if (status && swire_high && monitor) {
pr_debug("SWIRE is low\n");
poll_again = true;
swire_high = false;
break;
}

if (poll_again)
poll_again = false;
}

usleep_range(poll_us, poll_us + 1);
}

/*
* If poll_again is set, then VREG_OK should be polled for another
* 100 ms for valid SWIRE signal.
*/

if (poll_again) {
pr_debug("polling again for SWIRE\n");
i = AB_VREG_OK_POLL_AGAIN_TRIES;
poll_us = AB_VREG_OK_POLL_HIGH_TIME_US;
wait_time_us = 0;
goto loop;
}

/* If swire_high is set, then it's a valid SWIRE signal, return 0. */
if (swire_high) {
pr_debug("SWIRE is high\n");
return 0;
}

pr_err("AB_STATUS1: %x poll for VREG_OK %d timed out\n", val, status);
return -ETIMEDOUT;
}

static int qpnp_ibb_pd_control(struct qpnp_amoled *chip, bool en)
{
u8 val = en ? ENABLE_PD_BIT : 0;
Expand All @@ -336,126 +238,21 @@ static int qpnp_ibb_pd_control(struct qpnp_amoled *chip, bool en)
val);
}

static int qpnp_ibb_aod_config(struct qpnp_amoled *chip, bool aod)
{
int rc;
u8 ps_ctl, smart_ps_ctl, nlimit_dac;

pr_debug("aod: %d\n", aod);
if (aod) {
ps_ctl = 0x82;
smart_ps_ctl = 0;
nlimit_dac = 0;
} else {
ps_ctl = 0x02;
smart_ps_ctl = 0x80;
nlimit_dac = 0x3;
}

rc = qpnp_amoled_write(chip, IBB_SMART_PS_CTL(chip), &smart_ps_ctl, 1);
if (rc < 0)
return rc;

rc = qpnp_amoled_write(chip, IBB_NLIMIT_DAC(chip), &nlimit_dac, 1);
if (rc < 0)
return rc;

rc = qpnp_amoled_write(chip, IBB_PS_CTL(chip), &ps_ctl, 1);
return rc;
}

static void qpnp_amoled_aod_work(struct work_struct *work)
static int qpnp_amoled_pd_control(struct qpnp_amoled *chip, bool en)
{
struct qpnp_amoled *chip = container_of(work, struct qpnp_amoled,
aod_work);
u8 val = 0;
unsigned int mode;
u32 poll_time_us = 100000;
int rc;

mutex_lock(&chip->reg_lock);
mode = chip->ab.vreg.mode;
mutex_unlock(&chip->reg_lock);

pr_debug("mode: %d\n", mode);
if (mode == REGULATOR_MODE_NORMAL) {
rc = qpnp_ibb_aod_config(chip, true);
if (rc < 0)
goto error;

/* poll for VREG_OK high */
rc = qpnp_ab_poll_vreg_ok(chip, true, poll_time_us);
if (rc < 0)
goto error;

/*
* As per the hardware recommendation, Wait for ~10 ms after
* polling for VREG_OK before changing the configuration when
* exiting from AOD mode.
*/

usleep_range(10000, 10001);

rc = qpnp_ibb_aod_config(chip, false);
if (chip->ibb.pd_control) {
const int rc = qpnp_ibb_pd_control(chip, en);
if (rc < 0)
goto error;

if (chip->ibb.pd_control) {
rc = qpnp_ibb_pd_control(chip, true);
if (rc < 0)
goto error;
}

if (chip->ab.pd_control) {
rc = qpnp_ab_pd_control(chip, true);
if (rc < 0)
goto error;
}
} else if (mode == REGULATOR_MODE_IDLE) {
if (chip->ab.aod_entry_poll_time_ms > 0)
poll_time_us = chip->ab.aod_entry_poll_time_ms * 1000;

/* poll for VREG_OK low */
rc = qpnp_ab_poll_vreg_ok(chip, false, poll_time_us);
if (rc < 0)
goto error;

if (chip->ibb.pd_control) {
rc = qpnp_ibb_pd_control(chip, false);
if (rc < 0)
goto error;
}

if (chip->ab.pd_control) {
rc = qpnp_ab_pd_control(chip, false);
if (rc < 0)
goto error;
}
return rc;
}

val = 0xF1;
} else if (mode == REGULATOR_MODE_STANDBY) {
/* Restore the normal configuration without any delay */
rc = qpnp_ibb_aod_config(chip, false);
if (chip->ab.pd_control) {
const int rc = qpnp_ab_pd_control(chip, en);
if (rc < 0)
goto error;

if (chip->ibb.pd_control) {
rc = qpnp_ibb_pd_control(chip, true);
if (rc < 0)
goto error;
}

if (chip->ab.pd_control) {
rc = qpnp_ab_pd_control(chip, true);
if (rc < 0)
goto error;
}
return rc;
}

rc = qpnp_amoled_write(chip, AB_LDO_SW_DBG_CTL(chip), &val, 1);
error:
if (rc < 0)
pr_err("Failed to configure for mode %d\n", mode);
return 0;
}

static void qpnp_amoled_vout_override_work(struct work_struct *work)
Expand Down Expand Up @@ -495,25 +292,29 @@ static int qpnp_ab_ibb_regulator_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
struct qpnp_amoled *chip = rdev_get_drvdata(rdev);
unsigned int last_mode;
int rc;

if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_STANDBY &&
mode != REGULATOR_MODE_IDLE) {
pr_err("Unsupported mode %u\n", mode);
return -EINVAL;
}

pr_debug("mode: %d\n", mode);
if (mode == chip->ab.vreg.mode || mode == chip->ibb.vreg.mode)
return 0;

mutex_lock(&chip->reg_lock);
last_mode = chip->ab.vreg.mode;
chip->ab.vreg.mode = chip->ibb.vreg.mode = mode;
mutex_unlock(&chip->reg_lock);
pr_debug("mode: %d\n", mode);

if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_STANDBY) {
rc = qpnp_amoled_pd_control(chip, true);
} else if (mode == REGULATOR_MODE_IDLE) {
rc = qpnp_amoled_pd_control(chip, false);
}

if (last_mode == REGULATOR_MODE_IDLE || mode == REGULATOR_MODE_IDLE)
queue_work(chip->wq, &chip->aod_work);
if (!rc)
chip->ab.vreg.mode = chip->ibb.vreg.mode = mode;
else
pr_err("Failed to configure for mode %d\n", mode);

if (chip->oledb.vout_override) {
cancel_delayed_work_sync(&chip->vout_work);
Expand All @@ -523,6 +324,7 @@ static int qpnp_ab_ibb_regulator_set_mode(struct regulator_dev *rdev,
queue_delayed_work(chip->wq, &chip->vout_work, delay);
}
}

return 0;
}

Expand Down Expand Up @@ -773,9 +575,6 @@ static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
"qcom,swire-control");
chip->ab.pd_control = of_property_read_bool(temp,
"qcom,aod-pd-control");
of_property_read_u32(temp,
"qcom,aod-entry-poll-time-ms",
&chip->ab.aod_entry_poll_time_ms);
break;
case IBB_PERIPH_TYPE:
chip->ibb_base = base;
Expand Down Expand Up @@ -823,7 +622,6 @@ static int qpnp_amoled_regulator_probe(struct platform_device *pdev)
}

mutex_init(&chip->reg_lock);
INIT_WORK(&chip->aod_work, qpnp_amoled_aod_work);
INIT_DELAYED_WORK(&chip->vout_work, qpnp_amoled_vout_override_work);
chip->dev = &pdev->dev;

Expand Down Expand Up @@ -858,7 +656,6 @@ static int qpnp_amoled_regulator_remove(struct platform_device *pdev)
{
struct qpnp_amoled *chip = dev_get_drvdata(&pdev->dev);

cancel_work_sync(&chip->aod_work);
destroy_workqueue(chip->wq);
return 0;
}
Expand Down

0 comments on commit 543dc23

Please sign in to comment.