| From d71e4ca819b48079fca0b06222f6d96e4ab2f5c5 Mon Sep 17 00:00:00 2001 |
| From: Jameson Thies <jthies@google.com> |
| Date: Wed, 17 Jul 2024 00:49:48 +0000 |
| Subject: [PATCH] CHROMIUM: usb: typec: ucsi: Set sink path based on UCSI |
| charge control |
| |
| Add POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX as a property to the UCSI |
| power supply driver. When set to a positive value, enable the |
| connector's sink path. When set to a negative value, disable the |
| connector's sink path. |
| |
| BUG=b:351778077 |
| UPSTREAM-TASK=b:353605100 |
| TEST=emerge-brox chromeos-kernel-6_6, writing to |
| charge_control_limit_max causes a PR_Swap and sends SET_SINK_PATH when |
| expected. |
| |
| Change-Id: If8b55834f3a37ac58002dc0c91af0b320b98448f |
| Signed-off-by: Jameson Thies <jthies@google.com> |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/5713892 |
| Reviewed-by: Benson Leung <bleung@google.com> |
| Kcr-patch: 3c9f08de7f8f62174be9dd50bc7e244818bfaf3030dcbac4e6f10b60.patch |
| --- |
| drivers/usb/typec/ucsi/psy.c | 52 +++++++++++++++++++++++++++++++++++ |
| drivers/usb/typec/ucsi/ucsi.c | 18 ++++++++++++ |
| drivers/usb/typec/ucsi/ucsi.h | 5 ++++ |
| 3 files changed, 75 insertions(+) |
| |
| diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c |
| index 37414b119f6567f99cb1b7a32024d27b8e9d6721..b351839c8bf33fbc7b05649654ca10e1752a8898 100644 |
| --- a/drivers/usb/typec/ucsi/psy.c |
| +++ b/drivers/usb/typec/ucsi/psy.c |
| @@ -30,6 +30,7 @@ static enum power_supply_property ucsi_psy_props[] = { |
| POWER_SUPPLY_PROP_CURRENT_NOW, |
| POWER_SUPPLY_PROP_SCOPE, |
| POWER_SUPPLY_PROP_STATUS, |
| + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, |
| }; |
| |
| static int ucsi_psy_get_scope(struct ucsi_connector *con, |
| @@ -276,11 +277,60 @@ static int ucsi_psy_get_prop(struct power_supply *psy, |
| return ucsi_psy_get_scope(con, val); |
| case POWER_SUPPLY_PROP_STATUS: |
| return ucsi_psy_get_status(con, val); |
| + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: |
| + val->intval = 0; |
| + return 0; |
| + default: |
| + return -EINVAL; |
| + } |
| +} |
| + |
| +static int ucsi_psy_set_charge_control_limit_max(struct ucsi_connector *con, |
| + const union power_supply_propval *val) |
| +{ |
| + /* |
| + * Writing a negative value to the charge control limit max implies the |
| + * port should not accept charge. Disable the sink path for a negative |
| + * charge control limit, and enable the sink path for a positive charge |
| + * control limit. If the requested charge port is a source, update the |
| + * power role. |
| + */ |
| + int ret; |
| + bool sink_path = false; |
| + |
| + if (val->intval >= 0) { |
| + sink_path = true; |
| + if (!con->typec_cap.ops || !con->typec_cap.ops->pr_set) |
| + return -EINVAL; |
| + |
| + ret = con->typec_cap.ops->pr_set(con->port, TYPEC_SINK); |
| + if (ret < 0) |
| + return ret; |
| + } |
| + |
| + return ucsi_set_sink_path(con, sink_path); |
| +} |
| + |
| +static int ucsi_psy_set_prop(struct power_supply *psy, |
| + enum power_supply_property psp, |
| + const union power_supply_propval *val) |
| +{ |
| + struct ucsi_connector *con = power_supply_get_drvdata(psy); |
| + |
| + switch (psp) { |
| + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: |
| + return ucsi_psy_set_charge_control_limit_max(con, val); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| +static int ucsi_psy_prop_is_writeable(struct power_supply *psy, |
| + enum power_supply_property psp) |
| +{ |
| + return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX; |
| +} |
| + |
| int ucsi_register_port_psy(struct ucsi_connector *con) |
| { |
| struct power_supply_config psy_cfg = {}; |
| @@ -303,6 +353,8 @@ int ucsi_register_port_psy(struct ucsi_connector *con) |
| con->psy_desc.properties = ucsi_psy_props; |
| con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props); |
| con->psy_desc.get_property = ucsi_psy_get_prop; |
| + con->psy_desc.set_property = ucsi_psy_set_prop; |
| + con->psy_desc.property_is_writeable = ucsi_psy_prop_is_writeable; |
| |
| con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg); |
| |
| diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c |
| index e0f3925e401b3d6acf794754a0255c32fe8f01b9..2f0202b3b30239e40e1ac901a3f4fef996ec0d33 100644 |
| --- a/drivers/usb/typec/ucsi/ucsi.c |
| +++ b/drivers/usb/typec/ucsi/ucsi.c |
| @@ -1511,6 +1511,24 @@ static const struct typec_operations ucsi_ops = { |
| .pr_set = ucsi_pr_swap |
| }; |
| |
| +int ucsi_set_sink_path(struct ucsi_connector *con, bool sink_path) |
| +{ |
| + struct ucsi *ucsi = con->ucsi; |
| + u64 command; |
| + int ret; |
| + |
| + if (ucsi->version < UCSI_VERSION_2_0) |
| + return -EOPNOTSUPP; |
| + |
| + command = UCSI_SET_SINK_PATH | UCSI_CONNECTOR_NUMBER(con->num); |
| + command |= UCSI_SET_SINK_PATH_SINK_PATH(sink_path); |
| + ret = ucsi_send_command(ucsi, command, NULL, 0); |
| + if (ret < 0) |
| + dev_err(con->ucsi->dev, "SET_SINK_PATH failed (%d)\n", ret); |
| + |
| + return ret; |
| +} |
| + |
| /* Caller must call fwnode_handle_put() after use */ |
| static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) |
| { |
| diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h |
| index 2a72c618f5090463e92e3a6a58c2dd69f0e349c9..a21a3c90729494dc6e9f59808f97719892f57617 100644 |
| --- a/drivers/usb/typec/ucsi/ucsi.h |
| +++ b/drivers/usb/typec/ucsi/ucsi.h |
| @@ -115,6 +115,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); |
| #define UCSI_GET_CONNECTOR_STATUS 0x12 |
| #define UCSI_GET_ERROR_STATUS 0x13 |
| #define UCSI_GET_PD_MESSAGE 0x15 |
| +#define UCSI_SET_SINK_PATH 0x1c |
| |
| #define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) |
| #define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff) |
| @@ -190,6 +191,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); |
| #define UCSI_GET_PD_MESSAGE_TYPE_IDENTITY 4 |
| #define UCSI_GET_PD_MESSAGE_TYPE_REVISION 5 |
| |
| +/* SET_SINK_PATH command bits */ |
| +#define UCSI_SET_SINK_PATH_SINK_PATH(_r_) (((_r_) ? 1 : 0) << 23) |
| + |
| /* -------------------------------------------------------------------------- */ |
| |
| /* Error information returned by PPM in response to GET_ERROR_STATUS command. */ |
| @@ -461,6 +465,7 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command, |
| |
| void ucsi_altmode_update_active(struct ucsi_connector *con); |
| int ucsi_resume(struct ucsi *ucsi); |
| +int ucsi_set_sink_path(struct ucsi_connector *con, bool sink_path); |
| |
| void ucsi_notify_common(struct ucsi *ucsi, u32 cci); |
| int ucsi_sync_control_common(struct ucsi *ucsi, u64 command); |
| -- |
| 2.47.0.277.g8800431eea-goog |
| |