Commit c1b0bc2d authored by Heikki Krogerus's avatar Heikki Krogerus Committed by Greg Kroah-Hartman

usb: typec: Add support for UCSI interface

UCSI - USB Type-C Connector System Software Interface - is a
specification that defines set of registers and data
structures for controlling the USB Type-C ports. It's
designed for systems where an embedded controller (EC) is in
charge of the USB Type-C PHY or USB Power Delivery
controller. It is designed for systems with EC, but it is
not limited to them, and for example some USB Power Delivery
controllers will use it as their direct control interface.

With UCSI the EC (or USB PD controller) acts as the port
manager, implementing all USB Type-C and Power Delivery state
machines. The OS can use the interfaces for reading the
status of the ports and controlling basic operations like
role swapping.

The UCSI specification highlights the fact that it does not
define the interface method (PCI/I2C/ACPI/etc.).
Therefore the driver is implemented as library and every
supported interface method needs its own driver. Driver for
ACPI is provided in separate patch following this one.

The initial driver includes support for all required
features from UCSI specification version 1.0 (getting
connector capabilities and status, and support for power and
data role swapping), but none of the optional UCSI features
(alternate modes, power source capabilities, and cable
capabilities).
Signed-off-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c68bb0ef
......@@ -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
obj-$(CONFIG_TYPEC) += typec.o
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
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.
CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o
typec_ucsi-y := ucsi.o
typec_ucsi-$(CONFIG_FTRACE) += trace.o
#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 */
#define CREATE_TRACE_POINTS
#include "trace.h"
#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>
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment