thunderbolt: Expose router DROM through debugfs
authorMika Westerberg <mika.westerberg@linux.intel.com>
Sat, 21 Dec 2024 12:52:43 +0000 (14:52 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Sun, 5 Jan 2025 08:35:03 +0000 (10:35 +0200)
Router DROM contains information that might be usable for development
and debugging purposes. For example when new entries are added to the
USB4 spec it is useful to be able to look for them without need to
change the kernel.

For this reason expose the DROM through debugfs.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/debugfs.c
drivers/thunderbolt/eeprom.c
drivers/thunderbolt/tb.h

index fa61127b2c47cf9fe40d609c8adb5c9067fa0f2c..f8328ca7e22e59d01b02f79d5f3b869f44cfe758 100644 (file)
@@ -2413,6 +2413,8 @@ void tb_switch_debugfs_init(struct tb_switch *sw)
        sw->debugfs_dir = debugfs_dir;
        debugfs_create_file("regs", DEBUGFS_MODE, debugfs_dir, sw,
                            &switch_regs_fops);
+       if (sw->drom)
+               debugfs_create_blob("drom", 0400, debugfs_dir, &sw->drom_blob);
 
        tb_switch_for_each_port(sw, port) {
                struct dentry *debugfs_dir;
index eb241b270f790b2a1eafa3a5b87aacbd9c65da32..9c1d65d2655310a104e75ba18644268da5ebc0cb 100644 (file)
@@ -435,6 +435,29 @@ static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
        return 0;
 }
 
+static int tb_switch_drom_alloc(struct tb_switch *sw, size_t size)
+{
+       sw->drom = kzalloc(size, GFP_KERNEL);
+       if (!sw->drom)
+               return -ENOMEM;
+
+#ifdef CONFIG_DEBUG_FS
+       sw->drom_blob.data = sw->drom;
+       sw->drom_blob.size = size;
+#endif
+       return 0;
+}
+
+static void tb_switch_drom_free(struct tb_switch *sw)
+{
+#ifdef CONFIG_DEBUG_FS
+       sw->drom_blob.data = NULL;
+       sw->drom_blob.size = 0;
+#endif
+       kfree(sw->drom);
+       sw->drom = NULL;
+}
+
 /*
  * tb_drom_copy_efi - copy drom supplied by EFI to sw->drom if present
  */
@@ -447,9 +470,9 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
        if (len < 0 || len < sizeof(struct tb_drom_header))
                return -EINVAL;
 
-       sw->drom = kmalloc(len, GFP_KERNEL);
-       if (!sw->drom)
-               return -ENOMEM;
+       res = tb_switch_drom_alloc(sw, len);
+       if (res)
+               return res;
 
        res = device_property_read_u8_array(dev, "ThunderboltDROM", sw->drom,
                                                                        len);
@@ -464,8 +487,7 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
        return 0;
 
 err:
-       kfree(sw->drom);
-       sw->drom = NULL;
+       tb_switch_drom_free(sw);
        return -EINVAL;
 }
 
@@ -491,13 +513,15 @@ static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
 
        /* Size includes CRC8 + UID + CRC32 */
        *size += 1 + 8 + 4;
-       sw->drom = kzalloc(*size, GFP_KERNEL);
-       if (!sw->drom)
-               return -ENOMEM;
+       ret = tb_switch_drom_alloc(sw, *size);
+       if (ret)
+               return ret;
 
        ret = dma_port_flash_read(sw->dma_port, drom_offset, sw->drom, *size);
-       if (ret)
-               goto err_free;
+       if (ret) {
+               tb_switch_drom_free(sw);
+               return ret;
+       }
 
        /*
         * Read UID from the minimal DROM because the one in NVM is just
@@ -505,11 +529,6 @@ static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
         */
        tb_drom_read_uid_only(sw, &sw->uid);
        return 0;
-
-err_free:
-       kfree(sw->drom);
-       sw->drom = NULL;
-       return ret;
 }
 
 static int usb4_copy_drom(struct tb_switch *sw, u16 *size)
@@ -522,15 +541,13 @@ static int usb4_copy_drom(struct tb_switch *sw, u16 *size)
 
        /* Size includes CRC8 + UID + CRC32 */
        *size += 1 + 8 + 4;
-       sw->drom = kzalloc(*size, GFP_KERNEL);
-       if (!sw->drom)
-               return -ENOMEM;
+       ret = tb_switch_drom_alloc(sw, *size);
+       if (ret)
+               return ret;
 
        ret = usb4_switch_drom_read(sw, 0, sw->drom, *size);
-       if (ret) {
-               kfree(sw->drom);
-               sw->drom = NULL;
-       }
+       if (ret)
+               tb_switch_drom_free(sw);
 
        return ret;
 }
@@ -552,19 +569,14 @@ static int tb_drom_bit_bang(struct tb_switch *sw, u16 *size)
                return -EIO;
        }
 
-       sw->drom = kzalloc(*size, GFP_KERNEL);
-       if (!sw->drom)
-               return -ENOMEM;
+       ret = tb_switch_drom_alloc(sw, *size);
+       if (ret)
+               return ret;
 
        ret = tb_eeprom_read_n(sw, 0, sw->drom, *size);
        if (ret)
-               goto err;
-
-       return 0;
+               tb_switch_drom_free(sw);
 
-err:
-       kfree(sw->drom);
-       sw->drom = NULL;
        return ret;
 }
 
@@ -646,9 +658,7 @@ static int tb_drom_parse(struct tb_switch *sw, u16 size)
        return 0;
 
 err:
-       kfree(sw->drom);
-       sw->drom = NULL;
-
+       tb_switch_drom_free(sw);
        return ret;
 }
 
index ddbf0cd7837763c13dc0201ef8fb9288761e49d0..b54147a1ba8778ab88320637988a093e843478ed 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef TB_H_
 #define TB_H_
 
+#include <linux/debugfs.h>
 #include <linux/nvmem-provider.h>
 #include <linux/pci.h>
 #include <linux/thunderbolt.h>
@@ -160,6 +161,7 @@ struct tb_switch_tmu {
  * @max_pcie_credits: Router preferred number of buffers for PCIe
  * @max_dma_credits: Router preferred number of buffers for DMA/P2P
  * @clx: CLx states on the upstream link of the router
+ * @drom_blob: DROM debugfs blob wrapper
  *
  * When the switch is being added or removed to the domain (other
  * switches) you need to have domain lock held.
@@ -212,6 +214,9 @@ struct tb_switch {
        unsigned int max_pcie_credits;
        unsigned int max_dma_credits;
        unsigned int clx;
+#ifdef CONFIG_DEBUG_FS
+       struct debugfs_blob_wrapper drom_blob;
+#endif
 };
 
 /**