Commit df0383ff authored by Saranya Gopal's avatar Saranya Gopal Committed by Greg Kroah-Hartman
Browse files

usb: typec: ucsi: Add debugfs for ucsi commands



Add support for UCSI commands through the following debugfs:
  # /sys/kernel/debug/usb/ucsi/$UCSI_DEVICE/command
  # /sys/kernel/debug/usb/ucsi/$UCSI_DEVICE/response

Eg: To execute UCSI GetCapabilities:
  # echo 0x6 > /sys/kernel/debug/usb/ucsi/<ucsi device>/command
Then read the result,
  # cat /sys/kernel/debug/usb/ucsi/<ucsi device>/response
    0x02000320000000020000ff0400000445

UCSI command will be written into the command file and the
response for the command can be viewed under the response file.

Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarSaranya Gopal <saranya.gopal@intel.com>
Co-developed-by: default avatarRajaram Regupathy <rajaram.regupathy@intel.com>
Signed-off-by: default avatarRajaram Regupathy <rajaram.regupathy@intel.com>
Reviewed-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20230807105205.742819-1-saranya.gopal@intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3b563b90
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ config TYPEC_UCSI
	tristate "USB Type-C Connector System Software Interface driver"
	depends on !CPU_BIG_ENDIAN
	depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
	select USB_COMMON if DEBUG_FS
	help
	  USB Type-C Connector System Software Interface (UCSI) is a
	  specification for an interface that allows the operating system to
+2 −0
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@ obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o

typec_ucsi-y				:= ucsi.o

typec_ucsi-$(CONFIG_DEBUG_FS)		+= debugfs.o

typec_ucsi-$(CONFIG_TRACING)		+= trace.o

ifneq ($(CONFIG_POWER_SUPPLY),)
+99 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * UCSI debugfs interface
 *
 * Copyright (C) 2023 Intel Corporation
 *
 * Authors: Rajaram Regupathy <rajaram.regupathy@intel.com>
 *	    Gopal Saranya <saranya.gopal@intel.com>
 */
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/usb.h>

#include <asm/errno.h>

#include "ucsi.h"

static struct dentry *ucsi_debugfs_root;

static int ucsi_cmd(void *data, u64 val)
{
	struct ucsi *ucsi = data;
	int ret;

	memset(&ucsi->debugfs->response, 0, sizeof(ucsi->debugfs->response));
	ucsi->debugfs->status = 0;

	switch (UCSI_COMMAND(val)) {
	case UCSI_SET_UOM:
	case UCSI_SET_UOR:
	case UCSI_SET_PDR:
	case UCSI_CONNECTOR_RESET:
		ret = ucsi_send_command(ucsi, val, NULL, 0);
		break;
	case UCSI_GET_CAPABILITY:
	case UCSI_GET_CONNECTOR_CAPABILITY:
	case UCSI_GET_ALTERNATE_MODES:
	case UCSI_GET_CURRENT_CAM:
	case UCSI_GET_PDOS:
	case UCSI_GET_CABLE_PROPERTY:
	case UCSI_GET_CONNECTOR_STATUS:
		ret = ucsi_send_command(ucsi, val,
					&ucsi->debugfs->response,
					sizeof(ucsi->debugfs->response));
		break;
	default:
		ret = -EOPNOTSUPP;
	}

	if (ret < 0) {
		ucsi->debugfs->status = ret;
		return ret;
	}

	return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ucsi_cmd_fops, NULL, ucsi_cmd, "0x%llx\n");

static int ucsi_resp_show(struct seq_file *s, void *not_used)
{
	struct ucsi *ucsi = s->private;

	if (ucsi->debugfs->status)
		return ucsi->debugfs->status;

	seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high,
		   ucsi->debugfs->response.low);
	return 0;
}
DEFINE_SHOW_ATTRIBUTE(ucsi_resp);

void ucsi_debugfs_register(struct ucsi *ucsi)
{
	ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL);
	if (!ucsi->debugfs)
		return;

	ucsi->debugfs->dentry = debugfs_create_dir(dev_name(ucsi->dev), ucsi_debugfs_root);
	debugfs_create_file("command", 0200, ucsi->debugfs->dentry, ucsi, &ucsi_cmd_fops);
	debugfs_create_file("response", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_resp_fops);
}

void ucsi_debugfs_unregister(struct ucsi *ucsi)
{
	debugfs_remove_recursive(ucsi->debugfs->dentry);
	kfree(ucsi->debugfs);
}

void ucsi_debugfs_init(void)
{
	ucsi_debugfs_root = debugfs_create_dir("ucsi", usb_debug_root);
}

void ucsi_debugfs_exit(void)
{
	debugfs_remove(ucsi_debugfs_root);
}
+15 −0
Original line number Diff line number Diff line
@@ -1530,6 +1530,7 @@ EXPORT_SYMBOL_GPL(ucsi_create);
 */
void ucsi_destroy(struct ucsi *ucsi)
{
	ucsi_debugfs_unregister(ucsi);
	kfree(ucsi);
}
EXPORT_SYMBOL_GPL(ucsi_destroy);
@@ -1552,6 +1553,7 @@ int ucsi_register(struct ucsi *ucsi)

	queue_delayed_work(system_long_wq, &ucsi->work, 0);

	ucsi_debugfs_register(ucsi);
	return 0;
}
EXPORT_SYMBOL_GPL(ucsi_register);
@@ -1611,6 +1613,19 @@ void ucsi_unregister(struct ucsi *ucsi)
}
EXPORT_SYMBOL_GPL(ucsi_unregister);

static int __init ucsi_module_init(void)
{
	ucsi_debugfs_init();
	return 0;
}
module_init(ucsi_module_init);

static void __exit ucsi_module_exit(void)
{
	ucsi_debugfs_exit();
}
module_exit(ucsi_module_exit);

MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("USB Type-C Connector System Software Interface driver");
+24 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

struct ucsi;
struct ucsi_altmode;
struct dentry;

/* UCSI offsets (Bytes) */
#define UCSI_VERSION			0
@@ -277,6 +278,16 @@ struct ucsi_connector_status {

/* -------------------------------------------------------------------------- */

struct ucsi_debugfs_entry {
	u64 command;
	struct ucsi_data {
		u64 low;
		u64 high;
	} response;
	u32 status;
	struct dentry *dentry;
};

struct ucsi {
	u16 version;
	struct device *dev;
@@ -286,6 +297,7 @@ struct ucsi {

	struct ucsi_capability cap;
	struct ucsi_connector *connector;
	struct ucsi_debugfs_entry *debugfs;

	struct work_struct resume_work;
	struct delayed_work work;
@@ -388,6 +400,18 @@ static inline void
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */

#ifdef CONFIG_DEBUG_FS
void ucsi_debugfs_init(void);
void ucsi_debugfs_exit(void);
void ucsi_debugfs_register(struct ucsi *ucsi);
void ucsi_debugfs_unregister(struct ucsi *ucsi);
#else
static inline void ucsi_debugfs_init(void) { }
static inline void ucsi_debugfs_exit(void) { }
static inline void ucsi_debugfs_register(struct ucsi *ucsi) { }
static inline void ucsi_debugfs_unregister(struct ucsi *ucsi) { }
#endif /* CONFIG_DEBUG_FS */

/*
 * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink
 * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3