Merge "power_supply: Add additional USB PD properties"

This commit is contained in:
Linux Build Service Account 2016-10-15 01:19:58 -07:00 committed by Gerrit - the friendly Code Review server
commit cb74f5d749
4 changed files with 300 additions and 107 deletions

View file

@ -40,6 +40,9 @@ Optional properties:
- vconn-supply: Regulator that enables VCONN source output. This will
be supplied on the USB CC line that is not used for
communication when Ra resistance is detected.
- qcom,vconn-uses-external-source: Indicates whether VCONN supply is sourced
from an external regulator. If omitted, then it is
assumed it is connected to VBUS.
Example:
qcom,qpnp-pdphy@1700 {

View file

@ -267,6 +267,9 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(typec_power_role),
POWER_SUPPLY_ATTR(pd_allowed),
POWER_SUPPLY_ATTR(pd_active),
POWER_SUPPLY_ATTR(pd_in_hard_reset),
POWER_SUPPLY_ATTR(pd_current_max),
POWER_SUPPLY_ATTR(pd_usb_suspend_supported),
POWER_SUPPLY_ATTR(charger_temp),
POWER_SUPPLY_ATTR(charger_temp_max),
POWER_SUPPLY_ATTR(parallel_disable),

View file

@ -59,6 +59,7 @@ enum usbpd_state {
PE_PRS_SRC_SNK_SEND_SWAP,
PE_PRS_SRC_SNK_TRANSITION_TO_OFF,
PE_PRS_SRC_SNK_WAIT_SOURCE_ON,
PE_VCS_WAIT_FOR_VCONN,
};
static const char * const usbpd_state_strings[] = {
@ -94,6 +95,7 @@ static const char * const usbpd_state_strings[] = {
"PRS_SRC_SNK_Send_Swap",
"PRS_SRC_SNK_Transition_to_off",
"PRS_SRC_SNK_Wait_Source_on",
"VCS_Wait_for_VCONN",
};
enum usbpd_control_msg_type {
@ -170,6 +172,7 @@ static void *usbpd_ipc_log;
#define PS_SOURCE_OFF 750
#define SWAP_SOURCE_START_TIME 20
#define VDM_BUSY_TIME 50
#define VCONN_ON_TIME 100
/* tPSHardReset + tSafe0V + tSrcRecover + tSrcTurnOn */
#define SNK_HARD_RESET_RECOVER_TIME (35 + 650 + 1000 + 275)
@ -195,7 +198,7 @@ static void *usbpd_ipc_log;
#define PD_RDO_MISMATCH(rdo) ((rdo) >> 26 & 1)
#define PD_RDO_USB_COMM(rdo) ((rdo) >> 25 & 1)
#define PD_RDO_NO_USB_SUSP(rdo) ((rdo) >> 24 & 1)
#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 19 & 0x3FF)
#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 10 & 0x3FF)
#define PD_RDO_FIXED_CURR_MINMAX(rdo) ((rdo) & 0x3FF)
#define PD_SRC_PDO_TYPE(pdo) (((pdo) >> 30) & 3)
@ -223,9 +226,10 @@ static void *usbpd_ipc_log;
/* VDM header is the first 32-bit object following the 16-bit PD header */
#define VDM_HDR_SVID(hdr) ((hdr) >> 16)
#define VDM_HDR_TYPE(hdr) ((hdr) & 0x8000)
#define VDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3)
#define VDM_HDR_CMD(hdr) ((hdr) & 0x1f)
#define VDM_IS_SVDM(hdr) ((hdr) & 0x8000)
#define SVDM_HDR_OBJ_POS(hdr) (((hdr) >> 8) & 0x7)
#define SVDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3)
#define SVDM_HDR_CMD(hdr) ((hdr) & 0x1f)
#define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \
(((svid) << 16) | (1 << 15) | ((ver) << 13) \
@ -253,15 +257,11 @@ static bool ss_dev = true;
module_param(ss_dev, bool, S_IRUSR | S_IWUSR);
static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */
static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */
0x0002D096, /* 9V @ 1.5A */
0x0003C064 }; /* 12V @ 1A */
static const u32 default_snk_caps[] = { 0x2601905A }; /* 5V @ 900mA */
struct vdm_tx {
u32 data[7];
int size;
struct list_head entry;
};
struct usbpd {
@ -269,6 +269,7 @@ struct usbpd {
struct workqueue_struct *wq;
struct work_struct sm_work;
struct hrtimer timer;
bool sm_queued;
struct extcon_dev *extcon;
@ -307,6 +308,7 @@ struct usbpd {
struct regulator *vbus;
struct regulator *vconn;
bool vconn_enabled;
bool vconn_is_external;
u8 tx_msgid;
u8 rx_msgid;
@ -315,8 +317,9 @@ struct usbpd {
enum vdm_state vdm_state;
u16 *discovered_svids;
int num_svids;
struct vdm_tx *vdm_tx;
struct vdm_tx *vdm_tx_retry;
struct list_head vdm_tx_queue;
struct list_head svid_handlers;
struct list_head instance;
@ -440,6 +443,12 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos)
}
pd->requested_voltage = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000;
/* Can't sink more than 5V if VCONN is sourced from the VBUS input */
if (pd->vconn_enabled && !pd->vconn_is_external &&
pd->requested_voltage > 5000000)
return -ENOTSUPP;
pd->requested_current = curr;
pd->requested_pdo = pdo_pos;
pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10,
@ -485,6 +494,17 @@ static void pd_send_hard_reset(struct usbpd *pd)
pd->hard_reset = true;
}
static void kick_sm(struct usbpd *pd, int ms)
{
pm_stay_awake(&pd->dev);
pd->sm_queued = true;
if (ms)
hrtimer_start(&pd->timer, ms_to_ktime(ms), HRTIMER_MODE_REL);
else
queue_work(pd->wq, &pd->sm_work);
}
static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
{
if (type != HARD_RESET_SIG) {
@ -497,7 +517,7 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
pd->hard_reset = true;
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
}
static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
@ -534,6 +554,10 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
pd->rx_msgid = PD_MSG_HDR_ID(header);
/* discard Pings */
if (PD_MSG_HDR_TYPE(header) == MSG_PING && !len)
return;
/* check header's count field to see if it matches len */
if (PD_MSG_HDR_COUNT(header) != (len / 4)) {
usbpd_err(&pd->dev, "header count (%d) mismatch, len=%ld\n",
@ -541,11 +565,18 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
return;
}
/* block until previous message has been consumed by usbpd_sm */
if (pd->rx_msg_type)
flush_work(&pd->sm_work);
pd->rx_msg_type = PD_MSG_HDR_TYPE(header);
pd->rx_msg_len = PD_MSG_HDR_COUNT(header);
memcpy(&pd->rx_payload, buf, len);
queue_work(pd->wq, &pd->sm_work);
usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n",
pd->rx_msg_type, pd->rx_msg_len);
kick_sm(pd, 0);
}
static void phy_shutdown(struct usbpd *pd)
@ -587,7 +618,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->in_pr_swap = false;
set_power_role(pd, PR_NONE);
pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
break;
/* Source states */
@ -634,20 +665,22 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->current_state = PE_SRC_SEND_CAPABILITIES;
if (pd->in_pr_swap) {
pd->in_pr_swap = false;
hrtimer_start(&pd->timer,
ms_to_ktime(SWAP_SOURCE_START_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SWAP_SOURCE_START_TIME);
break;
}
/* fall-through */
case PE_SRC_SEND_CAPABILITIES:
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
break;
case PE_SRC_NEGOTIATE_CAPABILITY:
if (PD_RDO_OBJ_POS(pd->rdo) != 1) {
if (PD_RDO_OBJ_POS(pd->rdo) != 1 ||
PD_RDO_FIXED_CURR(pd->rdo) >
PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps) ||
PD_RDO_FIXED_CURR_MINMAX(pd->rdo) >
PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps)) {
/* send Reject */
ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG);
if (ret) {
@ -717,6 +750,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
case PE_SRC_TRANSITION_TO_DEFAULT:
pd->hard_reset = false;
if (pd->vconn_enabled)
regulator_disable(pd->vconn);
regulator_disable(pd->vbus);
@ -747,7 +782,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
case PE_SRC_HARD_RESET:
case PE_SNK_HARD_RESET:
/* hard reset may sleep; handle it in the workqueue */
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
break;
case PE_SRC_SEND_SOFT_RESET:
@ -765,8 +800,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
/* wait for ACCEPT */
hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SENDER_RESPONSE_TIME);
break;
/* Sink states */
@ -786,9 +820,11 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
}
/* Reset protocol layer */
pd->tx_msgid = 0;
pd->rx_msgid = -1;
pd->rx_msg_len = 0;
pd->rx_msg_type = 0;
pd->rx_msgid = -1;
if (!pd->in_pr_swap) {
if (pd->pd_phy_opened) {
@ -819,11 +855,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
case PE_SNK_WAIT_FOR_CAPABILITIES:
if (pd->rx_msg_len && pd->rx_msg_type)
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
else
hrtimer_start(&pd->timer,
ms_to_ktime(SINK_WAIT_CAP_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SINK_WAIT_CAP_TIME);
break;
case PE_SNK_EVALUATE_CAPABILITY:
@ -841,18 +875,19 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
case PE_SNK_SELECT_CAPABILITY:
ret = pd_send_msg(pd, MSG_REQUEST, &pd->rdo, 1, SOP_MSG);
if (ret)
if (ret) {
usbpd_err(&pd->dev, "Error sending Request\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
break;
}
/* wait for ACCEPT */
hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SENDER_RESPONSE_TIME);
break;
case PE_SNK_TRANSITION_SINK:
/* wait for PS_RDY */
hrtimer_start(&pd->timer, ms_to_ktime(PS_TRANSITION_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, PS_TRANSITION_TIME);
break;
case PE_SNK_READY:
@ -861,6 +896,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
case PE_SNK_TRANSITION_TO_DEFAULT:
pd->hard_reset = false;
if (pd->current_dr != DR_UFP) {
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0);
@ -877,17 +914,13 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->vconn_enabled = false;
}
pd->tx_msgid = 0;
val.intval = pd->requested_voltage; /* set range back to 5V */
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
pd->current_voltage = pd->requested_voltage;
/* max time for hard reset to toggle vbus off/on */
hrtimer_start(&pd->timer,
ms_to_ktime(SNK_HARD_RESET_RECOVER_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SNK_HARD_RESET_RECOVER_TIME);
break;
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
@ -904,8 +937,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd_phy_update_roles(pd->current_dr, PR_SRC);
/* wait for PS_RDY */
hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF),
HRTIMER_MODE_REL);
kick_sm(pd, PS_SOURCE_OFF);
break;
default:
@ -929,10 +961,10 @@ int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
/* already connected with this SVID discovered? */
if (pd->vdm_state >= DISCOVERED_SVIDS) {
u16 *psvid;
int i;
for (psvid = pd->discovered_svids; *psvid; psvid++) {
if (*psvid == hdlr->svid) {
for (i = 0; i < pd->num_svids; i++) {
if (pd->discovered_svids[i] == hdlr->svid) {
if (hdlr->connect)
hdlr->connect(hdlr);
break;
@ -954,7 +986,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
{
struct vdm_tx *vdm_tx;
if (!pd->in_explicit_contract)
if (!pd->in_explicit_contract || pd->vdm_tx)
return -EBUSY;
vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL);
@ -967,8 +999,10 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
vdm_tx->size = num_vdos + 1; /* include the header */
/* VDM will get sent in PE_SRC/SNK_READY state handling */
list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue);
queue_work(pd->wq, &pd->sm_work);
pd->vdm_tx = vdm_tx;
/* slight delay before queuing to prioritize handling of incoming VDM */
kick_sm(pd, 5);
return 0;
}
@ -994,8 +1028,8 @@ static void handle_vdm_rx(struct usbpd *pd)
u16 svid = VDM_HDR_SVID(vdm_hdr);
u16 *psvid;
u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */
u8 cmd = VDM_HDR_CMD(vdm_hdr);
u8 cmd_type = VDM_HDR_CMD_TYPE(vdm_hdr);
u8 cmd = SVDM_HDR_CMD(vdm_hdr);
u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr);
struct usbpd_svid_handler *handler;
usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n",
@ -1005,7 +1039,7 @@ static void handle_vdm_rx(struct usbpd *pd)
handler = find_svid_handler(pd, svid);
/* Unstructured VDM */
if (!VDM_HDR_TYPE(vdm_hdr)) {
if (!VDM_IS_SVDM(vdm_hdr)) {
if (handler && handler->vdm_received)
handler->vdm_received(handler, vdm_hdr, vdos, num_vdos);
return;
@ -1016,7 +1050,19 @@ static void handle_vdm_rx(struct usbpd *pd)
switch (cmd_type) {
case SVDM_CMD_TYPE_INITIATOR:
if (cmd == USBPD_SVDM_DISCOVER_IDENTITY) {
/*
* if this interrupts a previous exchange, abort the previous
* outgoing response
*/
if (pd->vdm_tx) {
usbpd_dbg(&pd->dev, "Discarding previously queued SVDM tx (SVID:0x%04x)\n",
VDM_HDR_SVID(pd->vdm_tx->data[0]));
kfree(pd->vdm_tx);
pd->vdm_tx = NULL;
}
if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) {
u32 tx_vdos[3] = {
ID_HDR_USB_HOST | ID_HDR_USB_DEVICE |
ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID,
@ -1027,9 +1073,9 @@ static void handle_vdm_rx(struct usbpd *pd)
usbpd_send_svdm(pd, USBPD_SID, cmd,
SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3);
} else {
usbpd_send_svdm(pd, USBPD_SID, cmd,
SVDM_CMD_TYPE_RESP_NAK, 0, NULL, 0);
} else if (cmd != USBPD_SVDM_ATTENTION) {
usbpd_send_svdm(pd, svid, cmd, SVDM_CMD_TYPE_RESP_NAK,
SVDM_HDR_OBJ_POS(vdm_hdr), NULL, 0);
}
break;
@ -1061,10 +1107,9 @@ static void handle_vdm_rx(struct usbpd *pd)
kfree(pd->vdm_tx_retry);
pd->vdm_tx_retry = NULL;
kfree(pd->discovered_svids);
/* TODO: handle > 12 SVIDs */
pd->discovered_svids = kzalloc((2 * num_vdos + 1) *
if (!pd->discovered_svids) {
pd->num_svids = 2 * num_vdos;
pd->discovered_svids = kcalloc(pd->num_svids,
sizeof(u16),
GFP_KERNEL);
if (!pd->discovered_svids) {
@ -1072,8 +1117,27 @@ static void handle_vdm_rx(struct usbpd *pd)
break;
}
/* convert 32-bit VDOs to list of 16-bit SVIDs */
psvid = pd->discovered_svids;
} else { /* handle > 12 SVIDs */
void *ptr;
size_t oldsize = pd->num_svids * sizeof(u16);
size_t newsize = oldsize +
(2 * num_vdos * sizeof(u16));
ptr = krealloc(pd->discovered_svids, newsize,
GFP_KERNEL);
if (!ptr) {
usbpd_err(&pd->dev, "unable to realloc SVIDs\n");
break;
}
pd->discovered_svids = ptr;
psvid = pd->discovered_svids + pd->num_svids;
memset(psvid, 0, (2 * num_vdos));
pd->num_svids += 2 * num_vdos;
}
/* convert 32-bit VDOs to list of 16-bit SVIDs */
for (i = 0; i < num_vdos * 2; i++) {
/*
* Within each 32-bit VDO,
@ -1097,8 +1161,22 @@ static void handle_vdm_rx(struct usbpd *pd)
usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n",
svid);
*psvid++ = svid;
}
}
/* if SVID supported notify handler */
/* if more than 12 SVIDs, resend the request */
if (num_vdos == 6 && vdos[5] != 0) {
usbpd_send_svdm(pd, USBPD_SID,
USBPD_SVDM_DISCOVER_SVIDS,
SVDM_CMD_TYPE_INITIATOR, 0,
NULL, 0);
break;
}
/* now that all SVIDs are discovered, notify handlers */
for (i = 0; i < pd->num_svids; i++) {
svid = pd->discovered_svids[i];
if (svid) {
handler = find_svid_handler(pd, svid);
if (handler && handler->connect)
handler->connect(handler);
@ -1148,10 +1226,9 @@ static void handle_vdm_rx(struct usbpd *pd)
}
/* wait tVDMBusy, then retry */
list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue);
pd->vdm_tx = pd->vdm_tx_retry;
pd->vdm_tx_retry = NULL;
hrtimer_start(&pd->timer, ms_to_ktime(VDM_BUSY_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, VDM_BUSY_TIME);
break;
default:
break;
@ -1165,16 +1242,14 @@ static void handle_vdm_tx(struct usbpd *pd)
int ret;
/* only send one VDM at a time */
if (!list_empty(&pd->vdm_tx_queue)) {
struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue,
struct vdm_tx, entry);
u32 vdm_hdr = vdm_tx->data[0];
if (pd->vdm_tx) {
u32 vdm_hdr = pd->vdm_tx->data[0];
ret = pd_send_msg(pd, MSG_VDM, vdm_tx->data, vdm_tx->size,
SOP_MSG);
ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data,
pd->vdm_tx->size, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending VDM command %d\n",
VDM_HDR_CMD(vdm_tx->data[0]));
SVDM_HDR_CMD(pd->vdm_tx->data[0]));
usbpd_set_state(pd, pd->current_pr == PR_SRC ?
PE_SRC_SEND_SOFT_RESET :
PE_SNK_SEND_SOFT_RESET);
@ -1183,24 +1258,25 @@ static void handle_vdm_tx(struct usbpd *pd)
return;
}
list_del(&vdm_tx->entry);
/*
* special case: keep initiated Discover ID/SVIDs
* around in case we need to re-try when receiving BUSY
*/
if (VDM_HDR_TYPE(vdm_hdr) &&
VDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
VDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
if (VDM_IS_SVDM(vdm_hdr) &&
SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
if (pd->vdm_tx_retry) {
usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n",
VDM_HDR_CMD(pd->vdm_tx_retry->data[0]));
SVDM_HDR_CMD(
pd->vdm_tx_retry->data[0]));
kfree(pd->vdm_tx_retry);
}
pd->vdm_tx_retry = vdm_tx;
pd->vdm_tx_retry = pd->vdm_tx;
} else {
kfree(vdm_tx);
kfree(pd->vdm_tx);
}
pd->vdm_tx = NULL;
}
}
@ -1216,13 +1292,9 @@ static void reset_vdm_state(struct usbpd *pd)
pd->vdm_tx_retry = NULL;
kfree(pd->discovered_svids);
pd->discovered_svids = NULL;
while (!list_empty(&pd->vdm_tx_queue)) {
struct vdm_tx *vdm_tx =
list_first_entry(&pd->vdm_tx_queue,
struct vdm_tx, entry);
list_del(&vdm_tx->entry);
kfree(vdm_tx);
}
pd->num_svids = 0;
kfree(pd->vdm_tx);
pd->vdm_tx = NULL;
}
static void dr_swap(struct usbpd *pd)
@ -1252,6 +1324,34 @@ static void dr_swap(struct usbpd *pd)
pd_phy_update_roles(pd->current_dr, pd->current_pr);
}
static void vconn_swap(struct usbpd *pd)
{
int ret;
if (pd->vconn_enabled) {
pd->current_state = PE_VCS_WAIT_FOR_VCONN;
kick_sm(pd, VCONN_ON_TIME);
} else {
ret = regulator_enable(pd->vconn);
if (ret) {
usbpd_err(&pd->dev, "Unable to enable vconn\n");
return;
}
pd->vconn_enabled = true;
ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending PS_RDY\n");
usbpd_set_state(pd, pd->current_pr == PR_SRC ?
PE_SRC_SEND_SOFT_RESET :
PE_SNK_SEND_SOFT_RESET);
return;
}
}
}
/* Handles current state and determines transitions */
static void usbpd_sm(struct work_struct *w)
{
@ -1265,6 +1365,7 @@ static void usbpd_sm(struct work_struct *w)
usbpd_state_strings[pd->current_state]);
hrtimer_cancel(&pd->timer);
pd->sm_queued = false;
if (pd->rx_msg_len)
data_recvd = pd->rx_msg_type;
@ -1274,7 +1375,7 @@ static void usbpd_sm(struct work_struct *w)
/* Disconnect? */
if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
if (pd->current_state == PE_UNKNOWN)
return;
goto sm_done;
usbpd_info(&pd->dev, "USB PD disconnect\n");
@ -1328,7 +1429,7 @@ static void usbpd_sm(struct work_struct *w)
pd->current_state = PE_UNKNOWN;
return;
goto sm_done;
}
/* Hard reset? */
@ -1339,7 +1440,8 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
else
usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
pd->hard_reset = false;
goto sm_done;
}
/* Soft reset? */
@ -1400,8 +1502,7 @@ static void usbpd_sm(struct work_struct *w)
break;
}
hrtimer_start(&pd->timer, ms_to_ktime(SRC_CAP_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SRC_CAP_TIME);
break;
}
@ -1416,14 +1517,16 @@ static void usbpd_sm(struct work_struct *w)
/* wait for REQUEST */
pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT;
hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SENDER_RESPONSE_TIME);
break;
case PE_SRC_SEND_CAPABILITIES_WAIT:
if (data_recvd == MSG_REQUEST) {
pd->rdo = pd->rx_payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
} else if (data_recvd || ctrl_recvd) {
usbpd_err(&pd->dev, "Unexpected message received\n");
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
} else {
usbpd_set_state(pd, PE_SRC_HARD_RESET);
}
@ -1439,6 +1542,14 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
break;
}
} else if (ctrl_recvd == MSG_GET_SINK_CAP) {
ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
default_snk_caps,
ARRAY_SIZE(default_snk_caps), SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Sink Caps\n");
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
}
} else if (data_recvd == MSG_REQUEST) {
pd->rdo = pd->rx_payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
@ -1470,10 +1581,17 @@ static void usbpd_sm(struct work_struct *w)
}
pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
hrtimer_start(&pd->timer,
ms_to_ktime(SRC_TRANSITION_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SRC_TRANSITION_TIME);
break;
} else if (ctrl_recvd == MSG_VCONN_SWAP) {
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Accept\n");
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
break;
}
vconn_swap(pd);
} else {
if (data_recvd == MSG_VDM)
handle_vdm_rx(pd);
@ -1538,6 +1656,9 @@ static void usbpd_sm(struct work_struct *w)
else
usbpd_set_state(pd,
PE_SNK_WAIT_FOR_CAPABILITIES);
} else if (pd->rx_msg_type) {
usbpd_err(&pd->dev, "Invalid response to sink request\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
} else {
/* timed out; go to hard reset */
usbpd_set_state(pd, PE_SNK_HARD_RESET);
@ -1566,9 +1687,9 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_READY:
if (data_recvd == MSG_SOURCE_CAPABILITIES)
if (data_recvd == MSG_SOURCE_CAPABILITIES) {
usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
else if (ctrl_recvd == MSG_GET_SINK_CAP) {
} else if (ctrl_recvd == MSG_GET_SINK_CAP) {
ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
default_snk_caps,
ARRAY_SIZE(default_snk_caps), SOP_MSG);
@ -1576,6 +1697,15 @@ static void usbpd_sm(struct work_struct *w)
usbpd_err(&pd->dev, "Error sending Sink Caps\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
}
} else if (ctrl_recvd == MSG_GET_SOURCE_CAP) {
ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
default_src_caps,
ARRAY_SIZE(default_src_caps), SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending SRC CAPs\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
break;
}
} else if (ctrl_recvd == MSG_DR_SWAP) {
if (pd->vdm_state == MODE_ENTERED) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
@ -1606,6 +1736,32 @@ static void usbpd_sm(struct work_struct *w)
pd->in_pr_swap = true;
usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
break;
} else if (ctrl_recvd == MSG_VCONN_SWAP) {
/*
* if VCONN is connected to VBUS, make sure we are
* not in high voltage contract, otherwise reject.
*/
if (!pd->vconn_is_external &&
(pd->requested_voltage > 5000000)) {
ret = pd_send_msg(pd, MSG_REJECT, NULL, 0,
SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Reject\n");
usbpd_set_state(pd,
PE_SNK_SEND_SOFT_RESET);
}
break;
}
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending Accept\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
break;
}
vconn_swap(pd);
} else {
if (data_recvd == MSG_VDM)
handle_vdm_rx(pd);
@ -1623,7 +1779,7 @@ static void usbpd_sm(struct work_struct *w)
POWER_SUPPLY_PROP_TYPEC_MODE, &val);
if (val.intval == POWER_SUPPLY_TYPEC_NONE) {
pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
}
}
break;
@ -1701,8 +1857,7 @@ static void usbpd_sm(struct work_struct *w)
}
pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
hrtimer_start(&pd->timer, ms_to_ktime(SRC_TRANSITION_TIME),
HRTIMER_MODE_REL);
kick_sm(pd, SRC_TRANSITION_TIME);
break;
case PE_PRS_SRC_SNK_TRANSITION_TO_OFF:
@ -1727,8 +1882,7 @@ static void usbpd_sm(struct work_struct *w)
}
pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON;
hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_ON),
HRTIMER_MODE_REL);
kick_sm(pd, PS_SOURCE_ON);
break;
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
@ -1778,6 +1932,26 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_STARTUP);
break;
case PE_VCS_WAIT_FOR_VCONN:
if (ctrl_recvd == MSG_PS_RDY) {
/*
* hopefully redundant check but in case not enabled
* avoids unbalanced regulator disable count
*/
if (pd->vconn_enabled)
regulator_disable(pd->vconn);
pd->vconn_enabled = false;
pd->current_state = pd->current_pr == PR_SRC ?
PE_SRC_READY : PE_SNK_READY;
} else {
/* timed out; go to hard reset */
usbpd_set_state(pd, pd->current_pr == PR_SRC ?
PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
}
break;
default:
usbpd_err(&pd->dev, "Unhandled state %s\n",
usbpd_state_strings[pd->current_state]);
@ -1786,6 +1960,10 @@ static void usbpd_sm(struct work_struct *w)
/* Rx message should have been consumed now */
pd->rx_msg_type = pd->rx_msg_len = 0;
sm_done:
if (!pd->sm_queued)
pm_relax(&pd->dev);
}
static inline const char *src_current(enum power_supply_typec_mode typec_mode)
@ -1897,7 +2075,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
switch (typec_mode) {
/* Disconnect */
case POWER_SUPPLY_TYPEC_NONE:
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
break;
/* Sink states */
@ -1909,7 +2087,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
if (pd->current_pr != PR_SINK ||
pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) {
pd->current_pr = PR_SINK;
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
}
break;
@ -1921,7 +2099,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
"" : " (powered)");
if (pd->current_pr != PR_SRC) {
pd->current_pr = PR_SRC;
queue_work(pd->wq, &pd->sm_work);
kick_sm(pd, 0);
}
break;
@ -2359,6 +2537,10 @@ struct usbpd *usbpd_create(struct device *parent)
if (ret)
goto free_pd;
ret = device_init_wakeup(&pd->dev, true);
if (ret)
goto free_pd;
ret = device_add(&pd->dev);
if (ret)
goto free_pd;
@ -2414,11 +2596,13 @@ struct usbpd *usbpd_create(struct device *parent)
goto unreg_psy;
}
pd->vconn_is_external = device_property_present(parent,
"qcom,vconn-uses-external-source");
pd->current_pr = PR_NONE;
pd->current_dr = DR_NONE;
list_add_tail(&pd->instance, &_usbpd);
INIT_LIST_HEAD(&pd->vdm_tx_queue);
INIT_LIST_HEAD(&pd->svid_handlers);
/* force read initial power_supply values */

View file

@ -216,6 +216,9 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
POWER_SUPPLY_PROP_PD_ALLOWED,
POWER_SUPPLY_PROP_PD_ACTIVE,
POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
POWER_SUPPLY_PROP_PD_CURRENT_MAX,
POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED,
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
POWER_SUPPLY_PROP_PARALLEL_DISABLE,