From 667b71ea21664412190ba5f0fdd38a0049bf61ba Mon Sep 17 00:00:00 2001 From: mahj8 Date: Thu, 1 Oct 2020 15:40:59 +0800 Subject: [PATCH] usb: pd: Add PDO Selection The PDO selection should be done by either the Kernel or User Space. Setup a callback that selects the PDO based on what the Voltage Max is set to when the function is called. refer to https://gerrit.mot.com/1403781 Based on Gerrit https://gerrit.mot.com/#/c/1301690/ Change-Id: I54a14b47e79637c681cc24c78a75a1206ecd903e Signed-off-by: mahj8 Reviewed-on: https://gerrit.mot.com/1770248 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- drivers/usb/pd/policy_engine.c | 103 +++++++++++++++++++++++++++++++++ include/linux/usb/usbpd.h | 5 ++ 2 files changed, 108 insertions(+) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 2839abb1d86c..53577aa7dd74 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -891,6 +891,32 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) pd->requested_current = curr; pd->requested_pdo = pdo_pos; + usbpd_warn(&pd->dev, "select_pdo: PDO:%d, %d uV, %d uA\n", + pdo_pos, uv, ua); + + return 0; +} + +static int pd_get_pdo(struct usbpd *pd, int pdo_pos, int *uv_max, int *uv_min, int *ua) +{ + u8 type; + u32 pdo = pd->received_pdos[pdo_pos - 1]; + + if (0 == pdo) + return -ENOTSUPP; + + type = PD_SRC_PDO_TYPE(pdo); + if (type == PD_SRC_PDO_TYPE_FIXED) { + *ua = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + *uv_max = *uv_min = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + } else if (type == PD_SRC_PDO_TYPE_AUGMENTED) { + *uv_max = PD_APDO_MAX_VOLT(pdo) * 100000; + *uv_min = PD_APDO_MIN_VOLT(pdo) * 100000; + *ua = PD_APDO_MAX_CURR(pdo) * 50000; + } else { + usbpd_err(&pd->dev, "Only Fixed or Programmable PDOs supported\n"); + return -ENOTSUPP; + } return 0; } @@ -4228,6 +4254,83 @@ static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr, return -EINVAL; } +int usbpd_select_pdo_match(struct usbpd *pd) +{ + int uv_diff = 0, max_uv_diff = 0, pdo = 0; + int pdo_max_uv = 0, pdo_min_uv = 0, pdo_ua = 0; + int uv_in, i; + int ret; + union power_supply_propval val; + + if (!pd) + return -EINVAL; + + mutex_lock(&pd->swap_lock); + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + /* Check the Max Input Voltage from USBPSY */ + power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, &val); + uv_in = val.intval; + /* start with 5 V */ + max_uv_diff = 5000000; + for (i = 1; i < 8; i++) { + if (!pd_get_pdo(pd, i, &pdo_max_uv, &pdo_min_uv, &pdo_ua)) { + if (pdo_max_uv <= uv_in) { + uv_diff = uv_in - pdo_max_uv; + if (uv_diff < max_uv_diff) { + max_uv_diff = uv_diff; + pdo = i; + } + } + } + } + + if (pdo < 1 || pdo > 7) { + usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo); + ret = -EINVAL; + goto out; + } + + pd_get_pdo(pd, pdo, &pdo_max_uv, &pdo_min_uv, &pdo_ua); + ret = pd_select_pdo(pd, pdo, pdo_max_uv, pdo_ua); + if (ret) + goto out; + else + ret = pdo_max_uv; + + reinit_completion(&pd->is_ready); + pd->send_request = true; + kick_sm(pd, 0); + + /* wait for operation to complete */ + if (!wait_for_completion_timeout(&pd->is_ready, + msecs_to_jiffies(1000))) { + usbpd_err(&pd->dev, "select_pdo: request timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* determine if request was accepted/rejected */ + if (pd->selected_pdo != pd->requested_pdo || + pd->current_voltage != pd->requested_voltage) { + usbpd_err(&pd->dev, "select_pdo: request rejected\n"); + ret = -EINVAL; + } + +out: + pd->send_request = false; + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_select_pdo_match); + static ssize_t select_pdo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { diff --git a/include/linux/usb/usbpd.h b/include/linux/usb/usbpd.h index 037abb0d25d0..378bbab17610 100644 --- a/include/linux/usb/usbpd.h +++ b/include/linux/usb/usbpd.h @@ -99,6 +99,11 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, int obj_pos, const u32 *vdos, int num_vdos); +/* + * Look for best match for PDO selection in Source Mode. + */ +int usbpd_select_pdo_match(struct usbpd *pd); + /* * Get current status of CC pin orientation. *