regulator: cpr3: support LDO handling for different LDO types

An LDO300 regulator is used by the GPU rail on msmfalcon. This LDO
is CPR managed and uses a different configuration/control scheme
compared to Kryo LDO. Add support for this LDO in the CPR3 framework.

CRs-Fixed: 1068294
Change-Id: Ia45152fe211f2ece1028c5cb978beebda86faba3
Signed-off-by: Tirupathi Reddy <tirupath@codeaurora.org>
This commit is contained in:
Tirupathi Reddy 2016-08-31 13:49:25 +05:30
parent a80e267a8c
commit 23603a7dc4
5 changed files with 234 additions and 65 deletions

View file

@ -32,7 +32,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/kryo-regulator.h>
#include <linux/regulator/msm-ldo-regulator.h>
#include "cpr3-regulator.h"

View file

@ -35,7 +35,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/kryo-regulator.h>
#include <linux/regulator/msm-ldo-regulator.h>
#include <soc/qcom/spm.h>
@ -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;

View file

@ -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. */

View file

@ -28,7 +28,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/kryo-regulator.h>
#include <linux/regulator/msm-ldo-regulator.h>
#include <soc/qcom/spm.h>
@ -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;

View file

@ -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__ */