diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index dfcfe459b7cf5605d4a00abc78e402c6e2468445..bc1b7745f1d4f3e73fc74b80a475d2bf02bcded5 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -19,4 +19,6 @@ config TYPEC_WCOVE
 	  To compile this driver as module, choose M here: the module will be
 	  called typec_wcove
 
+source "drivers/usb/typec/ucsi/Kconfig"
+
 endmenu
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index b9cb862221af39f36b28701576cec2011fccb845..bc214f15f1b5580111a93bbfb253143c6f2a15a8 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_TYPEC)		+= typec.o
 obj-$(CONFIG_TYPEC_WCOVE)	+= typec_wcove.o
+obj-$(CONFIG_TYPEC_UCSI)	+= ucsi/
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..da4c5c3d88703652bb17fe44d742424895a3555a
--- /dev/null
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -0,0 +1,23 @@
+config TYPEC_UCSI
+	tristate "USB Type-C Connector System Software Interface driver"
+	depends on !CPU_BIG_ENDIAN
+	select TYPEC
+	help
+	  USB Type-C Connector System Software Interface (UCSI) is a
+	  specification for an interface that allows the operating system to
+	  control the USB Type-C ports. On UCSI system the USB Type-C ports
+	  function autonomously by default, but in order to get the status of
+	  the ports and support basic operations like role swapping, the driver
+	  is required. UCSI is available on most of the new Intel based systems
+	  that are equipped with Embedded Controller and USB Type-C ports.
+
+	  UCSI specification does not define the interface method, so depending
+	  on the platform, ACPI, PCI, I2C, etc. may be used. Therefore this
+	  driver only provides the core part, and separate drivers are needed
+	  for every supported interface method.
+
+	  The UCSI specification can be downloaded from:
+	  http://www.intel.com/content/www/us/en/io/universal-serial-bus/usb-type-c-ucsi-spec.html
+
+	  To compile the driver as a module, choose M here: the module will be
+	  called typec_ucsi.
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..87dd6ee6c9f3280cd10cf79196d3bb07f35a3526
--- /dev/null
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -0,0 +1,7 @@
+CFLAGS_trace.o			:= -I$(src)
+
+obj-$(CONFIG_TYPEC_UCSI)	+= typec_ucsi.o
+
+typec_ucsi-y			:= ucsi.o
+
+typec_ucsi-$(CONFIG_FTRACE)	+= trace.o
diff --git a/drivers/usb/typec/ucsi/debug.h b/drivers/usb/typec/ucsi/debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4d8fc763e6cb5e8539ad84246670ce4b7bbec50
--- /dev/null
+++ b/drivers/usb/typec/ucsi/debug.h
@@ -0,0 +1,64 @@
+#ifndef __UCSI_DEBUG_H
+#define __UCSI_DEBUG_H
+
+#include "ucsi.h"
+
+static const char * const ucsi_cmd_strs[] = {
+	[0]				= "Unknown command",
+	[UCSI_PPM_RESET]		= "PPM_RESET",
+	[UCSI_CANCEL]			= "CANCEL",
+	[UCSI_CONNECTOR_RESET]		= "CONNECTOR_RESET",
+	[UCSI_ACK_CC_CI]		= "ACK_CC_CI",
+	[UCSI_SET_NOTIFICATION_ENABLE]	= "SET_NOTIFICATION_ENABLE",
+	[UCSI_GET_CAPABILITY]		= "GET_CAPABILITY",
+	[UCSI_GET_CONNECTOR_CAPABILITY]	= "GET_CONNECTOR_CAPABILITY",
+	[UCSI_SET_UOM]			= "SET_UOM",
+	[UCSI_SET_UOR]			= "SET_UOR",
+	[UCSI_SET_PDM]			= "SET_PDM",
+	[UCSI_SET_PDR]			= "SET_PDR",
+	[UCSI_GET_ALTERNATE_MODES]	= "GET_ALTERNATE_MODES",
+	[UCSI_GET_CAM_SUPPORTED]	= "GET_CAM_SUPPORTED",
+	[UCSI_GET_CURRENT_CAM]		= "GET_CURRENT_CAM",
+	[UCSI_SET_NEW_CAM]		= "SET_NEW_CAM",
+	[UCSI_GET_PDOS]			= "GET_PDOS",
+	[UCSI_GET_CABLE_PROPERTY]	= "GET_CABLE_PROPERTY",
+	[UCSI_GET_CONNECTOR_STATUS]	= "GET_CONNECTOR_STATUS",
+	[UCSI_GET_ERROR_STATUS]		= "GET_ERROR_STATUS",
+};
+
+static inline const char *ucsi_cmd_str(u64 raw_cmd)
+{
+	u8 cmd = raw_cmd & GENMASK(7, 0);
+
+	return ucsi_cmd_strs[(cmd >= ARRAY_SIZE(ucsi_cmd_strs)) ? 0 : cmd];
+}
+
+static const char * const ucsi_ack_strs[] = {
+	[0]				= "",
+	[UCSI_ACK_EVENT]		= "event",
+	[UCSI_ACK_CMD]			= "command",
+};
+
+static inline const char *ucsi_ack_str(u8 ack)
+{
+	return ucsi_ack_strs[(ack >= ARRAY_SIZE(ucsi_ack_strs)) ? 0 : ack];
+}
+
+static inline const char *ucsi_cci_str(u32 cci)
+{
+	if (cci & GENMASK(7, 0)) {
+		if (cci & BIT(29))
+			return "Event pending (ACK completed)";
+		if (cci & BIT(31))
+			return "Event pending (command completed)";
+		return "Connector Change";
+	}
+	if (cci & BIT(29))
+		return "ACK completed";
+	if (cci & BIT(31))
+		return "Command completed";
+
+	return "";
+}
+
+#endif /* __UCSI_DEBUG_H */
diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c
new file mode 100644
index 0000000000000000000000000000000000000000..006f65c72a344945008a66568ddbbd6074f5b7d2
--- /dev/null
+++ b/drivers/usb/typec/ucsi/trace.c
@@ -0,0 +1,2 @@
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h
new file mode 100644
index 0000000000000000000000000000000000000000..98b404404834bfe344b10b3936e6ab8f076c16e5
--- /dev/null
+++ b/drivers/usb/typec/ucsi/trace.h
@@ -0,0 +1,143 @@
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ucsi
+
+#if !defined(__UCSI_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __UCSI_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "ucsi.h"
+#include "debug.h"
+
+DECLARE_EVENT_CLASS(ucsi_log_ack,
+	TP_PROTO(u8 ack),
+	TP_ARGS(ack),
+	TP_STRUCT__entry(
+		__field(u8, ack)
+	),
+	TP_fast_assign(
+		__entry->ack = ack;
+	),
+	TP_printk("ACK %s", ucsi_ack_str(__entry->ack))
+);
+
+DEFINE_EVENT(ucsi_log_ack, ucsi_ack,
+	TP_PROTO(u8 ack),
+	TP_ARGS(ack)
+);
+
+DECLARE_EVENT_CLASS(ucsi_log_control,
+	TP_PROTO(struct ucsi_control *ctrl),
+	TP_ARGS(ctrl),
+	TP_STRUCT__entry(
+		__field(u64, ctrl)
+	),
+	TP_fast_assign(
+		__entry->ctrl = ctrl->raw_cmd;
+	),
+	TP_printk("control=%08llx (%s)", __entry->ctrl,
+		ucsi_cmd_str(__entry->ctrl))
+);
+
+DEFINE_EVENT(ucsi_log_control, ucsi_command,
+	TP_PROTO(struct ucsi_control *ctrl),
+	TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(ucsi_log_command,
+	TP_PROTO(struct ucsi_control *ctrl, int ret),
+	TP_ARGS(ctrl, ret),
+	TP_STRUCT__entry(
+		__field(u64, ctrl)
+		__field(int, ret)
+	),
+	TP_fast_assign(
+		__entry->ctrl = ctrl->raw_cmd;
+		__entry->ret = ret;
+	),
+	TP_printk("%s -> %s (err=%d)", ucsi_cmd_str(__entry->ctrl),
+		__entry->ret < 0 ? "FAIL" : "OK",
+		__entry->ret < 0 ? __entry->ret : 0)
+);
+
+DEFINE_EVENT(ucsi_log_command, ucsi_run_command,
+	TP_PROTO(struct ucsi_control *ctrl, int ret),
+	TP_ARGS(ctrl, ret)
+);
+
+DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm,
+	TP_PROTO(struct ucsi_control *ctrl, int ret),
+	TP_ARGS(ctrl, ret)
+);
+
+DECLARE_EVENT_CLASS(ucsi_log_cci,
+	TP_PROTO(u32 cci),
+	TP_ARGS(cci),
+	TP_STRUCT__entry(
+		__field(u32, cci)
+	),
+	TP_fast_assign(
+		__entry->cci = cci;
+	),
+	TP_printk("CCI=%08x %s", __entry->cci, ucsi_cci_str(__entry->cci))
+);
+
+DEFINE_EVENT(ucsi_log_cci, ucsi_notify,
+	TP_PROTO(u32 cci),
+	TP_ARGS(cci)
+);
+
+DECLARE_EVENT_CLASS(ucsi_log_connector_status,
+	TP_PROTO(int port, struct ucsi_connector_status *status),
+	TP_ARGS(port, status),
+	TP_STRUCT__entry(
+		__field(int, port)
+		__field(u16, change)
+		__field(u8, opmode)
+		__field(u8, connected)
+		__field(u8, pwr_dir)
+		__field(u8, partner_flags)
+		__field(u8, partner_type)
+		__field(u32, request_data_obj)
+		__field(u8, bc_status)
+	),
+	TP_fast_assign(
+		__entry->port = port - 1;
+		__entry->change = status->change;
+		__entry->opmode = status->pwr_op_mode;
+		__entry->connected = status->connected;
+		__entry->pwr_dir = status->pwr_dir;
+		__entry->partner_flags = status->partner_flags;
+		__entry->partner_type = status->partner_type;
+		__entry->request_data_obj = status->request_data_obj;
+		__entry->bc_status = status->bc_status;
+	),
+	TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, "
+		"sourcing=%d, partner_flags=%x, partner_type=%x, "
+		"request_data_obj=%08x, BC status=%x", __entry->port,
+		__entry->change, __entry->opmode, __entry->connected,
+		__entry->pwr_dir, __entry->partner_flags, __entry->partner_type,
+		__entry->request_data_obj, __entry->bc_status)
+);
+
+DEFINE_EVENT(ucsi_log_connector_status, ucsi_connector_change,
+	TP_PROTO(int port, struct ucsi_connector_status *status),
+	TP_ARGS(port, status)
+);
+
+DEFINE_EVENT(ucsi_log_connector_status, ucsi_register_port,
+	TP_PROTO(int port, struct ucsi_connector_status *status),
+	TP_ARGS(port, status)
+);
+
+#endif /* __UCSI_TRACE_H */
+
+/* This part must be outside protection */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
new file mode 100644
index 0000000000000000000000000000000000000000..714c5bcedf2b99b914f3f05a3068e88d47c3d0fb
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -0,0 +1,790 @@
+/*
+ * USB Type-C Connector System Software Interface driver
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@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/property.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/usb/typec.h>
+
+#include "ucsi.h"
+#include "trace.h"
+
+#define to_ucsi_connector(_cap_) container_of(_cap_, struct ucsi_connector, \
+					      typec_cap)
+
+/*
+ * UCSI_TIMEOUT_MS - PPM communication timeout
+ *
+ * Ideally we could use MIN_TIME_TO_RESPOND_WITH_BUSY (which is defined in UCSI
+ * specification) here as reference, but unfortunately we can't. It is very
+ * difficult to estimate the time it takes for the system to process the command
+ * before it is actually passed to the PPM.
+ */
+#define UCSI_TIMEOUT_MS		1000
+
+/*
+ * UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests
+ *
+ * 5 seconds is close to the time it takes for CapsCounter to reach 0, so even
+ * if the PPM does not generate Connector Change events before that with
+ * partners that do not support USB Power Delivery, this should still work.
+ */
+#define UCSI_SWAP_TIMEOUT_MS	5000
+
+enum ucsi_status {
+	UCSI_IDLE = 0,
+	UCSI_BUSY,
+	UCSI_ERROR,
+};
+
+struct ucsi_connector {
+	int num;
+
+	struct ucsi *ucsi;
+	struct work_struct work;
+	struct completion complete;
+
+	struct typec_port *port;
+	struct typec_partner *partner;
+
+	struct typec_capability typec_cap;
+
+	struct ucsi_connector_status status;
+	struct ucsi_connector_capability cap;
+};
+
+struct ucsi {
+	struct device *dev;
+	struct ucsi_ppm *ppm;
+
+	enum ucsi_status status;
+	struct completion complete;
+	struct ucsi_capability cap;
+	struct ucsi_connector *connector;
+
+	struct work_struct work;
+
+	/* PPM Communication lock */
+	struct mutex ppm_lock;
+
+	/* PPM communication flags */
+	unsigned long flags;
+#define EVENT_PENDING	0
+#define COMMAND_PENDING	1
+#define ACK_PENDING	2
+};
+
+static inline int ucsi_sync(struct ucsi *ucsi)
+{
+	if (ucsi->ppm && ucsi->ppm->sync)
+		return ucsi->ppm->sync(ucsi->ppm);
+	return 0;
+}
+
+static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl)
+{
+	int ret;
+
+	trace_ucsi_command(ctrl);
+
+	set_bit(COMMAND_PENDING, &ucsi->flags);
+
+	ret = ucsi->ppm->cmd(ucsi->ppm, ctrl);
+	if (ret)
+		goto err_clear_flag;
+
+	if (!wait_for_completion_timeout(&ucsi->complete,
+					 msecs_to_jiffies(UCSI_TIMEOUT_MS))) {
+		dev_warn(ucsi->dev, "PPM NOT RESPONDING\n");
+		ret = -ETIMEDOUT;
+	}
+
+err_clear_flag:
+	clear_bit(COMMAND_PENDING, &ucsi->flags);
+
+	return ret;
+}
+
+static int ucsi_ack(struct ucsi *ucsi, u8 ack)
+{
+	struct ucsi_control ctrl;
+	int ret;
+
+	trace_ucsi_ack(ack);
+
+	set_bit(ACK_PENDING, &ucsi->flags);
+
+	UCSI_CMD_ACK(ctrl, ack);
+	ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
+	if (ret)
+		goto out_clear_bit;
+
+	/* Waiting for ACK with ACK CMD, but not with EVENT for now */
+	if (ack == UCSI_ACK_EVENT)
+		goto out_clear_bit;
+
+	if (!wait_for_completion_timeout(&ucsi->complete,
+					 msecs_to_jiffies(UCSI_TIMEOUT_MS)))
+		ret = -ETIMEDOUT;
+
+out_clear_bit:
+	clear_bit(ACK_PENDING, &ucsi->flags);
+
+	if (ret)
+		dev_err(ucsi->dev, "%s: failed\n", __func__);
+
+	return ret;
+}
+
+static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
+			    void *data, size_t size)
+{
+	struct ucsi_control _ctrl;
+	u8 data_length;
+	u16 error;
+	int ret;
+
+	ret = ucsi_command(ucsi, ctrl);
+	if (ret)
+		goto err;
+
+	switch (ucsi->status) {
+	case UCSI_IDLE:
+		ret = ucsi_sync(ucsi);
+		if (ret)
+			dev_warn(ucsi->dev, "%s: sync failed\n", __func__);
+
+		if (data)
+			memcpy(data, ucsi->ppm->data->message_in, size);
+
+		data_length = ucsi->ppm->data->cci.data_length;
+
+		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
+		if (!ret)
+			ret = data_length;
+		break;
+	case UCSI_BUSY:
+		/* The caller decides whether to cancel or not */
+		ret = -EBUSY;
+		break;
+	case UCSI_ERROR:
+		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
+		if (ret)
+			break;
+
+		_ctrl.raw_cmd = 0;
+		_ctrl.cmd.cmd = UCSI_GET_ERROR_STATUS;
+		ret = ucsi_command(ucsi, &_ctrl);
+		if (ret) {
+			dev_err(ucsi->dev, "reading error failed!\n");
+			break;
+		}
+
+		memcpy(&error, ucsi->ppm->data->message_in, sizeof(error));
+
+		/* Something has really gone wrong */
+		if (WARN_ON(ucsi->status == UCSI_ERROR)) {
+			ret = -ENODEV;
+			break;
+		}
+
+		ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
+		if (ret)
+			break;
+
+		switch (error) {
+		case UCSI_ERROR_INCOMPATIBLE_PARTNER:
+			ret = -EOPNOTSUPP;
+			break;
+		case UCSI_ERROR_CC_COMMUNICATION_ERR:
+			ret = -ECOMM;
+			break;
+		case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
+			ret = -EPROTO;
+			break;
+		case UCSI_ERROR_DEAD_BATTERY:
+			dev_warn(ucsi->dev, "Dead battery condition!\n");
+			ret = -EPERM;
+			break;
+		/* The following mean a bug in this driver */
+		case UCSI_ERROR_INVALID_CON_NUM:
+		case UCSI_ERROR_UNREGONIZED_CMD:
+		case UCSI_ERROR_INVALID_CMD_ARGUMENT:
+			dev_warn(ucsi->dev,
+				 "%s: possible UCSI driver bug - error 0x%x\n",
+				 __func__, error);
+			ret = -EINVAL;
+			break;
+		default:
+			dev_warn(ucsi->dev,
+				 "%s: error without status\n", __func__);
+			ret = -EIO;
+			break;
+		}
+		break;
+	}
+
+err:
+	trace_ucsi_run_command(ctrl, ret);
+
+	return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
+{
+	switch (con->status.pwr_op_mode) {
+	case UCSI_CONSTAT_PWR_OPMODE_PD:
+		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A);
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A);
+		break;
+	default:
+		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB);
+		break;
+	}
+}
+
+static int ucsi_register_partner(struct ucsi_connector *con)
+{
+	struct typec_partner_desc partner;
+
+	if (con->partner)
+		return 0;
+
+	memset(&partner, 0, sizeof(partner));
+
+	switch (con->status.partner_type) {
+	case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
+		partner.accessory = TYPEC_ACCESSORY_DEBUG;
+		break;
+	case UCSI_CONSTAT_PARTNER_TYPE_AUDIO:
+		partner.accessory = TYPEC_ACCESSORY_AUDIO;
+		break;
+	default:
+		break;
+	}
+
+	partner.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD;
+
+	con->partner = typec_register_partner(con->port, &partner);
+	if (!con->partner) {
+		dev_err(con->ucsi->dev, "con%d: failed to register partner\n",
+			con->num);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void ucsi_unregister_partner(struct ucsi_connector *con)
+{
+	typec_unregister_partner(con->partner);
+	con->partner = NULL;
+}
+
+static void ucsi_connector_change(struct work_struct *work)
+{
+	struct ucsi_connector *con = container_of(work, struct ucsi_connector,
+						  work);
+	struct ucsi *ucsi = con->ucsi;
+	struct ucsi_control ctrl;
+	int ret;
+
+	mutex_lock(&ucsi->ppm_lock);
+
+	UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
+	ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
+	if (ret < 0) {
+		dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
+			__func__, ret);
+		goto out_unlock;
+	}
+
+	if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE)
+		ucsi_pwr_opmode_change(con);
+
+	if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
+		typec_set_pwr_role(con->port, con->status.pwr_dir);
+
+		/* Complete pending power role swap */
+		if (!completion_done(&con->complete))
+			complete(&con->complete);
+	}
+
+	if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) {
+		switch (con->status.partner_type) {
+		case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+			typec_set_data_role(con->port, TYPEC_HOST);
+			break;
+		case UCSI_CONSTAT_PARTNER_TYPE_DFP:
+			typec_set_data_role(con->port, TYPEC_DEVICE);
+			break;
+		default:
+			break;
+		}
+
+		/* Complete pending data role swap */
+		if (!completion_done(&con->complete))
+			complete(&con->complete);
+	}
+
+	if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) {
+		if (con->status.connected)
+			ucsi_register_partner(con);
+		else
+			ucsi_unregister_partner(con);
+	}
+
+	ret = ucsi_ack(ucsi, UCSI_ACK_EVENT);
+	if (ret)
+		dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
+
+	trace_ucsi_connector_change(con->num, &con->status);
+
+out_unlock:
+	clear_bit(EVENT_PENDING, &ucsi->flags);
+	mutex_unlock(&ucsi->ppm_lock);
+}
+
+/**
+ * ucsi_notify - PPM notification handler
+ * @ucsi: Source UCSI Interface for the notifications
+ *
+ * Handle notifications from PPM of @ucsi.
+ */
+void ucsi_notify(struct ucsi *ucsi)
+{
+	struct ucsi_cci *cci;
+
+	/* There is no requirement to sync here, but no harm either. */
+	ucsi_sync(ucsi);
+
+	cci = &ucsi->ppm->data->cci;
+
+	if (cci->error)
+		ucsi->status = UCSI_ERROR;
+	else if (cci->busy)
+		ucsi->status = UCSI_BUSY;
+	else
+		ucsi->status = UCSI_IDLE;
+
+	if (cci->cmd_complete && test_bit(COMMAND_PENDING, &ucsi->flags)) {
+		complete(&ucsi->complete);
+	} else if (cci->ack_complete && test_bit(ACK_PENDING, &ucsi->flags)) {
+		complete(&ucsi->complete);
+	} else if (cci->connector_change) {
+		struct ucsi_connector *con;
+
+		con = &ucsi->connector[cci->connector_change - 1];
+
+		if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
+			schedule_work(&con->work);
+	}
+
+	trace_ucsi_notify(ucsi->ppm->data->raw_cci);
+}
+EXPORT_SYMBOL_GPL(ucsi_notify);
+
+/* -------------------------------------------------------------------------- */
+
+static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
+{
+	struct ucsi_control ctrl;
+
+	UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard);
+
+	return ucsi_run_command(con->ucsi, &ctrl, NULL, 0);
+}
+
+static int ucsi_reset_ppm(struct ucsi *ucsi)
+{
+	struct ucsi_control ctrl;
+	unsigned long tmo;
+	int ret;
+
+	ctrl.raw_cmd = 0;
+	ctrl.cmd.cmd = UCSI_PPM_RESET;
+	trace_ucsi_command(&ctrl);
+	ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
+	if (ret)
+		goto err;
+
+	tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
+
+	do {
+		/* Here sync is critical. */
+		ret = ucsi_sync(ucsi);
+		if (ret)
+			goto err;
+
+		if (ucsi->ppm->data->cci.reset_complete)
+			break;
+
+		/* If the PPM is still doing something else, reset it again. */
+		if (ucsi->ppm->data->raw_cci) {
+			dev_warn_ratelimited(ucsi->dev,
+				"Failed to reset PPM! Trying again..\n");
+
+			trace_ucsi_command(&ctrl);
+			ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
+			if (ret)
+				goto err;
+		}
+
+		/* Letting the PPM settle down. */
+		msleep(20);
+
+		ret = -ETIMEDOUT;
+	} while (time_is_after_jiffies(tmo));
+
+err:
+	trace_ucsi_reset_ppm(&ctrl, ret);
+
+	return ret;
+}
+
+static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl)
+{
+	int ret;
+
+	ret = ucsi_run_command(con->ucsi, ctrl, NULL, 0);
+	if (ret == -ETIMEDOUT) {
+		struct ucsi_control c;
+
+		/* PPM most likely stopped responding. Resetting everything. */
+		ucsi_reset_ppm(con->ucsi);
+
+		UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
+		ucsi_run_command(con->ucsi, &c, NULL, 0);
+
+		ucsi_reset_connector(con, true);
+	}
+
+	return ret;
+}
+
+static int
+ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role)
+{
+	struct ucsi_connector *con = to_ucsi_connector(cap);
+	struct ucsi_control ctrl;
+	int ret = 0;
+
+	if (!con->partner)
+		return -ENOTCONN;
+
+	mutex_lock(&con->ucsi->ppm_lock);
+
+	if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP &&
+	     role == TYPEC_DEVICE) ||
+	    (con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP &&
+	     role == TYPEC_HOST))
+		goto out_unlock;
+
+	UCSI_CMD_SET_UOR(ctrl, con, role);
+	ret = ucsi_role_cmd(con, &ctrl);
+	if (ret < 0)
+		goto out_unlock;
+
+	mutex_unlock(&con->ucsi->ppm_lock);
+
+	if (!wait_for_completion_timeout(&con->complete,
+					msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
+		return -ETIMEDOUT;
+
+	return 0;
+
+out_unlock:
+	mutex_unlock(&con->ucsi->ppm_lock);
+
+	return ret;
+}
+
+static int
+ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role)
+{
+	struct ucsi_connector *con = to_ucsi_connector(cap);
+	struct ucsi_control ctrl;
+	int ret = 0;
+
+	if (!con->partner)
+		return -ENOTCONN;
+
+	mutex_lock(&con->ucsi->ppm_lock);
+
+	if (con->status.pwr_dir == role)
+		goto out_unlock;
+
+	UCSI_CMD_SET_PDR(ctrl, con, role);
+	ret = ucsi_role_cmd(con, &ctrl);
+	if (ret < 0)
+		goto out_unlock;
+
+	mutex_unlock(&con->ucsi->ppm_lock);
+
+	if (!wait_for_completion_timeout(&con->complete,
+					msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS)))
+		return -ETIMEDOUT;
+
+	mutex_lock(&con->ucsi->ppm_lock);
+
+	/* Something has gone wrong while swapping the role */
+	if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) {
+		ucsi_reset_connector(con, true);
+		ret = -EPROTO;
+	}
+
+out_unlock:
+	mutex_unlock(&con->ucsi->ppm_lock);
+
+	return ret;
+}
+
+static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
+{
+	struct fwnode_handle *fwnode;
+	int i = 1;
+
+	device_for_each_child_node(con->ucsi->dev, fwnode)
+		if (i++ == con->num)
+			return fwnode;
+	return NULL;
+}
+
+static int ucsi_register_port(struct ucsi *ucsi, int index)
+{
+	struct ucsi_connector *con = &ucsi->connector[index];
+	struct typec_capability *cap = &con->typec_cap;
+	enum typec_accessory *accessory = cap->accessory;
+	struct ucsi_control ctrl;
+	int ret;
+
+	INIT_WORK(&con->work, ucsi_connector_change);
+	init_completion(&con->complete);
+	con->num = index + 1;
+	con->ucsi = ucsi;
+
+	/* Get connector capability */
+	UCSI_CMD_GET_CONNECTOR_CAPABILITY(ctrl, con->num);
+	ret = ucsi_run_command(ucsi, &ctrl, &con->cap, sizeof(con->cap));
+	if (ret < 0)
+		return ret;
+
+	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->revision = ucsi->cap.typec_version;
+	cap->pd_revision = ucsi->cap.pd_version;
+	cap->prefer_role = TYPEC_NO_PREFERRED_ROLE;
+
+	if (con->cap.op_mode & UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY)
+		*accessory++ = TYPEC_ACCESSORY_AUDIO;
+	if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY)
+		*accessory = TYPEC_ACCESSORY_DEBUG;
+
+	cap->fwnode = ucsi_find_fwnode(con);
+	cap->dr_set = ucsi_dr_swap;
+	cap->pr_set = ucsi_pr_swap;
+
+	/* Register the connector */
+	con->port = typec_register_port(ucsi->dev, cap);
+	if (!con->port)
+		return -ENODEV;
+
+	/* Get the status */
+	UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
+	ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
+	if (ret < 0) {
+		dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
+		return 0;
+	}
+
+	ucsi_pwr_opmode_change(con);
+	typec_set_pwr_role(con->port, con->status.pwr_dir);
+
+	switch (con->status.partner_type) {
+	case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+		typec_set_data_role(con->port, TYPEC_HOST);
+		break;
+	case UCSI_CONSTAT_PARTNER_TYPE_DFP:
+		typec_set_data_role(con->port, TYPEC_DEVICE);
+		break;
+	default:
+		break;
+	}
+
+	/* Check if there is already something connected */
+	if (con->status.connected)
+		ucsi_register_partner(con);
+
+	trace_ucsi_register_port(con->num, &con->status);
+
+	return 0;
+}
+
+static void ucsi_init(struct work_struct *work)
+{
+	struct ucsi *ucsi = container_of(work, struct ucsi, work);
+	struct ucsi_connector *con;
+	struct ucsi_control ctrl;
+	int ret;
+	int i;
+
+	mutex_lock(&ucsi->ppm_lock);
+
+	/* Reset the PPM */
+	ret = ucsi_reset_ppm(ucsi);
+	if (ret) {
+		dev_err(ucsi->dev, "failed to reset PPM!\n");
+		goto err;
+	}
+
+	/* Enable basic notifications */
+	UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE |
+					UCSI_ENABLE_NTFY_ERROR);
+	ret = ucsi_run_command(ucsi, &ctrl, NULL, 0);
+	if (ret < 0)
+		goto err_reset;
+
+	/* Get PPM capabilities */
+	UCSI_CMD_GET_CAPABILITY(ctrl);
+	ret = ucsi_run_command(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap));
+	if (ret < 0)
+		goto err_reset;
+
+	if (!ucsi->cap.num_connectors) {
+		ret = -ENODEV;
+		goto err_reset;
+	}
+
+	/* Allocate the connectors. Released in ucsi_unregister_ppm() */
+	ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1,
+				  sizeof(*ucsi->connector), GFP_KERNEL);
+	if (!ucsi->connector) {
+		ret = -ENOMEM;
+		goto err_reset;
+	}
+
+	/* Register all connectors */
+	for (i = 0; i < ucsi->cap.num_connectors; i++) {
+		ret = ucsi_register_port(ucsi, i);
+		if (ret)
+			goto err_unregister;
+	}
+
+	/* Enable all notifications */
+	UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL);
+	ret = ucsi_run_command(ucsi, &ctrl, NULL, 0);
+	if (ret < 0)
+		goto err_unregister;
+
+	mutex_unlock(&ucsi->ppm_lock);
+
+	return;
+
+err_unregister:
+	for (con = ucsi->connector; con->port; con++) {
+		ucsi_unregister_partner(con);
+		typec_unregister_port(con->port);
+		con->port = NULL;
+	}
+
+err_reset:
+	ucsi_reset_ppm(ucsi);
+err:
+	mutex_unlock(&ucsi->ppm_lock);
+	dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
+}
+
+/**
+ * ucsi_register_ppm - Register UCSI PPM Interface
+ * @dev: Device interface to the PPM
+ * @ppm: The PPM interface
+ *
+ * Allocates UCSI instance, associates it with @ppm and returns it to the
+ * caller, and schedules initialization of the interface.
+ */
+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_WORK(&ucsi->work, ucsi_init);
+	init_completion(&ucsi->complete);
+	mutex_init(&ucsi->ppm_lock);
+
+	ucsi->dev = dev;
+	ucsi->ppm = ppm;
+
+	/*
+	 * Communication with the PPM takes a lot of time. It is not reasonable
+	 * to initialize the driver here. Using a work for now.
+	 */
+	queue_work(system_long_wq, &ucsi->work);
+
+	return ucsi;
+}
+EXPORT_SYMBOL_GPL(ucsi_register_ppm);
+
+/**
+ * ucsi_unregister_ppm - Unregister UCSI PPM Interface
+ * @ucsi: struct ucsi associated with the PPM
+ *
+ * Unregister UCSI PPM that was created with ucsi_register().
+ */
+void ucsi_unregister_ppm(struct ucsi *ucsi)
+{
+	struct ucsi_control ctrl;
+	int i;
+
+	/* Make sure that we are not in the middle of driver initialization */
+	cancel_work_sync(&ucsi->work);
+
+	mutex_lock(&ucsi->ppm_lock);
+
+	/* Disable everything except command complete notification */
+	UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE)
+	ucsi_run_command(ucsi, &ctrl, NULL, 0);
+
+	mutex_unlock(&ucsi->ppm_lock);
+
+	for (i = 0; i < ucsi->cap.num_connectors; i++) {
+		cancel_work_sync(&ucsi->connector[i].work);
+		ucsi_unregister_partner(&ucsi->connector[i]);
+		typec_unregister_port(ucsi->connector[i].port);
+	}
+
+	ucsi_reset_ppm(ucsi);
+
+	kfree(ucsi->connector);
+	kfree(ucsi);
+}
+EXPORT_SYMBOL_GPL(ucsi_unregister_ppm);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("USB Type-C Connector System Software Interface driver");
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b0d2f0918c6dd7fbf19207a47b2f6a43fcd9d57
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -0,0 +1,335 @@
+
+#ifndef __DRIVER_USB_TYPEC_UCSI_H
+#define __DRIVER_USB_TYPEC_UCSI_H
+
+#include <linux/bitops.h>
+#include <linux/types.h>
+
+/* -------------------------------------------------------------------------- */
+
+/* Command Status and Connector Change Indication (CCI) data structure */
+struct ucsi_cci {
+	u8:1; /* reserved */
+	u8 connector_change:7;
+	u8 data_length;
+	u16:9; /* reserved */
+	u16 not_supported:1;
+	u16 cancel_complete:1;
+	u16 reset_complete:1;
+	u16 busy:1;
+	u16 ack_complete:1;
+	u16 error:1;
+	u16 cmd_complete:1;
+} __packed;
+
+/* Default fields in CONTROL data structure */
+struct ucsi_command {
+	u8 cmd;
+	u8 length;
+	u64 data:48;
+} __packed;
+
+/* ACK Command structure */
+struct ucsi_ack_cmd {
+	u8 cmd;
+	u8 length;
+	u8 cci_ack:1;
+	u8 cmd_ack:1;
+	u8:6; /* reserved */
+} __packed;
+
+/* Connector Reset Command structure */
+struct ucsi_con_rst {
+	u8 cmd;
+	u8 length;
+	u8 con_num:7;
+	u8 hard_reset:1;
+} __packed;
+
+/* Set USB Operation Mode Command structure */
+struct ucsi_uor_cmd {
+	u8 cmd;
+	u8 length;
+	u16 con_num:7;
+	u16 role:3;
+#define UCSI_UOR_ROLE_DFP			BIT(0)
+#define UCSI_UOR_ROLE_UFP			BIT(1)
+#define UCSI_UOR_ROLE_DRP			BIT(2)
+	u16:6; /* reserved */
+} __packed;
+
+struct ucsi_control {
+	union {
+		u64 raw_cmd;
+		struct ucsi_command cmd;
+		struct ucsi_uor_cmd uor;
+		struct ucsi_ack_cmd ack;
+		struct ucsi_con_rst con_rst;
+	};
+};
+
+#define __UCSI_CMD(_ctrl_, _cmd_)					\
+{									\
+	(_ctrl_).raw_cmd = 0;						\
+	(_ctrl_).cmd.cmd = _cmd_;					\
+}
+
+/* Helper for preparing ucsi_control for CONNECTOR_RESET command. */
+#define UCSI_CMD_CONNECTOR_RESET(_ctrl_, _con_, _hard_)			\
+{									\
+	__UCSI_CMD(_ctrl_, UCSI_CONNECTOR_RESET)			\
+	(_ctrl_).con_rst.con_num = (_con_)->num;			\
+	(_ctrl_).con_rst.hard_reset = _hard_;				\
+}
+
+/* Helper for preparing ucsi_control for ACK_CC_CI command. */
+#define UCSI_CMD_ACK(_ctrl_, _ack_)					\
+{									\
+	__UCSI_CMD(_ctrl_, UCSI_ACK_CC_CI)				\
+	(_ctrl_).ack.cci_ack = ((_ack_) == UCSI_ACK_EVENT);		\
+	(_ctrl_).ack.cmd_ack = ((_ack_) == UCSI_ACK_CMD);		\
+}
+
+/* Helper for preparing ucsi_control for SET_NOTIFY_ENABLE command. */
+#define UCSI_CMD_SET_NTFY_ENABLE(_ctrl_, _ntfys_)			\
+{									\
+	__UCSI_CMD(_ctrl_, UCSI_SET_NOTIFICATION_ENABLE)		\
+	(_ctrl_).cmd.data = _ntfys_;					\
+}
+
+/* Helper for preparing ucsi_control for GET_CAPABILITY command. */
+#define UCSI_CMD_GET_CAPABILITY(_ctrl_)					\
+{									\
+	__UCSI_CMD(_ctrl_, UCSI_GET_CAPABILITY)				\
+}
+
+/* Helper for preparing ucsi_control for GET_CONNECTOR_CAPABILITY command. */
+#define UCSI_CMD_GET_CONNECTOR_CAPABILITY(_ctrl_, _con_)		\
+{									\
+	__UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_CAPABILITY)		\
+	(_ctrl_).cmd.data = _con_;					\
+}
+
+/* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. */
+#define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_)			\
+{									\
+	__UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_STATUS)			\
+	(_ctrl_).cmd.data = _con_;					\
+}
+
+#define __UCSI_ROLE(_ctrl_, _cmd_, _con_num_)				\
+{									\
+	__UCSI_CMD(_ctrl_, _cmd_)					\
+	(_ctrl_).uor.con_num = _con_num_;				\
+	(_ctrl_).uor.role = UCSI_UOR_ROLE_DRP;				\
+}
+
+/* Helper for preparing ucsi_control for SET_UOR command. */
+#define UCSI_CMD_SET_UOR(_ctrl_, _con_, _role_)				\
+{									\
+	__UCSI_ROLE(_ctrl_, UCSI_SET_UOR, (_con_)->num)		\
+	(_ctrl_).uor.role |= (_role_) == TYPEC_HOST ? UCSI_UOR_ROLE_DFP : \
+			  UCSI_UOR_ROLE_UFP;				\
+}
+
+/* Helper for preparing ucsi_control for SET_PDR command. */
+#define UCSI_CMD_SET_PDR(_ctrl_, _con_, _role_)			\
+{									\
+	__UCSI_ROLE(_ctrl_, UCSI_SET_PDR, (_con_)->num)		\
+	(_ctrl_).uor.role |= (_role_) == TYPEC_SOURCE ? UCSI_UOR_ROLE_DFP : \
+			UCSI_UOR_ROLE_UFP;				\
+}
+
+/* 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			0xdbe7
+
+/* 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)
+
+/* 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)
+	u32 num_connectors:8;
+	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;
+	u8:6; /* reserved */
+} __packed;
+
+struct ucsi_altmode {
+	u16 svid;
+	u32 mid;
+} __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:2; /* reserved */
+	u8 latency:4;
+	u8:4; /* reserved */
+} __packed;
+
+/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
+struct ucsi_connector_status {
+	u16 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_5	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		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:2;
+#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_TRICKLE_CHARGING	3
+	u8 provider_cap_limit_reason:4;
+#define UCSI_CONSTAT_CAP_PWR_LOWERED		0
+#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT	1
+	u8:2; /* reserved */
+} __packed;
+
+/* -------------------------------------------------------------------------- */
+
+struct ucsi;
+
+struct ucsi_data {
+	u16 version;
+	u16 reserved;
+	union {
+		u32 raw_cci;
+		struct ucsi_cci cci;
+	};
+	struct ucsi_control ctrl;
+	u32 message_in[4];
+	u32 message_out[4];
+} __packed;
+
+/*
+ * struct ucsi_ppm - Interface to UCSI Platform Policy Manager
+ * @data: memory location to the UCSI data structures
+ * @cmd: UCSI command execution routine
+ * @sync: Refresh UCSI mailbox (the data structures)
+ */
+struct ucsi_ppm {
+	struct ucsi_data *data;
+	int (*cmd)(struct ucsi_ppm *, struct ucsi_control *);
+	int (*sync)(struct ucsi_ppm *);
+};
+
+struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm);
+void ucsi_unregister_ppm(struct ucsi *ucsi);
+void ucsi_notify(struct ucsi *ucsi);
+
+#endif /* __DRIVER_USB_TYPEC_UCSI_H */