#include <linux/auxiliary_bus.h>
 #include <linux/bitfield.h>
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/intel_tpmi.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/security.h>
 #include <linux/sizes.h>
+#include <linux/string_helpers.h>
 
 #include "vsec.h"
 
  * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially
  *              this offset = "Address" from VSEC header + PFS Capability
  *              offset for this feature entry.
+ * @vsec_dev:  Pointer to intel_vsec_device structure for this TPMI device
  *
  * Represents TPMI instance information for one TPMI ID.
  */
 struct intel_tpmi_pm_feature {
        struct intel_tpmi_pfs_entry pfs_header;
        unsigned int vsec_offset;
+       struct intel_vsec_device *vsec_dev;
 };
 
 /**
  * @pfs_start:         Start of PFS offset for the TPMI instances in this device
  * @plat_info:         Stores platform info which can be used by the client drivers
  * @tpmi_control_mem:  Memory mapped IO for getting control information
+ * @dbgfs_dir:         debugfs entry pointer
  *
  * Stores the information for all TPMI devices enumerated from a single PCI device.
  */
        u64 pfs_start;
        struct intel_tpmi_plat_info plat_info;
        void __iomem *tpmi_control_mem;
+       struct dentry *dbgfs_dir;
 };
 
 /**
 }
 EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI);
 
+static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused)
+{
+       struct intel_tpmi_info *tpmi_info = s->private;
+       struct intel_tpmi_pm_feature *pfs;
+       int locked, disabled, ret, i;
+
+       seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start);
+       seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\n");
+       for (i = 0; i < tpmi_info->feature_count; ++i) {
+               pfs = &tpmi_info->tpmi_features[i];
+               ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &locked,
+                                              &disabled);
+               if (ret) {
+                       locked = 'U';
+                       disabled = 'U';
+               } else {
+                       disabled = disabled ? 'Y' : 'N';
+                       locked = locked ? 'Y' : 'N';
+               }
+               seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\n",
+                          pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries,
+                          pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset,
+                          pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled);
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(tpmi_pfs_dbg);
+
+#define MEM_DUMP_COLUMN_COUNT  8
+
+static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
+{
+       size_t row_size = MEM_DUMP_COLUMN_COUNT * sizeof(u32);
+       struct intel_tpmi_pm_feature *pfs = s->private;
+       int count, ret = 0;
+       void __iomem *mem;
+       u32 off, size;
+       u8 *buffer;
+
+       size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
+       if (!size)
+               return -EIO;
+
+       buffer = kmalloc(size, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       off = pfs->vsec_offset;
+
+       mutex_lock(&tpmi_dev_lock);
+
+       for (count = 0; count < pfs->pfs_header.num_entries; ++count) {
+               seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off);
+
+               mem = ioremap(off, size);
+               if (!mem) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               memcpy_fromio(buffer, mem, size);
+
+               seq_hex_dump(s, " ", DUMP_PREFIX_OFFSET, row_size, sizeof(u32), buffer, size,
+                            false);
+
+               iounmap(mem);
+
+               off += size;
+       }
+
+       mutex_unlock(&tpmi_dev_lock);
+
+       kfree(buffer);
+
+       return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(tpmi_mem_dump);
+
+static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos)
+{
+       struct seq_file *m = file->private_data;
+       struct intel_tpmi_pm_feature *pfs = m->private;
+       u32 addr, value, punit, size;
+       u32 num_elems, *array;
+       void __iomem *mem;
+       int ret;
+
+       size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
+       if (!size)
+               return -EIO;
+
+       ret = parse_int_array_user(userbuf, len, (int **)&array);
+       if (ret < 0)
+               return ret;
+
+       num_elems = *array;
+       if (num_elems != 3) {
+               ret = -EINVAL;
+               goto exit_write;
+       }
+
+       punit = array[1];
+       addr = array[2];
+       value = array[3];
+
+       if (punit >= pfs->pfs_header.num_entries) {
+               ret = -EINVAL;
+               goto exit_write;
+       }
+
+       if (addr >= size) {
+               ret = -EINVAL;
+               goto exit_write;
+       }
+
+       mutex_lock(&tpmi_dev_lock);
+
+       mem = ioremap(pfs->vsec_offset + punit * size, size);
+       if (!mem) {
+               ret = -ENOMEM;
+               goto unlock_mem_write;
+       }
+
+       writel(value, mem + addr);
+
+       iounmap(mem);
+
+       ret = len;
+
+unlock_mem_write:
+       mutex_unlock(&tpmi_dev_lock);
+
+exit_write:
+       kfree(array);
+
+       return ret;
+}
+
+static int mem_write_show(struct seq_file *s, void *unused)
+{
+       return 0;
+}
+
+static int mem_write_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mem_write_show, inode->i_private);
+}
+
+static const struct file_operations mem_write_ops = {
+       .open           = mem_write_open,
+       .read           = seq_read,
+       .write          = mem_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+#define tpmi_to_dev(info)      (&info->vsec_dev->pcidev->dev)
+
+static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info)
+{
+       char name[64];
+       int i;
+
+       snprintf(name, sizeof(name), "tpmi-%s", dev_name(tpmi_to_dev(tpmi_info)));
+       tpmi_info->dbgfs_dir = debugfs_create_dir(name, NULL);
+
+       debugfs_create_file("pfs_dump", 0444, tpmi_info->dbgfs_dir, tpmi_info, &tpmi_pfs_dbg_fops);
+
+       for (i = 0; i < tpmi_info->feature_count; ++i) {
+               struct intel_tpmi_pm_feature *pfs;
+               struct dentry *dir;
+
+               pfs = &tpmi_info->tpmi_features[i];
+               snprintf(name, sizeof(name), "tpmi-id-%02x", pfs->pfs_header.tpmi_id);
+               dir = debugfs_create_dir(name, tpmi_info->dbgfs_dir);
+
+               debugfs_create_file("mem_dump", 0444, dir, pfs, &tpmi_mem_dump_fops);
+               debugfs_create_file("mem_write", 0644, dir, pfs, &mem_write_ops);
+       }
+}
+
 static void tpmi_set_control_base(struct auxiliary_device *auxdev,
                                  struct intel_tpmi_info *tpmi_info,
                                  struct intel_tpmi_pm_feature *pfs)
        struct pci_dev *pci_dev = vsec_dev->pcidev;
        struct intel_tpmi_info *tpmi_info;
        u64 pfs_start = 0;
-       int i;
+       int ret, i;
 
        tpmi_info = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_info), GFP_KERNEL);
        if (!tpmi_info)
                int size, ret;
 
                pfs = &tpmi_info->tpmi_features[i];
+               pfs->vsec_dev = vsec_dev;
 
                res = &vsec_dev->resource[i];
                if (!res)
 
        auxiliary_set_drvdata(auxdev, tpmi_info);
 
-       return tpmi_create_devices(tpmi_info);
+       ret = tpmi_create_devices(tpmi_info);
+       if (ret)
+               return ret;
+
+       /*
+        * Allow debugfs when security policy allows. Everything this debugfs
+        * interface provides, can also be done via /dev/mem access. If
+        * /dev/mem interface is locked, don't allow debugfs to present any
+        * information. Also check for CAP_SYS_RAWIO as /dev/mem interface.
+        */
+       if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO))
+               tpmi_dbgfs_register(tpmi_info);
+
+       return 0;
 }
 
 static int tpmi_probe(struct auxiliary_device *auxdev,
        return intel_vsec_tpmi_init(auxdev);
 }
 
-/*
- * Remove callback is not needed currently as there is no
- * cleanup required. All memory allocs are device managed. All
- * devices created by this modules are also device managed.
- */
+static void tpmi_remove(struct auxiliary_device *auxdev)
+{
+       struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(auxdev);
+
+       debugfs_remove_recursive(tpmi_info->dbgfs_dir);
+}
 
 static const struct auxiliary_device_id tpmi_id_table[] = {
        { .name = "intel_vsec.tpmi" },
 static struct auxiliary_driver tpmi_aux_driver = {
        .id_table       = tpmi_id_table,
        .probe          = tpmi_probe,
+       .remove         = tpmi_remove,
 };
 
 module_auxiliary_driver(tpmi_aux_driver);