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