qpnp-smb2: Disable hvdcp based on user configuration
Some platforms do not support HVDCP charging. Provide means to disable them. Provide a votable for disabling HVDCP in preparation to handle VBUS and CC line shorted situation. Note that when hvdcp is disabled, we only disable voltage negotiations i.e. qc 2.0 detection remains enabled since it does not need voltage to change. Change-Id: Id7eaa46f08ac451a918a550f7837efbef78ab6f6 Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
This commit is contained in:
parent
9fde5f5830
commit
665d020cda
4 changed files with 123 additions and 19 deletions
|
@ -120,6 +120,15 @@ Charger specific properties:
|
|||
3 - Disable charging
|
||||
4 - Suspend USB input
|
||||
|
||||
- qcom,hvdcp-disable
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: Specifies if hvdcp charging is to be enabled or not.
|
||||
If this property is not specified hvdcp will be enabled.
|
||||
If this property is specified, hvdcp 2.0 detection will still
|
||||
happen but the adapter won't be asked to switch to a higher
|
||||
voltage point.
|
||||
|
||||
=============================================
|
||||
Second Level Nodes - SMB2 Charger Peripherals
|
||||
=============================================
|
||||
|
@ -153,7 +162,6 @@ pmicobalt_charger: qcom,qpnp-smb2 {
|
|||
io-channels = <&pmic_rradc 0>;
|
||||
io-channel-names = "rradc_batt_id";
|
||||
|
||||
qcom,suspend-input;
|
||||
dpdm-supply = <&qusb_phy0>;
|
||||
|
||||
qcom,step-soc-thresholds = <60 70 80 90>;
|
||||
|
|
|
@ -218,6 +218,7 @@ struct smb_dt_props {
|
|||
s32 step_cc_delta[STEP_CHARGING_MAX_STEPS];
|
||||
struct device_node *revid_dev_node;
|
||||
int float_option;
|
||||
bool hvdcp_disable;
|
||||
};
|
||||
|
||||
struct smb2 {
|
||||
|
@ -329,6 +330,9 @@ static int smb2_parse_dt(struct smb2 *chip)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->dt.hvdcp_disable = of_property_read_bool(node,
|
||||
"qcom,hvdcp-disable");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1031,6 +1035,8 @@ static int smb2_init_hw(struct smb2 *chip)
|
|||
DEFAULT_VOTER, true, chip->dt.usb_icl_ua);
|
||||
vote(chg->dc_icl_votable,
|
||||
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
|
||||
vote(chg->hvdcp_disable_votable, DEFAULT_VOTER,
|
||||
chip->dt.hvdcp_disable, 0);
|
||||
|
||||
/* Configure charge enable for software control; active high */
|
||||
rc = smblib_masked_write(chg, CHGR_CFG2_REG,
|
||||
|
|
|
@ -208,47 +208,97 @@ struct apsd_result {
|
|||
const enum power_supply_type pst;
|
||||
};
|
||||
|
||||
enum {
|
||||
UNKNOWN,
|
||||
SDP,
|
||||
CDP,
|
||||
DCP,
|
||||
OCP,
|
||||
FLOAT,
|
||||
HVDCP2,
|
||||
HVDCP3,
|
||||
MAX_TYPES
|
||||
};
|
||||
|
||||
static const struct apsd_result const smblib_apsd_results[] = {
|
||||
{"UNKNOWN", 0, POWER_SUPPLY_TYPE_UNKNOWN},
|
||||
{"SDP", SDP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB},
|
||||
{"CDP", CDP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_CDP},
|
||||
{"DCP", DCP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_DCP},
|
||||
{"OCP", OCP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_DCP},
|
||||
{"FLOAT", FLOAT_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_DCP},
|
||||
{"HVDCP2", DCP_CHARGER_BIT | QC_2P0_BIT, POWER_SUPPLY_TYPE_USB_HVDCP},
|
||||
{"HVDCP3", DCP_CHARGER_BIT | QC_3P0_BIT, POWER_SUPPLY_TYPE_USB_HVDCP_3},
|
||||
[UNKNOWN] = {
|
||||
.name = "UNKNOWN",
|
||||
.bit = 0,
|
||||
.pst = POWER_SUPPLY_TYPE_UNKNOWN
|
||||
},
|
||||
[SDP] = {
|
||||
.name = "SDP",
|
||||
.bit = SDP_CHARGER_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB
|
||||
},
|
||||
[CDP] = {
|
||||
.name = "CDP",
|
||||
.bit = CDP_CHARGER_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB_CDP
|
||||
},
|
||||
[DCP] = {
|
||||
.name = "DCP",
|
||||
.bit = DCP_CHARGER_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB_DCP
|
||||
},
|
||||
[OCP] = {
|
||||
.name = "OCP",
|
||||
.bit = OCP_CHARGER_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB_DCP
|
||||
},
|
||||
[FLOAT] = {
|
||||
.name = "FLOAT",
|
||||
.bit = FLOAT_CHARGER_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB_DCP
|
||||
},
|
||||
[HVDCP2] = {
|
||||
.name = "HVDCP2",
|
||||
.bit = DCP_CHARGER_BIT | QC_2P0_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB_HVDCP
|
||||
},
|
||||
[HVDCP3] = {
|
||||
.name = "HVDCP3",
|
||||
.bit = DCP_CHARGER_BIT | QC_3P0_BIT,
|
||||
.pst = POWER_SUPPLY_TYPE_USB_HVDCP_3,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg)
|
||||
{
|
||||
int rc, i;
|
||||
u8 stat;
|
||||
u8 apsd_stat, stat;
|
||||
const struct apsd_result *result = &smblib_apsd_results[UNKNOWN];
|
||||
|
||||
rc = smblib_read(chg, APSD_STATUS_REG, &stat);
|
||||
rc = smblib_read(chg, APSD_STATUS_REG, &apsd_stat);
|
||||
if (rc < 0) {
|
||||
dev_err(chg->dev, "Couldn't read APSD_STATUS rc=%d\n", rc);
|
||||
return &smblib_apsd_results[0];
|
||||
return result;
|
||||
}
|
||||
smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat);
|
||||
smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", apsd_stat);
|
||||
|
||||
if (!(stat & APSD_DTC_STATUS_DONE_BIT))
|
||||
return &smblib_apsd_results[0];
|
||||
if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT))
|
||||
return result;
|
||||
|
||||
rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat);
|
||||
if (rc < 0) {
|
||||
dev_err(chg->dev, "Couldn't read APSD_RESULT_STATUS rc=%d\n",
|
||||
rc);
|
||||
return &smblib_apsd_results[0];
|
||||
return result;
|
||||
}
|
||||
stat &= APSD_RESULT_STATUS_MASK;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(smblib_apsd_results); i++) {
|
||||
if (smblib_apsd_results[i].bit == stat)
|
||||
return &smblib_apsd_results[i];
|
||||
result = &smblib_apsd_results[i];
|
||||
}
|
||||
|
||||
dev_err(chg->dev, "Couldn't find an APSD result for 0x%02x\n", stat);
|
||||
return &smblib_apsd_results[0];
|
||||
if (apsd_stat & QC_CHARGER_BIT) {
|
||||
/* since its a qc_charger, either return HVDCP3 or HVDCP2 */
|
||||
if (result != &smblib_apsd_results[HVDCP3])
|
||||
result = &smblib_apsd_results[HVDCP2];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -794,6 +844,37 @@ static int smblib_pl_enable_indirect_vote_callback(struct votable *votable,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int smblib_hvdcp_disable_vote_callback(struct votable *votable,
|
||||
void *data,
|
||||
int hvdcp_disable, const char *client)
|
||||
{
|
||||
struct smb_charger *chg = data;
|
||||
int rc;
|
||||
u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT
|
||||
| HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_EN_BIT;
|
||||
|
||||
/*
|
||||
* Disable the autonomous bit and auth bit for disabling hvdcp.
|
||||
* This ensures only qc 2.0 detection runs but no vbus
|
||||
* negotiation happens.
|
||||
*/
|
||||
if (hvdcp_disable)
|
||||
val = HVDCP_EN_BIT;
|
||||
|
||||
rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
|
||||
HVDCP_EN_BIT
|
||||
| HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT
|
||||
| HVDCP_AUTH_ALG_EN_CFG_BIT,
|
||||
val);
|
||||
if (rc < 0) {
|
||||
dev_err(chg->dev, "Couldn't %s hvdcp rc=%d\n",
|
||||
hvdcp_disable ? "disable" : "enable", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************
|
||||
* OTG REGULATOR *
|
||||
*****************/
|
||||
|
@ -2455,6 +2536,14 @@ static int smblib_create_votables(struct smb_charger *chg)
|
|||
return rc;
|
||||
}
|
||||
|
||||
chg->hvdcp_disable_votable = create_votable("HVDCP_DISABLE",
|
||||
VOTE_SET_ANY,
|
||||
smblib_hvdcp_disable_vote_callback,
|
||||
chg);
|
||||
if (IS_ERR(chg->hvdcp_disable_votable)) {
|
||||
rc = PTR_ERR(chg->hvdcp_disable_votable);
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,7 @@ struct smb_charger {
|
|||
struct votable *pl_disable_votable;
|
||||
struct votable *chg_disable_votable;
|
||||
struct votable *pl_enable_votable_indirect;
|
||||
struct votable *hvdcp_disable_votable;
|
||||
|
||||
/* work */
|
||||
struct work_struct bms_update_work;
|
||||
|
|
Loading…
Add table
Reference in a new issue