[PATCH 2/3] usb: type-c: USB Type-C Connector System Software Interface

From: Heikki Krogerus
Date: Tue Feb 09 2016 - 12:03:39 EST


USB Type-C Connector System Software Interface (UCSI) is a
specification that defines registers and data structures
used to interface with the USB Type-C connectors on a system.

The specification is public and available at:
http://www.intel.com/content/www/us/en/io/universal-serial-bus/usb-type-c-ucsi-spec.html

Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
---
drivers/usb/type-c/Kconfig | 8 +
drivers/usb/type-c/Makefile | 1 +
drivers/usb/type-c/ucsi.c | 450 ++++++++++++++++++++++++++++++++++++++++++++
drivers/usb/type-c/ucsi.h | 219 +++++++++++++++++++++
4 files changed, 678 insertions(+)
create mode 100644 drivers/usb/type-c/ucsi.c
create mode 100644 drivers/usb/type-c/ucsi.h

diff --git a/drivers/usb/type-c/Kconfig b/drivers/usb/type-c/Kconfig
index b229fb9..02abd74 100644
--- a/drivers/usb/type-c/Kconfig
+++ b/drivers/usb/type-c/Kconfig
@@ -4,4 +4,12 @@ menu "USB PD and Type-C drivers"
config TYPEC
tristate

+config TYPEC_UCSI
+ tristate "USB Type-C Connector System Software Interface"
+ select TYPEC
+ help
+ USB Type-C Connector System Software Interface (UCSI) describes the
+ registers and data structures used to interface with the USB Type-C
+ connectors on a system.
+
endmenu
diff --git a/drivers/usb/type-c/Makefile b/drivers/usb/type-c/Makefile
index 1012a8b..ab974ba 100644
--- a/drivers/usb/type-c/Makefile
+++ b/drivers/usb/type-c/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_TYPEC) += typec.o
+obj-$(CONFIG_TYPEC_UCSI) += ucsi.o
diff --git a/drivers/usb/type-c/ucsi.c b/drivers/usb/type-c/ucsi.c
new file mode 100644
index 0000000..0107a85
--- /dev/null
+++ b/drivers/usb/type-c/ucsi.c
@@ -0,0 +1,450 @@
+/*
+ * ucsi.c - USB Type-C Connector System Software Interface
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb/typec.h>
+
+#include "ucsi.h"
+
+#define UCSI_ERROR 1
+#define UCSI_BUSY 2
+
+#define to_ucsi_connector(_port_) container_of(_port_->cap, \
+ struct ucsi_connector, \
+ typec_cap)
+
+#define cci_to_connector(_ucsi_, cci) (_ucsi_->connector + \
+ UCSI_CCI_CONNECTOR_CHANGE(cci) - 1)
+
+struct ucsi_connector {
+ unsigned num;
+ struct ucsi *ucsi;
+ struct work_struct work;
+ struct typec_port *port;
+ struct typec_capability typec_cap;
+ struct ucsi_connector_capability cap;
+};
+
+struct ucsi {
+ struct device *dev;
+ struct ucsi_ppm *ppm;
+
+ int status;
+ struct completion complete;
+ struct ucsi_capability cap;
+ struct ucsi_connector *connector;
+};
+
+static int ucsi_ack(struct ucsi *ucsi, u8 cmd)
+{
+ struct ucsi_control *ctrl = (void *)&ucsi->ppm->data->control;
+ int ret;
+
+ ucsi->ppm->data->control = 0;
+ ctrl->cmd = UCSI_ACK_CC_CI;
+ ctrl->data = cmd;
+
+ ret = ucsi->ppm->cmd(ucsi->ppm);
+ if (ret)
+ return ret;
+
+ /* Waiting for ACK also with ACK CMD for now */
+ wait_for_completion(&ucsi->complete);
+ return 0;
+}
+
+static int ucsi_run_cmd(struct ucsi *ucsi, void *data, size_t size)
+{
+ int status;
+ int ret;
+
+ dev_vdbg(ucsi->dev, "%s control 0x%llx\n", __func__,
+ ucsi->ppm->data->control);
+
+ ret = ucsi->ppm->cmd(ucsi->ppm);
+ if (ret)
+ return ret;
+
+ /* REVISIT: We may need to set UCSI_CCI_CMD_COMPLETE flag here */
+ wait_for_completion(&ucsi->complete);
+
+ status = ucsi->status;
+ if (status != UCSI_ERROR && size)
+ memcpy(data, ucsi->ppm->data->message_in, size);
+
+ ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
+ if (ret)
+ goto out;
+
+ if (status == UCSI_ERROR) {
+ u16 error;
+
+ ucsi->ppm->data->control = UCSI_GET_ERROR_STATUS;
+ ret = ucsi->ppm->cmd(ucsi->ppm);
+ if (ret)
+ goto out;
+
+ wait_for_completion(&ucsi->complete);
+
+ /* Something has really gone wrong */
+ if (ucsi->status == UCSI_ERROR) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ memcpy(&error, ucsi->ppm->data->message_in, sizeof(error));
+
+ ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
+ if (ret)
+ goto out;
+
+ switch (error) {
+ case UCSI_ERROR_INVALID_CON_NUM:
+ ret = -ENXIO;
+ break;
+ case UCSI_ERROR_INCOMPATIBLE_PARTNER:
+ case UCSI_ERROR_CC_COMMUNICATION_ERR:
+ case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
+ ret = -EIO;
+ break;
+ case UCSI_ERROR_DEAD_BATTERY:
+ dev_warn(ucsi->dev, "Dead Battery Condition!\n");
+ ret = -EPERM;
+ break;
+ case UCSI_ERROR_UNREGONIZED_CMD:
+ case UCSI_ERROR_INVALID_CMD_ARGUMENT:
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+out:
+ ucsi->ppm->data->control = 0;
+ return ret;
+}
+
+static int ucsi_dr_swap(struct typec_port *port)
+{
+ struct ucsi_connector *con = to_ucsi_connector(port);
+ struct ucsi_uor_cmd *ctrl = (void *)&con->ucsi->ppm->data->control;
+
+ ctrl->cmd = UCSI_SET_UOR;
+ ctrl->con_num = con->num;
+ ctrl->role = port->data_role == TYPEC_HOST ?
+ UCSI_UOR_ROLE_UFP : UCSI_UOR_ROLE_DFP;
+ if (port->cap->type == TYPEC_PORT_DRP)
+ ctrl->role |= UCSI_UOR_ROLE_DRP;
+
+ return ucsi_run_cmd(con->ucsi, NULL, 0);
+}
+
+static int ucsi_pr_swap(struct typec_port *port)
+{
+ struct ucsi_connector *con = to_ucsi_connector(port);
+ struct ucsi_uor_cmd *ctrl = (void *)&con->ucsi->ppm->data->control;
+
+ /* The command structure is identical to SET_UOR command structure */
+ ctrl->cmd = UCSI_SET_PDR;
+ ctrl->con_num = con->num;
+ ctrl->role = port->pwr_role == TYPEC_PWR_SOURCE ?
+ UCSI_UOR_ROLE_UFP : UCSI_UOR_ROLE_DFP;
+ /* Always accepting power swap requests from partner for now */
+ ctrl->role |= UCSI_UOR_ROLE_DRP;
+
+ return ucsi_run_cmd(con->ucsi, NULL, 0);
+}
+
+static int ucsi_get_constat(struct ucsi_connector *con,
+ struct ucsi_connector_status *constat)
+{
+ struct ucsi_control *ctrl = (void *)&con->ucsi->ppm->data->control;
+
+ ctrl->cmd = UCSI_GET_CONNECTOR_STATUS;
+ ctrl->data = con->num;
+
+ return ucsi_run_cmd(con->ucsi, constat, sizeof(*constat));
+}
+
+static int
+ucsi_connect(struct ucsi_connector *con, struct ucsi_connector_status *constat)
+{
+ struct typec_port *port = con->port;
+
+ port->connected = true;
+
+ if (constat->partner_flags & UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE)
+ port->partner_type = TYPEC_PARTNER_ALTMODE;
+ else
+ port->partner_type = TYPEC_PARTNER_USB;
+
+ switch (constat->partner_type) {
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE_NO_UFP:
+ /* REVISIT: We don't care about just the cable for now */
+ return 0;
+ case UCSI_CONSTAT_PARTNER_TYPE_DFP:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
+ port->pwr_role = TYPEC_PWR_SINK;
+ port->data_role = TYPEC_DEVICE;
+ break;
+ case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+ port->pwr_role = TYPEC_PWR_SOURCE;
+ port->data_role = TYPEC_HOST;
+ break;
+ case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
+ port->partner_type = TYPEC_PARTNER_DEBUG;
+ goto out;
+ case UCSI_CONSTAT_PARTNER_TYPE_AUDIO:
+ port->partner_type = TYPEC_PARTNER_AUDIO;
+ goto out;
+ }
+
+ switch (constat->pwr_op_mode) {
+ case UCSI_CONSTAT_PWR_OPMODE_NONE:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ port->pwr_opmode = TYPEC_PWR_MODE_USB;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ port->partner_type = TYPEC_PARTNER_CHARGER;
+ port->pwr_opmode = TYPEC_PWR_MODE_BC1_2;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ port->pwr_opmode = TYPEC_PWR_MODE_PD;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_3:
+ port->pwr_opmode = TYPEC_PWR_MODE_1_5A;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ port->pwr_opmode = TYPEC_PWR_MODE_3_0A;
+ break;
+ default:
+ break;
+ }
+out:
+ return typec_connect(port);
+}
+
+static void ucsi_disconnect(struct ucsi_connector *con)
+{
+ con->port->partner_type = TYPEC_PARTNER_NONE;
+ con->port->connected = false;
+ typec_disconnect(con->port);
+}
+
+static void ucsi_connector_change(struct work_struct *work)
+{
+ struct ucsi_connector *con = container_of(work, struct ucsi_connector,
+ work);
+ struct ucsi_connector_status constat;
+
+ ucsi_ack(con->ucsi, UCSI_ACK_EVENT);
+
+ if (WARN_ON(ucsi_get_constat(con, &constat) != 0))
+ return;
+
+ if (constat.constat_change & UCSI_CONSTAT_CONNECT_CHANGE) {
+ if (constat.connected)
+ ucsi_connect(con, &constat);
+ else
+ ucsi_disconnect(con);
+ }
+}
+
+/**
+ * ucsi_interrupt - UCSI Notification Handler
+ * @ucsi: Source UCSI Interface for the notifications
+ *
+ * Handle notifications from @ucsi.
+ */
+int ucsi_interrupt(struct ucsi *ucsi)
+{
+ u32 cci = ucsi->ppm->data->cci;
+
+ if (!cci)
+ return 0;
+
+ if (UCSI_CCI_CONNECTOR_CHANGE(cci)) {
+ struct ucsi_connector *con = cci_to_connector(ucsi, cci);
+
+ schedule_work(&con->work);
+ return 1;
+ }
+
+ ucsi->status = 0;
+
+ /* REVISIT: We don't actually do anything with this for now */
+ if (cci & UCSI_CCI_BUSY)
+ ucsi->status = UCSI_BUSY;
+
+ if (cci & UCSI_CCI_ERROR)
+ ucsi->status = UCSI_ERROR;
+
+ if (cci & UCSI_CCI_ACK_CMD || cci & UCSI_CCI_CMD_COMPLETED)
+ complete(&ucsi->complete);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(ucsi_interrupt);
+
+/**
+ * ucsi_init - Initialize an UCSI Interface
+ * @ucsi: The UCSI Interface
+ *
+ * Registers all the USB Type-C ports governed by the PPM of @ucsi and enables
+ * all the notifications from the PPM.
+ */
+int ucsi_init(struct ucsi *ucsi)
+{
+ struct ucsi_control *ctrl = (void *)&ucsi->ppm->data->control;
+ struct ucsi_connector *con;
+ int ret;
+ int i;
+
+ /* Enable basic notifications */
+ ctrl->cmd = UCSI_SET_NOTIFICATION_ENABLE;
+ ctrl->data = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
+ ret = ucsi_run_cmd(ucsi, NULL, 0);
+ if (ret)
+ return ret;
+
+ /* Get PPM capabilities */
+ ctrl->cmd = UCSI_GET_CAPABILITY;
+ ret = ucsi_run_cmd(ucsi, &ucsi->cap, sizeof(ucsi->cap));
+ if (ret)
+ return ret;
+
+ ucsi->connector = kcalloc(ucsi->cap.num_connectors,
+ sizeof(struct ucsi_connector), GFP_KERNEL);
+ if (!ucsi->connector)
+ return -ENOMEM;
+
+ for (i = 0, con = ucsi->connector; i < ucsi->cap.num_connectors;
+ i++, con++) {
+ struct typec_capability *cap = &con->typec_cap;
+ struct ucsi_connector_status constat;
+
+ /* Get connector capability */
+ ctrl->cmd = UCSI_GET_CONNECTOR_CAPABILITY;
+ ctrl->data = i + 1;
+ ret = ucsi_run_cmd(ucsi, &con->cap, sizeof(con->cap));
+ if (ret)
+ goto err;
+
+ /* Register the connector */
+
+ if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
+ cap->type = TYPEC_PORT_DRP;
+ else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DFP)
+ cap->type = TYPEC_PORT_DFP;
+ else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP)
+ cap->type = TYPEC_PORT_UFP;
+
+ cap->usb_pd = !!(ucsi->cap.attributes &
+ UCSI_CAP_ATTR_USB_PD);
+ cap->audio_accessory = !!(con->cap.op_mode &
+ UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY);
+ cap->debug_accessory = !!(con->cap.op_mode &
+ UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY);
+
+ /* TODO: Alt modes */
+
+ cap->dr_swap = ucsi_dr_swap;
+ cap->pr_swap = ucsi_pr_swap;
+
+ con->port = typec_register_port(ucsi->dev, cap);
+ if (IS_ERR(con->port)) {
+ ret = PTR_ERR(con->port);
+ goto err;
+ }
+
+ con->num = i + 1;
+ con->ucsi = ucsi;
+ INIT_WORK(&con->work, ucsi_connector_change);
+
+ /* Check if the connector is connected */
+ if (WARN_ON(ucsi_get_constat(con, &constat) != 0))
+ continue;
+
+ if (constat.connected)
+ ucsi_connect(con, &constat);
+ }
+
+ /* Enable all notifications */
+ ctrl->cmd = UCSI_SET_NOTIFICATION_ENABLE;
+ ctrl->data = UCSI_ENABLE_NTFY_ALL;
+ ret = ucsi_run_cmd(ucsi, NULL, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ if (i > 0)
+ for (; i >= 0; i--, con--)
+ typec_unregister_port(con->port);
+
+ kfree(ucsi->connector);
+ return ret;
+}
+EXPORT_SYMBOL(ucsi_init);
+
+/**
+ * ucsi_register_ppm - Register UCSI PPM Interface
+ * @dev: Device interface to the PPM
+ * @ppm: The PPM interface
+ *
+ * Allocates an UCSI instance, associates it with @ppm and returns it to the
+ * caller.
+ */
+struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm)
+{
+ struct ucsi *ucsi;
+
+ ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
+ if (!ucsi)
+ return ERR_PTR(-ENOMEM);
+
+ init_completion(&ucsi->complete);
+ ucsi->dev = dev;
+ ucsi->ppm = ppm;
+
+ return ucsi;
+}
+EXPORT_SYMBOL_GPL(ucsi_register_ppm);
+
+/**
+ * ucsi_unregister_ppm - Unregister UCSI PPM Interface
+ * @ucsi: struct ucsi associated with the PPM
+ *
+ * Unregister an UCSI PPM that was created with ucsi_register().
+ */
+void ucsi_unregister_ppm(struct ucsi *ucsi)
+{
+ struct ucsi_connector *con;
+ int i;
+
+ /* Disable all notifications */
+ ucsi->ppm->data->control = UCSI_SET_NOTIFICATION_ENABLE;
+ ucsi->ppm->cmd(ucsi->ppm);
+
+ for (i = 0, con = ucsi->connector; i < ucsi->cap.num_connectors;
+ i++, con++)
+ typec_unregister_port(con->port);
+
+ kfree(ucsi->connector);
+ kfree(ucsi);
+}
+EXPORT_SYMBOL_GPL(ucsi_unregister_ppm);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("USB Type-C System Software Interface driver");
diff --git a/drivers/usb/type-c/ucsi.h b/drivers/usb/type-c/ucsi.h
new file mode 100644
index 0000000..0ec6366
--- /dev/null
+++ b/drivers/usb/type-c/ucsi.h
@@ -0,0 +1,219 @@
+
+#include <linux/types.h>
+
+/* -------------------------------------------------------------------------- */
+
+struct ucsi_data {
+ __u16 version;
+ __u16 RESERVED;
+ __u32 cci;
+ __u64 control;
+ __u32 message_in[4];
+ __u32 message_out[4];
+} __packed;
+
+struct ucsi_control {
+ __u8 cmd;
+ __u8 length;
+ __u64 data:48;
+} __packed;
+
+/* Command Status and Connector Change Indication (CCI) bits */
+#define UCSI_CCI_CONNECTOR_CHANGE(c) ((c >> 1) & 0x7f)
+#define UCSI_CCI_DATA_LENGTH(c) ((c >> 8) & 0xff)
+#define UCSI_CCI_NOT_SUPPORTED BIT(25)
+#define UCSI_CCI_CANCEL_CMD BIT(26)
+#define UCSI_CCI_RESET_CMD BIT(27)
+#define UCSI_CCI_BUSY BIT(28)
+#define UCSI_CCI_ACK_CMD BIT(29)
+#define UCSI_CCI_ERROR BIT(30)
+#define UCSI_CCI_CMD_COMPLETED BIT(31)
+
+/* Commands */
+#define UCSI_PPM_RESET 0x01
+#define UCSI_CANCEL 0x02
+#define UCSI_CONNECTOR_RESET 0x03
+#define UCSI_ACK_CC_CI 0x04
+#define UCSI_SET_NOTIFICATION_ENABLE 0x05
+#define UCSI_GET_CAPABILITY 0x06
+#define UCSI_GET_CONNECTOR_CAPABILITY 0x07
+#define UCSI_SET_UOM 0x08
+#define UCSI_SET_UOR 0x09
+#define UCSI_SET_PDM 0x0A
+#define UCSI_SET_PDR 0x0B
+#define UCSI_GET_ALTERNATE_MODES 0x0C
+#define UCSI_GET_CAM_SUPPORTED 0x0D
+#define UCSI_GET_CURRENT_CAM 0x0E
+#define UCSI_SET_NEW_CAM 0x0F
+#define UCSI_GET_PDOS 0x10
+#define UCSI_GET_CABLE_PROPERTY 0x11
+#define UCSI_GET_CONNECTOR_STATUS 0x12
+#define UCSI_GET_ERROR_STATUS 0x13
+
+/* ACK_CC_CI commands */
+#define UCSI_ACK_EVENT 1
+#define UCSI_ACK_CMD 2
+
+/* Bits for SET_NOTIFICATION_ENABLE command */
+#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0)
+#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1)
+#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(2)
+#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(5)
+#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(6)
+#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(7)
+#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(8)
+#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(9)
+#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(11)
+#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(12)
+#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(14)
+#define UCSI_ENABLE_NTFY_ERROR BIT(15)
+#define UCSI_ENABLE_NTFY_ALL 0xdbf3
+
+/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
+#define UCSI_ERROR_UNREGONIZED_CMD BIT(0)
+#define UCSI_ERROR_INVALID_CON_NUM BIT(1)
+#define UCSI_ERROR_INVALID_CMD_ARGUMENT BIT(2)
+#define UCSI_ERROR_INCOMPATIBLE_PARTNER BIT(3)
+#define UCSI_ERROR_CC_COMMUNICATION_ERR BIT(4)
+#define UCSI_ERROR_DEAD_BATTERY BIT(5)
+#define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL BIT(6)
+
+/* Set USB Operation Role Command structure */
+struct ucsi_uor_cmd {
+ __u8 cmd;
+ __u8 length;
+ __u8 con_num:7;
+ __u64 role:3;
+#define UCSI_UOR_ROLE_DFP BIT(0)
+#define UCSI_UOR_ROLE_UFP BIT(1)
+#define UCSI_UOR_ROLE_DRP BIT(2)
+ __u64 data:38;
+} __packed;
+
+/* Data structure filled by PPM in response to GET_CAPABILITY command. */
+struct ucsi_capability {
+ __u32 attributes;
+#define UCSI_CAP_ATTR_DISABLE_STATE BIT(0)
+#define UCSI_CAP_ATTR_BATTERY_CHARGING BIT(1)
+#define UCSI_CAP_ATTR_USB_PD BIT(2)
+#define UCSI_CAP_ATTR_TYPEC_CURRENT BIT(6)
+#define UCSI_CAP_ATTR_POWER_AC_SUPPLY BIT(8)
+#define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
+#define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
+ __u8 num_connectors;
+ __u32 features:24;
+#define UCSI_CAP_SET_UOM BIT(0)
+#define UCSI_CAP_SET_PDM BIT(1)
+#define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
+#define UCSI_CAP_ALT_MODE_OVERRIDE BIT(3)
+#define UCSI_CAP_PDO_DETAILS BIT(4)
+#define UCSI_CAP_CABLE_DETAILS BIT(5)
+#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
+#define UCSI_CAP_PD_RESET BIT(7)
+ __u8 num_alt_modes;
+ __u8 RESERVED;
+ __u16 bc_version;
+ __u16 pd_version;
+ __u16 typec_version;
+} __packed;
+
+/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */
+struct ucsi_connector_capability {
+ __u8 op_mode;
+#define UCSI_CONCAP_OPMODE_DFP BIT(0)
+#define UCSI_CONCAP_OPMODE_UFP BIT(1)
+#define UCSI_CONCAP_OPMODE_DRP BIT(2)
+#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY BIT(3)
+#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY BIT(4)
+#define UCSI_CONCAP_OPMODE_USB2 BIT(5)
+#define UCSI_CONCAP_OPMODE_USB3 BIT(6)
+#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7)
+ __u8 provider:1;
+ __u8 consumer:1;
+} __packed;
+
+/* Data structure filled by PPM in response to GET_ALTERNATE_MODES command. */
+struct ucsi_alt_modes {
+ __u32 svid0;
+ __u16 mid0;
+ __u32 svid1;
+ __u16 mid1;
+} __packed;
+
+/* Data structure filled by PPM in response to GET_CABLE_PROPERTY command. */
+struct ucsi_cable_property {
+ __u16 speed_supported;
+ __u8 current_capability;
+ __u8 vbus_in_cable:1;
+ __u8 active_cable:1;
+ __u8 directionality:1;
+ __u8 plug_type:2;
+#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
+#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
+#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
+#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
+ __u8 mode_support:1;
+ __u8 RESERVED_2:2;
+ __u8 latency:4;
+ __u8 RESERVED_4:4;
+} __packed;
+
+/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
+struct ucsi_connector_status {
+ __u16 constat_change;
+#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1)
+#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2)
+#define UCSI_CONSTAT_PDOS_CHANGE BIT(5)
+#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6)
+#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7)
+#define UCSI_CONSTAT_CAM_CHANGE BIT(8)
+#define UCSI_CONSTAT_BC_CHANGE BIT(9)
+#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11)
+#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12)
+#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14)
+#define UCSI_CONSTAT_ERROR BIT(15)
+ __u16 pwr_op_mode:3;
+#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
+#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
+#define UCSI_CONSTAT_PWR_OPMODE_BC 2
+#define UCSI_CONSTAT_PWR_OPMODE_PD 3
+#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_3 4
+#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
+ __u16 connected:1;
+ __u16 pwr_dir:1;
+ __u16 partner_flags:8;
+#define UCSI_CONSTAT_PARTNER_FLAG_USB BIT(0)
+#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE BIT(1)
+ __u16 partner_type:3;
+#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
+#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
+#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_NO_UFP 3 /* Powered Cable */
+#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
+#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
+#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
+ __u32 request_data_obj;
+ __u8 bc_status;
+#define UCSI_CONSTAT_BC_NOT_CHARGING 0
+#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
+#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
+#define UCSI_CONSTAT_BC_TRICLE_CHARGING 3
+} __packed;
+
+/* -------------------------------------------------------------------------- */
+
+struct ucsi;
+
+/*
+ * struct ucsi_ppm - Interface to an UCSI Platform Policy Manager
+ * @data: memory location to the UCSI data structures
+ * @cmd: UCSI command execution routine
+ */
+struct ucsi_ppm {
+ struct ucsi_data *data;
+ int (*cmd)(struct ucsi_ppm *);
+};
+
+struct ucsi *ucsi_register_ppm(struct device *, struct ucsi_ppm *);
+void ucsi_unregister_ppm(struct ucsi *);
+int ucsi_init(struct ucsi *);
+int ucsi_interrupt(struct ucsi *);
--
2.7.0