blob: 7fbc42ab3fde04a7c362f6116ae77906b05c4fe0 [file] [log] [blame]
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*
* Authors:
* Raman Kumar Banka <raman.k2@samsung.com>
*
* PMIC rails controller for airbrush state manager.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*/
#include <linux/airbrush-sm-ctrl.h>
int ab_mark_pmic_rail(struct ab_state_context *sc,
enum block_name blk_name,
bool enable,
enum block_state to_block_substate_id)
{
dev_dbg(sc->dev,
"marking rails %d for block %u substate id %u\n",
enable, blk_name, to_block_substate_id);
switch (blk_name) {
case BLK_IPU:
case BLK_TPU:
sc->smps1_state = enable;
sc->ldo3_state = enable;
break;
case BLK_AON:
sc->smps2_state = enable;
sc->ldo4_state = enable;
sc->ldo5_state = enable;
break;
case DRAM:
sc->ldo2_state = enable;
if (enable) {
if (to_block_substate_id != BLOCK_STATE_0) {
sc->ldo1_state = true;
sc->smps3_state = true;
}
} else {
if (to_block_substate_id == BLOCK_STATE_0) {
sc->ldo1_state = false;
sc->smps3_state = false;
}
}
break;
case BLK_MIF:
case BLK_FSYS:
break;
default:
return -EINVAL;
}
return 0;
}
int ab_pmic_off(struct ab_state_context *sc)
{
int ret1, ret2 = 0;
dev_dbg(sc->dev, "Turning OFF PMIC rails\n");
if (!sc->ldo2_state && regulator_is_enabled(sc->ldo2)) {
dev_dbg(sc->dev, "Disable ldo2\n");
ret1 = regulator_disable(sc->ldo2);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable LDO2, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
}
if (!sc->ldo3_state && regulator_is_enabled(sc->ldo3)) {
dev_dbg(sc->dev, "Disable ldo3\n");
ret1 = regulator_disable(sc->ldo3);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable LDO3, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
usleep_range(2000, 3000);
}
if (!sc->smps1_state && regulator_is_enabled(sc->smps1)) {
dev_dbg(sc->dev, "Disable smps1\n");
ret1 = regulator_disable(sc->smps1);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable SMPS1, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
}
if (!sc->ldo5_state && regulator_is_enabled(sc->ldo5)) {
dev_dbg(sc->dev, "Disable ldo5\n");
ret1 = regulator_disable(sc->ldo5);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable LDO5, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
/* NOTE: delay required by b/120785608 */
if (!sc->ldo4_state || !sc->smps2_state)
usleep_range(sc->ldo5_delay, sc->ldo5_delay + 1);
}
if (!sc->ldo4_state && regulator_is_enabled(sc->ldo4)) {
dev_dbg(sc->dev, "Disable ldo4\n");
ret1 = regulator_disable(sc->ldo4);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable LDO4, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
if (sc->ldo4_delay)
usleep_range(sc->ldo4_delay, sc->ldo4_delay + 1);
}
if (!sc->smps3_state && regulator_is_enabled(sc->smps3)) {
dev_dbg(sc->dev, "Disable smps3\n");
ret1 = regulator_disable(sc->smps3);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable SMPS3, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
/*
* Delay added between SMPS3 and LDO1 for correctness per
* characerization results and sequecing requirements.
* See b/131924947.
*/
usleep_range(4000, 4100);
}
if (!sc->ldo1_state && regulator_is_enabled(sc->ldo1)) {
dev_dbg(sc->dev, "Disable ldo1\n");
ret1 = regulator_disable(sc->ldo1);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable LDO1, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
}
if (!sc->smps2_state && regulator_is_enabled(sc->smps2)) {
dev_dbg(sc->dev, "Disable smps2\n");
ret1 = regulator_disable(sc->smps2);
if (ret1 < 0)
dev_err(sc->dev,
"failed to disable SMPS2, ret %d\n", ret1);
ret2 = ret2 ? ret2 : ret1;
if (sc->smps2_delay)
usleep_range(sc->smps2_delay, sc->smps2_delay + 1);
}
/* NOTE: delay required by b/120785608 */
if (!sc->smps2_state &&
!sc->ldo1_state &&
!sc->smps3_state &&
!sc->ldo4_state &&
!sc->ldo5_state &&
!sc->smps1_state &&
!sc->ldo3_state &&
!sc->ldo2_state)
usleep_range(sc->s60_delay, sc->s60_delay + 1);
return ret2;
}
int ab_pmic_on(struct ab_state_context *sc)
{
dev_dbg(sc->dev, "Turning ON PMIC rails\n");
if (sc->smps2_state && !regulator_is_enabled(sc->smps2)) {
dev_dbg(sc->dev, "Enable smps2\n");
if (regulator_enable(sc->smps2))
goto fail_regulator_enable;
}
if (sc->ldo1_state && !regulator_is_enabled(sc->ldo1)) {
dev_dbg(sc->dev, "Enable ldo1\n");
if (regulator_enable(sc->ldo1))
goto fail_regulator_enable;
}
if (sc->smps3 && !regulator_is_enabled(sc->smps3)) {
dev_dbg(sc->dev, "Enable smps3\n");
if (regulator_enable(sc->smps3))
goto fail_regulator_enable;
}
if (sc->ldo4_state && !regulator_is_enabled(sc->ldo4)) {
dev_dbg(sc->dev, "Enable ldo4\n");
if (regulator_enable(sc->ldo4))
goto fail_regulator_enable;
}
if (sc->ldo5_state && !regulator_is_enabled(sc->ldo5)) {
dev_dbg(sc->dev, "Enable ldo5\n");
if (regulator_enable(sc->ldo5))
goto fail_regulator_enable;
}
if (sc->smps1_state && !regulator_is_enabled(sc->smps1)) {
dev_dbg(sc->dev, "Enable smps1\n");
if (regulator_enable(sc->smps1))
goto fail_regulator_enable;
}
if (sc->ldo3_state && !regulator_is_enabled(sc->ldo3)) {
dev_dbg(sc->dev, "Enable ldo3\n");
if (regulator_enable(sc->ldo3))
goto fail_regulator_enable;
}
if (sc->ldo2_state && !regulator_is_enabled(sc->ldo2)) {
dev_dbg(sc->dev, "Enable ldo2\n");
if (regulator_enable(sc->ldo2))
goto fail_regulator_enable;
}
return 0;
fail_regulator_enable:
if (regulator_is_enabled(sc->ldo3))
regulator_disable(sc->ldo3);
sc->ldo3_state = false;
if (regulator_is_enabled(sc->smps1))
regulator_disable(sc->smps1);
sc->smps1_state = false;
if (regulator_is_enabled(sc->ldo5))
regulator_disable(sc->ldo5);
sc->ldo5_state = false;
if (regulator_is_enabled(sc->ldo4))
regulator_disable(sc->ldo4);
sc->ldo4_state = false;
if (regulator_is_enabled(sc->smps3))
regulator_disable(sc->smps3);
sc->smps3_state = false;
if (regulator_is_enabled(sc->ldo1))
regulator_disable(sc->ldo1);
sc->ldo1_state = false;
if (regulator_is_enabled(sc->smps2))
regulator_disable(sc->smps2);
sc->smps2_state = false;
dev_err(sc->dev, "PMIC power up failure\n");
return -ENODEV;
}
static int ab_register_notifier(struct ab_state_context *sc)
{
struct device *dev = sc->dev;
int ret = 0;
ret |= devm_regulator_register_notifier(sc->smps1, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->smps2, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->smps3, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->ldo1, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->ldo2, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->ldo3, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->ldo4, &sc->regulator_nb);
ret |= devm_regulator_register_notifier(sc->ldo5, &sc->regulator_nb);
if (ret) {
dev_err(dev, "failed to register notifier block\n");
return ret;
}
return 0;
}
/* Called during sm driver probe. */
int ab_get_pmic_resources(struct ab_state_context *sc)
{
struct device *dev = sc->dev;
if (!sc->soc_pwrgood) {
sc->soc_pwrgood =
devm_gpiod_get(dev, "soc-pwrgood", GPIOD_OUT_LOW);
if (IS_ERR(sc->soc_pwrgood)) {
dev_warn(dev,
"Could not get pmic_soc_pwrgood gpio (%ld), PMIC driver may not be probed yet\n",
PTR_ERR(sc->soc_pwrgood));
return PTR_ERR(sc->soc_pwrgood);
}
}
if (!sc->ddr_sr) {
sc->ddr_sr =
devm_gpiod_get(dev, "ddr-sr", GPIOD_OUT_LOW);
if (IS_ERR(sc->ddr_sr)) {
dev_err(dev, "Could not get pmic_ddr_sr gpio (%ld)\n",
PTR_ERR(sc->ddr_sr));
return PTR_ERR(sc->ddr_sr);
}
}
if (!sc->ddr_iso) {
sc->ddr_iso =
devm_gpiod_get(dev, "ddr-iso", GPIOD_OUT_LOW);
if (IS_ERR(sc->ddr_iso)) {
dev_err(dev, "Could not get pmic_ddr_iso gpio (%ld)\n",
PTR_ERR(sc->ddr_iso));
return PTR_ERR(sc->ddr_iso);
}
}
if (!sc->smps1) {
sc->smps1 = devm_regulator_get(dev, "s2mpg01_smps1");
if (IS_ERR(sc->smps1)) {
dev_err(dev, "failed to get s2mpg01_smps1 supply (%ld)\n",
PTR_ERR(sc->smps1));
return PTR_ERR(sc->smps1);
}
}
if (!sc->smps2) {
sc->smps2 = devm_regulator_get(dev, "s2mpg01_smps2");
if (IS_ERR(sc->smps2)) {
dev_err(dev, "failed to get s2mpg01_smps2 supply (%ld)\n",
PTR_ERR(sc->smps2));
return PTR_ERR(sc->smps2);
}
}
if (!sc->smps3) {
sc->smps3 = devm_regulator_get(dev, "s2mpg01_smps3");
if (IS_ERR(sc->smps3)) {
dev_err(dev, "failed to get s2mpg01_smps3 supply (%ld)\n",
PTR_ERR(sc->smps3));
return PTR_ERR(sc->smps3);
}
}
if (!sc->ldo1) {
sc->ldo1 = devm_regulator_get(dev, "s2mpg01_ldo1");
if (IS_ERR(sc->ldo1)) {
dev_err(dev, "failed to get s2mpg01_ldo1 supply (%ld)\n",
PTR_ERR(sc->ldo1));
return PTR_ERR(sc->ldo1);
}
}
if (!sc->ldo2) {
sc->ldo2 = devm_regulator_get(dev, "s2mpg01_ldo2");
if (IS_ERR(sc->ldo2)) {
dev_err(dev, "failed to get s2mpg01_ldo2 supply (%ld)\n",
PTR_ERR(sc->ldo2));
return PTR_ERR(sc->ldo2);
}
}
if (!sc->ldo3) {
sc->ldo3 = devm_regulator_get(dev, "s2mpg01_ldo3");
if (IS_ERR(sc->ldo3)) {
dev_err(dev, "failed to get s2mpg01_ldo3 supply (%ld)\n",
PTR_ERR(sc->ldo3));
return PTR_ERR(sc->ldo3);
}
}
if (!sc->ldo4) {
sc->ldo4 = devm_regulator_get(dev, "s2mpg01_ldo4");
if (IS_ERR(sc->ldo4)) {
dev_err(dev, "failed to get s2mpg01_ldo4 supply (%ld)\n",
PTR_ERR(sc->ldo4));
return PTR_ERR(sc->ldo4);
}
}
if (!sc->ldo5) {
sc->ldo5 = devm_regulator_get(dev, "s2mpg01_ldo5");
if (IS_ERR(sc->ldo5)) {
dev_err(dev, "failed to get s2mpg01_ldo5 supply (%ld)\n",
PTR_ERR(sc->ldo5));
return PTR_ERR(sc->ldo5);
}
}
return ab_register_notifier(sc);
}