usb: typec: ucsi: Add debugfs for ucsi commands
authorSaranya Gopal <saranya.gopal@intel.com>
Mon, 7 Aug 2023 10:52:05 +0000 (16:22 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 8 Aug 2023 08:36:18 +0000 (10:36 +0200)
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: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Saranya Gopal <saranya.gopal@intel.com>
Co-developed-by: Rajaram Regupathy <rajaram.regupathy@intel.com>
Signed-off-by: Rajaram Regupathy <rajaram.regupathy@intel.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20230807105205.742819-1-saranya.gopal@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/ucsi/Kconfig
drivers/usb/typec/ucsi/Makefile
drivers/usb/typec/ucsi/debugfs.c [new file with mode: 0644]
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h

index b3bb0191987ed50496a90bd444da132d6e17e5a1..bdcb1764cfae3a71852af90669c622b056504fca 100644 (file)
@@ -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
index 77f09e13695613b1fef5ad8148a3429cbd61ae0b..b4679f94696b6b653703b1601a201843630a24de 100644 (file)
@@ -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),)
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
new file mode 100644 (file)
index 0000000..0c7bf88
--- /dev/null
@@ -0,0 +1,99 @@
+// 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);
+}
index f6901319639d9c28cc7f5e50aa59b4f66b09cfcd..c6dfe3dff3465eddf5e106f325a3cfac9e262968 100644 (file)
@@ -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");
index c09af859f573ae5f0db7bbb94e6307305c77c7fd..474315a72c7707ab5ae96a2664ada020a906d4b0 100644 (file)
@@ -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