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:
Abhijeet Dharmapurikar 2016-10-10 16:43:43 -07:00
parent 9fde5f5830
commit 665d020cda
4 changed files with 123 additions and 19 deletions

View file

@ -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>;

View file

@ -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,

View file

@ -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;
}

View file

@ -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;