2020-10-29 22:29:31

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 0/7] platform/chrome: cros_ec_typec: Register partner PD information

This series adds support to retrieve Type C PD(Power Delivery) Discovery
information from the Chrome OS EC, and register this information with
the Type C connector class framework.

There are also a couple of patches which fix minor bugs with the
existing cros-ec-typec driver.

v1:
https://lore.kernel.org/lkml/[email protected]/

Changes in v2:
- Added missing static to function definition.
- Changes alt mode list traversal based on review comments.
- Rebased series on top of chrome-platform-5.11.

Prashant Malani (7):
platform/chrome: cros_ec_typec: Relocate set_port_params_v*()
functions
platform/chrome: cros_ec_typec: Fix remove partner logic
platform/chrome: cros_ec_typec: Clear partner identity on device
removal
platform/chrome: cros_ec: Import Type C host commands
platform/chrome: cros_ec_typec: Introduce TYPEC_STATUS
platform/chrome: cros_ec_typec: Parse partner PD ID VDOs
platform/chrome: cros_ec_typec: Register partner altmodes

drivers/platform/chrome/cros_ec_typec.c | 329 ++++++++++++++----
.../linux/platform_data/cros_ec_commands.h | 155 +++++++++
2 files changed, 415 insertions(+), 69 deletions(-)

--
2.29.1.341.ge80a0c044ae-goog


2020-10-29 22:29:46

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 1/7] platform/chrome: cros_ec_typec: Relocate set_port_params_v*() functions

Move the cros_typec_set_port_params_v0/v1() functions closer to the
place where they are called, cros_typec_port_update().

While we are performing the relocation, also move cros_typec_get_mux_info()
closer to its call-site.

No functional changes are introduced by this commit.

Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- No changes.

drivers/platform/chrome/cros_ec_typec.c | 136 ++++++++++++------------
1 file changed, 68 insertions(+), 68 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 31be31161350..49083e21317d 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -329,74 +329,6 @@ static int cros_typec_ec_command(struct cros_typec_data *typec,
return ret;
}

-static void cros_typec_set_port_params_v0(struct cros_typec_data *typec,
- int port_num, struct ec_response_usb_pd_control *resp)
-{
- struct typec_port *port = typec->ports[port_num]->port;
- enum typec_orientation polarity;
-
- if (!resp->enabled)
- polarity = TYPEC_ORIENTATION_NONE;
- else if (!resp->polarity)
- polarity = TYPEC_ORIENTATION_NORMAL;
- else
- polarity = TYPEC_ORIENTATION_REVERSE;
-
- typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK);
- typec_set_orientation(port, polarity);
-}
-
-static void cros_typec_set_port_params_v1(struct cros_typec_data *typec,
- int port_num, struct ec_response_usb_pd_control_v1 *resp)
-{
- struct typec_port *port = typec->ports[port_num]->port;
- enum typec_orientation polarity;
- bool pd_en;
- int ret;
-
- if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
- polarity = TYPEC_ORIENTATION_NONE;
- else if (!resp->polarity)
- polarity = TYPEC_ORIENTATION_NORMAL;
- else
- polarity = TYPEC_ORIENTATION_REVERSE;
- typec_set_orientation(port, polarity);
- typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ?
- TYPEC_HOST : TYPEC_DEVICE);
- typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ?
- TYPEC_SOURCE : TYPEC_SINK);
- typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ?
- TYPEC_SOURCE : TYPEC_SINK);
-
- /* Register/remove partners when a connect/disconnect occurs. */
- if (resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) {
- if (typec->ports[port_num]->partner)
- return;
-
- pd_en = resp->enabled & PD_CTRL_RESP_ENABLED_PD_CAPABLE;
- ret = cros_typec_add_partner(typec, port_num, pd_en);
- if (ret)
- dev_warn(typec->dev,
- "Failed to register partner on port: %d\n",
- port_num);
- } else {
- if (!typec->ports[port_num]->partner)
- return;
- cros_typec_remove_partner(typec, port_num);
- }
-}
-
-static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
- struct ec_response_usb_pd_mux_info *resp)
-{
- struct ec_params_usb_pd_mux_info req = {
- .port = port_num,
- };
-
- return cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_INFO, &req,
- sizeof(req), resp, sizeof(*resp));
-}
-
static int cros_typec_usb_safe_state(struct cros_typec_port *port)
{
port->state.mode = TYPEC_STATE_SAFE;
@@ -573,6 +505,74 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
return ret;
}

+static void cros_typec_set_port_params_v0(struct cros_typec_data *typec,
+ int port_num, struct ec_response_usb_pd_control *resp)
+{
+ struct typec_port *port = typec->ports[port_num]->port;
+ enum typec_orientation polarity;
+
+ if (!resp->enabled)
+ polarity = TYPEC_ORIENTATION_NONE;
+ else if (!resp->polarity)
+ polarity = TYPEC_ORIENTATION_NORMAL;
+ else
+ polarity = TYPEC_ORIENTATION_REVERSE;
+
+ typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK);
+ typec_set_orientation(port, polarity);
+}
+
+static void cros_typec_set_port_params_v1(struct cros_typec_data *typec,
+ int port_num, struct ec_response_usb_pd_control_v1 *resp)
+{
+ struct typec_port *port = typec->ports[port_num]->port;
+ enum typec_orientation polarity;
+ bool pd_en;
+ int ret;
+
+ if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED))
+ polarity = TYPEC_ORIENTATION_NONE;
+ else if (!resp->polarity)
+ polarity = TYPEC_ORIENTATION_NORMAL;
+ else
+ polarity = TYPEC_ORIENTATION_REVERSE;
+ typec_set_orientation(port, polarity);
+ typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ?
+ TYPEC_HOST : TYPEC_DEVICE);
+ typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ?
+ TYPEC_SOURCE : TYPEC_SINK);
+ typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ?
+ TYPEC_SOURCE : TYPEC_SINK);
+
+ /* Register/remove partners when a connect/disconnect occurs. */
+ if (resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) {
+ if (typec->ports[port_num]->partner)
+ return;
+
+ pd_en = resp->enabled & PD_CTRL_RESP_ENABLED_PD_CAPABLE;
+ ret = cros_typec_add_partner(typec, port_num, pd_en);
+ if (ret)
+ dev_warn(typec->dev,
+ "Failed to register partner on port: %d\n",
+ port_num);
+ } else {
+ if (!typec->ports[port_num]->partner)
+ return;
+ cros_typec_remove_partner(typec, port_num);
+ }
+}
+
+static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
+ struct ec_response_usb_pd_mux_info *resp)
+{
+ struct ec_params_usb_pd_mux_info req = {
+ .port = port_num,
+ };
+
+ return cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_INFO, &req,
+ sizeof(req), resp, sizeof(*resp));
+}
+
static int cros_typec_port_update(struct cros_typec_data *typec, int port_num)
{
struct ec_params_usb_pd_control req;
--
2.29.1.341.ge80a0c044ae-goog

2020-10-29 22:30:10

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 2/7] platform/chrome: cros_ec_typec: Fix remove partner logic

The cros_unregister_ports() function can be called in situations where
the partner has not been registered yet, and so its related data
structures would not have been initialized. Calling
cros_typec_remove_partner() in such a situation can lead to null pointer
dereferences. So, only call cros_typec_remove_partner() if there is a
valid registered partner pointer.

Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- No changes.

drivers/platform/chrome/cros_ec_typec.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 49083e21317d..2665d8125910 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -190,7 +190,10 @@ static void cros_unregister_ports(struct cros_typec_data *typec)
for (i = 0; i < typec->num_ports; i++) {
if (!typec->ports[i])
continue;
- cros_typec_remove_partner(typec, i);
+
+ if (typec->ports[i]->partner)
+ cros_typec_remove_partner(typec, i);
+
usb_role_switch_put(typec->ports[i]->role_sw);
typec_switch_put(typec->ports[i]->ori_sw);
typec_mux_put(typec->ports[i]->mux);
--
2.29.1.341.ge80a0c044ae-goog

2020-10-29 22:30:31

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 4/7] platform/chrome: cros_ec: Import Type C host commands

Import the EC_CMD_TYPEC_STATUS and EC_CMD_TYPEC_DISCOVERY Chrome OS EC
host commands from the EC code base [1].

These commands can be used by the application processor to query Power
Delivery (PD) discovery information concerning connected Type C
peripherals.

Also add the EC_FEATURE_TYPEC_CMD feature flag, which is used to
determine whether these commands are supported by the EC.

[1]:
https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master/include/ec_commands.h

Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- No changes.

.../linux/platform_data/cros_ec_commands.h | 155 ++++++++++++++++++
1 file changed, 155 insertions(+)

diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h
index 1fcfe9e63cb9..7f54fdcdd8cb 100644
--- a/include/linux/platform_data/cros_ec_commands.h
+++ b/include/linux/platform_data/cros_ec_commands.h
@@ -1284,6 +1284,8 @@ enum ec_feature_code {
EC_FEATURE_SCP = 39,
/* The MCU is an Integrated Sensor Hub */
EC_FEATURE_ISH = 40,
+ /* New TCPMv2 TYPEC_ prefaced commands supported */
+ EC_FEATURE_TYPEC_CMD = 41,
};

#define EC_FEATURE_MASK_0(event_code) BIT(event_code % 32)
@@ -5528,6 +5530,159 @@ struct ec_response_regulator_get_voltage {
uint32_t voltage_mv;
} __ec_align4;

+/*
+ * Gather all discovery information for the given port and partner type.
+ *
+ * Note that if discovery has not yet completed, only the currently completed
+ * responses will be filled in. If the discovery data structures are changed
+ * in the process of the command running, BUSY will be returned.
+ *
+ * VDO field sizes are set to the maximum possible number of VDOs a VDM may
+ * contain, while the number of SVIDs here is selected to fit within the PROTO2
+ * maximum parameter size.
+ */
+#define EC_CMD_TYPEC_DISCOVERY 0x0131
+
+enum typec_partner_type {
+ TYPEC_PARTNER_SOP = 0,
+ TYPEC_PARTNER_SOP_PRIME = 1,
+};
+
+struct ec_params_typec_discovery {
+ uint8_t port;
+ uint8_t partner_type; /* enum typec_partner_type */
+} __ec_align1;
+
+struct svid_mode_info {
+ uint16_t svid;
+ uint16_t mode_count; /* Number of modes partner sent */
+ uint32_t mode_vdo[6]; /* Max VDOs allowed after VDM header is 6 */
+};
+
+struct ec_response_typec_discovery {
+ uint8_t identity_count; /* Number of identity VDOs partner sent */
+ uint8_t svid_count; /* Number of SVIDs partner sent */
+ uint16_t reserved;
+ uint32_t discovery_vdo[6]; /* Max VDOs allowed after VDM header is 6 */
+ struct svid_mode_info svids[0];
+} __ec_align1;
+
+/*
+ * Gather all status information for a port.
+ *
+ * Note: this covers many of the return fields from the deprecated
+ * EC_CMD_USB_PD_CONTROL command, except those that are redundant with the
+ * discovery data. The "enum pd_cc_states" is defined with the deprecated
+ * EC_CMD_USB_PD_CONTROL command.
+ *
+ * This also combines in the EC_CMD_USB_PD_MUX_INFO flags.
+ */
+#define EC_CMD_TYPEC_STATUS 0x0133
+
+/*
+ * Power role.
+ *
+ * Note this is also used for PD header creation, and values align to those in
+ * the Power Delivery Specification Revision 3.0 (See
+ * 6.2.1.1.4 Port Power Role).
+ */
+enum pd_power_role {
+ PD_ROLE_SINK = 0,
+ PD_ROLE_SOURCE = 1
+};
+
+/*
+ * Data role.
+ *
+ * Note this is also used for PD header creation, and the first two values
+ * align to those in the Power Delivery Specification Revision 3.0 (See
+ * 6.2.1.1.6 Port Data Role).
+ */
+enum pd_data_role {
+ PD_ROLE_UFP = 0,
+ PD_ROLE_DFP = 1,
+ PD_ROLE_DISCONNECTED = 2,
+};
+
+enum pd_vconn_role {
+ PD_ROLE_VCONN_OFF = 0,
+ PD_ROLE_VCONN_SRC = 1,
+};
+
+/*
+ * Note: BIT(0) may be used to determine whether the polarity is CC1 or CC2,
+ * regardless of whether a debug accessory is connected.
+ */
+enum tcpc_cc_polarity {
+ /*
+ * _CCx: is used to indicate the polarity while not connected to
+ * a Debug Accessory. Only one CC line will assert a resistor and
+ * the other will be open.
+ */
+ POLARITY_CC1 = 0,
+ POLARITY_CC2 = 1,
+
+ /*
+ * _CCx_DTS is used to indicate the polarity while connected to a
+ * SRC Debug Accessory. Assert resistors on both lines.
+ */
+ POLARITY_CC1_DTS = 2,
+ POLARITY_CC2_DTS = 3,
+
+ /*
+ * The current TCPC code relies on these specific POLARITY values.
+ * Adding in a check to verify if the list grows for any reason
+ * that this will give a hint that other places need to be
+ * adjusted.
+ */
+ POLARITY_COUNT
+};
+
+#define PD_STATUS_EVENT_SOP_DISC_DONE BIT(0)
+#define PD_STATUS_EVENT_SOP_PRIME_DISC_DONE BIT(1)
+
+struct ec_params_typec_status {
+ uint8_t port;
+} __ec_align1;
+
+struct ec_response_typec_status {
+ uint8_t pd_enabled; /* PD communication enabled - bool */
+ uint8_t dev_connected; /* Device connected - bool */
+ uint8_t sop_connected; /* Device is SOP PD capable - bool */
+ uint8_t source_cap_count; /* Number of Source Cap PDOs */
+
+ uint8_t power_role; /* enum pd_power_role */
+ uint8_t data_role; /* enum pd_data_role */
+ uint8_t vconn_role; /* enum pd_vconn_role */
+ uint8_t sink_cap_count; /* Number of Sink Cap PDOs */
+
+ uint8_t polarity; /* enum tcpc_cc_polarity */
+ uint8_t cc_state; /* enum pd_cc_states */
+ uint8_t dp_pin; /* DP pin mode (MODE_DP_IN_[A-E]) */
+ uint8_t mux_state; /* USB_PD_MUX* - encoded mux state */
+
+ char tc_state[32]; /* TC state name */
+
+ uint32_t events; /* PD_STATUS_EVENT bitmask */
+
+ /*
+ * BCD PD revisions for partners
+ *
+ * The format has the PD major reversion in the upper nibble, and PD
+ * minor version in the next nibble. Following two nibbles are
+ * currently 0.
+ * ex. PD 3.2 would map to 0x3200
+ *
+ * PD major/minor will be 0 if no PD device is connected.
+ */
+ uint16_t sop_revision;
+ uint16_t sop_prime_revision;
+
+ uint32_t source_cap_pdos[7]; /* Max 7 PDOs can be present */
+
+ uint32_t sink_cap_pdos[7]; /* Max 7 PDOs can be present */
+} __ec_align1;
+
/*****************************************************************************/
/* The command range 0x200-0x2FF is reserved for Rotor. */

--
2.29.1.341.ge80a0c044ae-goog

2020-10-29 22:31:22

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 5/7] platform/chrome: cros_ec_typec: Introduce TYPEC_STATUS

Make a call to the newly introduced EC_CMD_TYPEC_STATUS command.
Currently we just check to see if the SOP (port-partner) discovery was
done and emit a debug level print for it.

Subsequent patches will retrieve and parse the discovery data and fill
out the Type C connector class data structures.

Also check the EC_FEATURE_TYPEC_CMD feature flag at probe, and only call
the new TYPEC_STATUS command if the feature flag is supported.

Reported-by: kernel test robot <[email protected]>
Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- Added missing 'static' keyword for cros_typec_cmds_supported()
function.

drivers/platform/chrome/cros_ec_typec.c | 52 +++++++++++++++++++++++++
1 file changed, 52 insertions(+)

diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index faef56bcb9c5..f578d0bfbe5a 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -48,6 +48,9 @@ struct cros_typec_port {

/* Port alt modes. */
struct typec_altmode p_altmode[CROS_EC_ALTMODE_MAX];
+
+ /* Flag indicating that PD discovery data parsing is completed. */
+ bool disc_done;
};

/* Platform-specific data for the Chrome OS EC Type C controller. */
@@ -60,6 +63,7 @@ struct cros_typec_data {
struct cros_typec_port *ports[EC_USB_PD_MAX_PORTS];
struct notifier_block nb;
struct work_struct port_work;
+ bool typec_cmd_supported;
};

static int cros_typec_parse_port_props(struct typec_capability *cap,
@@ -182,6 +186,7 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec,
typec_unregister_partner(port->partner);
port->partner = NULL;
memset(&port->p_identity, 0, sizeof(port->p_identity));
+ port->disc_done = false;
}

static void cros_unregister_ports(struct cros_typec_data *typec)
@@ -577,6 +582,31 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
sizeof(req), resp, sizeof(*resp));
}

+static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num)
+{
+ struct ec_response_typec_status resp;
+ struct ec_params_typec_status req = {
+ .port = port_num,
+ };
+ int ret;
+
+ ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_STATUS, &req, sizeof(req),
+ &resp, sizeof(resp));
+ if (ret < 0) {
+ dev_warn(typec->dev, "EC_CMD_TYPEC_STATUS failed for port: %d\n", port_num);
+ return;
+ }
+
+ if (typec->ports[port_num]->disc_done)
+ return;
+
+ /* Handle any events appropriately. */
+ if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) {
+ dev_dbg(typec->dev, "SOP Discovery done for port: %d\n", port_num);
+ typec->ports[port_num]->disc_done = true;
+ }
+}
+
static int cros_typec_port_update(struct cros_typec_data *typec, int port_num)
{
struct ec_params_usb_pd_control req;
@@ -613,6 +643,9 @@ static int cros_typec_port_update(struct cros_typec_data *typec, int port_num)
cros_typec_set_port_params_v0(typec, port_num,
(struct ec_response_usb_pd_control *) &resp);

+ if (typec->typec_cmd_supported)
+ cros_typec_handle_status(typec, port_num);
+
/* Update the switches if they exist, according to requested state */
ret = cros_typec_get_mux_info(typec, port_num, &mux_resp);
if (ret < 0) {
@@ -661,6 +694,23 @@ static int cros_typec_get_cmd_version(struct cros_typec_data *typec)
return 0;
}

+/* Check the EC feature flags to see if TYPEC_* commands are supported. */
+static int cros_typec_cmds_supported(struct cros_typec_data *typec)
+{
+ struct ec_response_get_features resp = {};
+ int ret;
+
+ ret = cros_typec_ec_command(typec, 0, EC_CMD_GET_FEATURES, NULL, 0,
+ &resp, sizeof(resp));
+ if (ret < 0) {
+ dev_warn(typec->dev,
+ "Failed to get features, assuming typec commands unsupported.\n");
+ return 0;
+ }
+
+ return resp.flags[EC_FEATURE_TYPEC_CMD / 32] & EC_FEATURE_MASK_1(EC_FEATURE_TYPEC_CMD);
+}
+
static void cros_typec_port_work(struct work_struct *work)
{
struct cros_typec_data *typec = container_of(work, struct cros_typec_data, port_work);
@@ -720,6 +770,8 @@ static int cros_typec_probe(struct platform_device *pdev)
return ret;
}

+ typec->typec_cmd_supported = !!cros_typec_cmds_supported(typec);
+
ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
&resp, sizeof(resp));
if (ret < 0)
--
2.29.1.341.ge80a0c044ae-goog

2020-10-29 22:31:39

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 6/7] platform/chrome: cros_ec_typec: Parse partner PD ID VDOs

Use EC_CMD_TYPE_DISCOVERY to retrieve and store the discovery data for
the port partner. With that data, update the PD Identity VDO values for
the partner, which were earlier not initialized.

Cc: Heikki Krogerus <[email protected]>
Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- No changes

drivers/platform/chrome/cros_ec_typec.c | 60 ++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index f578d0bfbe5a..f14550dac614 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -14,6 +14,7 @@
#include <linux/platform_data/cros_usbpd_notify.h>
#include <linux/platform_device.h>
#include <linux/usb/pd.h>
+#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec.h>
#include <linux/usb/typec_altmode.h>
#include <linux/usb/typec_dp.h>
@@ -51,6 +52,7 @@ struct cros_typec_port {

/* Flag indicating that PD discovery data parsing is completed. */
bool disc_done;
+ struct ec_response_typec_discovery *sop_disc;
};

/* Platform-specific data for the Chrome OS EC Type C controller. */
@@ -298,6 +300,12 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
port_num);

cros_typec_register_port_altmodes(typec, port_num);
+
+ cros_port->sop_disc = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL);
+ if (!cros_port->sop_disc) {
+ ret = -ENOMEM;
+ goto unregister_ports;
+ }
}

return 0;
@@ -582,6 +590,51 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
sizeof(req), resp, sizeof(*resp));
}

+static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num)
+{
+ struct cros_typec_port *port = typec->ports[port_num];
+ struct ec_response_typec_discovery *sop_disc = port->sop_disc;
+ struct ec_params_typec_discovery req = {
+ .port = port_num,
+ .partner_type = TYPEC_PARTNER_SOP,
+ };
+ int ret = 0;
+ int i;
+
+ if (!port->partner) {
+ dev_err(typec->dev,
+ "SOP Discovery received without partner registered, port: %d\n",
+ port_num);
+ ret = -EINVAL;
+ goto disc_exit;
+ }
+
+ memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE);
+ ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req),
+ sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE);
+ if (ret < 0) {
+ dev_err(typec->dev, "Failed to get SOP discovery data for port: %d\n", port_num);
+ goto disc_exit;
+ }
+
+ /* First, update the PD identity VDOs for the partner. */
+ if (sop_disc->identity_count > 0)
+ port->p_identity.id_header = sop_disc->discovery_vdo[0];
+ if (sop_disc->identity_count > 1)
+ port->p_identity.cert_stat = sop_disc->discovery_vdo[1];
+ if (sop_disc->identity_count > 2)
+ port->p_identity.product = sop_disc->discovery_vdo[2];
+
+ /* Copy the remaining identity VDOs till a maximum of 6. */
+ for (i = 3; i < sop_disc->identity_count && i < VDO_MAX_OBJECTS; i++)
+ port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i];
+
+ ret = typec_partner_set_identity(port->partner);
+
+disc_exit:
+ return ret;
+}
+
static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num)
{
struct ec_response_typec_status resp;
@@ -602,7 +655,12 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num

/* Handle any events appropriately. */
if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) {
- dev_dbg(typec->dev, "SOP Discovery done for port: %d\n", port_num);
+ ret = cros_typec_handle_sop_disc(typec, port_num);
+ if (ret < 0) {
+ dev_err(typec->dev, "Couldn't parse SOP Disc data, port: %d\n", port_num);
+ return;
+ }
+
typec->ports[port_num]->disc_done = true;
}
}
--
2.29.1.341.ge80a0c044ae-goog

2020-10-29 22:32:26

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 3/7] platform/chrome: cros_ec_typec: Clear partner identity on device removal

The partner identity struct isn't reset when a partner is removed,
meaning a subsequent partner can inherit an old partner's identity VDOs
before discovery is complete. So, clear that struct when a partner
removal is detected.

Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- No changes.

drivers/platform/chrome/cros_ec_typec.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 2665d8125910..faef56bcb9c5 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -181,6 +181,7 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec,

typec_unregister_partner(port->partner);
port->partner = NULL;
+ memset(&port->p_identity, 0, sizeof(port->p_identity));
}

static void cros_unregister_ports(struct cros_typec_data *typec)
--
2.29.1.341.ge80a0c044ae-goog

2020-10-29 22:33:52

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 7/7] platform/chrome: cros_ec_typec: Register partner altmodes

Use the discovery data from the Chrome EC to register parter altmodes
with the Type C Connector Class framework. Also introduce a node
struct to keep track of the list of registered alt modes.

Cc: Heikki Krogerus <[email protected]>
Signed-off-by: Prashant Malani <[email protected]>
---

Changes in v2:
- Changed list traversal during alt mode unregistering to use
list_for_each_entry_safe() base on review comment.

drivers/platform/chrome/cros_ec_typec.c | 77 +++++++++++++++++++++++++
1 file changed, 77 insertions(+)

diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index f14550dac614..ce031a10eb1b 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -7,6 +7,7 @@
*/

#include <linux/acpi.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/cros_ec_commands.h>
@@ -31,6 +32,12 @@ enum {
CROS_EC_ALTMODE_MAX,
};

+/* Container for altmode pointer nodes. */
+struct cros_typec_altmode_node {
+ struct typec_altmode *amode;
+ struct list_head list;
+};
+
/* Per port data. */
struct cros_typec_port {
struct typec_port *port;
@@ -53,6 +60,7 @@ struct cros_typec_port {
/* Flag indicating that PD discovery data parsing is completed. */
bool disc_done;
struct ec_response_typec_discovery *sop_disc;
+ struct list_head partner_mode_list;
};

/* Platform-specific data for the Chrome OS EC Type C controller. */
@@ -172,11 +180,25 @@ static int cros_typec_add_partner(struct cros_typec_data *typec, int port_num,
return ret;
}

+static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num)
+{
+ struct cros_typec_port *port = typec->ports[port_num];
+ struct cros_typec_altmode_node *node, *tmp;
+
+ list_for_each_entry_safe(node, tmp, &port->partner_mode_list, list) {
+ list_del(&node->list);
+ typec_unregister_altmode(node->amode);
+ devm_kfree(typec->dev, node);
+ }
+}
+
static void cros_typec_remove_partner(struct cros_typec_data *typec,
int port_num)
{
struct cros_typec_port *port = typec->ports[port_num];

+ cros_typec_unregister_altmodes(typec, port_num);
+
port->state.alt = NULL;
port->state.mode = TYPEC_STATE_USB;
port->state.data = NULL;
@@ -306,6 +328,8 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
ret = -ENOMEM;
goto unregister_ports;
}
+
+ INIT_LIST_HEAD(&cros_port->partner_mode_list);
}

return 0;
@@ -590,6 +614,49 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
sizeof(req), resp, sizeof(*resp));
}

+static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num)
+{
+ struct cros_typec_port *port = typec->ports[port_num];
+ struct ec_response_typec_discovery *sop_disc = port->sop_disc;
+ struct cros_typec_altmode_node *node;
+ struct typec_altmode_desc desc;
+ struct typec_altmode *amode;
+ int ret = 0;
+ int i, j;
+
+ for (i = 0; i < sop_disc->svid_count; i++) {
+ for (j = 0; j < sop_disc->svids[i].mode_count; j++) {
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = sop_disc->svids[i].svid;
+ desc.mode = j;
+ desc.vdo = sop_disc->svids[i].mode_vdo[j];
+
+ amode = typec_partner_register_altmode(port->partner, &desc);
+ if (IS_ERR(amode)) {
+ ret = PTR_ERR(amode);
+ goto err_cleanup;
+ }
+
+ /* If no memory is available we should unregister and exit. */
+ node = devm_kzalloc(typec->dev, sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ ret = -ENOMEM;
+ typec_unregister_altmode(amode);
+ goto err_cleanup;
+ }
+
+ node->amode = amode;
+ list_add_tail(&node->list, &port->partner_mode_list);
+ }
+ }
+
+ return 0;
+
+err_cleanup:
+ cros_typec_unregister_altmodes(typec, port_num);
+ return ret;
+}
+
static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num)
{
struct cros_typec_port *port = typec->ports[port_num];
@@ -630,6 +697,16 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu
port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i];

ret = typec_partner_set_identity(port->partner);
+ if (ret < 0) {
+ dev_err(typec->dev, "Failed to update partner PD identity, port: %d\n", port_num);
+ goto disc_exit;
+ }
+
+ ret = cros_typec_register_altmodes(typec, port_num);
+ if (ret < 0) {
+ dev_err(typec->dev, "Failed to register partner altmodes, port: %d\n", port_num);
+ goto disc_exit;
+ }

disc_exit:
return ret;
--
2.29.1.341.ge80a0c044ae-goog

2020-10-30 13:34:12

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v2 7/7] platform/chrome: cros_ec_typec: Register partner altmodes

On Thu, Oct 29, 2020 at 03:27:42PM -0700, Prashant Malani wrote:
> Use the discovery data from the Chrome EC to register parter altmodes
> with the Type C Connector Class framework. Also introduce a node
> struct to keep track of the list of registered alt modes.
>
> Cc: Heikki Krogerus <[email protected]>
> Signed-off-by: Prashant Malani <[email protected]>

Acked-by: Heikki Krogerus <[email protected]>

> ---
>
> Changes in v2:
> - Changed list traversal during alt mode unregistering to use
> list_for_each_entry_safe() base on review comment.
>
> drivers/platform/chrome/cros_ec_typec.c | 77 +++++++++++++++++++++++++
> 1 file changed, 77 insertions(+)
>
> diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
> index f14550dac614..ce031a10eb1b 100644
> --- a/drivers/platform/chrome/cros_ec_typec.c
> +++ b/drivers/platform/chrome/cros_ec_typec.c
> @@ -7,6 +7,7 @@
> */
>
> #include <linux/acpi.h>
> +#include <linux/list.h>
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/platform_data/cros_ec_commands.h>
> @@ -31,6 +32,12 @@ enum {
> CROS_EC_ALTMODE_MAX,
> };
>
> +/* Container for altmode pointer nodes. */
> +struct cros_typec_altmode_node {
> + struct typec_altmode *amode;
> + struct list_head list;
> +};
> +
> /* Per port data. */
> struct cros_typec_port {
> struct typec_port *port;
> @@ -53,6 +60,7 @@ struct cros_typec_port {
> /* Flag indicating that PD discovery data parsing is completed. */
> bool disc_done;
> struct ec_response_typec_discovery *sop_disc;
> + struct list_head partner_mode_list;
> };
>
> /* Platform-specific data for the Chrome OS EC Type C controller. */
> @@ -172,11 +180,25 @@ static int cros_typec_add_partner(struct cros_typec_data *typec, int port_num,
> return ret;
> }
>
> +static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num)
> +{
> + struct cros_typec_port *port = typec->ports[port_num];
> + struct cros_typec_altmode_node *node, *tmp;
> +
> + list_for_each_entry_safe(node, tmp, &port->partner_mode_list, list) {
> + list_del(&node->list);
> + typec_unregister_altmode(node->amode);
> + devm_kfree(typec->dev, node);
> + }
> +}
> +
> static void cros_typec_remove_partner(struct cros_typec_data *typec,
> int port_num)
> {
> struct cros_typec_port *port = typec->ports[port_num];
>
> + cros_typec_unregister_altmodes(typec, port_num);
> +
> port->state.alt = NULL;
> port->state.mode = TYPEC_STATE_USB;
> port->state.data = NULL;
> @@ -306,6 +328,8 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
> ret = -ENOMEM;
> goto unregister_ports;
> }
> +
> + INIT_LIST_HEAD(&cros_port->partner_mode_list);
> }
>
> return 0;
> @@ -590,6 +614,49 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
> sizeof(req), resp, sizeof(*resp));
> }
>
> +static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num)
> +{
> + struct cros_typec_port *port = typec->ports[port_num];
> + struct ec_response_typec_discovery *sop_disc = port->sop_disc;
> + struct cros_typec_altmode_node *node;
> + struct typec_altmode_desc desc;
> + struct typec_altmode *amode;
> + int ret = 0;
> + int i, j;
> +
> + for (i = 0; i < sop_disc->svid_count; i++) {
> + for (j = 0; j < sop_disc->svids[i].mode_count; j++) {
> + memset(&desc, 0, sizeof(desc));
> + desc.svid = sop_disc->svids[i].svid;
> + desc.mode = j;
> + desc.vdo = sop_disc->svids[i].mode_vdo[j];
> +
> + amode = typec_partner_register_altmode(port->partner, &desc);
> + if (IS_ERR(amode)) {
> + ret = PTR_ERR(amode);
> + goto err_cleanup;
> + }
> +
> + /* If no memory is available we should unregister and exit. */
> + node = devm_kzalloc(typec->dev, sizeof(*node), GFP_KERNEL);
> + if (!node) {
> + ret = -ENOMEM;
> + typec_unregister_altmode(amode);
> + goto err_cleanup;
> + }
> +
> + node->amode = amode;
> + list_add_tail(&node->list, &port->partner_mode_list);
> + }
> + }
> +
> + return 0;
> +
> +err_cleanup:
> + cros_typec_unregister_altmodes(typec, port_num);
> + return ret;
> +}
> +
> static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num)
> {
> struct cros_typec_port *port = typec->ports[port_num];
> @@ -630,6 +697,16 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu
> port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i];
>
> ret = typec_partner_set_identity(port->partner);
> + if (ret < 0) {
> + dev_err(typec->dev, "Failed to update partner PD identity, port: %d\n", port_num);
> + goto disc_exit;
> + }
> +
> + ret = cros_typec_register_altmodes(typec, port_num);
> + if (ret < 0) {
> + dev_err(typec->dev, "Failed to register partner altmodes, port: %d\n", port_num);
> + goto disc_exit;
> + }
>
> disc_exit:
> return ret;
> --
> 2.29.1.341.ge80a0c044ae-goog

thanks,

--
heikki

2020-10-30 13:35:41

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v2 6/7] platform/chrome: cros_ec_typec: Parse partner PD ID VDOs

On Thu, Oct 29, 2020 at 03:27:40PM -0700, Prashant Malani wrote:
> Use EC_CMD_TYPE_DISCOVERY to retrieve and store the discovery data for
> the port partner. With that data, update the PD Identity VDO values for
> the partner, which were earlier not initialized.
>
> Cc: Heikki Krogerus <[email protected]>
> Signed-off-by: Prashant Malani <[email protected]>

FWIW:

Acked-by: Heikki Krogerus <[email protected]>

> ---
>
> Changes in v2:
> - No changes
>
> drivers/platform/chrome/cros_ec_typec.c | 60 ++++++++++++++++++++++++-
> 1 file changed, 59 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
> index f578d0bfbe5a..f14550dac614 100644
> --- a/drivers/platform/chrome/cros_ec_typec.c
> +++ b/drivers/platform/chrome/cros_ec_typec.c
> @@ -14,6 +14,7 @@
> #include <linux/platform_data/cros_usbpd_notify.h>
> #include <linux/platform_device.h>
> #include <linux/usb/pd.h>
> +#include <linux/usb/pd_vdo.h>
> #include <linux/usb/typec.h>
> #include <linux/usb/typec_altmode.h>
> #include <linux/usb/typec_dp.h>
> @@ -51,6 +52,7 @@ struct cros_typec_port {
>
> /* Flag indicating that PD discovery data parsing is completed. */
> bool disc_done;
> + struct ec_response_typec_discovery *sop_disc;
> };
>
> /* Platform-specific data for the Chrome OS EC Type C controller. */
> @@ -298,6 +300,12 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
> port_num);
>
> cros_typec_register_port_altmodes(typec, port_num);
> +
> + cros_port->sop_disc = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL);
> + if (!cros_port->sop_disc) {
> + ret = -ENOMEM;
> + goto unregister_ports;
> + }
> }
>
> return 0;
> @@ -582,6 +590,51 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
> sizeof(req), resp, sizeof(*resp));
> }
>
> +static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num)
> +{
> + struct cros_typec_port *port = typec->ports[port_num];
> + struct ec_response_typec_discovery *sop_disc = port->sop_disc;
> + struct ec_params_typec_discovery req = {
> + .port = port_num,
> + .partner_type = TYPEC_PARTNER_SOP,
> + };
> + int ret = 0;
> + int i;
> +
> + if (!port->partner) {
> + dev_err(typec->dev,
> + "SOP Discovery received without partner registered, port: %d\n",
> + port_num);
> + ret = -EINVAL;
> + goto disc_exit;
> + }
> +
> + memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE);
> + ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req),
> + sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE);
> + if (ret < 0) {
> + dev_err(typec->dev, "Failed to get SOP discovery data for port: %d\n", port_num);
> + goto disc_exit;
> + }
> +
> + /* First, update the PD identity VDOs for the partner. */
> + if (sop_disc->identity_count > 0)
> + port->p_identity.id_header = sop_disc->discovery_vdo[0];
> + if (sop_disc->identity_count > 1)
> + port->p_identity.cert_stat = sop_disc->discovery_vdo[1];
> + if (sop_disc->identity_count > 2)
> + port->p_identity.product = sop_disc->discovery_vdo[2];
> +
> + /* Copy the remaining identity VDOs till a maximum of 6. */
> + for (i = 3; i < sop_disc->identity_count && i < VDO_MAX_OBJECTS; i++)
> + port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i];
> +
> + ret = typec_partner_set_identity(port->partner);
> +
> +disc_exit:
> + return ret;
> +}
> +
> static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num)
> {
> struct ec_response_typec_status resp;
> @@ -602,7 +655,12 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num
>
> /* Handle any events appropriately. */
> if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) {
> - dev_dbg(typec->dev, "SOP Discovery done for port: %d\n", port_num);
> + ret = cros_typec_handle_sop_disc(typec, port_num);
> + if (ret < 0) {
> + dev_err(typec->dev, "Couldn't parse SOP Disc data, port: %d\n", port_num);
> + return;
> + }
> +
> typec->ports[port_num]->disc_done = true;
> }
> }

thanks,

--
heikki

2020-11-13 09:02:03

by Enric Balletbo i Serra

[permalink] [raw]
Subject: Re: [PATCH v2 0/7] platform/chrome: cros_ec_typec: Register partner PD information

On Thu, 29 Oct 2020 15:27:28 -0700, Prashant Malani wrote:
> This series adds support to retrieve Type C PD(Power Delivery) Discovery
> information from the Chrome OS EC, and register this information with
> the Type C connector class framework.
>
> There are also a couple of patches which fix minor bugs with the
> existing cros-ec-typec driver.
>
> [...]

Applied, thanks!

[1/7] platform/chrome: cros_ec_typec: Relocate set_port_params_v*() functions
commit: 0498710be002b35bcb43895c4133a4c4bbfd837e
[2/7] platform/chrome: cros_ec_typec: Fix remove partner logic
commit: 7ab5a673f4ce65875c76e9812d2e6da063b87fb7
[3/7] platform/chrome: cros_ec_typec: Clear partner identity on device removal
commit: 514acf1cefd020eb21d7c180050a8d66b723d2d8
[4/7] platform/chrome: cros_ec: Import Type C host commands
commit: cd2c40ff90b0e385c18f881ab5e17f7137864223
[5/7] platform/chrome: cros_ec_typec: Introduce TYPEC_STATUS
commit: 80f8cef60d79f23c02e546ba3de2fce84d5e8bdb
[6/7] platform/chrome: cros_ec_typec: Parse partner PD ID VDOs
commit: f6f668118918f533676e51f3214f5a104562b59c
[7/7] platform/chrome: cros_ec_typec: Register partner altmodes
commit: de0f49487db3667f5204dcec6d3482c9bd1a0a30

Best regards,
--
Enric Balletbo i Serra <[email protected]>