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.kroge...@linux.intel.com>
---
 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.kroge...@linux.intel.com>
+ *
+ * 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.kroge...@linux.intel.com>");
+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

Reply via email to