diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index 0dbf381b9118..091780525207 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "cpr3-regulator.h" diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index 0df2b80ceca5..e3e418100eef 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include @@ -1761,7 +1761,12 @@ static int cpr3_regulator_config_ldo_retention(struct cpr3_regulator *vreg, { struct regulator *ldo_ret_reg = vreg->ldo_ret_regulator; int retention_volt, rc; - enum kryo_supply_mode mode; + enum msm_ldo_supply_mode mode; + + if (!ldo_ret_reg) { + /* LDO retention regulator is not defined */ + return 0; + } retention_volt = regulator_get_voltage(ldo_ret_reg); if (retention_volt < 0) { @@ -1783,8 +1788,8 @@ static int cpr3_regulator_config_ldo_retention(struct cpr3_regulator *vreg, } /** - * cpr3_regulator_config_ldo_mem_acc() - configure the mem-acc regulator - * corner based upon a future LDO regulator voltage setpoint + * cpr3_regulator_config_kryo_ldo_mem_acc() - configure the mem-acc regulator + * corner based upon a future Kryo LDO regulator voltage setpoint * @vreg: Pointer to the CPR3 regulator * @new_volt: New voltage in microvolts that the LDO regulator needs * to end up at @@ -1796,7 +1801,7 @@ static int cpr3_regulator_config_ldo_retention(struct cpr3_regulator *vreg, * * Return: 0 on success, errno on failure */ -static int cpr3_regulator_config_ldo_mem_acc(struct cpr3_regulator *vreg, +static int cpr3_regulator_config_kryo_ldo_mem_acc(struct cpr3_regulator *vreg, int new_volt) { struct cpr3_controller *ctrl = vreg->thread->ctrl; @@ -1857,6 +1862,51 @@ static int cpr3_regulator_config_ldo_mem_acc(struct cpr3_regulator *vreg, return 0; } +/** + * cpr3_regulator_kryo_bhs_prepare() - configure the Kryo LDO regulator + * associated with a CPR3 regulator in preparation for BHS + * mode switch. + * @vreg: Pointer to the CPR3 regulator + * @vdd_volt: Last known settled voltage in microvolts for the VDD + * supply + * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for + * the VDD supply + * + * This function performs the necessary steps prior to switching a Kryo LDO + * regulator to BHS mode (LDO bypassed mode). + * + * Return: 0 on success, errno on failure + */ +static int cpr3_regulator_kryo_bhs_prepare(struct cpr3_regulator *vreg, + int vdd_volt, int vdd_ceiling_volt) +{ + struct regulator *ldo_reg = vreg->ldo_regulator; + int bhs_volt, rc; + + bhs_volt = vdd_volt - vreg->ldo_min_headroom_volt; + if (bhs_volt > vreg->ldo_max_volt) { + cpr3_debug(vreg, "limited to LDO output of %d uV when switching to BHS mode\n", + vreg->ldo_max_volt); + bhs_volt = vreg->ldo_max_volt; + } + + rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg, bhs_volt); + if (rc) { + cpr3_err(vreg, "failed to configure mem-acc settings\n"); + return rc; + } + + rc = regulator_set_voltage(ldo_reg, bhs_volt, min(vdd_ceiling_volt, + vreg->ldo_max_volt)); + if (rc) { + cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n", + bhs_volt, rc); + return rc; + } + + return rc; +} + /** * cpr3_regulator_set_bhs_mode() - configure the LDO regulator associated with * a CPR3 regulator to BHS mode @@ -1873,32 +1923,21 @@ static int cpr3_regulator_set_bhs_mode(struct cpr3_regulator *vreg, int vdd_volt, int vdd_ceiling_volt) { struct regulator *ldo_reg = vreg->ldo_regulator; - int bhs_volt, rc; + int rc; - bhs_volt = vdd_volt - vreg->ldo_min_headroom_volt; - if (bhs_volt > vreg->ldo_max_volt) { - cpr3_debug(vreg, "limited to LDO output of %d uV when switching to BHS mode\n", - vreg->ldo_max_volt); - bhs_volt = vreg->ldo_max_volt; - } - - rc = cpr3_regulator_config_ldo_mem_acc(vreg, bhs_volt); - if (rc) { - cpr3_err(vreg, "failed to configure mem-acc settings\n"); - return rc; - } - - rc = regulator_set_voltage(ldo_reg, bhs_volt, min(vdd_ceiling_volt, - vreg->ldo_max_volt)); - if (rc) { - cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n", - bhs_volt, rc); - return rc; + if (vreg->ldo_type == CPR3_LDO_KRYO) { + rc = cpr3_regulator_kryo_bhs_prepare(vreg, vdd_volt, + vdd_ceiling_volt); + if (rc) { + cpr3_err(vreg, "cpr3 regulator bhs mode prepare failed, rc=%d\n", + rc); + return rc; + } } rc = regulator_allow_bypass(ldo_reg, BHS_MODE); if (rc) { - cpr3_err(vreg, "regulator_allow_bypass(ldo) == %s failed, rc=%d\n", + cpr3_err(vreg, "regulator_allow_bypass(bhs) == %s failed, rc=%d\n", BHS_MODE ? "true" : "false", rc); return rc; } @@ -2009,44 +2048,34 @@ static int cpr3_regulator_ldo_apm_prepare(struct cpr3_controller *ctrl, } /** - * cpr3_regulator_config_vreg_ldo() - configure the voltage and bypass state for - * the LDO regulator associated with a single CPR3 regulator. + * cpr3_regulator_config_vreg_kryo_ldo() - configure the voltage and bypass + * state for the Kryo LDO regulator associated with a single CPR3 + * regulator. * * @vreg: Pointer to the CPR3 regulator * @vdd_floor_volt: Last known aggregated floor voltage in microvolts for * the VDD supply * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for * the VDD supply - * @new_volt: New voltage in microvolts that VDD supply needs to - * end up at + * @ref_volt: Reference voltage in microvolts corresponds either to + * the aggregated floor voltage or the next VDD supply + * setpoint. * @last_volt: Last known voltage in microvolts for the VDD supply * - * This function performs all relevant LDO or BHS configurations if an LDO + * This function performs all relevant LDO or BHS configurations if a Kryo LDO * regulator is specified. * * Return: 0 on success, errno on failure */ -static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg, +static int cpr3_regulator_config_vreg_kryo_ldo(struct cpr3_regulator *vreg, int vdd_floor_volt, int vdd_ceiling_volt, - int new_volt, int last_volt) + int ref_volt, int last_volt) { struct cpr3_controller *ctrl = vreg->thread->ctrl; struct regulator *ldo_reg = vreg->ldo_regulator; struct cpr3_corner *current_corner; enum msm_apm_supply apm_mode; int rc, ldo_volt, final_ldo_volt, bhs_volt, max_volt, safe_volt; - int ref_volt; - - ref_volt = ctrl->use_hw_closed_loop ? vdd_floor_volt : - new_volt; - - rc = cpr3_regulator_config_ldo_retention(vreg, ref_volt); - if (rc) - return rc; - - if (!vreg->vreg_enabled || vreg->current_corner - == CPR3_REGULATOR_CORNER_INVALID) - return 0; current_corner = &vreg->corner[vreg->current_corner]; ldo_volt = current_corner->open_loop_volt @@ -2087,7 +2116,7 @@ static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg, bhs_volt), vreg->ldo_max_volt); - rc = cpr3_regulator_config_ldo_mem_acc(vreg, + rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg, safe_volt); if (rc) { cpr3_err(vreg, "failed to configure mem-acc settings\n"); @@ -2119,7 +2148,7 @@ static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg, else final_ldo_volt = ldo_volt; - rc = cpr3_regulator_config_ldo_mem_acc(vreg, + rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg, final_ldo_volt); if (rc) { cpr3_err(vreg, "failed to configure mem-acc settings\n"); @@ -2145,6 +2174,115 @@ static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg, return 0; } +/** + * cpr3_regulator_config_vreg_ldo300() - configure the voltage and bypass state + * for the LDO300 regulator associated with a single CPR3 + * regulator. + * + * @vreg: Pointer to the CPR3 regulator + * @new_volt: New voltage in microvolts that VDD supply needs to + * end up at + * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for + * the VDD supply + * + * This function performs all relevant LDO or BHS configurations for an LDO300 + * type regulator. + * + * Return: 0 on success, errno on failure + */ +static int cpr3_regulator_config_vreg_ldo300(struct cpr3_regulator *vreg, + int new_volt, int vdd_ceiling_volt) +{ + struct regulator *ldo_reg = vreg->ldo_regulator; + struct cpr3_corner *corner; + bool mode; + int rc = 0; + + corner = &vreg->corner[vreg->current_corner]; + mode = corner->ldo_mode_allowed ? LDO_MODE : BHS_MODE; + + if (mode == LDO_MODE) { + rc = regulator_set_voltage(ldo_reg, new_volt, vdd_ceiling_volt); + if (rc) { + cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n", + new_volt, rc); + return rc; + } + } + + if (vreg->ldo_regulator_bypass != mode) { + rc = regulator_allow_bypass(ldo_reg, mode); + if (rc) { + cpr3_err(vreg, "regulator_allow_bypass(%s) is failed, rc=%d\n", + mode == LDO_MODE ? "ldo" : "bhs", rc); + return rc; + } + vreg->ldo_regulator_bypass = mode; + } + + return rc; +} + +/** + * cpr3_regulator_config_vreg_ldo() - configure the voltage and bypass state for + * the LDO regulator associated with a single CPR3 regulator. + * + * @vreg: Pointer to the CPR3 regulator + * @vdd_floor_volt: Last known aggregated floor voltage in microvolts for + * the VDD supply + * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for + * the VDD supply + * @new_volt: New voltage in microvolts that VDD supply needs to + * end up at + * @last_volt: Last known voltage in microvolts for the VDD supply + * + * This function identifies the type of LDO regulator associated with a CPR3 + * regulator and invokes the LDO specific configuration functions. + * + * Return: 0 on success, errno on failure + */ +static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg, + int vdd_floor_volt, int vdd_ceiling_volt, + int new_volt, int last_volt) +{ + struct cpr3_controller *ctrl = vreg->thread->ctrl; + int ref_volt, rc; + + ref_volt = ctrl->use_hw_closed_loop ? vdd_floor_volt : + new_volt; + + rc = cpr3_regulator_config_ldo_retention(vreg, ref_volt); + if (rc) + return rc; + + if (!vreg->vreg_enabled || + vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID) + return 0; + + switch (vreg->ldo_type) { + case CPR3_LDO_KRYO: + rc = cpr3_regulator_config_vreg_kryo_ldo(vreg, vdd_floor_volt, + vdd_ceiling_volt, ref_volt, last_volt); + if (rc) + cpr3_err(vreg, "kryo ldo regulator config failed, rc=%d\n", + rc); + break; + case CPR3_LDO300: + rc = cpr3_regulator_config_vreg_ldo300(vreg, new_volt, + vdd_ceiling_volt); + if (rc) + cpr3_err(vreg, "ldo300 regulator config failed, rc=%d\n", + rc); + break; + default: + cpr3_err(vreg, "invalid ldo regulator type = %d\n", + vreg->ldo_type); + rc = -EINVAL; + } + + return rc; +} + /** * cpr3_regulator_config_ldo() - configure the voltage and bypass state for the * LDO regulator associated with each CPR3 regulator of a CPR3 @@ -2529,6 +2667,12 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, int rc; if (new_volt < last_volt) { + if (ctrl->support_ldo300_vreg) { + rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); + if (rc) + return rc; + } + /* Decreasing VDD voltage */ rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt, ctrl->aggr_corner.ceiling_volt, @@ -2539,10 +2683,11 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, return rc; } - rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); - if (rc) - return rc; - + if (!ctrl->support_ldo300_vreg) { + rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); + if (rc) + return rc; + } } else { /* Increasing VDD voltage */ if (ctrl->system_regulator) { @@ -2903,6 +3048,8 @@ static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner, aggr_corner->mem_acc_volt = max(aggr_corner->mem_acc_volt, corner->mem_acc_volt); aggr_corner->irq_en |= corner->irq_en; + aggr_corner->use_open_loop |= corner->use_open_loop; + aggr_corner->ldo_mode_allowed |= corner->ldo_mode_allowed; if (aggr_quot) { aggr_corner->ro_mask &= corner->ro_mask; @@ -3281,7 +3428,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) * Only enable the CPR controller if it is possible to set more than * one vdd-supply voltage. */ - if (aggr_corner.ceiling_volt > aggr_corner.floor_volt) + if (aggr_corner.ceiling_volt > aggr_corner.floor_volt && + !aggr_corner.use_open_loop) cpr3_ctrl_loop_enable(ctrl); ctrl->last_corner_was_closed_loop = ctrl->cpr_enabled; diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index 3c652ca8d489..2e997b526b90 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -141,6 +141,9 @@ struct cpr4_sdelta { * @use_open_loop: Boolean indicating that open-loop (i.e CPR disabled) as * opposed to closed-loop operation must be used for this * corner on CPRh controllers. + * @ldo_mode_allowed: Boolean which indicates if LDO mode is allowed for this + * corner. This field is applicable for CPR4 controllers + * that manage LDO300 supply regulator. * @sdelta: The CPR4 controller specific data for this corner. This * field is applicable for CPR4 controllers. * @@ -174,6 +177,7 @@ struct cpr3_corner { u32 irq_en; int aging_derate; bool use_open_loop; + bool ldo_mode_allowed; struct cpr4_sdelta *sdelta; }; @@ -192,6 +196,18 @@ struct cprh_corner_band { struct cpr4_sdelta *sdelta; }; +/** + * enum cpr3_ldo_type - Constants which define the LDO supply regulator + * types used to manage the subsystem component rail voltage. + * %CPR3_LDO_KRYO: Kryo LDO regulator used to sub-regulate the HMSS + * per-cluster voltage. + * %CPR3_LDO300: LDO regulator used to sub-regulate the GFX voltage. + */ +enum cpr3_ldo_type { + CPR3_LDO_KRYO = 0, + CPR3_LDO300 = 1, +}; + /** * struct cpr3_regulator - CPR3 logical regulator instance associated with a * given CPR3 hardware thread @@ -275,6 +291,7 @@ struct cprh_corner_band { * participated in the last aggregation event * @debug_corner: Index identifying voltage corner used for displaying * corner configuration values in debugfs + * @ldo_type: LDO regulator type. * @ldo_min_headroom_volt: Minimum voltage difference in microvolts required * between the VDD supply voltage and the LDO output in * order for the LDO operate @@ -358,6 +375,7 @@ struct cpr3_regulator { int last_closed_loop_corner; bool aggregated; int debug_corner; + enum cpr3_ldo_type ldo_type; int ldo_min_headroom_volt; int ldo_max_headroom_volt; int ldo_adjust_volt; @@ -715,6 +733,8 @@ struct cpr3_panic_regs_info { * @panic_regs_info: Array of panic registers information which provides the * list of registers to dump when the device crashes. * @panic_notifier: Notifier block registered to global panic notifier list. + * @support_ldo300_vreg: Boolean value which indicates that this CPR controller + * manages an underlying LDO regulator of type LDO300. * * This structure contains both configuration and runtime state data. The * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled, @@ -815,6 +835,7 @@ struct cpr3_controller { u32 voltage_settling_time; struct cpr3_panic_regs_info *panic_regs_info; struct notifier_block panic_notifier; + bool support_ldo300_vreg; }; /* Used for rounding voltages to the closest physically available set point. */ diff --git a/drivers/regulator/kryo-regulator.c b/drivers/regulator/kryo-regulator.c index c2cddae0b30b..cf7830469db5 100644 --- a/drivers/regulator/kryo-regulator.c +++ b/drivers/regulator/kryo-regulator.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include @@ -89,9 +89,9 @@ struct kryo_regulator { struct regulator_dev *retention_rdev; struct regulator_desc retention_desc; const char *name; - enum kryo_supply_mode mode; - enum kryo_supply_mode retention_mode; - enum kryo_supply_mode pre_lpm_state_mode; + enum msm_ldo_supply_mode mode; + enum msm_ldo_supply_mode retention_mode; + enum msm_ldo_supply_mode pre_lpm_state_mode; void __iomem *reg_base; void __iomem *pm_apcc_base; struct dentry *debugfs; @@ -248,7 +248,7 @@ static int kryo_set_ldo_volt(struct kryo_regulator *kvreg, int volt) /* Locks must be held by the caller */ static int kryo_configure_mode(struct kryo_regulator *kvreg, - enum kryo_supply_mode mode) + enum msm_ldo_supply_mode mode) { u32 reg; int timeout = PWR_GATE_SWITCH_TIMEOUT_US; diff --git a/include/linux/regulator/kryo-regulator.h b/include/linux/regulator/msm-ldo-regulator.h similarity index 72% rename from include/linux/regulator/kryo-regulator.h rename to include/linux/regulator/msm-ldo-regulator.h index ab51f8629d2d..ad04e294cfe6 100644 --- a/include/linux/regulator/kryo-regulator.h +++ b/include/linux/regulator/msm-ldo-regulator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * 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 @@ -11,22 +11,22 @@ * GNU General Public License for more details. */ -#ifndef __KRYO_REGULATOR_H__ -#define __KRYO_REGULATOR_H__ +#ifndef __MSM_LDO_REGULATOR_H__ +#define __MSM_LDO_REGULATOR_H__ /** - * enum kryo_supply_mode - supported operating modes by this regulator type. + * enum msm_ldo_supply_mode - supported operating modes by this regulator type. * Use negative logic to ensure BHS mode is treated as the safe default by the * the regulator framework. This is necessary since LDO mode can only be enabled * when several constraints are satisfied. Consumers of this regulator are * expected to request changes in operating modes through the use of - * regulator_allow_bypass() passing in the desired Kryo supply mode. + * regulator_allow_bypass() passing in the desired LDO supply mode. * %BHS_MODE: to select BHS as operating mode * %LDO_MODE: to select LDO as operating mode */ -enum kryo_supply_mode { +enum msm_ldo_supply_mode { BHS_MODE = false, LDO_MODE = true, }; -#endif /* __KRYO_REGULATOR_H__ */ +#endif /* __MSM_LDO_REGULATOR_H__ */