Commit 07c5ba91 authored by Josh Collier's avatar Josh Collier Committed by Jason Gunthorpe
Browse files

IB/hfi1: Add debugfs to control expansion ROM write protect



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: default avatarDennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: default avatarJosh Collier <josh.d.collier@intel.com>
Signed-off-by: default avatarDennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 57425822
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
@@ -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),