IB/hfi1: Add debugfs to control expansion ROM write protect
authorJosh Collier <josh.d.collier@intel.com>
Thu, 11 Apr 2019 14:07:42 +0000 (07:07 -0700)
committerJason Gunthorpe <jgg@mellanox.com>
Wed, 24 Apr 2019 14:26:41 +0000 (11:26 -0300)
Some kernels now enable CONFIG_IO_STRICT_DEVMEM which prevents multiple
handles to PCI resource0. In order to continue to support expansion ROM
updates while the driver is loaded, the driver must now provide an
interface to control the expansion ROM write protection.

This patch adds an exprom_wp debugfs interface that allows the hfi1_eprom
user tool to disable the expansion ROM write protection by opening the
file and writing a '1'.  The write protection is released when writing a
'0' or automatically re-enabled when the file handle is closed.  The
current implementation will only allow one handle to be opened at a time
across all hfi1 devices.

Reviewed-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Josh Collier <josh.d.collier@intel.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
drivers/infiniband/hw/hfi1/debugfs.c

index 057bb82..15efb4a 100644 (file)
@@ -1080,6 +1080,77 @@ static int qsfp2_debugfs_release(struct inode *in, struct file *fp)
        return __qsfp_debugfs_release(in, fp, 1);
 }
 
+#define EXPROM_WRITE_ENABLE BIT_ULL(14)
+
+static bool exprom_wp_disabled;
+
+static int exprom_wp_set(struct hfi1_devdata *dd, bool disable)
+{
+       u64 gpio_val = 0;
+
+       if (disable) {
+               gpio_val = EXPROM_WRITE_ENABLE;
+               exprom_wp_disabled = true;
+               dd_dev_info(dd, "Disable Expansion ROM Write Protection\n");
+       } else {
+               exprom_wp_disabled = false;
+               dd_dev_info(dd, "Enable Expansion ROM Write Protection\n");
+       }
+
+       write_csr(dd, ASIC_GPIO_OUT, gpio_val);
+       write_csr(dd, ASIC_GPIO_OE, gpio_val);
+
+       return 0;
+}
+
+static ssize_t exprom_wp_debugfs_read(struct file *file, char __user *buf,
+                                     size_t count, loff_t *ppos)
+{
+       return 0;
+}
+
+static ssize_t exprom_wp_debugfs_write(struct file *file,
+                                      const char __user *buf, size_t count,
+                                      loff_t *ppos)
+{
+       struct hfi1_pportdata *ppd = private2ppd(file);
+       char cdata;
+
+       if (count != 1)
+               return -EINVAL;
+       if (get_user(cdata, buf))
+               return -EFAULT;
+       if (cdata == '0')
+               exprom_wp_set(ppd->dd, false);
+       else if (cdata == '1')
+               exprom_wp_set(ppd->dd, true);
+       else
+               return -EINVAL;
+
+       return 1;
+}
+
+static unsigned long exprom_in_use;
+
+static int exprom_wp_debugfs_open(struct inode *in, struct file *fp)
+{
+       if (test_and_set_bit(0, &exprom_in_use))
+               return -EBUSY;
+
+       return 0;
+}
+
+static int exprom_wp_debugfs_release(struct inode *in, struct file *fp)
+{
+       struct hfi1_pportdata *ppd = private2ppd(fp);
+
+       if (exprom_wp_disabled)
+               exprom_wp_set(ppd->dd, false);
+       clear_bit(0, &exprom_in_use);
+
+       return 0;
+}
+
 #define DEBUGFS_OPS(nm, readroutine, writeroutine)     \
 { \
        .name = nm, \
@@ -1119,6 +1190,9 @@ static const struct counter_info port_cntr_ops[] = {
                     qsfp1_debugfs_open, qsfp1_debugfs_release),
        DEBUGFS_XOPS("qsfp2", qsfp2_debugfs_read, qsfp2_debugfs_write,
                     qsfp2_debugfs_open, qsfp2_debugfs_release),
+       DEBUGFS_XOPS("exprom_wp", exprom_wp_debugfs_read,
+                    exprom_wp_debugfs_write, exprom_wp_debugfs_open,
+                    exprom_wp_debugfs_release),
        DEBUGFS_OPS("asic_flags", asic_flags_read, asic_flags_write),
        DEBUGFS_OPS("dc8051_memory", dc8051_memory_read, NULL),
        DEBUGFS_OPS("lcb", debugfs_lcb_read, debugfs_lcb_write),