All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
@ 2013-10-22 12:20 Viswas G
  2013-10-29 10:19 ` Jack Wang
  0 siblings, 1 reply; 8+ messages in thread
From: Viswas G @ 2013-10-22 12:20 UTC (permalink / raw)
  To: linux-scsi
  Cc: xjtuwjp, Sangeetha.Gnanasekaran, Nikith.Ganigarakoppal, Viswas.G,
	AnandKumar.Santhanam


Signed-off-by: Viswas G <Viswas.G@pmcs.com>
---
 drivers/scsi/pm8001/pm8001_ctl.c  |  248 ++++++++++++++++++++++++++++++++++++-
 drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
 drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
 drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
 drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
 drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
 6 files changed, 524 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index 247cb1c..3c9ca21 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -40,7 +40,8 @@
 #include <linux/firmware.h>
 #include <linux/slab.h>
 #include "pm8001_sas.h"
-#include "pm8001_ctl.h"
+
+int pm8001_major = -1;
 
 /* scsi host attributes */
 
@@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
 	NULL,
 };
 
+/**
+ * pm8001_open - open the configuration file
+ * @inode: inode being opened
+ * @file: file handle attached
+ *
+ * Called when the configuration device is opened. Does the needed
+ * set up on the handle and then returns
+ *
+ */
+static int pm8001_open(struct inode *inode, struct file *file)
+{
+	struct pm8001_hba_info *pm8001_ha;
+	unsigned minor_number = iminor(inode);
+	int err = -ENODEV;
+
+	list_for_each_entry(pm8001_ha, &hba_list, list) {
+		if (pm8001_ha->id == minor_number) {
+			file->private_data = pm8001_ha;
+			err = 0;
+			break;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * pm8001_close - close the configuration file
+ * @inode: inode being opened
+ * @file: file handle attached
+ *
+ * Called when the configuration device is closed. Does the needed
+ * set up on the handle and then returns
+ *
+ */
+static int pm8001_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
+			      unsigned long arg)
+{
+	u32 ret = 0;
+	struct ioctl_info_buffer info_buf;
+	union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
+
+	strcpy(info_buf.information.sz_name, DRV_NAME);
+
+	info_buf.information.usmajor_revision = DRV_MAJOR;
+	info_buf.information.usminor_revision = DRV_MINOR;
+	info_buf.information.usbuild_revision = DRV_BUILD;
+	if (pm8001_ha->chip_id == chip_8001) {
+		info_buf.information.maxoutstandingIO =
+			main_tbl.pm8001_tbl.max_out_io;
+		info_buf.information.maxdevices =
+			(main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
+	} else {
+		info_buf.information.maxoutstandingIO =
+			main_tbl.pm80xx_tbl.max_out_io;
+		info_buf.information.maxdevices =
+			(main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
+	}
+	info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
+
+	if (copy_to_user((void *)arg, (void *)&info_buf,
+			 sizeof(struct ioctl_info_buffer))) {
+		ret = ADPT_IOCTL_CALL_FAILED;
+	}
+
+	return ret;
+}
+
+static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
+			      unsigned long arg)
+{
+	struct gpio_buffer buffer;
+	struct pm8001_gpio *payload;
+	struct gpio_ioctl_resp *gpio_resp;
+	DECLARE_COMPLETION_ONSTACK(completion);
+	unsigned long timeout;
+	u32 ret = 0, operation;
+
+	mutex_lock(&pm8001_ha->ioctl_mutex);
+
+	if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
+		sizeof(struct gpio_buffer))) {
+		ret = ADPT_IOCTL_CALL_FAILED;
+		goto exit;
+	}
+
+	pm8001_ha->ioctl_completion = &completion;
+	payload = &buffer.gpio_payload;
+	operation = payload->operation;
+	ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
+	if (ret != 0) {
+		ret = ADPT_IOCTL_CALL_FAILED;
+		goto exit;
+	}
+
+	timeout = (unsigned long)buffer.header.timeout * 1000;
+
+	mod_timer(&pm8001_ha->ioctl_timer, jiffies + msecs_to_jiffies(timeout));
+
+	wait_for_completion(&completion);
+
+	if (pm8001_ha->ioctl_timer_expired) {
+		ret = ADPT_IOCTL_CALL_TIMEOUT;
+		goto exit;
+	}
+	gpio_resp = &pm8001_ha->gpio_resp;
+
+	buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
+
+	if (operation == GPIO_READ) {
+		payload->rd_wr_val		= gpio_resp->gpio_rd_val;
+		payload->input_enable		= gpio_resp->gpio_in_enabled;
+		payload->pinsetup1		= gpio_resp->gpio_pinsetup1;
+		payload->pinsetup2		= gpio_resp->gpio_pinsetup2;
+		payload->event_level		= gpio_resp->gpio_evt_change;
+		payload->event_rising_edge	= gpio_resp->gpio_evt_rise;
+		payload->event_falling_edge	= gpio_resp->gpio_evt_fall;
+
+		if (copy_to_user((void *)arg, (void *)&buffer,
+				 sizeof(struct gpio_buffer))) {
+			ret = ADPT_IOCTL_CALL_FAILED;
+		}
+	} else {
+		if (copy_to_user((void *)arg, (void *)&buffer.header,
+				 sizeof(struct ioctl_header))) {
+			ret = ADPT_IOCTL_CALL_FAILED;
+		}
+	}
+exit:
+	pm8001_ha->ioctl_timer_expired = 0;
+	mutex_unlock(&pm8001_ha->ioctl_mutex);
+	return ret;
+}
+
+/**
+ * pm8001_ioctl - pm8001 configuration request
+ * @inode: inode of device
+ * @file: file handle
+ * @cmd: ioctl command code
+ * @arg: argument
+ *
+ * Handles a configuration ioctl.
+ *
+ */
+static long pm8001_ioctl(struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	u32 ret = -EACCES;
+	struct pm8001_hba_info *pm8001_ha;
+	struct ioctl_header header;
+
+	pm8001_ha = file->private_data;
+
+	switch (cmd) {
+
+	case ADPT_IOCTL_GPIO:
+		ret = pm8001_gpio_ioctl(pm8001_ha, arg);
+		break;
+	case ADPT_IOCTL_INFO:
+		ret = pm8001_info_ioctl(pm8001_ha, arg);
+		break;
+	default:
+		ret = ADPT_IOCTL_CALL_INVALID_CODE;
+	}
+	if (ret == 0)
+		return ret;
+	header.return_code = ret;
+	ret = -EACCES;
+	if (copy_to_user((void *)arg, (void *)&header,
+			sizeof(struct ioctl_header))) {
+		return -EFAULT;
+	}
+	PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
+	return ret;
+}
+
+/**
+ * pm8001_poll - pm8001 poll request function
+ * @file: file handle
+ * @wait: poll table to wait
+ *
+ * Handles a poll request.
+ *
+ */
+unsigned int pm8001_poll(struct file *file, poll_table *wait)
+{
+	struct pm8001_hba_info *pm8001_ha;
+	unsigned int mask = 0;
+
+	pm8001_ha = file->private_data;
+
+	poll_wait(file, &pm8001_ha->pollq, wait);
+
+	if (pm8001_ha->gpio_event_occured == 1) {
+		pm8001_ha->gpio_event_occured = 0;
+		mask |= POLLIN | POLLRDNORM;
+	}
+
+	return mask;
+}
+
+static const struct file_operations pm8001_fops = {
+	.owner		= THIS_MODULE,
+	.open		= pm8001_open,
+	.release	= pm8001_close,
+	.unlocked_ioctl	= pm8001_ioctl,
+	.poll		= pm8001_poll,
+};
+
+/**
+ * pm8001_setup_chrdev - registers a char device
+ *
+ * Return value
+ *	0 in case of success, otherwise non-zero
+ */
+int pm8001_setup_chrdev(void)
+{
+	pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
+	if (pm8001_major < 0) {
+		pr_warn("pm8001 : unable to register \"%s\" device\n",
+				DRV_NAME);
+		return pm8001_major;
+	}
+	return 0;
+}
+
+/**
+ * pm8001_release_chrdev - unregisters per-adapter management interface
+ *
+ * Return value
+ *	none
+ */
+void pm8001_release_chrdev(void)
+{
+	if (pm8001_major > -1) {
+		unregister_chrdev(pm8001_major, DRV_NAME);
+		pm8001_major = -1;
+	}
+}
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h
index d0d43a2..0064dd3 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.h
+++ b/drivers/scsi/pm8001/pm8001_ctl.h
@@ -55,6 +55,61 @@
 #define FAIL_OUT_MEMORY                 0x000c00
 #define FLASH_IN_PROGRESS               0x001000
 
+#define ADPT_IOCTL_CALL_SUCCESS		0x00
+#define ADPT_IOCTL_CALL_FAILED		0x01
+#define ADPT_IOCTL_CALL_INVALID_CODE	0x03
+#define ADPT_IOCTL_CALL_INVALID_DEVICE	0x04
+#define ADPT_IOCTL_CALL_TIMEOUT		0x08
+
+#define GPIO_READ	0
+#define GPIO_WRITE	1
+#define GPIO_PINSETUP	2
+#define GPIO_EVENTSETUP	3
+
+struct ioctl_header {
+	u32 io_controller_num;
+	u32 length;
+	u32 return_code;
+	u32 timeout;
+	u32 direction;
+};
+
+struct ioctl_drv_info {
+	u8	sz_name[64];
+	u16	usmajor_revision;
+	u16	usminor_revision;
+	u16	usbuild_revision;
+	u16	reserved0;
+	u32	maxdevices;
+	u32	maxoutstandingIO;
+	u32	reserved[16];
+};
+
+struct pm8001_gpio {
+	u32 operation;
+	u32 mask;
+	u32 rd_wr_val;
+	u32 input_enable;
+	u32 pinsetup1;
+	u32 pinsetup2;
+	u32 event_level;
+	u32 event_rising_edge;
+	u32 event_falling_edge;
+};
+
+struct ioctl_info_buffer {
+	struct ioctl_header	header;
+	struct ioctl_drv_info	information;
+};
+
+struct gpio_buffer {
+	struct ioctl_header	header;
+	struct pm8001_gpio	gpio_payload;
+};
+
+#define ADPT_IOCTL_INFO		_IOR(0, 0, struct ioctl_info_buffer *)
+#define ADPT_IOCTL_GPIO		_IOWR(0, 1, struct  gpio_buffer *)
+
 #define IB_OB_READ_TIMES                256
 #define SYSFS_OFFSET                    1024
 #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index 662bf13..ba2e659 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
 	if (!pm8001_ha)
 		return;
 
+	del_timer(&pm8001_ha->ioctl_timer);
+
 	for (i = 0; i < USI_MAX_MEMCNT; i++) {
 		if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
 			pci_free_consistent(pm8001_ha->pdev,
@@ -444,6 +446,24 @@ static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
 	return 0;
 }
 
+void pm8001_ioctl_timer_callback(unsigned long data)
+{
+	struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info *)data;
+
+	PM8001_FAIL_DBG(pm8001_ha,
+		pm8001_printk("Timer expired for GPIO response\n"));
+
+	spin_lock(&pm8001_ha->ioctl_lock);
+
+	if (pm8001_ha->ioctl_completion != NULL) {
+		pm8001_ha->ioctl_timer_expired = 1;
+		complete(pm8001_ha->ioctl_completion);
+		pm8001_ha->ioctl_completion = NULL;
+	}
+
+	spin_unlock(&pm8001_ha->ioctl_lock);
+}
+
 /**
  * pm8001_pci_alloc - initialize our ha card structure
  * @pdev: pci device.
@@ -479,6 +499,15 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
 	else
 		pm8001_ha->iomb_size = IOMB_SIZE_SPC;
 
+	mutex_init(&pm8001_ha->ioctl_mutex);
+	pm8001_ha->ioctl_completion = NULL;
+	init_waitqueue_head(&pm8001_ha->pollq);
+	pm8001_ha->gpio_event_occured = 0;
+	spin_lock_init(&pm8001_ha->ioctl_lock);
+	setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
+		     (unsigned long)pm8001_ha);
+
+
 #ifdef PM8001_USE_TASKLET
 	/**
 	* default tasklet for non msi-x interrupt handler/first msi-x
@@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
 	rc = pci_register_driver(&pm8001_pci_driver);
 	if (rc)
 		goto err_tp;
+
+	rc = pm8001_setup_chrdev();
+	if (rc)
+		goto err_ctl;
+
 	return 0;
 
+err_ctl:
+	pci_unregister_driver(&pm8001_pci_driver);
 err_tp:
 	sas_release_transport(pm8001_stt);
 err_wq:
@@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
 	pci_unregister_driver(&pm8001_pci_driver);
 	sas_release_transport(pm8001_stt);
 	destroy_workqueue(pm8001_wq);
+	pm8001_release_chrdev();
 }
 
 module_init(pm8001_init);
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index 9241c04..9171f0a 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -51,14 +51,21 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/workqueue.h>
+#include <linux/poll.h>
+#include <linux/poll.h>
+#include <linux/timer.h>
 #include <scsi/libsas.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/sas_ata.h>
 #include <linux/atomic.h>
 #include "pm8001_defs.h"
+#include "pm8001_ctl.h"
 
 #define DRV_NAME		"pm80xx"
 #define DRV_VERSION		"0.1.37"
+#define DRV_MAJOR		1
+#define DRV_MINOR		0
+#define DRV_BUILD		15
 #define PM8001_FAIL_LOGGING	0x01 /* Error message logging */
 #define PM8001_INIT_LOGGING	0x02 /* driver init logging */
 #define PM8001_DISC_LOGGING	0x04 /* discovery layer logging */
@@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
 	u8	*func_specific;
 };
 
+struct gpio_ioctl_resp {
+	u32 tag;
+	u32 gpio_rd_val;
+	u32 gpio_in_enabled;
+	u32 gpio_pinsetup1;
+	u32 gpio_pinsetup2;
+	u32 gpio_evt_change;
+	u32 gpio_evt_rise;
+	u32 gpio_evt_fall;
+};
+
 #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF
 #define MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
 #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
@@ -229,6 +247,8 @@ struct pm8001_dispatch {
 	int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
 		u32 state);
 	int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
+	int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
+		struct pm8001_gpio *gpio_payload);
 };
 
 struct pm8001_chip_info {
@@ -527,6 +547,14 @@ struct pm8001_hba_info {
 	u32			int_vector;
 	const struct firmware 	*fw_image;
 	u8			outq[PM8001_MAX_MSIX_VEC];
+	struct completion	*ioctl_completion;
+	struct mutex		ioctl_mutex;
+	spinlock_t		ioctl_lock;
+	u32			gpio_event_occured;
+	u32			ioctl_timer_expired;
+	struct timer_list	ioctl_timer;
+	struct gpio_ioctl_resp	gpio_resp;
+	wait_queue_head_t	pollq;
 };
 
 struct pm8001_work {
@@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev, u32, char *buf);
 /* ctl shared API */
 extern struct device_attribute *pm8001_host_attrs[];
 
+int pm8001_setup_chrdev(void);
+void pm8001_release_chrdev(void);
 #endif
 
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c
index 4ebc79b..5423844 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.c
+++ b/drivers/scsi/pm8001/pm80xx_hwi.c
@@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct pm8001_hba_info *pm8001_ha,
 	return 0;
 }
 
+static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+	int ret;
+	struct gpio_ioctl_resp *pgpio_resp;
+	struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 4);
+
+	spin_lock(&pm8001_ha->ioctl_lock);
+	if (pm8001_ha->ioctl_completion != NULL) {
+		ret = del_timer(&pm8001_ha->ioctl_timer);
+		if (ret)
+			PM8001_MSG_DBG(pm8001_ha,
+				pm8001_printk("The timer was still in use.\n"));
+		pm8001_ha->ioctl_timer_expired = 0;
+		pgpio_resp = &pm8001_ha->gpio_resp;
+		pgpio_resp->gpio_rd_val = le32_to_cpu(ppayload->gpio_rd_val);
+		pgpio_resp->gpio_in_enabled =
+			le32_to_cpu(ppayload->gpio_in_enabled);
+		pgpio_resp->gpio_pinsetup1 =
+			le32_to_cpu(ppayload->gpio_pinsetup1);
+		pgpio_resp->gpio_pinsetup2 =
+			le32_to_cpu(ppayload->gpio_pinsetup2);
+		pgpio_resp->gpio_evt_change =
+			le32_to_cpu(ppayload->gpio_evt_change);
+		pgpio_resp->gpio_evt_rise =
+			le32_to_cpu(ppayload->gpio_evt_rise);
+		pgpio_resp->gpio_evt_fall =
+			le32_to_cpu(ppayload->gpio_evt_fall);
+
+		complete(pm8001_ha->ioctl_completion);
+		pm8001_ha->ioctl_completion = NULL;
+	}
+	spin_unlock(&pm8001_ha->ioctl_lock);
+	return 0;
+}
+
+static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+
+	u32 gpio_event = 0;
+	struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
+	gpio_event = le32_to_cpu(ppayload->gpio_event);
+	PM8001_MSG_DBG(pm8001_ha,
+		       pm8001_printk("GPIO event: 0x%X\n", gpio_event));
+	pm8001_ha->gpio_event_occured = 1;
+	wake_up_interruptible(&pm8001_ha->pollq);
+	return 0;
+}
+
 /**
  * process_one_iomb - process one outbound Queue memory block
  * @pm8001_ha: our hba card information
@@ -3567,10 +3615,12 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
 	case OPC_OUB_GPIO_RESPONSE:
 		PM8001_MSG_DBG(pm8001_ha,
 			pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
+		mpi_gpio_resp(pm8001_ha, piomb);
 		break;
 	case OPC_OUB_GPIO_EVENT:
 		PM8001_MSG_DBG(pm8001_ha,
 			pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
+		mpi_gpio_event(pm8001_ha, piomb);
 		break;
 	case OPC_OUB_GENERAL_EVENT:
 		PM8001_MSG_DBG(pm8001_ha,
@@ -4510,6 +4560,61 @@ static u32 pm80xx_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
 }
 
 /**
+ * pm80xx_chip_gpio_req - support for GPIO operation
+ * @pm8001_ha: our hba card information.
+ * @ioctl_payload: the payload for the GPIO operation
+ */
+
+int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
+				struct pm8001_gpio *gpio_payload)
+{
+	struct gpio_req payload;
+	struct inbound_queue_table *circularQ;
+	int ret;
+	u32 tag;
+	u32 opc = OPC_INB_GPIO;
+
+	if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
+		return ADPT_IOCTL_CALL_INVALID_DEVICE;
+
+	ret = pm8001_tag_alloc(pm8001_ha, &tag);
+	if (ret)
+		return -1;
+
+	memset(&payload, 0, sizeof(payload));
+	circularQ = &pm8001_ha->inbnd_q_tbl[0];
+	payload.tag = cpu_to_le32(tag);
+	switch (gpio_payload->operation) {
+	case GPIO_READ:
+		payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
+		break;
+	case GPIO_WRITE:
+		payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
+		payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
+		payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
+		break;
+	case GPIO_PINSETUP:
+		payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
+		payload.gpio_in_enabled =
+			cpu_to_le32(gpio_payload->input_enable);
+		payload.gpio_pinsetup1 = cpu_to_le32(gpio_payload->pinsetup1);
+		payload.gpio_pinsetup2 = cpu_to_le32(gpio_payload->pinsetup2);
+		break;
+	case GPIO_EVENTSETUP:
+		payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
+		payload.gpio_evt_change =
+			cpu_to_le32(gpio_payload->event_level);
+		payload.gpio_evt_rise =
+			cpu_to_le32(gpio_payload->event_rising_edge);
+		payload.gpio_evt_fall =
+			cpu_to_le32(gpio_payload->event_falling_edge);
+		break;
+	}
+	ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+	return ret;
+}
+
+/**
  * pm8001_chip_isr - PM8001 isr handler.
  * @pm8001_ha: our hba card information.
  * @irq: irq number.
@@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
 	.set_nvmd_req		= pm8001_chip_set_nvmd_req,
 	.fw_flash_update_req	= pm8001_chip_fw_flash_update_req,
 	.set_dev_state_req	= pm8001_chip_set_dev_state_req,
+	.gpio_req		= pm80xx_chip_gpio_req,
 };
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h
index c86816b..671940a 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.h
+++ b/drivers/scsi/pm8001/pm80xx_hwi.h
@@ -422,6 +422,55 @@ struct hw_event_ack_req {
 } __attribute__((packed, aligned(4)));
 
 /*
+ * brief the data structure of GPIO Commannd
+ * use to control MPI GPIOs (64 bytes)
+ */
+struct gpio_req {
+	__le32  tag;
+	__le32  eobid_ge_gs_gr_gw;
+	__le32  gpio_wr_msk;
+	__le32  gpio_wr_val;
+	__le32  gpio_in_enabled;
+	__le32  gpio_pinsetup1;
+	__le32  gpio_pinsetup2;
+	__le32  gpio_evt_change;
+	__le32  gpio_evt_rise;
+	__le32  gpio_evt_fall;
+	u32     reserved[5];
+} __attribute__((packed, aligned(4)));
+
+#define GPIO_GW_BIT 0x1
+#define GPIO_GR_BIT 0x2
+#define GPIO_GS_BIT 0x4
+#define GPIO_GE_BIT 0x8
+
+/*
+ * brief the data structure of GPIO Response
+ * indicates the completion of GPIO command (64 bytes)
+ */
+struct gpio_resp {
+	__le32  tag;
+	u32     reserved[2];
+	__le32  gpio_rd_val;
+	__le32  gpio_in_enabled;
+	__le32  gpio_pinsetup1;
+	__le32  gpio_pinsetup2;
+	__le32  gpio_evt_change;
+	__le32  gpio_evt_rise;
+	__le32  gpio_evt_fall;
+	u32     reserved1[5];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of GPIO Event
+ * indicates the generation of GPIO event (64 bytes)
+ */
+struct gpio_event {
+	__le32  gpio_event;
+	u32     reserved[14];
+} __attribute__((packed, aligned(4)));
+
+/*
  * brief the data structure of PHY_START Response Command
  * indicates the completion of PHY_START command (64 bytes)
  */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-10-22 12:20 [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers Viswas G
@ 2013-10-29 10:19 ` Jack Wang
  2013-11-04 10:13   ` Viswas G
  0 siblings, 1 reply; 8+ messages in thread
From: Jack Wang @ 2013-10-29 10:19 UTC (permalink / raw)
  To: Viswas G
  Cc: linux-scsi, Sangeetha Gnanasekaran, Nikith Ganigarakoppal,
	Anand Kumar Santhanam

Hi Viswas,

As ioctl interface is not welcome for new feature, that's why we removed
ioctl interface when pm8001 accepted into mainline.

I suggest you use bsg interface for this, see sas_host_smp.c for details.

Regards,
Jack

On 10/22/2013 02:20 PM, Viswas G wrote:
> 
> Signed-off-by: Viswas G <Viswas.G@pmcs.com>
> ---
>  drivers/scsi/pm8001/pm8001_ctl.c  |  248
> ++++++++++++++++++++++++++++++++++++-
>  drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
>  drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
>  drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
>  drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
>  drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
>  6 files changed, 524 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c
> b/drivers/scsi/pm8001/pm8001_ctl.c
> index 247cb1c..3c9ca21 100644
> --- a/drivers/scsi/pm8001/pm8001_ctl.c
> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
> @@ -40,7 +40,8 @@
>  #include <linux/firmware.h>
>  #include <linux/slab.h>
>  #include "pm8001_sas.h"
> -#include "pm8001_ctl.h"
> +
> +int pm8001_major = -1;
> 
>  /* scsi host attributes */
> 
> @@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
>         NULL,
>  };
> 
> +/**
> + * pm8001_open - open the configuration file
> + * @inode: inode being opened
> + * @file: file handle attached
> + *
> + * Called when the configuration device is opened. Does the needed
> + * set up on the handle and then returns
> + *
> + */
> +static int pm8001_open(struct inode *inode, struct file *file)
> +{
> +       struct pm8001_hba_info *pm8001_ha;
> +       unsigned minor_number = iminor(inode);
> +       int err = -ENODEV;
> +
> +       list_for_each_entry(pm8001_ha, &hba_list, list) {
> +               if (pm8001_ha->id == minor_number) {
> +                       file->private_data = pm8001_ha;
> +                       err = 0;
> +                       break;
> +               }
> +       }
> +
> +       return err;
> +}
> +
> +/**
> + * pm8001_close - close the configuration file
> + * @inode: inode being opened
> + * @file: file handle attached
> + *
> + * Called when the configuration device is closed. Does the needed
> + * set up on the handle and then returns
> + *
> + */
> +static int pm8001_close(struct inode *inode, struct file *file)
> +{
> +       return 0;
> +}
> +
> +static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
> +                             unsigned long arg)
> +{
> +       u32 ret = 0;
> +       struct ioctl_info_buffer info_buf;
> +       union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
> +
> +       strcpy(info_buf.information.sz_name, DRV_NAME);
> +
> +       info_buf.information.usmajor_revision = DRV_MAJOR;
> +       info_buf.information.usminor_revision = DRV_MINOR;
> +       info_buf.information.usbuild_revision = DRV_BUILD;
> +       if (pm8001_ha->chip_id == chip_8001) {
> +               info_buf.information.maxoutstandingIO =
> +                       main_tbl.pm8001_tbl.max_out_io;
> +               info_buf.information.maxdevices =
> +                       (main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
> +       } else {
> +               info_buf.information.maxoutstandingIO =
> +                       main_tbl.pm80xx_tbl.max_out_io;
> +               info_buf.information.maxdevices =
> +                       (main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
> +       }
> +       info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
> +
> +       if (copy_to_user((void *)arg, (void *)&info_buf,
> +                        sizeof(struct ioctl_info_buffer))) {
> +               ret = ADPT_IOCTL_CALL_FAILED;
> +       }
> +
> +       return ret;
> +}
> +
> +static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
> +                             unsigned long arg)
> +{
> +       struct gpio_buffer buffer;
> +       struct pm8001_gpio *payload;
> +       struct gpio_ioctl_resp *gpio_resp;
> +       DECLARE_COMPLETION_ONSTACK(completion);
> +       unsigned long timeout;
> +       u32 ret = 0, operation;
> +
> +       mutex_lock(&pm8001_ha->ioctl_mutex);
> +
> +       if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
> +               sizeof(struct gpio_buffer))) {
> +               ret = ADPT_IOCTL_CALL_FAILED;
> +               goto exit;
> +       }
> +
> +       pm8001_ha->ioctl_completion = &completion;
> +       payload = &buffer.gpio_payload;
> +       operation = payload->operation;
> +       ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
> +       if (ret != 0) {
> +               ret = ADPT_IOCTL_CALL_FAILED;
> +               goto exit;
> +       }
> +
> +       timeout = (unsigned long)buffer.header.timeout * 1000;
> +
> +       mod_timer(&pm8001_ha->ioctl_timer, jiffies +
> msecs_to_jiffies(timeout));
> +
> +       wait_for_completion(&completion);
> +
> +       if (pm8001_ha->ioctl_timer_expired) {
> +               ret = ADPT_IOCTL_CALL_TIMEOUT;
> +               goto exit;
> +       }
> +       gpio_resp = &pm8001_ha->gpio_resp;
> +
> +       buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
> +
> +       if (operation == GPIO_READ) {
> +               payload->rd_wr_val              = gpio_resp->gpio_rd_val;
> +               payload->input_enable           =
> gpio_resp->gpio_in_enabled;
> +               payload->pinsetup1              = gpio_resp->gpio_pinsetup1;
> +               payload->pinsetup2              = gpio_resp->gpio_pinsetup2;
> +               payload->event_level            =
> gpio_resp->gpio_evt_change;
> +               payload->event_rising_edge      = gpio_resp->gpio_evt_rise;
> +               payload->event_falling_edge     = gpio_resp->gpio_evt_fall;
> +
> +               if (copy_to_user((void *)arg, (void *)&buffer,
> +                                sizeof(struct gpio_buffer))) {
> +                       ret = ADPT_IOCTL_CALL_FAILED;
> +               }
> +       } else {
> +               if (copy_to_user((void *)arg, (void *)&buffer.header,
> +                                sizeof(struct ioctl_header))) {
> +                       ret = ADPT_IOCTL_CALL_FAILED;
> +               }
> +       }
> +exit:
> +       pm8001_ha->ioctl_timer_expired = 0;
> +       mutex_unlock(&pm8001_ha->ioctl_mutex);
> +       return ret;
> +}
> +
> +/**
> + * pm8001_ioctl - pm8001 configuration request
> + * @inode: inode of device
> + * @file: file handle
> + * @cmd: ioctl command code
> + * @arg: argument
> + *
> + * Handles a configuration ioctl.
> + *
> + */
> +static long pm8001_ioctl(struct file *file,
> +                        unsigned int cmd, unsigned long arg)
> +{
> +       u32 ret = -EACCES;
> +       struct pm8001_hba_info *pm8001_ha;
> +       struct ioctl_header header;
> +
> +       pm8001_ha = file->private_data;
> +
> +       switch (cmd) {
> +
> +       case ADPT_IOCTL_GPIO:
> +               ret = pm8001_gpio_ioctl(pm8001_ha, arg);
> +               break;
> +       case ADPT_IOCTL_INFO:
> +               ret = pm8001_info_ioctl(pm8001_ha, arg);
> +               break;
> +       default:
> +               ret = ADPT_IOCTL_CALL_INVALID_CODE;
> +       }
> +       if (ret == 0)
> +               return ret;
> +       header.return_code = ret;
> +       ret = -EACCES;
> +       if (copy_to_user((void *)arg, (void *)&header,
> +                       sizeof(struct ioctl_header))) {
> +               return -EFAULT;
> +       }
> +       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
> +       return ret;
> +}
> +
> +/**
> + * pm8001_poll - pm8001 poll request function
> + * @file: file handle
> + * @wait: poll table to wait
> + *
> + * Handles a poll request.
> + *
> + */
> +unsigned int pm8001_poll(struct file *file, poll_table *wait)
> +{
> +       struct pm8001_hba_info *pm8001_ha;
> +       unsigned int mask = 0;
> +
> +       pm8001_ha = file->private_data;
> +
> +       poll_wait(file, &pm8001_ha->pollq, wait);
> +
> +       if (pm8001_ha->gpio_event_occured == 1) {
> +               pm8001_ha->gpio_event_occured = 0;
> +               mask |= POLLIN | POLLRDNORM;
> +       }
> +
> +       return mask;
> +}
> +
> +static const struct file_operations pm8001_fops = {
> +       .owner          = THIS_MODULE,
> +       .open           = pm8001_open,
> +       .release        = pm8001_close,
> +       .unlocked_ioctl = pm8001_ioctl,
> +       .poll           = pm8001_poll,
> +};
> +
> +/**
> + * pm8001_setup_chrdev - registers a char device
> + *
> + * Return value
> + *     0 in case of success, otherwise non-zero
> + */
> +int pm8001_setup_chrdev(void)
> +{
> +       pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
> +       if (pm8001_major < 0) {
> +               pr_warn("pm8001 : unable to register \"%s\" device\n",
> +                               DRV_NAME);
> +               return pm8001_major;
> +       }
> +       return 0;
> +}
> +
> +/**
> + * pm8001_release_chrdev - unregisters per-adapter management interface
> + *
> + * Return value
> + *     none
> + */
> +void pm8001_release_chrdev(void)
> +{
> +       if (pm8001_major > -1) {
> +               unregister_chrdev(pm8001_major, DRV_NAME);
> +               pm8001_major = -1;
> +       }
> +}
> +
> diff --git a/drivers/scsi/pm8001/pm8001_ctl.h
> b/drivers/scsi/pm8001/pm8001_ctl.h
> index d0d43a2..0064dd3 100644
> --- a/drivers/scsi/pm8001/pm8001_ctl.h
> +++ b/drivers/scsi/pm8001/pm8001_ctl.h
> @@ -55,6 +55,61 @@
>  #define FAIL_OUT_MEMORY                 0x000c00
>  #define FLASH_IN_PROGRESS               0x001000
> 
> +#define ADPT_IOCTL_CALL_SUCCESS                0x00
> +#define ADPT_IOCTL_CALL_FAILED         0x01
> +#define ADPT_IOCTL_CALL_INVALID_CODE   0x03
> +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04
> +#define ADPT_IOCTL_CALL_TIMEOUT                0x08
> +
> +#define GPIO_READ      0
> +#define GPIO_WRITE     1
> +#define GPIO_PINSETUP  2
> +#define GPIO_EVENTSETUP        3
> +
> +struct ioctl_header {
> +       u32 io_controller_num;
> +       u32 length;
> +       u32 return_code;
> +       u32 timeout;
> +       u32 direction;
> +};
> +
> +struct ioctl_drv_info {
> +       u8      sz_name[64];
> +       u16     usmajor_revision;
> +       u16     usminor_revision;
> +       u16     usbuild_revision;
> +       u16     reserved0;
> +       u32     maxdevices;
> +       u32     maxoutstandingIO;
> +       u32     reserved[16];
> +};
> +
> +struct pm8001_gpio {
> +       u32 operation;
> +       u32 mask;
> +       u32 rd_wr_val;
> +       u32 input_enable;
> +       u32 pinsetup1;
> +       u32 pinsetup2;
> +       u32 event_level;
> +       u32 event_rising_edge;
> +       u32 event_falling_edge;
> +};
> +
> +struct ioctl_info_buffer {
> +       struct ioctl_header     header;
> +       struct ioctl_drv_info   information;
> +};
> +
> +struct gpio_buffer {
> +       struct ioctl_header     header;
> +       struct pm8001_gpio      gpio_payload;
> +};
> +
> +#define ADPT_IOCTL_INFO                _IOR(0, 0, struct
> ioctl_info_buffer *)
> +#define ADPT_IOCTL_GPIO                _IOWR(0, 1, struct  gpio_buffer *)
> +
>  #define IB_OB_READ_TIMES                256
>  #define SYSFS_OFFSET                    1024
>  #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
> diff --git a/drivers/scsi/pm8001/pm8001_init.c
> b/drivers/scsi/pm8001/pm8001_init.c
> index 662bf13..ba2e659 100644
> --- a/drivers/scsi/pm8001/pm8001_init.c
> +++ b/drivers/scsi/pm8001/pm8001_init.c
> @@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info
> *pm8001_ha)
>         if (!pm8001_ha)
>                 return;
> 
> +       del_timer(&pm8001_ha->ioctl_timer);
> +
>         for (i = 0; i < USI_MAX_MEMCNT; i++) {
>                 if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
>                         pci_free_consistent(pm8001_ha->pdev,
> @@ -444,6 +446,24 @@ static int pm8001_ioremap(struct pm8001_hba_info
> *pm8001_ha)
>         return 0;
>  }
> 
> +void pm8001_ioctl_timer_callback(unsigned long data)
> +{
> +       struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info *)data;
> +
> +       PM8001_FAIL_DBG(pm8001_ha,
> +               pm8001_printk("Timer expired for GPIO response\n"));
> +
> +       spin_lock(&pm8001_ha->ioctl_lock);
> +
> +       if (pm8001_ha->ioctl_completion != NULL) {
> +               pm8001_ha->ioctl_timer_expired = 1;
> +               complete(pm8001_ha->ioctl_completion);
> +               pm8001_ha->ioctl_completion = NULL;
> +       }
> +
> +       spin_unlock(&pm8001_ha->ioctl_lock);
> +}
> +
>  /**
>   * pm8001_pci_alloc - initialize our ha card structure
>   * @pdev: pci device.
> @@ -479,6 +499,15 @@ static struct pm8001_hba_info
> *pm8001_pci_alloc(struct pci_dev *pdev,
>         else
>                 pm8001_ha->iomb_size = IOMB_SIZE_SPC;
> 
> +       mutex_init(&pm8001_ha->ioctl_mutex);
> +       pm8001_ha->ioctl_completion = NULL;
> +       init_waitqueue_head(&pm8001_ha->pollq);
> +       pm8001_ha->gpio_event_occured = 0;
> +       spin_lock_init(&pm8001_ha->ioctl_lock);
> +       setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
> +                    (unsigned long)pm8001_ha);
> +
> +
>  #ifdef PM8001_USE_TASKLET
>         /**
>         * default tasklet for non msi-x interrupt handler/first msi-x
> @@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
>         rc = pci_register_driver(&pm8001_pci_driver);
>         if (rc)
>                 goto err_tp;
> +
> +       rc = pm8001_setup_chrdev();
> +       if (rc)
> +               goto err_ctl;
> +
>         return 0;
> 
> +err_ctl:
> +       pci_unregister_driver(&pm8001_pci_driver);
>  err_tp:
>         sas_release_transport(pm8001_stt);
>  err_wq:
> @@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
>         pci_unregister_driver(&pm8001_pci_driver);
>         sas_release_transport(pm8001_stt);
>         destroy_workqueue(pm8001_wq);
> +       pm8001_release_chrdev();
>  }
> 
>  module_init(pm8001_init);
> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
> b/drivers/scsi/pm8001/pm8001_sas.h
> index 9241c04..9171f0a 100644
> --- a/drivers/scsi/pm8001/pm8001_sas.h
> +++ b/drivers/scsi/pm8001/pm8001_sas.h
> @@ -51,14 +51,21 @@
>  #include <linux/pci.h>
>  #include <linux/interrupt.h>
>  #include <linux/workqueue.h>
> +#include <linux/poll.h>
> +#include <linux/poll.h>
> +#include <linux/timer.h>
>  #include <scsi/libsas.h>
>  #include <scsi/scsi_tcq.h>
>  #include <scsi/sas_ata.h>
>  #include <linux/atomic.h>
>  #include "pm8001_defs.h"
> +#include "pm8001_ctl.h"
> 
>  #define DRV_NAME               "pm80xx"
>  #define DRV_VERSION            "0.1.37"
> +#define DRV_MAJOR              1
> +#define DRV_MINOR              0
> +#define DRV_BUILD              15
>  #define PM8001_FAIL_LOGGING    0x01 /* Error message logging */
>  #define PM8001_INIT_LOGGING    0x02 /* driver init logging */
>  #define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
> @@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
>         u8      *func_specific;
>  };
> 
> +struct gpio_ioctl_resp {
> +       u32 tag;
> +       u32 gpio_rd_val;
> +       u32 gpio_in_enabled;
> +       u32 gpio_pinsetup1;
> +       u32 gpio_pinsetup2;
> +       u32 gpio_evt_change;
> +       u32 gpio_evt_rise;
> +       u32 gpio_evt_fall;
> +};
> +
>  #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF
>  #define MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
>  #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
> @@ -229,6 +247,8 @@ struct pm8001_dispatch {
>         int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
>                 u32 state);
>         int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
> +       int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
> +               struct pm8001_gpio *gpio_payload);
>  };
> 
>  struct pm8001_chip_info {
> @@ -527,6 +547,14 @@ struct pm8001_hba_info {
>         u32                     int_vector;
>         const struct firmware   *fw_image;
>         u8                      outq[PM8001_MAX_MSIX_VEC];
> +       struct completion       *ioctl_completion;
> +       struct mutex            ioctl_mutex;
> +       spinlock_t              ioctl_lock;
> +       u32                     gpio_event_occured;
> +       u32                     ioctl_timer_expired;
> +       struct timer_list       ioctl_timer;
> +       struct gpio_ioctl_resp  gpio_resp;
> +       wait_queue_head_t       pollq;
>  };
> 
>  struct pm8001_work {
> @@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev,
> u32, char *buf);
>  /* ctl shared API */
>  extern struct device_attribute *pm8001_host_attrs[];
> 
> +int pm8001_setup_chrdev(void);
> +void pm8001_release_chrdev(void);
>  #endif
> 
> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c
> b/drivers/scsi/pm8001/pm80xx_hwi.c
> index 4ebc79b..5423844 100644
> --- a/drivers/scsi/pm8001/pm80xx_hwi.c
> +++ b/drivers/scsi/pm8001/pm80xx_hwi.c
> @@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct
> pm8001_hba_info *pm8001_ha,
>         return 0;
>  }
> 
> +static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
> +{
> +       int ret;
> +       struct gpio_ioctl_resp *pgpio_resp;
> +       struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 4);
> +
> +       spin_lock(&pm8001_ha->ioctl_lock);
> +       if (pm8001_ha->ioctl_completion != NULL) {
> +               ret = del_timer(&pm8001_ha->ioctl_timer);
> +               if (ret)
> +                       PM8001_MSG_DBG(pm8001_ha,
> +                               pm8001_printk("The timer was still in
> use.\n"));
> +               pm8001_ha->ioctl_timer_expired = 0;
> +               pgpio_resp = &pm8001_ha->gpio_resp;
> +               pgpio_resp->gpio_rd_val =
> le32_to_cpu(ppayload->gpio_rd_val);
> +               pgpio_resp->gpio_in_enabled =
> +                       le32_to_cpu(ppayload->gpio_in_enabled);
> +               pgpio_resp->gpio_pinsetup1 =
> +                       le32_to_cpu(ppayload->gpio_pinsetup1);
> +               pgpio_resp->gpio_pinsetup2 =
> +                       le32_to_cpu(ppayload->gpio_pinsetup2);
> +               pgpio_resp->gpio_evt_change =
> +                       le32_to_cpu(ppayload->gpio_evt_change);
> +               pgpio_resp->gpio_evt_rise =
> +                       le32_to_cpu(ppayload->gpio_evt_rise);
> +               pgpio_resp->gpio_evt_fall =
> +                       le32_to_cpu(ppayload->gpio_evt_fall);
> +
> +               complete(pm8001_ha->ioctl_completion);
> +               pm8001_ha->ioctl_completion = NULL;
> +       }
> +       spin_unlock(&pm8001_ha->ioctl_lock);
> +       return 0;
> +}
> +
> +static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
> +{
> +
> +       u32 gpio_event = 0;
> +       struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
> +       gpio_event = le32_to_cpu(ppayload->gpio_event);
> +       PM8001_MSG_DBG(pm8001_ha,
> +                      pm8001_printk("GPIO event: 0x%X\n", gpio_event));
> +       pm8001_ha->gpio_event_occured = 1;
> +       wake_up_interruptible(&pm8001_ha->pollq);
> +       return 0;
> +}
> +
>  /**
>   * process_one_iomb - process one outbound Queue memory block
>   * @pm8001_ha: our hba card information
> @@ -3567,10 +3615,12 @@ static void process_one_iomb(struct
> pm8001_hba_info *pm8001_ha, void *piomb)
>         case OPC_OUB_GPIO_RESPONSE:
>                 PM8001_MSG_DBG(pm8001_ha,
>                         pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
> +               mpi_gpio_resp(pm8001_ha, piomb);
>                 break;
>         case OPC_OUB_GPIO_EVENT:
>                 PM8001_MSG_DBG(pm8001_ha,
>                         pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
> +               mpi_gpio_event(pm8001_ha, piomb);
>                 break;
>         case OPC_OUB_GENERAL_EVENT:
>                 PM8001_MSG_DBG(pm8001_ha,
> @@ -4510,6 +4560,61 @@ static u32 pm80xx_chip_is_our_interupt(struct
> pm8001_hba_info *pm8001_ha)
>  }
> 
>  /**
> + * pm80xx_chip_gpio_req - support for GPIO operation
> + * @pm8001_ha: our hba card information.
> + * @ioctl_payload: the payload for the GPIO operation
> + */
> +
> +int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
> +                               struct pm8001_gpio *gpio_payload)
> +{
> +       struct gpio_req payload;
> +       struct inbound_queue_table *circularQ;
> +       int ret;
> +       u32 tag;
> +       u32 opc = OPC_INB_GPIO;
> +
> +       if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
> +               return ADPT_IOCTL_CALL_INVALID_DEVICE;
> +
> +       ret = pm8001_tag_alloc(pm8001_ha, &tag);
> +       if (ret)
> +               return -1;
> +
> +       memset(&payload, 0, sizeof(payload));
> +       circularQ = &pm8001_ha->inbnd_q_tbl[0];
> +       payload.tag = cpu_to_le32(tag);
> +       switch (gpio_payload->operation) {
> +       case GPIO_READ:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
> +               break;
> +       case GPIO_WRITE:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
> +               payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
> +               payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
> +               break;
> +       case GPIO_PINSETUP:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
> +               payload.gpio_in_enabled =
> +                       cpu_to_le32(gpio_payload->input_enable);
> +               payload.gpio_pinsetup1 =
> cpu_to_le32(gpio_payload->pinsetup1);
> +               payload.gpio_pinsetup2 =
> cpu_to_le32(gpio_payload->pinsetup2);
> +               break;
> +       case GPIO_EVENTSETUP:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
> +               payload.gpio_evt_change =
> +                       cpu_to_le32(gpio_payload->event_level);
> +               payload.gpio_evt_rise =
> +                       cpu_to_le32(gpio_payload->event_rising_edge);
> +               payload.gpio_evt_fall =
> +                       cpu_to_le32(gpio_payload->event_falling_edge);
> +               break;
> +       }
> +       ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
> +       return ret;
> +}
> +
> +/**
>   * pm8001_chip_isr - PM8001 isr handler.
>   * @pm8001_ha: our hba card information.
>   * @irq: irq number.
> @@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
>         .set_nvmd_req           = pm8001_chip_set_nvmd_req,
>         .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
>         .set_dev_state_req      = pm8001_chip_set_dev_state_req,
> +       .gpio_req               = pm80xx_chip_gpio_req,
>  };
> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h
> b/drivers/scsi/pm8001/pm80xx_hwi.h
> index c86816b..671940a 100644
> --- a/drivers/scsi/pm8001/pm80xx_hwi.h
> +++ b/drivers/scsi/pm8001/pm80xx_hwi.h
> @@ -422,6 +422,55 @@ struct hw_event_ack_req {
>  } __attribute__((packed, aligned(4)));
> 
>  /*
> + * brief the data structure of GPIO Commannd
> + * use to control MPI GPIOs (64 bytes)
> + */
> +struct gpio_req {
> +       __le32  tag;
> +       __le32  eobid_ge_gs_gr_gw;
> +       __le32  gpio_wr_msk;
> +       __le32  gpio_wr_val;
> +       __le32  gpio_in_enabled;
> +       __le32  gpio_pinsetup1;
> +       __le32  gpio_pinsetup2;
> +       __le32  gpio_evt_change;
> +       __le32  gpio_evt_rise;
> +       __le32  gpio_evt_fall;
> +       u32     reserved[5];
> +} __attribute__((packed, aligned(4)));
> +
> +#define GPIO_GW_BIT 0x1
> +#define GPIO_GR_BIT 0x2
> +#define GPIO_GS_BIT 0x4
> +#define GPIO_GE_BIT 0x8
> +
> +/*
> + * brief the data structure of GPIO Response
> + * indicates the completion of GPIO command (64 bytes)
> + */
> +struct gpio_resp {
> +       __le32  tag;
> +       u32     reserved[2];
> +       __le32  gpio_rd_val;
> +       __le32  gpio_in_enabled;
> +       __le32  gpio_pinsetup1;
> +       __le32  gpio_pinsetup2;
> +       __le32  gpio_evt_change;
> +       __le32  gpio_evt_rise;
> +       __le32  gpio_evt_fall;
> +       u32     reserved1[5];
> +} __attribute__((packed, aligned(4)));
> +
> +/*
> + * brief the data structure of GPIO Event
> + * indicates the generation of GPIO event (64 bytes)
> + */
> +struct gpio_event {
> +       __le32  gpio_event;
> +       u32     reserved[14];
> +} __attribute__((packed, aligned(4)));
> +
> +/*
>   * brief the data structure of PHY_START Response Command
>   * indicates the completion of PHY_START command (64 bytes)
>   */
> --
> 1.7.1
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-10-29 10:19 ` Jack Wang
@ 2013-11-04 10:13   ` Viswas G
  2013-11-04 10:30     ` Jack Wang
  0 siblings, 1 reply; 8+ messages in thread
From: Viswas G @ 2013-11-04 10:13 UTC (permalink / raw)
  To: Jack Wang
  Cc: linux-scsi, Sangeetha Gnanasekaran, Nikith Ganigarakoppal,
	Anand Kumar Santhanam, Suresh Thiagarajan

Hi Jack,

We wanted to control the GPIO in the HBA only. Bsg interface gets created only for enclosure or expander. 

For our HBA, bsg interface will not be created since it does not have an enclosure target inside. That's why we wanted to use IOCTL. Please advise.

Best Regards,
Viswas G

-----Original Message-----
From: Jack Wang [mailto:xjtuwjp@gmail.com] 
Sent: Tuesday, October 29, 2013 3:49 PM
To: Viswas G
Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam
Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers

Hi Viswas,

As ioctl interface is not welcome for new feature, that's why we removed ioctl interface when pm8001 accepted into mainline.

I suggest you use bsg interface for this, see sas_host_smp.c for details.

Regards,
Jack

On 10/22/2013 02:20 PM, Viswas G wrote:
> 
> Signed-off-by: Viswas G <Viswas.G@pmcs.com>
> ---
>  drivers/scsi/pm8001/pm8001_ctl.c  |  248
> ++++++++++++++++++++++++++++++++++++-
>  drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
>  drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
>  drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
>  drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
>  drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
>  6 files changed, 524 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c
> b/drivers/scsi/pm8001/pm8001_ctl.c
> index 247cb1c..3c9ca21 100644
> --- a/drivers/scsi/pm8001/pm8001_ctl.c
> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
> @@ -40,7 +40,8 @@
>  #include <linux/firmware.h>
>  #include <linux/slab.h>
>  #include "pm8001_sas.h"
> -#include "pm8001_ctl.h"
> +
> +int pm8001_major = -1;
> 
>  /* scsi host attributes */
> 
> @@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
>         NULL,
>  };
> 
> +/**
> + * pm8001_open - open the configuration file
> + * @inode: inode being opened
> + * @file: file handle attached
> + *
> + * Called when the configuration device is opened. Does the needed
> + * set up on the handle and then returns
> + *
> + */
> +static int pm8001_open(struct inode *inode, struct file *file) {
> +       struct pm8001_hba_info *pm8001_ha;
> +       unsigned minor_number = iminor(inode);
> +       int err = -ENODEV;
> +
> +       list_for_each_entry(pm8001_ha, &hba_list, list) {
> +               if (pm8001_ha->id == minor_number) {
> +                       file->private_data = pm8001_ha;
> +                       err = 0;
> +                       break;
> +               }
> +       }
> +
> +       return err;
> +}
> +
> +/**
> + * pm8001_close - close the configuration file
> + * @inode: inode being opened
> + * @file: file handle attached
> + *
> + * Called when the configuration device is closed. Does the needed
> + * set up on the handle and then returns
> + *
> + */
> +static int pm8001_close(struct inode *inode, struct file *file) {
> +       return 0;
> +}
> +
> +static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
> +                             unsigned long arg) {
> +       u32 ret = 0;
> +       struct ioctl_info_buffer info_buf;
> +       union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
> +
> +       strcpy(info_buf.information.sz_name, DRV_NAME);
> +
> +       info_buf.information.usmajor_revision = DRV_MAJOR;
> +       info_buf.information.usminor_revision = DRV_MINOR;
> +       info_buf.information.usbuild_revision = DRV_BUILD;
> +       if (pm8001_ha->chip_id == chip_8001) {
> +               info_buf.information.maxoutstandingIO =
> +                       main_tbl.pm8001_tbl.max_out_io;
> +               info_buf.information.maxdevices =
> +                       (main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
> +       } else {
> +               info_buf.information.maxoutstandingIO =
> +                       main_tbl.pm80xx_tbl.max_out_io;
> +               info_buf.information.maxdevices =
> +                       (main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
> +       }
> +       info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
> +
> +       if (copy_to_user((void *)arg, (void *)&info_buf,
> +                        sizeof(struct ioctl_info_buffer))) {
> +               ret = ADPT_IOCTL_CALL_FAILED;
> +       }
> +
> +       return ret;
> +}
> +
> +static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
> +                             unsigned long arg) {
> +       struct gpio_buffer buffer;
> +       struct pm8001_gpio *payload;
> +       struct gpio_ioctl_resp *gpio_resp;
> +       DECLARE_COMPLETION_ONSTACK(completion);
> +       unsigned long timeout;
> +       u32 ret = 0, operation;
> +
> +       mutex_lock(&pm8001_ha->ioctl_mutex);
> +
> +       if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
> +               sizeof(struct gpio_buffer))) {
> +               ret = ADPT_IOCTL_CALL_FAILED;
> +               goto exit;
> +       }
> +
> +       pm8001_ha->ioctl_completion = &completion;
> +       payload = &buffer.gpio_payload;
> +       operation = payload->operation;
> +       ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
> +       if (ret != 0) {
> +               ret = ADPT_IOCTL_CALL_FAILED;
> +               goto exit;
> +       }
> +
> +       timeout = (unsigned long)buffer.header.timeout * 1000;
> +
> +       mod_timer(&pm8001_ha->ioctl_timer, jiffies +
> msecs_to_jiffies(timeout));
> +
> +       wait_for_completion(&completion);
> +
> +       if (pm8001_ha->ioctl_timer_expired) {
> +               ret = ADPT_IOCTL_CALL_TIMEOUT;
> +               goto exit;
> +       }
> +       gpio_resp = &pm8001_ha->gpio_resp;
> +
> +       buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
> +
> +       if (operation == GPIO_READ) {
> +               payload->rd_wr_val              = gpio_resp->gpio_rd_val;
> +               payload->input_enable           =
> gpio_resp->gpio_in_enabled;
> +               payload->pinsetup1              = gpio_resp->gpio_pinsetup1;
> +               payload->pinsetup2              = gpio_resp->gpio_pinsetup2;
> +               payload->event_level            =
> gpio_resp->gpio_evt_change;
> +               payload->event_rising_edge      = gpio_resp->gpio_evt_rise;
> +               payload->event_falling_edge     = gpio_resp->gpio_evt_fall;
> +
> +               if (copy_to_user((void *)arg, (void *)&buffer,
> +                                sizeof(struct gpio_buffer))) {
> +                       ret = ADPT_IOCTL_CALL_FAILED;
> +               }
> +       } else {
> +               if (copy_to_user((void *)arg, (void *)&buffer.header,
> +                                sizeof(struct ioctl_header))) {
> +                       ret = ADPT_IOCTL_CALL_FAILED;
> +               }
> +       }
> +exit:
> +       pm8001_ha->ioctl_timer_expired = 0;
> +       mutex_unlock(&pm8001_ha->ioctl_mutex);
> +       return ret;
> +}
> +
> +/**
> + * pm8001_ioctl - pm8001 configuration request
> + * @inode: inode of device
> + * @file: file handle
> + * @cmd: ioctl command code
> + * @arg: argument
> + *
> + * Handles a configuration ioctl.
> + *
> + */
> +static long pm8001_ioctl(struct file *file,
> +                        unsigned int cmd, unsigned long arg) {
> +       u32 ret = -EACCES;
> +       struct pm8001_hba_info *pm8001_ha;
> +       struct ioctl_header header;
> +
> +       pm8001_ha = file->private_data;
> +
> +       switch (cmd) {
> +
> +       case ADPT_IOCTL_GPIO:
> +               ret = pm8001_gpio_ioctl(pm8001_ha, arg);
> +               break;
> +       case ADPT_IOCTL_INFO:
> +               ret = pm8001_info_ioctl(pm8001_ha, arg);
> +               break;
> +       default:
> +               ret = ADPT_IOCTL_CALL_INVALID_CODE;
> +       }
> +       if (ret == 0)
> +               return ret;
> +       header.return_code = ret;
> +       ret = -EACCES;
> +       if (copy_to_user((void *)arg, (void *)&header,
> +                       sizeof(struct ioctl_header))) {
> +               return -EFAULT;
> +       }
> +       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
> +       return ret;
> +}
> +
> +/**
> + * pm8001_poll - pm8001 poll request function
> + * @file: file handle
> + * @wait: poll table to wait
> + *
> + * Handles a poll request.
> + *
> + */
> +unsigned int pm8001_poll(struct file *file, poll_table *wait) {
> +       struct pm8001_hba_info *pm8001_ha;
> +       unsigned int mask = 0;
> +
> +       pm8001_ha = file->private_data;
> +
> +       poll_wait(file, &pm8001_ha->pollq, wait);
> +
> +       if (pm8001_ha->gpio_event_occured == 1) {
> +               pm8001_ha->gpio_event_occured = 0;
> +               mask |= POLLIN | POLLRDNORM;
> +       }
> +
> +       return mask;
> +}
> +
> +static const struct file_operations pm8001_fops = {
> +       .owner          = THIS_MODULE,
> +       .open           = pm8001_open,
> +       .release        = pm8001_close,
> +       .unlocked_ioctl = pm8001_ioctl,
> +       .poll           = pm8001_poll,
> +};
> +
> +/**
> + * pm8001_setup_chrdev - registers a char device
> + *
> + * Return value
> + *     0 in case of success, otherwise non-zero
> + */
> +int pm8001_setup_chrdev(void)
> +{
> +       pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
> +       if (pm8001_major < 0) {
> +               pr_warn("pm8001 : unable to register \"%s\" device\n",
> +                               DRV_NAME);
> +               return pm8001_major;
> +       }
> +       return 0;
> +}
> +
> +/**
> + * pm8001_release_chrdev - unregisters per-adapter management 
> +interface
> + *
> + * Return value
> + *     none
> + */
> +void pm8001_release_chrdev(void)
> +{
> +       if (pm8001_major > -1) {
> +               unregister_chrdev(pm8001_major, DRV_NAME);
> +               pm8001_major = -1;
> +       }
> +}
> +
> diff --git a/drivers/scsi/pm8001/pm8001_ctl.h
> b/drivers/scsi/pm8001/pm8001_ctl.h
> index d0d43a2..0064dd3 100644
> --- a/drivers/scsi/pm8001/pm8001_ctl.h
> +++ b/drivers/scsi/pm8001/pm8001_ctl.h
> @@ -55,6 +55,61 @@
>  #define FAIL_OUT_MEMORY                 0x000c00
>  #define FLASH_IN_PROGRESS               0x001000
> 
> +#define ADPT_IOCTL_CALL_SUCCESS                0x00
> +#define ADPT_IOCTL_CALL_FAILED         0x01
> +#define ADPT_IOCTL_CALL_INVALID_CODE   0x03
> +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04
> +#define ADPT_IOCTL_CALL_TIMEOUT                0x08
> +
> +#define GPIO_READ      0
> +#define GPIO_WRITE     1
> +#define GPIO_PINSETUP  2
> +#define GPIO_EVENTSETUP        3
> +
> +struct ioctl_header {
> +       u32 io_controller_num;
> +       u32 length;
> +       u32 return_code;
> +       u32 timeout;
> +       u32 direction;
> +};
> +
> +struct ioctl_drv_info {
> +       u8      sz_name[64];
> +       u16     usmajor_revision;
> +       u16     usminor_revision;
> +       u16     usbuild_revision;
> +       u16     reserved0;
> +       u32     maxdevices;
> +       u32     maxoutstandingIO;
> +       u32     reserved[16];
> +};
> +
> +struct pm8001_gpio {
> +       u32 operation;
> +       u32 mask;
> +       u32 rd_wr_val;
> +       u32 input_enable;
> +       u32 pinsetup1;
> +       u32 pinsetup2;
> +       u32 event_level;
> +       u32 event_rising_edge;
> +       u32 event_falling_edge;
> +};
> +
> +struct ioctl_info_buffer {
> +       struct ioctl_header     header;
> +       struct ioctl_drv_info   information;
> +};
> +
> +struct gpio_buffer {
> +       struct ioctl_header     header;
> +       struct pm8001_gpio      gpio_payload;
> +};
> +
> +#define ADPT_IOCTL_INFO                _IOR(0, 0, struct
> ioctl_info_buffer *)
> +#define ADPT_IOCTL_GPIO                _IOWR(0, 1, struct  gpio_buffer *)
> +
>  #define IB_OB_READ_TIMES                256
>  #define SYSFS_OFFSET                    1024
>  #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
> diff --git a/drivers/scsi/pm8001/pm8001_init.c
> b/drivers/scsi/pm8001/pm8001_init.c
> index 662bf13..ba2e659 100644
> --- a/drivers/scsi/pm8001/pm8001_init.c
> +++ b/drivers/scsi/pm8001/pm8001_init.c
> @@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info
> *pm8001_ha)
>         if (!pm8001_ha)
>                 return;
> 
> +       del_timer(&pm8001_ha->ioctl_timer);
> +
>         for (i = 0; i < USI_MAX_MEMCNT; i++) {
>                 if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
>                         pci_free_consistent(pm8001_ha->pdev,
> @@ -444,6 +446,24 @@ static int pm8001_ioremap(struct pm8001_hba_info
> *pm8001_ha)
>         return 0;
>  }
> 
> +void pm8001_ioctl_timer_callback(unsigned long data) {
> +       struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info 
> +*)data;
> +
> +       PM8001_FAIL_DBG(pm8001_ha,
> +               pm8001_printk("Timer expired for GPIO response\n"));
> +
> +       spin_lock(&pm8001_ha->ioctl_lock);
> +
> +       if (pm8001_ha->ioctl_completion != NULL) {
> +               pm8001_ha->ioctl_timer_expired = 1;
> +               complete(pm8001_ha->ioctl_completion);
> +               pm8001_ha->ioctl_completion = NULL;
> +       }
> +
> +       spin_unlock(&pm8001_ha->ioctl_lock);
> +}
> +
>  /**
>   * pm8001_pci_alloc - initialize our ha card structure
>   * @pdev: pci device.
> @@ -479,6 +499,15 @@ static struct pm8001_hba_info 
> *pm8001_pci_alloc(struct pci_dev *pdev,
>         else
>                 pm8001_ha->iomb_size = IOMB_SIZE_SPC;
> 
> +       mutex_init(&pm8001_ha->ioctl_mutex);
> +       pm8001_ha->ioctl_completion = NULL;
> +       init_waitqueue_head(&pm8001_ha->pollq);
> +       pm8001_ha->gpio_event_occured = 0;
> +       spin_lock_init(&pm8001_ha->ioctl_lock);
> +       setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
> +                    (unsigned long)pm8001_ha);
> +
> +
>  #ifdef PM8001_USE_TASKLET
>         /**
>         * default tasklet for non msi-x interrupt handler/first msi-x 
> @@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
>         rc = pci_register_driver(&pm8001_pci_driver);
>         if (rc)
>                 goto err_tp;
> +
> +       rc = pm8001_setup_chrdev();
> +       if (rc)
> +               goto err_ctl;
> +
>         return 0;
> 
> +err_ctl:
> +       pci_unregister_driver(&pm8001_pci_driver);
>  err_tp:
>         sas_release_transport(pm8001_stt);
>  err_wq:
> @@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
>         pci_unregister_driver(&pm8001_pci_driver);
>         sas_release_transport(pm8001_stt);
>         destroy_workqueue(pm8001_wq);
> +       pm8001_release_chrdev();
>  }
> 
>  module_init(pm8001_init);
> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
> b/drivers/scsi/pm8001/pm8001_sas.h
> index 9241c04..9171f0a 100644
> --- a/drivers/scsi/pm8001/pm8001_sas.h
> +++ b/drivers/scsi/pm8001/pm8001_sas.h
> @@ -51,14 +51,21 @@
>  #include <linux/pci.h>
>  #include <linux/interrupt.h>
>  #include <linux/workqueue.h>
> +#include <linux/poll.h>
> +#include <linux/poll.h>
> +#include <linux/timer.h>
>  #include <scsi/libsas.h>
>  #include <scsi/scsi_tcq.h>
>  #include <scsi/sas_ata.h>
>  #include <linux/atomic.h>
>  #include "pm8001_defs.h"
> +#include "pm8001_ctl.h"
> 
>  #define DRV_NAME               "pm80xx"
>  #define DRV_VERSION            "0.1.37"
> +#define DRV_MAJOR              1
> +#define DRV_MINOR              0
> +#define DRV_BUILD              15
>  #define PM8001_FAIL_LOGGING    0x01 /* Error message logging */
>  #define PM8001_INIT_LOGGING    0x02 /* driver init logging */
>  #define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
> @@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
>         u8      *func_specific;
>  };
> 
> +struct gpio_ioctl_resp {
> +       u32 tag;
> +       u32 gpio_rd_val;
> +       u32 gpio_in_enabled;
> +       u32 gpio_pinsetup1;
> +       u32 gpio_pinsetup2;
> +       u32 gpio_evt_change;
> +       u32 gpio_evt_rise;
> +       u32 gpio_evt_fall;
> +};
> +
>  #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF  #define 
> MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
>  #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
> @@ -229,6 +247,8 @@ struct pm8001_dispatch {
>         int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
>                 u32 state);
>         int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
> +       int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
> +               struct pm8001_gpio *gpio_payload);
>  };
> 
>  struct pm8001_chip_info {
> @@ -527,6 +547,14 @@ struct pm8001_hba_info {
>         u32                     int_vector;
>         const struct firmware   *fw_image;
>         u8                      outq[PM8001_MAX_MSIX_VEC];
> +       struct completion       *ioctl_completion;
> +       struct mutex            ioctl_mutex;
> +       spinlock_t              ioctl_lock;
> +       u32                     gpio_event_occured;
> +       u32                     ioctl_timer_expired;
> +       struct timer_list       ioctl_timer;
> +       struct gpio_ioctl_resp  gpio_resp;
> +       wait_queue_head_t       pollq;
>  };
> 
>  struct pm8001_work {
> @@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev, 
> u32, char *buf);
>  /* ctl shared API */
>  extern struct device_attribute *pm8001_host_attrs[];
> 
> +int pm8001_setup_chrdev(void);
> +void pm8001_release_chrdev(void);
>  #endif
> 
> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c
> b/drivers/scsi/pm8001/pm80xx_hwi.c
> index 4ebc79b..5423844 100644
> --- a/drivers/scsi/pm8001/pm80xx_hwi.c
> +++ b/drivers/scsi/pm8001/pm80xx_hwi.c
> @@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct 
> pm8001_hba_info *pm8001_ha,
>         return 0;
>  }
> 
> +static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void 
> +*piomb) {
> +       int ret;
> +       struct gpio_ioctl_resp *pgpio_resp;
> +       struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 4);
> +
> +       spin_lock(&pm8001_ha->ioctl_lock);
> +       if (pm8001_ha->ioctl_completion != NULL) {
> +               ret = del_timer(&pm8001_ha->ioctl_timer);
> +               if (ret)
> +                       PM8001_MSG_DBG(pm8001_ha,
> +                               pm8001_printk("The timer was still in
> use.\n"));
> +               pm8001_ha->ioctl_timer_expired = 0;
> +               pgpio_resp = &pm8001_ha->gpio_resp;
> +               pgpio_resp->gpio_rd_val =
> le32_to_cpu(ppayload->gpio_rd_val);
> +               pgpio_resp->gpio_in_enabled =
> +                       le32_to_cpu(ppayload->gpio_in_enabled);
> +               pgpio_resp->gpio_pinsetup1 =
> +                       le32_to_cpu(ppayload->gpio_pinsetup1);
> +               pgpio_resp->gpio_pinsetup2 =
> +                       le32_to_cpu(ppayload->gpio_pinsetup2);
> +               pgpio_resp->gpio_evt_change =
> +                       le32_to_cpu(ppayload->gpio_evt_change);
> +               pgpio_resp->gpio_evt_rise =
> +                       le32_to_cpu(ppayload->gpio_evt_rise);
> +               pgpio_resp->gpio_evt_fall =
> +                       le32_to_cpu(ppayload->gpio_evt_fall);
> +
> +               complete(pm8001_ha->ioctl_completion);
> +               pm8001_ha->ioctl_completion = NULL;
> +       }
> +       spin_unlock(&pm8001_ha->ioctl_lock);
> +       return 0;
> +}
> +
> +static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void 
> +*piomb) {
> +
> +       u32 gpio_event = 0;
> +       struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
> +       gpio_event = le32_to_cpu(ppayload->gpio_event);
> +       PM8001_MSG_DBG(pm8001_ha,
> +                      pm8001_printk("GPIO event: 0x%X\n", gpio_event));
> +       pm8001_ha->gpio_event_occured = 1;
> +       wake_up_interruptible(&pm8001_ha->pollq);
> +       return 0;
> +}
> +
>  /**
>   * process_one_iomb - process one outbound Queue memory block
>   * @pm8001_ha: our hba card information @@ -3567,10 +3615,12 @@ 
> static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void 
> *piomb)
>         case OPC_OUB_GPIO_RESPONSE:
>                 PM8001_MSG_DBG(pm8001_ha,
>                         pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
> +               mpi_gpio_resp(pm8001_ha, piomb);
>                 break;
>         case OPC_OUB_GPIO_EVENT:
>                 PM8001_MSG_DBG(pm8001_ha,
>                         pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
> +               mpi_gpio_event(pm8001_ha, piomb);
>                 break;
>         case OPC_OUB_GENERAL_EVENT:
>                 PM8001_MSG_DBG(pm8001_ha, @@ -4510,6 +4560,61 @@ 
> static u32 pm80xx_chip_is_our_interupt(struct
> pm8001_hba_info *pm8001_ha)
>  }
> 
>  /**
> + * pm80xx_chip_gpio_req - support for GPIO operation
> + * @pm8001_ha: our hba card information.
> + * @ioctl_payload: the payload for the GPIO operation */
> +
> +int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
> +                               struct pm8001_gpio *gpio_payload) {
> +       struct gpio_req payload;
> +       struct inbound_queue_table *circularQ;
> +       int ret;
> +       u32 tag;
> +       u32 opc = OPC_INB_GPIO;
> +
> +       if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
> +               return ADPT_IOCTL_CALL_INVALID_DEVICE;
> +
> +       ret = pm8001_tag_alloc(pm8001_ha, &tag);
> +       if (ret)
> +               return -1;
> +
> +       memset(&payload, 0, sizeof(payload));
> +       circularQ = &pm8001_ha->inbnd_q_tbl[0];
> +       payload.tag = cpu_to_le32(tag);
> +       switch (gpio_payload->operation) {
> +       case GPIO_READ:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
> +               break;
> +       case GPIO_WRITE:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
> +               payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
> +               payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
> +               break;
> +       case GPIO_PINSETUP:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
> +               payload.gpio_in_enabled =
> +                       cpu_to_le32(gpio_payload->input_enable);
> +               payload.gpio_pinsetup1 =
> cpu_to_le32(gpio_payload->pinsetup1);
> +               payload.gpio_pinsetup2 =
> cpu_to_le32(gpio_payload->pinsetup2);
> +               break;
> +       case GPIO_EVENTSETUP:
> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
> +               payload.gpio_evt_change =
> +                       cpu_to_le32(gpio_payload->event_level);
> +               payload.gpio_evt_rise =
> +                       cpu_to_le32(gpio_payload->event_rising_edge);
> +               payload.gpio_evt_fall =
> +                       cpu_to_le32(gpio_payload->event_falling_edge);
> +               break;
> +       }
> +       ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
> +       return ret;
> +}
> +
> +/**
>   * pm8001_chip_isr - PM8001 isr handler.
>   * @pm8001_ha: our hba card information.
>   * @irq: irq number.
> @@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
>         .set_nvmd_req           = pm8001_chip_set_nvmd_req,
>         .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
>         .set_dev_state_req      = pm8001_chip_set_dev_state_req,
> +       .gpio_req               = pm80xx_chip_gpio_req,
>  };
> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h
> b/drivers/scsi/pm8001/pm80xx_hwi.h
> index c86816b..671940a 100644
> --- a/drivers/scsi/pm8001/pm80xx_hwi.h
> +++ b/drivers/scsi/pm8001/pm80xx_hwi.h
> @@ -422,6 +422,55 @@ struct hw_event_ack_req {  } 
> __attribute__((packed, aligned(4)));
> 
>  /*
> + * brief the data structure of GPIO Commannd
> + * use to control MPI GPIOs (64 bytes)  */ struct gpio_req {
> +       __le32  tag;
> +       __le32  eobid_ge_gs_gr_gw;
> +       __le32  gpio_wr_msk;
> +       __le32  gpio_wr_val;
> +       __le32  gpio_in_enabled;
> +       __le32  gpio_pinsetup1;
> +       __le32  gpio_pinsetup2;
> +       __le32  gpio_evt_change;
> +       __le32  gpio_evt_rise;
> +       __le32  gpio_evt_fall;
> +       u32     reserved[5];
> +} __attribute__((packed, aligned(4)));
> +
> +#define GPIO_GW_BIT 0x1
> +#define GPIO_GR_BIT 0x2
> +#define GPIO_GS_BIT 0x4
> +#define GPIO_GE_BIT 0x8
> +
> +/*
> + * brief the data structure of GPIO Response
> + * indicates the completion of GPIO command (64 bytes)  */ struct 
> +gpio_resp {
> +       __le32  tag;
> +       u32     reserved[2];
> +       __le32  gpio_rd_val;
> +       __le32  gpio_in_enabled;
> +       __le32  gpio_pinsetup1;
> +       __le32  gpio_pinsetup2;
> +       __le32  gpio_evt_change;
> +       __le32  gpio_evt_rise;
> +       __le32  gpio_evt_fall;
> +       u32     reserved1[5];
> +} __attribute__((packed, aligned(4)));
> +
> +/*
> + * brief the data structure of GPIO Event
> + * indicates the generation of GPIO event (64 bytes)  */ struct 
> +gpio_event {
> +       __le32  gpio_event;
> +       u32     reserved[14];
> +} __attribute__((packed, aligned(4)));
> +
> +/*
>   * brief the data structure of PHY_START Response Command
>   * indicates the completion of PHY_START command (64 bytes)
>   */
> --
> 1.7.1
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-11-04 10:13   ` Viswas G
@ 2013-11-04 10:30     ` Jack Wang
  2013-11-11  5:57       ` Viswas G
  0 siblings, 1 reply; 8+ messages in thread
From: Jack Wang @ 2013-11-04 10:30 UTC (permalink / raw)
  To: Viswas G
  Cc: linux-scsi, Sangeetha Gnanasekaran, Nikith Ganigarakoppal,
	Anand Kumar Santhanam, Suresh Thiagarajan

On 11/04/2013 11:13 AM, Viswas G wrote:
> Hi Jack,
> 
> We wanted to control the GPIO in the HBA only. Bsg interface gets created only for enclosure or expander. 
> 
> For our HBA, bsg interface will not be created since it does not have an enclosure target inside. That's why we wanted to use IOCTL. Please advise.
> 
> Best Regards,
> Viswas G
> 
Hi Viswas,

No, bsg interface also support HBA.
Here is two example output from LSI mpt2sas:

smp_rep_manufacturer /dev/bsg/sas_host0
Report manufacturer response:
  SAS-1.1 format: 0
  vendor identification: LSI
  product identification: Virtual SMP Port
  product revision level:
smp_read_gpio /dev/bsg/sas_host0
Read GPIO register response:
  GPIO_CFG[0]:
    version: 0
    GPIO enable: 1
    cfg register count: 2
    gp register count: 1
    supported drive count: 16

Regards,
Jack

> -----Original Message-----
> From: Jack Wang [mailto:xjtuwjp@gmail.com] 
> Sent: Tuesday, October 29, 2013 3:49 PM
> To: Viswas G
> Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam
> Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
> 
> Hi Viswas,
> 
> As ioctl interface is not welcome for new feature, that's why we removed ioctl interface when pm8001 accepted into mainline.
> 
> I suggest you use bsg interface for this, see sas_host_smp.c for details.
> 
> Regards,
> Jack
> 
> On 10/22/2013 02:20 PM, Viswas G wrote:
>>
>> Signed-off-by: Viswas G <Viswas.G@pmcs.com>
>> ---
>>  drivers/scsi/pm8001/pm8001_ctl.c  |  248
>> ++++++++++++++++++++++++++++++++++++-
>>  drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
>>  drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
>>  drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
>>  drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
>>  drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
>>  6 files changed, 524 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c
>> b/drivers/scsi/pm8001/pm8001_ctl.c
>> index 247cb1c..3c9ca21 100644
>> --- a/drivers/scsi/pm8001/pm8001_ctl.c
>> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
>> @@ -40,7 +40,8 @@
>>  #include <linux/firmware.h>
>>  #include <linux/slab.h>
>>  #include "pm8001_sas.h"
>> -#include "pm8001_ctl.h"
>> +
>> +int pm8001_major = -1;
>>
>>  /* scsi host attributes */
>>
>> @@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
>>         NULL,
>>  };
>>
>> +/**
>> + * pm8001_open - open the configuration file
>> + * @inode: inode being opened
>> + * @file: file handle attached
>> + *
>> + * Called when the configuration device is opened. Does the needed
>> + * set up on the handle and then returns
>> + *
>> + */
>> +static int pm8001_open(struct inode *inode, struct file *file) {
>> +       struct pm8001_hba_info *pm8001_ha;
>> +       unsigned minor_number = iminor(inode);
>> +       int err = -ENODEV;
>> +
>> +       list_for_each_entry(pm8001_ha, &hba_list, list) {
>> +               if (pm8001_ha->id == minor_number) {
>> +                       file->private_data = pm8001_ha;
>> +                       err = 0;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       return err;
>> +}
>> +
>> +/**
>> + * pm8001_close - close the configuration file
>> + * @inode: inode being opened
>> + * @file: file handle attached
>> + *
>> + * Called when the configuration device is closed. Does the needed
>> + * set up on the handle and then returns
>> + *
>> + */
>> +static int pm8001_close(struct inode *inode, struct file *file) {
>> +       return 0;
>> +}
>> +
>> +static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
>> +                             unsigned long arg) {
>> +       u32 ret = 0;
>> +       struct ioctl_info_buffer info_buf;
>> +       union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
>> +
>> +       strcpy(info_buf.information.sz_name, DRV_NAME);
>> +
>> +       info_buf.information.usmajor_revision = DRV_MAJOR;
>> +       info_buf.information.usminor_revision = DRV_MINOR;
>> +       info_buf.information.usbuild_revision = DRV_BUILD;
>> +       if (pm8001_ha->chip_id == chip_8001) {
>> +               info_buf.information.maxoutstandingIO =
>> +                       main_tbl.pm8001_tbl.max_out_io;
>> +               info_buf.information.maxdevices =
>> +                       (main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
>> +       } else {
>> +               info_buf.information.maxoutstandingIO =
>> +                       main_tbl.pm80xx_tbl.max_out_io;
>> +               info_buf.information.maxdevices =
>> +                       (main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
>> +       }
>> +       info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>> +
>> +       if (copy_to_user((void *)arg, (void *)&info_buf,
>> +                        sizeof(struct ioctl_info_buffer))) {
>> +               ret = ADPT_IOCTL_CALL_FAILED;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
>> +                             unsigned long arg) {
>> +       struct gpio_buffer buffer;
>> +       struct pm8001_gpio *payload;
>> +       struct gpio_ioctl_resp *gpio_resp;
>> +       DECLARE_COMPLETION_ONSTACK(completion);
>> +       unsigned long timeout;
>> +       u32 ret = 0, operation;
>> +
>> +       mutex_lock(&pm8001_ha->ioctl_mutex);
>> +
>> +       if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
>> +               sizeof(struct gpio_buffer))) {
>> +               ret = ADPT_IOCTL_CALL_FAILED;
>> +               goto exit;
>> +       }
>> +
>> +       pm8001_ha->ioctl_completion = &completion;
>> +       payload = &buffer.gpio_payload;
>> +       operation = payload->operation;
>> +       ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
>> +       if (ret != 0) {
>> +               ret = ADPT_IOCTL_CALL_FAILED;
>> +               goto exit;
>> +       }
>> +
>> +       timeout = (unsigned long)buffer.header.timeout * 1000;
>> +
>> +       mod_timer(&pm8001_ha->ioctl_timer, jiffies +
>> msecs_to_jiffies(timeout));
>> +
>> +       wait_for_completion(&completion);
>> +
>> +       if (pm8001_ha->ioctl_timer_expired) {
>> +               ret = ADPT_IOCTL_CALL_TIMEOUT;
>> +               goto exit;
>> +       }
>> +       gpio_resp = &pm8001_ha->gpio_resp;
>> +
>> +       buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>> +
>> +       if (operation == GPIO_READ) {
>> +               payload->rd_wr_val              = gpio_resp->gpio_rd_val;
>> +               payload->input_enable           =
>> gpio_resp->gpio_in_enabled;
>> +               payload->pinsetup1              = gpio_resp->gpio_pinsetup1;
>> +               payload->pinsetup2              = gpio_resp->gpio_pinsetup2;
>> +               payload->event_level            =
>> gpio_resp->gpio_evt_change;
>> +               payload->event_rising_edge      = gpio_resp->gpio_evt_rise;
>> +               payload->event_falling_edge     = gpio_resp->gpio_evt_fall;
>> +
>> +               if (copy_to_user((void *)arg, (void *)&buffer,
>> +                                sizeof(struct gpio_buffer))) {
>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>> +               }
>> +       } else {
>> +               if (copy_to_user((void *)arg, (void *)&buffer.header,
>> +                                sizeof(struct ioctl_header))) {
>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>> +               }
>> +       }
>> +exit:
>> +       pm8001_ha->ioctl_timer_expired = 0;
>> +       mutex_unlock(&pm8001_ha->ioctl_mutex);
>> +       return ret;
>> +}
>> +
>> +/**
>> + * pm8001_ioctl - pm8001 configuration request
>> + * @inode: inode of device
>> + * @file: file handle
>> + * @cmd: ioctl command code
>> + * @arg: argument
>> + *
>> + * Handles a configuration ioctl.
>> + *
>> + */
>> +static long pm8001_ioctl(struct file *file,
>> +                        unsigned int cmd, unsigned long arg) {
>> +       u32 ret = -EACCES;
>> +       struct pm8001_hba_info *pm8001_ha;
>> +       struct ioctl_header header;
>> +
>> +       pm8001_ha = file->private_data;
>> +
>> +       switch (cmd) {
>> +
>> +       case ADPT_IOCTL_GPIO:
>> +               ret = pm8001_gpio_ioctl(pm8001_ha, arg);
>> +               break;
>> +       case ADPT_IOCTL_INFO:
>> +               ret = pm8001_info_ioctl(pm8001_ha, arg);
>> +               break;
>> +       default:
>> +               ret = ADPT_IOCTL_CALL_INVALID_CODE;
>> +       }
>> +       if (ret == 0)
>> +               return ret;
>> +       header.return_code = ret;
>> +       ret = -EACCES;
>> +       if (copy_to_user((void *)arg, (void *)&header,
>> +                       sizeof(struct ioctl_header))) {
>> +               return -EFAULT;
>> +       }
>> +       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
>> +       return ret;
>> +}
>> +
>> +/**
>> + * pm8001_poll - pm8001 poll request function
>> + * @file: file handle
>> + * @wait: poll table to wait
>> + *
>> + * Handles a poll request.
>> + *
>> + */
>> +unsigned int pm8001_poll(struct file *file, poll_table *wait) {
>> +       struct pm8001_hba_info *pm8001_ha;
>> +       unsigned int mask = 0;
>> +
>> +       pm8001_ha = file->private_data;
>> +
>> +       poll_wait(file, &pm8001_ha->pollq, wait);
>> +
>> +       if (pm8001_ha->gpio_event_occured == 1) {
>> +               pm8001_ha->gpio_event_occured = 0;
>> +               mask |= POLLIN | POLLRDNORM;
>> +       }
>> +
>> +       return mask;
>> +}
>> +
>> +static const struct file_operations pm8001_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .open           = pm8001_open,
>> +       .release        = pm8001_close,
>> +       .unlocked_ioctl = pm8001_ioctl,
>> +       .poll           = pm8001_poll,
>> +};
>> +
>> +/**
>> + * pm8001_setup_chrdev - registers a char device
>> + *
>> + * Return value
>> + *     0 in case of success, otherwise non-zero
>> + */
>> +int pm8001_setup_chrdev(void)
>> +{
>> +       pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
>> +       if (pm8001_major < 0) {
>> +               pr_warn("pm8001 : unable to register \"%s\" device\n",
>> +                               DRV_NAME);
>> +               return pm8001_major;
>> +       }
>> +       return 0;
>> +}
>> +
>> +/**
>> + * pm8001_release_chrdev - unregisters per-adapter management 
>> +interface
>> + *
>> + * Return value
>> + *     none
>> + */
>> +void pm8001_release_chrdev(void)
>> +{
>> +       if (pm8001_major > -1) {
>> +               unregister_chrdev(pm8001_major, DRV_NAME);
>> +               pm8001_major = -1;
>> +       }
>> +}
>> +
>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.h
>> b/drivers/scsi/pm8001/pm8001_ctl.h
>> index d0d43a2..0064dd3 100644
>> --- a/drivers/scsi/pm8001/pm8001_ctl.h
>> +++ b/drivers/scsi/pm8001/pm8001_ctl.h
>> @@ -55,6 +55,61 @@
>>  #define FAIL_OUT_MEMORY                 0x000c00
>>  #define FLASH_IN_PROGRESS               0x001000
>>
>> +#define ADPT_IOCTL_CALL_SUCCESS                0x00
>> +#define ADPT_IOCTL_CALL_FAILED         0x01
>> +#define ADPT_IOCTL_CALL_INVALID_CODE   0x03
>> +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04
>> +#define ADPT_IOCTL_CALL_TIMEOUT                0x08
>> +
>> +#define GPIO_READ      0
>> +#define GPIO_WRITE     1
>> +#define GPIO_PINSETUP  2
>> +#define GPIO_EVENTSETUP        3
>> +
>> +struct ioctl_header {
>> +       u32 io_controller_num;
>> +       u32 length;
>> +       u32 return_code;
>> +       u32 timeout;
>> +       u32 direction;
>> +};
>> +
>> +struct ioctl_drv_info {
>> +       u8      sz_name[64];
>> +       u16     usmajor_revision;
>> +       u16     usminor_revision;
>> +       u16     usbuild_revision;
>> +       u16     reserved0;
>> +       u32     maxdevices;
>> +       u32     maxoutstandingIO;
>> +       u32     reserved[16];
>> +};
>> +
>> +struct pm8001_gpio {
>> +       u32 operation;
>> +       u32 mask;
>> +       u32 rd_wr_val;
>> +       u32 input_enable;
>> +       u32 pinsetup1;
>> +       u32 pinsetup2;
>> +       u32 event_level;
>> +       u32 event_rising_edge;
>> +       u32 event_falling_edge;
>> +};
>> +
>> +struct ioctl_info_buffer {
>> +       struct ioctl_header     header;
>> +       struct ioctl_drv_info   information;
>> +};
>> +
>> +struct gpio_buffer {
>> +       struct ioctl_header     header;
>> +       struct pm8001_gpio      gpio_payload;
>> +};
>> +
>> +#define ADPT_IOCTL_INFO                _IOR(0, 0, struct
>> ioctl_info_buffer *)
>> +#define ADPT_IOCTL_GPIO                _IOWR(0, 1, struct  gpio_buffer *)
>> +
>>  #define IB_OB_READ_TIMES                256
>>  #define SYSFS_OFFSET                    1024
>>  #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
>> diff --git a/drivers/scsi/pm8001/pm8001_init.c
>> b/drivers/scsi/pm8001/pm8001_init.c
>> index 662bf13..ba2e659 100644
>> --- a/drivers/scsi/pm8001/pm8001_init.c
>> +++ b/drivers/scsi/pm8001/pm8001_init.c
>> @@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info
>> *pm8001_ha)
>>         if (!pm8001_ha)
>>                 return;
>>
>> +       del_timer(&pm8001_ha->ioctl_timer);
>> +
>>         for (i = 0; i < USI_MAX_MEMCNT; i++) {
>>                 if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
>>                         pci_free_consistent(pm8001_ha->pdev,
>> @@ -444,6 +446,24 @@ static int pm8001_ioremap(struct pm8001_hba_info
>> *pm8001_ha)
>>         return 0;
>>  }
>>
>> +void pm8001_ioctl_timer_callback(unsigned long data) {
>> +       struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info 
>> +*)data;
>> +
>> +       PM8001_FAIL_DBG(pm8001_ha,
>> +               pm8001_printk("Timer expired for GPIO response\n"));
>> +
>> +       spin_lock(&pm8001_ha->ioctl_lock);
>> +
>> +       if (pm8001_ha->ioctl_completion != NULL) {
>> +               pm8001_ha->ioctl_timer_expired = 1;
>> +               complete(pm8001_ha->ioctl_completion);
>> +               pm8001_ha->ioctl_completion = NULL;
>> +       }
>> +
>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>> +}
>> +
>>  /**
>>   * pm8001_pci_alloc - initialize our ha card structure
>>   * @pdev: pci device.
>> @@ -479,6 +499,15 @@ static struct pm8001_hba_info 
>> *pm8001_pci_alloc(struct pci_dev *pdev,
>>         else
>>                 pm8001_ha->iomb_size = IOMB_SIZE_SPC;
>>
>> +       mutex_init(&pm8001_ha->ioctl_mutex);
>> +       pm8001_ha->ioctl_completion = NULL;
>> +       init_waitqueue_head(&pm8001_ha->pollq);
>> +       pm8001_ha->gpio_event_occured = 0;
>> +       spin_lock_init(&pm8001_ha->ioctl_lock);
>> +       setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
>> +                    (unsigned long)pm8001_ha);
>> +
>> +
>>  #ifdef PM8001_USE_TASKLET
>>         /**
>>         * default tasklet for non msi-x interrupt handler/first msi-x 
>> @@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
>>         rc = pci_register_driver(&pm8001_pci_driver);
>>         if (rc)
>>                 goto err_tp;
>> +
>> +       rc = pm8001_setup_chrdev();
>> +       if (rc)
>> +               goto err_ctl;
>> +
>>         return 0;
>>
>> +err_ctl:
>> +       pci_unregister_driver(&pm8001_pci_driver);
>>  err_tp:
>>         sas_release_transport(pm8001_stt);
>>  err_wq:
>> @@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
>>         pci_unregister_driver(&pm8001_pci_driver);
>>         sas_release_transport(pm8001_stt);
>>         destroy_workqueue(pm8001_wq);
>> +       pm8001_release_chrdev();
>>  }
>>
>>  module_init(pm8001_init);
>> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
>> b/drivers/scsi/pm8001/pm8001_sas.h
>> index 9241c04..9171f0a 100644
>> --- a/drivers/scsi/pm8001/pm8001_sas.h
>> +++ b/drivers/scsi/pm8001/pm8001_sas.h
>> @@ -51,14 +51,21 @@
>>  #include <linux/pci.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/workqueue.h>
>> +#include <linux/poll.h>
>> +#include <linux/poll.h>
>> +#include <linux/timer.h>
>>  #include <scsi/libsas.h>
>>  #include <scsi/scsi_tcq.h>
>>  #include <scsi/sas_ata.h>
>>  #include <linux/atomic.h>
>>  #include "pm8001_defs.h"
>> +#include "pm8001_ctl.h"
>>
>>  #define DRV_NAME               "pm80xx"
>>  #define DRV_VERSION            "0.1.37"
>> +#define DRV_MAJOR              1
>> +#define DRV_MINOR              0
>> +#define DRV_BUILD              15
>>  #define PM8001_FAIL_LOGGING    0x01 /* Error message logging */
>>  #define PM8001_INIT_LOGGING    0x02 /* driver init logging */
>>  #define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
>> @@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
>>         u8      *func_specific;
>>  };
>>
>> +struct gpio_ioctl_resp {
>> +       u32 tag;
>> +       u32 gpio_rd_val;
>> +       u32 gpio_in_enabled;
>> +       u32 gpio_pinsetup1;
>> +       u32 gpio_pinsetup2;
>> +       u32 gpio_evt_change;
>> +       u32 gpio_evt_rise;
>> +       u32 gpio_evt_fall;
>> +};
>> +
>>  #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF  #define 
>> MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
>>  #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
>> @@ -229,6 +247,8 @@ struct pm8001_dispatch {
>>         int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
>>                 u32 state);
>>         int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
>> +       int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
>> +               struct pm8001_gpio *gpio_payload);
>>  };
>>
>>  struct pm8001_chip_info {
>> @@ -527,6 +547,14 @@ struct pm8001_hba_info {
>>         u32                     int_vector;
>>         const struct firmware   *fw_image;
>>         u8                      outq[PM8001_MAX_MSIX_VEC];
>> +       struct completion       *ioctl_completion;
>> +       struct mutex            ioctl_mutex;
>> +       spinlock_t              ioctl_lock;
>> +       u32                     gpio_event_occured;
>> +       u32                     ioctl_timer_expired;
>> +       struct timer_list       ioctl_timer;
>> +       struct gpio_ioctl_resp  gpio_resp;
>> +       wait_queue_head_t       pollq;
>>  };
>>
>>  struct pm8001_work {
>> @@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev, 
>> u32, char *buf);
>>  /* ctl shared API */
>>  extern struct device_attribute *pm8001_host_attrs[];
>>
>> +int pm8001_setup_chrdev(void);
>> +void pm8001_release_chrdev(void);
>>  #endif
>>
>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c
>> b/drivers/scsi/pm8001/pm80xx_hwi.c
>> index 4ebc79b..5423844 100644
>> --- a/drivers/scsi/pm8001/pm80xx_hwi.c
>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.c
>> @@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct 
>> pm8001_hba_info *pm8001_ha,
>>         return 0;
>>  }
>>
>> +static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void 
>> +*piomb) {
>> +       int ret;
>> +       struct gpio_ioctl_resp *pgpio_resp;
>> +       struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 4);
>> +
>> +       spin_lock(&pm8001_ha->ioctl_lock);
>> +       if (pm8001_ha->ioctl_completion != NULL) {
>> +               ret = del_timer(&pm8001_ha->ioctl_timer);
>> +               if (ret)
>> +                       PM8001_MSG_DBG(pm8001_ha,
>> +                               pm8001_printk("The timer was still in
>> use.\n"));
>> +               pm8001_ha->ioctl_timer_expired = 0;
>> +               pgpio_resp = &pm8001_ha->gpio_resp;
>> +               pgpio_resp->gpio_rd_val =
>> le32_to_cpu(ppayload->gpio_rd_val);
>> +               pgpio_resp->gpio_in_enabled =
>> +                       le32_to_cpu(ppayload->gpio_in_enabled);
>> +               pgpio_resp->gpio_pinsetup1 =
>> +                       le32_to_cpu(ppayload->gpio_pinsetup1);
>> +               pgpio_resp->gpio_pinsetup2 =
>> +                       le32_to_cpu(ppayload->gpio_pinsetup2);
>> +               pgpio_resp->gpio_evt_change =
>> +                       le32_to_cpu(ppayload->gpio_evt_change);
>> +               pgpio_resp->gpio_evt_rise =
>> +                       le32_to_cpu(ppayload->gpio_evt_rise);
>> +               pgpio_resp->gpio_evt_fall =
>> +                       le32_to_cpu(ppayload->gpio_evt_fall);
>> +
>> +               complete(pm8001_ha->ioctl_completion);
>> +               pm8001_ha->ioctl_completion = NULL;
>> +       }
>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>> +       return 0;
>> +}
>> +
>> +static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void 
>> +*piomb) {
>> +
>> +       u32 gpio_event = 0;
>> +       struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
>> +       gpio_event = le32_to_cpu(ppayload->gpio_event);
>> +       PM8001_MSG_DBG(pm8001_ha,
>> +                      pm8001_printk("GPIO event: 0x%X\n", gpio_event));
>> +       pm8001_ha->gpio_event_occured = 1;
>> +       wake_up_interruptible(&pm8001_ha->pollq);
>> +       return 0;
>> +}
>> +
>>  /**
>>   * process_one_iomb - process one outbound Queue memory block
>>   * @pm8001_ha: our hba card information @@ -3567,10 +3615,12 @@ 
>> static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void 
>> *piomb)
>>         case OPC_OUB_GPIO_RESPONSE:
>>                 PM8001_MSG_DBG(pm8001_ha,
>>                         pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
>> +               mpi_gpio_resp(pm8001_ha, piomb);
>>                 break;
>>         case OPC_OUB_GPIO_EVENT:
>>                 PM8001_MSG_DBG(pm8001_ha,
>>                         pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
>> +               mpi_gpio_event(pm8001_ha, piomb);
>>                 break;
>>         case OPC_OUB_GENERAL_EVENT:
>>                 PM8001_MSG_DBG(pm8001_ha, @@ -4510,6 +4560,61 @@ 
>> static u32 pm80xx_chip_is_our_interupt(struct
>> pm8001_hba_info *pm8001_ha)
>>  }
>>
>>  /**
>> + * pm80xx_chip_gpio_req - support for GPIO operation
>> + * @pm8001_ha: our hba card information.
>> + * @ioctl_payload: the payload for the GPIO operation */
>> +
>> +int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
>> +                               struct pm8001_gpio *gpio_payload) {
>> +       struct gpio_req payload;
>> +       struct inbound_queue_table *circularQ;
>> +       int ret;
>> +       u32 tag;
>> +       u32 opc = OPC_INB_GPIO;
>> +
>> +       if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
>> +               return ADPT_IOCTL_CALL_INVALID_DEVICE;
>> +
>> +       ret = pm8001_tag_alloc(pm8001_ha, &tag);
>> +       if (ret)
>> +               return -1;
>> +
>> +       memset(&payload, 0, sizeof(payload));
>> +       circularQ = &pm8001_ha->inbnd_q_tbl[0];
>> +       payload.tag = cpu_to_le32(tag);
>> +       switch (gpio_payload->operation) {
>> +       case GPIO_READ:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
>> +               break;
>> +       case GPIO_WRITE:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
>> +               payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
>> +               payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
>> +               break;
>> +       case GPIO_PINSETUP:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
>> +               payload.gpio_in_enabled =
>> +                       cpu_to_le32(gpio_payload->input_enable);
>> +               payload.gpio_pinsetup1 =
>> cpu_to_le32(gpio_payload->pinsetup1);
>> +               payload.gpio_pinsetup2 =
>> cpu_to_le32(gpio_payload->pinsetup2);
>> +               break;
>> +       case GPIO_EVENTSETUP:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
>> +               payload.gpio_evt_change =
>> +                       cpu_to_le32(gpio_payload->event_level);
>> +               payload.gpio_evt_rise =
>> +                       cpu_to_le32(gpio_payload->event_rising_edge);
>> +               payload.gpio_evt_fall =
>> +                       cpu_to_le32(gpio_payload->event_falling_edge);
>> +               break;
>> +       }
>> +       ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
>> +       return ret;
>> +}
>> +
>> +/**
>>   * pm8001_chip_isr - PM8001 isr handler.
>>   * @pm8001_ha: our hba card information.
>>   * @irq: irq number.
>> @@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
>>         .set_nvmd_req           = pm8001_chip_set_nvmd_req,
>>         .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
>>         .set_dev_state_req      = pm8001_chip_set_dev_state_req,
>> +       .gpio_req               = pm80xx_chip_gpio_req,
>>  };
>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h
>> b/drivers/scsi/pm8001/pm80xx_hwi.h
>> index c86816b..671940a 100644
>> --- a/drivers/scsi/pm8001/pm80xx_hwi.h
>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.h
>> @@ -422,6 +422,55 @@ struct hw_event_ack_req {  } 
>> __attribute__((packed, aligned(4)));
>>
>>  /*
>> + * brief the data structure of GPIO Commannd
>> + * use to control MPI GPIOs (64 bytes)  */ struct gpio_req {
>> +       __le32  tag;
>> +       __le32  eobid_ge_gs_gr_gw;
>> +       __le32  gpio_wr_msk;
>> +       __le32  gpio_wr_val;
>> +       __le32  gpio_in_enabled;
>> +       __le32  gpio_pinsetup1;
>> +       __le32  gpio_pinsetup2;
>> +       __le32  gpio_evt_change;
>> +       __le32  gpio_evt_rise;
>> +       __le32  gpio_evt_fall;
>> +       u32     reserved[5];
>> +} __attribute__((packed, aligned(4)));
>> +
>> +#define GPIO_GW_BIT 0x1
>> +#define GPIO_GR_BIT 0x2
>> +#define GPIO_GS_BIT 0x4
>> +#define GPIO_GE_BIT 0x8
>> +
>> +/*
>> + * brief the data structure of GPIO Response
>> + * indicates the completion of GPIO command (64 bytes)  */ struct 
>> +gpio_resp {
>> +       __le32  tag;
>> +       u32     reserved[2];
>> +       __le32  gpio_rd_val;
>> +       __le32  gpio_in_enabled;
>> +       __le32  gpio_pinsetup1;
>> +       __le32  gpio_pinsetup2;
>> +       __le32  gpio_evt_change;
>> +       __le32  gpio_evt_rise;
>> +       __le32  gpio_evt_fall;
>> +       u32     reserved1[5];
>> +} __attribute__((packed, aligned(4)));
>> +
>> +/*
>> + * brief the data structure of GPIO Event
>> + * indicates the generation of GPIO event (64 bytes)  */ struct 
>> +gpio_event {
>> +       __le32  gpio_event;
>> +       u32     reserved[14];
>> +} __attribute__((packed, aligned(4)));
>> +
>> +/*
>>   * brief the data structure of PHY_START Response Command
>>   * indicates the completion of PHY_START command (64 bytes)
>>   */
>> --
>> 1.7.1
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-11-04 10:30     ` Jack Wang
@ 2013-11-11  5:57       ` Viswas G
  2013-11-11  8:58         ` Jack Wang
  0 siblings, 1 reply; 8+ messages in thread
From: Viswas G @ 2013-11-11  5:57 UTC (permalink / raw)
  To: Jack Wang
  Cc: linux-scsi, Sangeetha Gnanasekaran, Nikith Ganigarakoppal,
	Anand Kumar Santhanam, Suresh Thiagarajan

Hi Jack,

The GPIO feature we implemented here is for controlling and configuring the GPIO pins present in the HBA and it is not related to the GPIO registers present in the SGPIO. Following is one of the GPIO operations we do from application.

When application wants to know the insertion/removal of a SAS cable in any of the port, it configures the GPIO for corresponding port to generate event for SAS cable insertion or removal using the IOCTL to the HBA driver and waits by calling poll function. When driver receives the GPIO event for the SAS cable insertion or removal then it intimates the application.

We are using IOCTL instead of sysfs interface since we have to pass structures between user space and kernel space. Again, in the kernel space, we have to parse user buffer from application and convert it to the corresponding data structure. We wanted to avoid the parsing complexity by using ioctl interface.

Regards,
Viswas G


-----Original Message-----
From: Jack Wang [mailto:xjtuwjp@gmail.com] 
Sent: Monday, November 04, 2013 4:00 PM
To: Viswas G
Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam; Suresh Thiagarajan
Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers

On 11/04/2013 11:13 AM, Viswas G wrote:
> Hi Jack,
> 
> We wanted to control the GPIO in the HBA only. Bsg interface gets created only for enclosure or expander. 
> 
> For our HBA, bsg interface will not be created since it does not have an enclosure target inside. That's why we wanted to use IOCTL. Please advise.
> 
> Best Regards,
> Viswas G
> 
Hi Viswas,

No, bsg interface also support HBA.
Here is two example output from LSI mpt2sas:

smp_rep_manufacturer /dev/bsg/sas_host0
Report manufacturer response:
  SAS-1.1 format: 0
  vendor identification: LSI
  product identification: Virtual SMP Port
  product revision level:
smp_read_gpio /dev/bsg/sas_host0
Read GPIO register response:
  GPIO_CFG[0]:
    version: 0
    GPIO enable: 1
    cfg register count: 2
    gp register count: 1
    supported drive count: 16

Regards,
Jack

> -----Original Message-----
> From: Jack Wang [mailto:xjtuwjp@gmail.com] 
> Sent: Tuesday, October 29, 2013 3:49 PM
> To: Viswas G
> Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam
> Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
> 
> Hi Viswas,
> 
> As ioctl interface is not welcome for new feature, that's why we removed ioctl interface when pm8001 accepted into mainline.
> 
> I suggest you use bsg interface for this, see sas_host_smp.c for details.
> 
> Regards,
> Jack
> 
> On 10/22/2013 02:20 PM, Viswas G wrote:
>>
>> Signed-off-by: Viswas G <Viswas.G@pmcs.com>
>> ---
>>  drivers/scsi/pm8001/pm8001_ctl.c  |  248
>> ++++++++++++++++++++++++++++++++++++-
>>  drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
>>  drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
>>  drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
>>  drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
>>  drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
>>  6 files changed, 524 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c
>> b/drivers/scsi/pm8001/pm8001_ctl.c
>> index 247cb1c..3c9ca21 100644
>> --- a/drivers/scsi/pm8001/pm8001_ctl.c
>> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
>> @@ -40,7 +40,8 @@
>>  #include <linux/firmware.h>
>>  #include <linux/slab.h>
>>  #include "pm8001_sas.h"
>> -#include "pm8001_ctl.h"
>> +
>> +int pm8001_major = -1;
>>
>>  /* scsi host attributes */
>>
>> @@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
>>         NULL,
>>  };
>>
>> +/**
>> + * pm8001_open - open the configuration file
>> + * @inode: inode being opened
>> + * @file: file handle attached
>> + *
>> + * Called when the configuration device is opened. Does the needed
>> + * set up on the handle and then returns
>> + *
>> + */
>> +static int pm8001_open(struct inode *inode, struct file *file) {
>> +       struct pm8001_hba_info *pm8001_ha;
>> +       unsigned minor_number = iminor(inode);
>> +       int err = -ENODEV;
>> +
>> +       list_for_each_entry(pm8001_ha, &hba_list, list) {
>> +               if (pm8001_ha->id == minor_number) {
>> +                       file->private_data = pm8001_ha;
>> +                       err = 0;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       return err;
>> +}
>> +
>> +/**
>> + * pm8001_close - close the configuration file
>> + * @inode: inode being opened
>> + * @file: file handle attached
>> + *
>> + * Called when the configuration device is closed. Does the needed
>> + * set up on the handle and then returns
>> + *
>> + */
>> +static int pm8001_close(struct inode *inode, struct file *file) {
>> +       return 0;
>> +}
>> +
>> +static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
>> +                             unsigned long arg) {
>> +       u32 ret = 0;
>> +       struct ioctl_info_buffer info_buf;
>> +       union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
>> +
>> +       strcpy(info_buf.information.sz_name, DRV_NAME);
>> +
>> +       info_buf.information.usmajor_revision = DRV_MAJOR;
>> +       info_buf.information.usminor_revision = DRV_MINOR;
>> +       info_buf.information.usbuild_revision = DRV_BUILD;
>> +       if (pm8001_ha->chip_id == chip_8001) {
>> +               info_buf.information.maxoutstandingIO =
>> +                       main_tbl.pm8001_tbl.max_out_io;
>> +               info_buf.information.maxdevices =
>> +                       (main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
>> +       } else {
>> +               info_buf.information.maxoutstandingIO =
>> +                       main_tbl.pm80xx_tbl.max_out_io;
>> +               info_buf.information.maxdevices =
>> +                       (main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
>> +       }
>> +       info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>> +
>> +       if (copy_to_user((void *)arg, (void *)&info_buf,
>> +                        sizeof(struct ioctl_info_buffer))) {
>> +               ret = ADPT_IOCTL_CALL_FAILED;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
>> +                             unsigned long arg) {
>> +       struct gpio_buffer buffer;
>> +       struct pm8001_gpio *payload;
>> +       struct gpio_ioctl_resp *gpio_resp;
>> +       DECLARE_COMPLETION_ONSTACK(completion);
>> +       unsigned long timeout;
>> +       u32 ret = 0, operation;
>> +
>> +       mutex_lock(&pm8001_ha->ioctl_mutex);
>> +
>> +       if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
>> +               sizeof(struct gpio_buffer))) {
>> +               ret = ADPT_IOCTL_CALL_FAILED;
>> +               goto exit;
>> +       }
>> +
>> +       pm8001_ha->ioctl_completion = &completion;
>> +       payload = &buffer.gpio_payload;
>> +       operation = payload->operation;
>> +       ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
>> +       if (ret != 0) {
>> +               ret = ADPT_IOCTL_CALL_FAILED;
>> +               goto exit;
>> +       }
>> +
>> +       timeout = (unsigned long)buffer.header.timeout * 1000;
>> +
>> +       mod_timer(&pm8001_ha->ioctl_timer, jiffies +
>> msecs_to_jiffies(timeout));
>> +
>> +       wait_for_completion(&completion);
>> +
>> +       if (pm8001_ha->ioctl_timer_expired) {
>> +               ret = ADPT_IOCTL_CALL_TIMEOUT;
>> +               goto exit;
>> +       }
>> +       gpio_resp = &pm8001_ha->gpio_resp;
>> +
>> +       buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>> +
>> +       if (operation == GPIO_READ) {
>> +               payload->rd_wr_val              = gpio_resp->gpio_rd_val;
>> +               payload->input_enable           =
>> gpio_resp->gpio_in_enabled;
>> +               payload->pinsetup1              = gpio_resp->gpio_pinsetup1;
>> +               payload->pinsetup2              = gpio_resp->gpio_pinsetup2;
>> +               payload->event_level            =
>> gpio_resp->gpio_evt_change;
>> +               payload->event_rising_edge      = gpio_resp->gpio_evt_rise;
>> +               payload->event_falling_edge     = gpio_resp->gpio_evt_fall;
>> +
>> +               if (copy_to_user((void *)arg, (void *)&buffer,
>> +                                sizeof(struct gpio_buffer))) {
>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>> +               }
>> +       } else {
>> +               if (copy_to_user((void *)arg, (void *)&buffer.header,
>> +                                sizeof(struct ioctl_header))) {
>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>> +               }
>> +       }
>> +exit:
>> +       pm8001_ha->ioctl_timer_expired = 0;
>> +       mutex_unlock(&pm8001_ha->ioctl_mutex);
>> +       return ret;
>> +}
>> +
>> +/**
>> + * pm8001_ioctl - pm8001 configuration request
>> + * @inode: inode of device
>> + * @file: file handle
>> + * @cmd: ioctl command code
>> + * @arg: argument
>> + *
>> + * Handles a configuration ioctl.
>> + *
>> + */
>> +static long pm8001_ioctl(struct file *file,
>> +                        unsigned int cmd, unsigned long arg) {
>> +       u32 ret = -EACCES;
>> +       struct pm8001_hba_info *pm8001_ha;
>> +       struct ioctl_header header;
>> +
>> +       pm8001_ha = file->private_data;
>> +
>> +       switch (cmd) {
>> +
>> +       case ADPT_IOCTL_GPIO:
>> +               ret = pm8001_gpio_ioctl(pm8001_ha, arg);
>> +               break;
>> +       case ADPT_IOCTL_INFO:
>> +               ret = pm8001_info_ioctl(pm8001_ha, arg);
>> +               break;
>> +       default:
>> +               ret = ADPT_IOCTL_CALL_INVALID_CODE;
>> +       }
>> +       if (ret == 0)
>> +               return ret;
>> +       header.return_code = ret;
>> +       ret = -EACCES;
>> +       if (copy_to_user((void *)arg, (void *)&header,
>> +                       sizeof(struct ioctl_header))) {
>> +               return -EFAULT;
>> +       }
>> +       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
>> +       return ret;
>> +}
>> +
>> +/**
>> + * pm8001_poll - pm8001 poll request function
>> + * @file: file handle
>> + * @wait: poll table to wait
>> + *
>> + * Handles a poll request.
>> + *
>> + */
>> +unsigned int pm8001_poll(struct file *file, poll_table *wait) {
>> +       struct pm8001_hba_info *pm8001_ha;
>> +       unsigned int mask = 0;
>> +
>> +       pm8001_ha = file->private_data;
>> +
>> +       poll_wait(file, &pm8001_ha->pollq, wait);
>> +
>> +       if (pm8001_ha->gpio_event_occured == 1) {
>> +               pm8001_ha->gpio_event_occured = 0;
>> +               mask |= POLLIN | POLLRDNORM;
>> +       }
>> +
>> +       return mask;
>> +}
>> +
>> +static const struct file_operations pm8001_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .open           = pm8001_open,
>> +       .release        = pm8001_close,
>> +       .unlocked_ioctl = pm8001_ioctl,
>> +       .poll           = pm8001_poll,
>> +};
>> +
>> +/**
>> + * pm8001_setup_chrdev - registers a char device
>> + *
>> + * Return value
>> + *     0 in case of success, otherwise non-zero
>> + */
>> +int pm8001_setup_chrdev(void)
>> +{
>> +       pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
>> +       if (pm8001_major < 0) {
>> +               pr_warn("pm8001 : unable to register \"%s\" device\n",
>> +                               DRV_NAME);
>> +               return pm8001_major;
>> +       }
>> +       return 0;
>> +}
>> +
>> +/**
>> + * pm8001_release_chrdev - unregisters per-adapter management 
>> +interface
>> + *
>> + * Return value
>> + *     none
>> + */
>> +void pm8001_release_chrdev(void)
>> +{
>> +       if (pm8001_major > -1) {
>> +               unregister_chrdev(pm8001_major, DRV_NAME);
>> +               pm8001_major = -1;
>> +       }
>> +}
>> +
>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.h
>> b/drivers/scsi/pm8001/pm8001_ctl.h
>> index d0d43a2..0064dd3 100644
>> --- a/drivers/scsi/pm8001/pm8001_ctl.h
>> +++ b/drivers/scsi/pm8001/pm8001_ctl.h
>> @@ -55,6 +55,61 @@
>>  #define FAIL_OUT_MEMORY                 0x000c00
>>  #define FLASH_IN_PROGRESS               0x001000
>>
>> +#define ADPT_IOCTL_CALL_SUCCESS                0x00
>> +#define ADPT_IOCTL_CALL_FAILED         0x01
>> +#define ADPT_IOCTL_CALL_INVALID_CODE   0x03
>> +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04
>> +#define ADPT_IOCTL_CALL_TIMEOUT                0x08
>> +
>> +#define GPIO_READ      0
>> +#define GPIO_WRITE     1
>> +#define GPIO_PINSETUP  2
>> +#define GPIO_EVENTSETUP        3
>> +
>> +struct ioctl_header {
>> +       u32 io_controller_num;
>> +       u32 length;
>> +       u32 return_code;
>> +       u32 timeout;
>> +       u32 direction;
>> +};
>> +
>> +struct ioctl_drv_info {
>> +       u8      sz_name[64];
>> +       u16     usmajor_revision;
>> +       u16     usminor_revision;
>> +       u16     usbuild_revision;
>> +       u16     reserved0;
>> +       u32     maxdevices;
>> +       u32     maxoutstandingIO;
>> +       u32     reserved[16];
>> +};
>> +
>> +struct pm8001_gpio {
>> +       u32 operation;
>> +       u32 mask;
>> +       u32 rd_wr_val;
>> +       u32 input_enable;
>> +       u32 pinsetup1;
>> +       u32 pinsetup2;
>> +       u32 event_level;
>> +       u32 event_rising_edge;
>> +       u32 event_falling_edge;
>> +};
>> +
>> +struct ioctl_info_buffer {
>> +       struct ioctl_header     header;
>> +       struct ioctl_drv_info   information;
>> +};
>> +
>> +struct gpio_buffer {
>> +       struct ioctl_header     header;
>> +       struct pm8001_gpio      gpio_payload;
>> +};
>> +
>> +#define ADPT_IOCTL_INFO                _IOR(0, 0, struct
>> ioctl_info_buffer *)
>> +#define ADPT_IOCTL_GPIO                _IOWR(0, 1, struct  gpio_buffer *)
>> +
>>  #define IB_OB_READ_TIMES                256
>>  #define SYSFS_OFFSET                    1024
>>  #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
>> diff --git a/drivers/scsi/pm8001/pm8001_init.c
>> b/drivers/scsi/pm8001/pm8001_init.c
>> index 662bf13..ba2e659 100644
>> --- a/drivers/scsi/pm8001/pm8001_init.c
>> +++ b/drivers/scsi/pm8001/pm8001_init.c
>> @@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info
>> *pm8001_ha)
>>         if (!pm8001_ha)
>>                 return;
>>
>> +       del_timer(&pm8001_ha->ioctl_timer);
>> +
>>         for (i = 0; i < USI_MAX_MEMCNT; i++) {
>>                 if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
>>                         pci_free_consistent(pm8001_ha->pdev,
>> @@ -444,6 +446,24 @@ static int pm8001_ioremap(struct pm8001_hba_info
>> *pm8001_ha)
>>         return 0;
>>  }
>>
>> +void pm8001_ioctl_timer_callback(unsigned long data) {
>> +       struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info 
>> +*)data;
>> +
>> +       PM8001_FAIL_DBG(pm8001_ha,
>> +               pm8001_printk("Timer expired for GPIO response\n"));
>> +
>> +       spin_lock(&pm8001_ha->ioctl_lock);
>> +
>> +       if (pm8001_ha->ioctl_completion != NULL) {
>> +               pm8001_ha->ioctl_timer_expired = 1;
>> +               complete(pm8001_ha->ioctl_completion);
>> +               pm8001_ha->ioctl_completion = NULL;
>> +       }
>> +
>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>> +}
>> +
>>  /**
>>   * pm8001_pci_alloc - initialize our ha card structure
>>   * @pdev: pci device.
>> @@ -479,6 +499,15 @@ static struct pm8001_hba_info 
>> *pm8001_pci_alloc(struct pci_dev *pdev,
>>         else
>>                 pm8001_ha->iomb_size = IOMB_SIZE_SPC;
>>
>> +       mutex_init(&pm8001_ha->ioctl_mutex);
>> +       pm8001_ha->ioctl_completion = NULL;
>> +       init_waitqueue_head(&pm8001_ha->pollq);
>> +       pm8001_ha->gpio_event_occured = 0;
>> +       spin_lock_init(&pm8001_ha->ioctl_lock);
>> +       setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
>> +                    (unsigned long)pm8001_ha);
>> +
>> +
>>  #ifdef PM8001_USE_TASKLET
>>         /**
>>         * default tasklet for non msi-x interrupt handler/first msi-x 
>> @@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
>>         rc = pci_register_driver(&pm8001_pci_driver);
>>         if (rc)
>>                 goto err_tp;
>> +
>> +       rc = pm8001_setup_chrdev();
>> +       if (rc)
>> +               goto err_ctl;
>> +
>>         return 0;
>>
>> +err_ctl:
>> +       pci_unregister_driver(&pm8001_pci_driver);
>>  err_tp:
>>         sas_release_transport(pm8001_stt);
>>  err_wq:
>> @@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
>>         pci_unregister_driver(&pm8001_pci_driver);
>>         sas_release_transport(pm8001_stt);
>>         destroy_workqueue(pm8001_wq);
>> +       pm8001_release_chrdev();
>>  }
>>
>>  module_init(pm8001_init);
>> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
>> b/drivers/scsi/pm8001/pm8001_sas.h
>> index 9241c04..9171f0a 100644
>> --- a/drivers/scsi/pm8001/pm8001_sas.h
>> +++ b/drivers/scsi/pm8001/pm8001_sas.h
>> @@ -51,14 +51,21 @@
>>  #include <linux/pci.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/workqueue.h>
>> +#include <linux/poll.h>
>> +#include <linux/poll.h>
>> +#include <linux/timer.h>
>>  #include <scsi/libsas.h>
>>  #include <scsi/scsi_tcq.h>
>>  #include <scsi/sas_ata.h>
>>  #include <linux/atomic.h>
>>  #include "pm8001_defs.h"
>> +#include "pm8001_ctl.h"
>>
>>  #define DRV_NAME               "pm80xx"
>>  #define DRV_VERSION            "0.1.37"
>> +#define DRV_MAJOR              1
>> +#define DRV_MINOR              0
>> +#define DRV_BUILD              15
>>  #define PM8001_FAIL_LOGGING    0x01 /* Error message logging */
>>  #define PM8001_INIT_LOGGING    0x02 /* driver init logging */
>>  #define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
>> @@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
>>         u8      *func_specific;
>>  };
>>
>> +struct gpio_ioctl_resp {
>> +       u32 tag;
>> +       u32 gpio_rd_val;
>> +       u32 gpio_in_enabled;
>> +       u32 gpio_pinsetup1;
>> +       u32 gpio_pinsetup2;
>> +       u32 gpio_evt_change;
>> +       u32 gpio_evt_rise;
>> +       u32 gpio_evt_fall;
>> +};
>> +
>>  #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF  #define 
>> MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
>>  #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
>> @@ -229,6 +247,8 @@ struct pm8001_dispatch {
>>         int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
>>                 u32 state);
>>         int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
>> +       int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
>> +               struct pm8001_gpio *gpio_payload);
>>  };
>>
>>  struct pm8001_chip_info {
>> @@ -527,6 +547,14 @@ struct pm8001_hba_info {
>>         u32                     int_vector;
>>         const struct firmware   *fw_image;
>>         u8                      outq[PM8001_MAX_MSIX_VEC];
>> +       struct completion       *ioctl_completion;
>> +       struct mutex            ioctl_mutex;
>> +       spinlock_t              ioctl_lock;
>> +       u32                     gpio_event_occured;
>> +       u32                     ioctl_timer_expired;
>> +       struct timer_list       ioctl_timer;
>> +       struct gpio_ioctl_resp  gpio_resp;
>> +       wait_queue_head_t       pollq;
>>  };
>>
>>  struct pm8001_work {
>> @@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev, 
>> u32, char *buf);
>>  /* ctl shared API */
>>  extern struct device_attribute *pm8001_host_attrs[];
>>
>> +int pm8001_setup_chrdev(void);
>> +void pm8001_release_chrdev(void);
>>  #endif
>>
>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c
>> b/drivers/scsi/pm8001/pm80xx_hwi.c
>> index 4ebc79b..5423844 100644
>> --- a/drivers/scsi/pm8001/pm80xx_hwi.c
>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.c
>> @@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct 
>> pm8001_hba_info *pm8001_ha,
>>         return 0;
>>  }
>>
>> +static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void 
>> +*piomb) {
>> +       int ret;
>> +       struct gpio_ioctl_resp *pgpio_resp;
>> +       struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 4);
>> +
>> +       spin_lock(&pm8001_ha->ioctl_lock);
>> +       if (pm8001_ha->ioctl_completion != NULL) {
>> +               ret = del_timer(&pm8001_ha->ioctl_timer);
>> +               if (ret)
>> +                       PM8001_MSG_DBG(pm8001_ha,
>> +                               pm8001_printk("The timer was still in
>> use.\n"));
>> +               pm8001_ha->ioctl_timer_expired = 0;
>> +               pgpio_resp = &pm8001_ha->gpio_resp;
>> +               pgpio_resp->gpio_rd_val =
>> le32_to_cpu(ppayload->gpio_rd_val);
>> +               pgpio_resp->gpio_in_enabled =
>> +                       le32_to_cpu(ppayload->gpio_in_enabled);
>> +               pgpio_resp->gpio_pinsetup1 =
>> +                       le32_to_cpu(ppayload->gpio_pinsetup1);
>> +               pgpio_resp->gpio_pinsetup2 =
>> +                       le32_to_cpu(ppayload->gpio_pinsetup2);
>> +               pgpio_resp->gpio_evt_change =
>> +                       le32_to_cpu(ppayload->gpio_evt_change);
>> +               pgpio_resp->gpio_evt_rise =
>> +                       le32_to_cpu(ppayload->gpio_evt_rise);
>> +               pgpio_resp->gpio_evt_fall =
>> +                       le32_to_cpu(ppayload->gpio_evt_fall);
>> +
>> +               complete(pm8001_ha->ioctl_completion);
>> +               pm8001_ha->ioctl_completion = NULL;
>> +       }
>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>> +       return 0;
>> +}
>> +
>> +static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void 
>> +*piomb) {
>> +
>> +       u32 gpio_event = 0;
>> +       struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
>> +       gpio_event = le32_to_cpu(ppayload->gpio_event);
>> +       PM8001_MSG_DBG(pm8001_ha,
>> +                      pm8001_printk("GPIO event: 0x%X\n", gpio_event));
>> +       pm8001_ha->gpio_event_occured = 1;
>> +       wake_up_interruptible(&pm8001_ha->pollq);
>> +       return 0;
>> +}
>> +
>>  /**
>>   * process_one_iomb - process one outbound Queue memory block
>>   * @pm8001_ha: our hba card information @@ -3567,10 +3615,12 @@ 
>> static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void 
>> *piomb)
>>         case OPC_OUB_GPIO_RESPONSE:
>>                 PM8001_MSG_DBG(pm8001_ha,
>>                         pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
>> +               mpi_gpio_resp(pm8001_ha, piomb);
>>                 break;
>>         case OPC_OUB_GPIO_EVENT:
>>                 PM8001_MSG_DBG(pm8001_ha,
>>                         pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
>> +               mpi_gpio_event(pm8001_ha, piomb);
>>                 break;
>>         case OPC_OUB_GENERAL_EVENT:
>>                 PM8001_MSG_DBG(pm8001_ha, @@ -4510,6 +4560,61 @@ 
>> static u32 pm80xx_chip_is_our_interupt(struct
>> pm8001_hba_info *pm8001_ha)
>>  }
>>
>>  /**
>> + * pm80xx_chip_gpio_req - support for GPIO operation
>> + * @pm8001_ha: our hba card information.
>> + * @ioctl_payload: the payload for the GPIO operation */
>> +
>> +int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
>> +                               struct pm8001_gpio *gpio_payload) {
>> +       struct gpio_req payload;
>> +       struct inbound_queue_table *circularQ;
>> +       int ret;
>> +       u32 tag;
>> +       u32 opc = OPC_INB_GPIO;
>> +
>> +       if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
>> +               return ADPT_IOCTL_CALL_INVALID_DEVICE;
>> +
>> +       ret = pm8001_tag_alloc(pm8001_ha, &tag);
>> +       if (ret)
>> +               return -1;
>> +
>> +       memset(&payload, 0, sizeof(payload));
>> +       circularQ = &pm8001_ha->inbnd_q_tbl[0];
>> +       payload.tag = cpu_to_le32(tag);
>> +       switch (gpio_payload->operation) {
>> +       case GPIO_READ:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
>> +               break;
>> +       case GPIO_WRITE:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
>> +               payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
>> +               payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
>> +               break;
>> +       case GPIO_PINSETUP:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
>> +               payload.gpio_in_enabled =
>> +                       cpu_to_le32(gpio_payload->input_enable);
>> +               payload.gpio_pinsetup1 =
>> cpu_to_le32(gpio_payload->pinsetup1);
>> +               payload.gpio_pinsetup2 =
>> cpu_to_le32(gpio_payload->pinsetup2);
>> +               break;
>> +       case GPIO_EVENTSETUP:
>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
>> +               payload.gpio_evt_change =
>> +                       cpu_to_le32(gpio_payload->event_level);
>> +               payload.gpio_evt_rise =
>> +                       cpu_to_le32(gpio_payload->event_rising_edge);
>> +               payload.gpio_evt_fall =
>> +                       cpu_to_le32(gpio_payload->event_falling_edge);
>> +               break;
>> +       }
>> +       ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
>> +       return ret;
>> +}
>> +
>> +/**
>>   * pm8001_chip_isr - PM8001 isr handler.
>>   * @pm8001_ha: our hba card information.
>>   * @irq: irq number.
>> @@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
>>         .set_nvmd_req           = pm8001_chip_set_nvmd_req,
>>         .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
>>         .set_dev_state_req      = pm8001_chip_set_dev_state_req,
>> +       .gpio_req               = pm80xx_chip_gpio_req,
>>  };
>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h
>> b/drivers/scsi/pm8001/pm80xx_hwi.h
>> index c86816b..671940a 100644
>> --- a/drivers/scsi/pm8001/pm80xx_hwi.h
>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.h
>> @@ -422,6 +422,55 @@ struct hw_event_ack_req {  } 
>> __attribute__((packed, aligned(4)));
>>
>>  /*
>> + * brief the data structure of GPIO Commannd
>> + * use to control MPI GPIOs (64 bytes)  */ struct gpio_req {
>> +       __le32  tag;
>> +       __le32  eobid_ge_gs_gr_gw;
>> +       __le32  gpio_wr_msk;
>> +       __le32  gpio_wr_val;
>> +       __le32  gpio_in_enabled;
>> +       __le32  gpio_pinsetup1;
>> +       __le32  gpio_pinsetup2;
>> +       __le32  gpio_evt_change;
>> +       __le32  gpio_evt_rise;
>> +       __le32  gpio_evt_fall;
>> +       u32     reserved[5];
>> +} __attribute__((packed, aligned(4)));
>> +
>> +#define GPIO_GW_BIT 0x1
>> +#define GPIO_GR_BIT 0x2
>> +#define GPIO_GS_BIT 0x4
>> +#define GPIO_GE_BIT 0x8
>> +
>> +/*
>> + * brief the data structure of GPIO Response
>> + * indicates the completion of GPIO command (64 bytes)  */ struct 
>> +gpio_resp {
>> +       __le32  tag;
>> +       u32     reserved[2];
>> +       __le32  gpio_rd_val;
>> +       __le32  gpio_in_enabled;
>> +       __le32  gpio_pinsetup1;
>> +       __le32  gpio_pinsetup2;
>> +       __le32  gpio_evt_change;
>> +       __le32  gpio_evt_rise;
>> +       __le32  gpio_evt_fall;
>> +       u32     reserved1[5];
>> +} __attribute__((packed, aligned(4)));
>> +
>> +/*
>> + * brief the data structure of GPIO Event
>> + * indicates the generation of GPIO event (64 bytes)  */ struct 
>> +gpio_event {
>> +       __le32  gpio_event;
>> +       u32     reserved[14];
>> +} __attribute__((packed, aligned(4)));
>> +
>> +/*
>>   * brief the data structure of PHY_START Response Command
>>   * indicates the completion of PHY_START command (64 bytes)
>>   */
>> --
>> 1.7.1
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-11-11  5:57       ` Viswas G
@ 2013-11-11  8:58         ` Jack Wang
  2013-11-15  6:14           ` Viswas G
  2013-11-16 18:25           ` James Bottomley
  0 siblings, 2 replies; 8+ messages in thread
From: Jack Wang @ 2013-11-11  8:58 UTC (permalink / raw)
  To: Viswas G, James Bottomley
  Cc: linux-scsi, Sangeetha Gnanasekaran, Nikith Ganigarakoppal,
	Anand Kumar Santhanam, Suresh Thiagarajan

Hi James,

About this gpio feature, do you think it's OK to implement with IOCTL or
we can use exist bsg interface in libsas and add another function call?

Regards,
Jack

On 11/11/2013 06:57 AM, Viswas G wrote:
> Hi Jack,
> 
> The GPIO feature we implemented here is for controlling and configuring the GPIO pins present in the HBA and it is not related to the GPIO registers present in the SGPIO. Following is one of the GPIO operations we do from application.
> 
> When application wants to know the insertion/removal of a SAS cable in any of the port, it configures the GPIO for corresponding port to generate event for SAS cable insertion or removal using the IOCTL to the HBA driver and waits by calling poll function. When driver receives the GPIO event for the SAS cable insertion or removal then it intimates the application.
> 
> We are using IOCTL instead of sysfs interface since we have to pass structures between user space and kernel space. Again, in the kernel space, we have to parse user buffer from application and convert it to the corresponding data structure. We wanted to avoid the parsing complexity by using ioctl interface.
> 
> Regards,
> Viswas G
> 
> 
> -----Original Message-----
> From: Jack Wang [mailto:xjtuwjp@gmail.com] 
> Sent: Monday, November 04, 2013 4:00 PM
> To: Viswas G
> Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam; Suresh Thiagarajan
> Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
> 
> On 11/04/2013 11:13 AM, Viswas G wrote:
>> Hi Jack,
>>
>> We wanted to control the GPIO in the HBA only. Bsg interface gets created only for enclosure or expander. 
>>
>> For our HBA, bsg interface will not be created since it does not have an enclosure target inside. That's why we wanted to use IOCTL. Please advise.
>>
>> Best Regards,
>> Viswas G
>>
> Hi Viswas,
> 
> No, bsg interface also support HBA.
> Here is two example output from LSI mpt2sas:
> 
> smp_rep_manufacturer /dev/bsg/sas_host0
> Report manufacturer response:
>   SAS-1.1 format: 0
>   vendor identification: LSI
>   product identification: Virtual SMP Port
>   product revision level:
> smp_read_gpio /dev/bsg/sas_host0
> Read GPIO register response:
>   GPIO_CFG[0]:
>     version: 0
>     GPIO enable: 1
>     cfg register count: 2
>     gp register count: 1
>     supported drive count: 16
> 
> Regards,
> Jack
> 
>> -----Original Message-----
>> From: Jack Wang [mailto:xjtuwjp@gmail.com] 
>> Sent: Tuesday, October 29, 2013 3:49 PM
>> To: Viswas G
>> Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam
>> Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
>>
>> Hi Viswas,
>>
>> As ioctl interface is not welcome for new feature, that's why we removed ioctl interface when pm8001 accepted into mainline.
>>
>> I suggest you use bsg interface for this, see sas_host_smp.c for details.
>>
>> Regards,
>> Jack
>>
>> On 10/22/2013 02:20 PM, Viswas G wrote:
>>>
>>> Signed-off-by: Viswas G <Viswas.G@pmcs.com>
>>> ---
>>>  drivers/scsi/pm8001/pm8001_ctl.c  |  248
>>> ++++++++++++++++++++++++++++++++++++-
>>>  drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
>>>  drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
>>>  drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
>>>  drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
>>>  drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
>>>  6 files changed, 524 insertions(+), 1 deletions(-)
>>>
>>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c
>>> b/drivers/scsi/pm8001/pm8001_ctl.c
>>> index 247cb1c..3c9ca21 100644
>>> --- a/drivers/scsi/pm8001/pm8001_ctl.c
>>> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
>>> @@ -40,7 +40,8 @@
>>>  #include <linux/firmware.h>
>>>  #include <linux/slab.h>
>>>  #include "pm8001_sas.h"
>>> -#include "pm8001_ctl.h"
>>> +
>>> +int pm8001_major = -1;
>>>
>>>  /* scsi host attributes */
>>>
>>> @@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
>>>         NULL,
>>>  };
>>>
>>> +/**
>>> + * pm8001_open - open the configuration file
>>> + * @inode: inode being opened
>>> + * @file: file handle attached
>>> + *
>>> + * Called when the configuration device is opened. Does the needed
>>> + * set up on the handle and then returns
>>> + *
>>> + */
>>> +static int pm8001_open(struct inode *inode, struct file *file) {
>>> +       struct pm8001_hba_info *pm8001_ha;
>>> +       unsigned minor_number = iminor(inode);
>>> +       int err = -ENODEV;
>>> +
>>> +       list_for_each_entry(pm8001_ha, &hba_list, list) {
>>> +               if (pm8001_ha->id == minor_number) {
>>> +                       file->private_data = pm8001_ha;
>>> +                       err = 0;
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       return err;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_close - close the configuration file
>>> + * @inode: inode being opened
>>> + * @file: file handle attached
>>> + *
>>> + * Called when the configuration device is closed. Does the needed
>>> + * set up on the handle and then returns
>>> + *
>>> + */
>>> +static int pm8001_close(struct inode *inode, struct file *file) {
>>> +       return 0;
>>> +}
>>> +
>>> +static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
>>> +                             unsigned long arg) {
>>> +       u32 ret = 0;
>>> +       struct ioctl_info_buffer info_buf;
>>> +       union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
>>> +
>>> +       strcpy(info_buf.information.sz_name, DRV_NAME);
>>> +
>>> +       info_buf.information.usmajor_revision = DRV_MAJOR;
>>> +       info_buf.information.usminor_revision = DRV_MINOR;
>>> +       info_buf.information.usbuild_revision = DRV_BUILD;
>>> +       if (pm8001_ha->chip_id == chip_8001) {
>>> +               info_buf.information.maxoutstandingIO =
>>> +                       main_tbl.pm8001_tbl.max_out_io;
>>> +               info_buf.information.maxdevices =
>>> +                       (main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
>>> +       } else {
>>> +               info_buf.information.maxoutstandingIO =
>>> +                       main_tbl.pm80xx_tbl.max_out_io;
>>> +               info_buf.information.maxdevices =
>>> +                       (main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
>>> +       }
>>> +       info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>>> +
>>> +       if (copy_to_user((void *)arg, (void *)&info_buf,
>>> +                        sizeof(struct ioctl_info_buffer))) {
>>> +               ret = ADPT_IOCTL_CALL_FAILED;
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
>>> +                             unsigned long arg) {
>>> +       struct gpio_buffer buffer;
>>> +       struct pm8001_gpio *payload;
>>> +       struct gpio_ioctl_resp *gpio_resp;
>>> +       DECLARE_COMPLETION_ONSTACK(completion);
>>> +       unsigned long timeout;
>>> +       u32 ret = 0, operation;
>>> +
>>> +       mutex_lock(&pm8001_ha->ioctl_mutex);
>>> +
>>> +       if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
>>> +               sizeof(struct gpio_buffer))) {
>>> +               ret = ADPT_IOCTL_CALL_FAILED;
>>> +               goto exit;
>>> +       }
>>> +
>>> +       pm8001_ha->ioctl_completion = &completion;
>>> +       payload = &buffer.gpio_payload;
>>> +       operation = payload->operation;
>>> +       ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
>>> +       if (ret != 0) {
>>> +               ret = ADPT_IOCTL_CALL_FAILED;
>>> +               goto exit;
>>> +       }
>>> +
>>> +       timeout = (unsigned long)buffer.header.timeout * 1000;
>>> +
>>> +       mod_timer(&pm8001_ha->ioctl_timer, jiffies +
>>> msecs_to_jiffies(timeout));
>>> +
>>> +       wait_for_completion(&completion);
>>> +
>>> +       if (pm8001_ha->ioctl_timer_expired) {
>>> +               ret = ADPT_IOCTL_CALL_TIMEOUT;
>>> +               goto exit;
>>> +       }
>>> +       gpio_resp = &pm8001_ha->gpio_resp;
>>> +
>>> +       buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>>> +
>>> +       if (operation == GPIO_READ) {
>>> +               payload->rd_wr_val              = gpio_resp->gpio_rd_val;
>>> +               payload->input_enable           =
>>> gpio_resp->gpio_in_enabled;
>>> +               payload->pinsetup1              = gpio_resp->gpio_pinsetup1;
>>> +               payload->pinsetup2              = gpio_resp->gpio_pinsetup2;
>>> +               payload->event_level            =
>>> gpio_resp->gpio_evt_change;
>>> +               payload->event_rising_edge      = gpio_resp->gpio_evt_rise;
>>> +               payload->event_falling_edge     = gpio_resp->gpio_evt_fall;
>>> +
>>> +               if (copy_to_user((void *)arg, (void *)&buffer,
>>> +                                sizeof(struct gpio_buffer))) {
>>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>>> +               }
>>> +       } else {
>>> +               if (copy_to_user((void *)arg, (void *)&buffer.header,
>>> +                                sizeof(struct ioctl_header))) {
>>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>>> +               }
>>> +       }
>>> +exit:
>>> +       pm8001_ha->ioctl_timer_expired = 0;
>>> +       mutex_unlock(&pm8001_ha->ioctl_mutex);
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_ioctl - pm8001 configuration request
>>> + * @inode: inode of device
>>> + * @file: file handle
>>> + * @cmd: ioctl command code
>>> + * @arg: argument
>>> + *
>>> + * Handles a configuration ioctl.
>>> + *
>>> + */
>>> +static long pm8001_ioctl(struct file *file,
>>> +                        unsigned int cmd, unsigned long arg) {
>>> +       u32 ret = -EACCES;
>>> +       struct pm8001_hba_info *pm8001_ha;
>>> +       struct ioctl_header header;
>>> +
>>> +       pm8001_ha = file->private_data;
>>> +
>>> +       switch (cmd) {
>>> +
>>> +       case ADPT_IOCTL_GPIO:
>>> +               ret = pm8001_gpio_ioctl(pm8001_ha, arg);
>>> +               break;
>>> +       case ADPT_IOCTL_INFO:
>>> +               ret = pm8001_info_ioctl(pm8001_ha, arg);
>>> +               break;
>>> +       default:
>>> +               ret = ADPT_IOCTL_CALL_INVALID_CODE;
>>> +       }
>>> +       if (ret == 0)
>>> +               return ret;
>>> +       header.return_code = ret;
>>> +       ret = -EACCES;
>>> +       if (copy_to_user((void *)arg, (void *)&header,
>>> +                       sizeof(struct ioctl_header))) {
>>> +               return -EFAULT;
>>> +       }
>>> +       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_poll - pm8001 poll request function
>>> + * @file: file handle
>>> + * @wait: poll table to wait
>>> + *
>>> + * Handles a poll request.
>>> + *
>>> + */
>>> +unsigned int pm8001_poll(struct file *file, poll_table *wait) {
>>> +       struct pm8001_hba_info *pm8001_ha;
>>> +       unsigned int mask = 0;
>>> +
>>> +       pm8001_ha = file->private_data;
>>> +
>>> +       poll_wait(file, &pm8001_ha->pollq, wait);
>>> +
>>> +       if (pm8001_ha->gpio_event_occured == 1) {
>>> +               pm8001_ha->gpio_event_occured = 0;
>>> +               mask |= POLLIN | POLLRDNORM;
>>> +       }
>>> +
>>> +       return mask;
>>> +}
>>> +
>>> +static const struct file_operations pm8001_fops = {
>>> +       .owner          = THIS_MODULE,
>>> +       .open           = pm8001_open,
>>> +       .release        = pm8001_close,
>>> +       .unlocked_ioctl = pm8001_ioctl,
>>> +       .poll           = pm8001_poll,
>>> +};
>>> +
>>> +/**
>>> + * pm8001_setup_chrdev - registers a char device
>>> + *
>>> + * Return value
>>> + *     0 in case of success, otherwise non-zero
>>> + */
>>> +int pm8001_setup_chrdev(void)
>>> +{
>>> +       pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
>>> +       if (pm8001_major < 0) {
>>> +               pr_warn("pm8001 : unable to register \"%s\" device\n",
>>> +                               DRV_NAME);
>>> +               return pm8001_major;
>>> +       }
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_release_chrdev - unregisters per-adapter management 
>>> +interface
>>> + *
>>> + * Return value
>>> + *     none
>>> + */
>>> +void pm8001_release_chrdev(void)
>>> +{
>>> +       if (pm8001_major > -1) {
>>> +               unregister_chrdev(pm8001_major, DRV_NAME);
>>> +               pm8001_major = -1;
>>> +       }
>>> +}
>>> +
>>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.h
>>> b/drivers/scsi/pm8001/pm8001_ctl.h
>>> index d0d43a2..0064dd3 100644
>>> --- a/drivers/scsi/pm8001/pm8001_ctl.h
>>> +++ b/drivers/scsi/pm8001/pm8001_ctl.h
>>> @@ -55,6 +55,61 @@
>>>  #define FAIL_OUT_MEMORY                 0x000c00
>>>  #define FLASH_IN_PROGRESS               0x001000
>>>
>>> +#define ADPT_IOCTL_CALL_SUCCESS                0x00
>>> +#define ADPT_IOCTL_CALL_FAILED         0x01
>>> +#define ADPT_IOCTL_CALL_INVALID_CODE   0x03
>>> +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04
>>> +#define ADPT_IOCTL_CALL_TIMEOUT                0x08
>>> +
>>> +#define GPIO_READ      0
>>> +#define GPIO_WRITE     1
>>> +#define GPIO_PINSETUP  2
>>> +#define GPIO_EVENTSETUP        3
>>> +
>>> +struct ioctl_header {
>>> +       u32 io_controller_num;
>>> +       u32 length;
>>> +       u32 return_code;
>>> +       u32 timeout;
>>> +       u32 direction;
>>> +};
>>> +
>>> +struct ioctl_drv_info {
>>> +       u8      sz_name[64];
>>> +       u16     usmajor_revision;
>>> +       u16     usminor_revision;
>>> +       u16     usbuild_revision;
>>> +       u16     reserved0;
>>> +       u32     maxdevices;
>>> +       u32     maxoutstandingIO;
>>> +       u32     reserved[16];
>>> +};
>>> +
>>> +struct pm8001_gpio {
>>> +       u32 operation;
>>> +       u32 mask;
>>> +       u32 rd_wr_val;
>>> +       u32 input_enable;
>>> +       u32 pinsetup1;
>>> +       u32 pinsetup2;
>>> +       u32 event_level;
>>> +       u32 event_rising_edge;
>>> +       u32 event_falling_edge;
>>> +};
>>> +
>>> +struct ioctl_info_buffer {
>>> +       struct ioctl_header     header;
>>> +       struct ioctl_drv_info   information;
>>> +};
>>> +
>>> +struct gpio_buffer {
>>> +       struct ioctl_header     header;
>>> +       struct pm8001_gpio      gpio_payload;
>>> +};
>>> +
>>> +#define ADPT_IOCTL_INFO                _IOR(0, 0, struct
>>> ioctl_info_buffer *)
>>> +#define ADPT_IOCTL_GPIO                _IOWR(0, 1, struct  gpio_buffer *)
>>> +
>>>  #define IB_OB_READ_TIMES                256
>>>  #define SYSFS_OFFSET                    1024
>>>  #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
>>> diff --git a/drivers/scsi/pm8001/pm8001_init.c
>>> b/drivers/scsi/pm8001/pm8001_init.c
>>> index 662bf13..ba2e659 100644
>>> --- a/drivers/scsi/pm8001/pm8001_init.c
>>> +++ b/drivers/scsi/pm8001/pm8001_init.c
>>> @@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info
>>> *pm8001_ha)
>>>         if (!pm8001_ha)
>>>                 return;
>>>
>>> +       del_timer(&pm8001_ha->ioctl_timer);
>>> +
>>>         for (i = 0; i < USI_MAX_MEMCNT; i++) {
>>>                 if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
>>>                         pci_free_consistent(pm8001_ha->pdev,
>>> @@ -444,6 +446,24 @@ static int pm8001_ioremap(struct pm8001_hba_info
>>> *pm8001_ha)
>>>         return 0;
>>>  }
>>>
>>> +void pm8001_ioctl_timer_callback(unsigned long data) {
>>> +       struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info 
>>> +*)data;
>>> +
>>> +       PM8001_FAIL_DBG(pm8001_ha,
>>> +               pm8001_printk("Timer expired for GPIO response\n"));
>>> +
>>> +       spin_lock(&pm8001_ha->ioctl_lock);
>>> +
>>> +       if (pm8001_ha->ioctl_completion != NULL) {
>>> +               pm8001_ha->ioctl_timer_expired = 1;
>>> +               complete(pm8001_ha->ioctl_completion);
>>> +               pm8001_ha->ioctl_completion = NULL;
>>> +       }
>>> +
>>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>>> +}
>>> +
>>>  /**
>>>   * pm8001_pci_alloc - initialize our ha card structure
>>>   * @pdev: pci device.
>>> @@ -479,6 +499,15 @@ static struct pm8001_hba_info 
>>> *pm8001_pci_alloc(struct pci_dev *pdev,
>>>         else
>>>                 pm8001_ha->iomb_size = IOMB_SIZE_SPC;
>>>
>>> +       mutex_init(&pm8001_ha->ioctl_mutex);
>>> +       pm8001_ha->ioctl_completion = NULL;
>>> +       init_waitqueue_head(&pm8001_ha->pollq);
>>> +       pm8001_ha->gpio_event_occured = 0;
>>> +       spin_lock_init(&pm8001_ha->ioctl_lock);
>>> +       setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
>>> +                    (unsigned long)pm8001_ha);
>>> +
>>> +
>>>  #ifdef PM8001_USE_TASKLET
>>>         /**
>>>         * default tasklet for non msi-x interrupt handler/first msi-x 
>>> @@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
>>>         rc = pci_register_driver(&pm8001_pci_driver);
>>>         if (rc)
>>>                 goto err_tp;
>>> +
>>> +       rc = pm8001_setup_chrdev();
>>> +       if (rc)
>>> +               goto err_ctl;
>>> +
>>>         return 0;
>>>
>>> +err_ctl:
>>> +       pci_unregister_driver(&pm8001_pci_driver);
>>>  err_tp:
>>>         sas_release_transport(pm8001_stt);
>>>  err_wq:
>>> @@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
>>>         pci_unregister_driver(&pm8001_pci_driver);
>>>         sas_release_transport(pm8001_stt);
>>>         destroy_workqueue(pm8001_wq);
>>> +       pm8001_release_chrdev();
>>>  }
>>>
>>>  module_init(pm8001_init);
>>> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
>>> b/drivers/scsi/pm8001/pm8001_sas.h
>>> index 9241c04..9171f0a 100644
>>> --- a/drivers/scsi/pm8001/pm8001_sas.h
>>> +++ b/drivers/scsi/pm8001/pm8001_sas.h
>>> @@ -51,14 +51,21 @@
>>>  #include <linux/pci.h>
>>>  #include <linux/interrupt.h>
>>>  #include <linux/workqueue.h>
>>> +#include <linux/poll.h>
>>> +#include <linux/poll.h>
>>> +#include <linux/timer.h>
>>>  #include <scsi/libsas.h>
>>>  #include <scsi/scsi_tcq.h>
>>>  #include <scsi/sas_ata.h>
>>>  #include <linux/atomic.h>
>>>  #include "pm8001_defs.h"
>>> +#include "pm8001_ctl.h"
>>>
>>>  #define DRV_NAME               "pm80xx"
>>>  #define DRV_VERSION            "0.1.37"
>>> +#define DRV_MAJOR              1
>>> +#define DRV_MINOR              0
>>> +#define DRV_BUILD              15
>>>  #define PM8001_FAIL_LOGGING    0x01 /* Error message logging */
>>>  #define PM8001_INIT_LOGGING    0x02 /* driver init logging */
>>>  #define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
>>> @@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
>>>         u8      *func_specific;
>>>  };
>>>
>>> +struct gpio_ioctl_resp {
>>> +       u32 tag;
>>> +       u32 gpio_rd_val;
>>> +       u32 gpio_in_enabled;
>>> +       u32 gpio_pinsetup1;
>>> +       u32 gpio_pinsetup2;
>>> +       u32 gpio_evt_change;
>>> +       u32 gpio_evt_rise;
>>> +       u32 gpio_evt_fall;
>>> +};
>>> +
>>>  #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF  #define 
>>> MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
>>>  #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
>>> @@ -229,6 +247,8 @@ struct pm8001_dispatch {
>>>         int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
>>>                 u32 state);
>>>         int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
>>> +       int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
>>> +               struct pm8001_gpio *gpio_payload);
>>>  };
>>>
>>>  struct pm8001_chip_info {
>>> @@ -527,6 +547,14 @@ struct pm8001_hba_info {
>>>         u32                     int_vector;
>>>         const struct firmware   *fw_image;
>>>         u8                      outq[PM8001_MAX_MSIX_VEC];
>>> +       struct completion       *ioctl_completion;
>>> +       struct mutex            ioctl_mutex;
>>> +       spinlock_t              ioctl_lock;
>>> +       u32                     gpio_event_occured;
>>> +       u32                     ioctl_timer_expired;
>>> +       struct timer_list       ioctl_timer;
>>> +       struct gpio_ioctl_resp  gpio_resp;
>>> +       wait_queue_head_t       pollq;
>>>  };
>>>
>>>  struct pm8001_work {
>>> @@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev, 
>>> u32, char *buf);
>>>  /* ctl shared API */
>>>  extern struct device_attribute *pm8001_host_attrs[];
>>>
>>> +int pm8001_setup_chrdev(void);
>>> +void pm8001_release_chrdev(void);
>>>  #endif
>>>
>>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c
>>> b/drivers/scsi/pm8001/pm80xx_hwi.c
>>> index 4ebc79b..5423844 100644
>>> --- a/drivers/scsi/pm8001/pm80xx_hwi.c
>>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.c
>>> @@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct 
>>> pm8001_hba_info *pm8001_ha,
>>>         return 0;
>>>  }
>>>
>>> +static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void 
>>> +*piomb) {
>>> +       int ret;
>>> +       struct gpio_ioctl_resp *pgpio_resp;
>>> +       struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 4);
>>> +
>>> +       spin_lock(&pm8001_ha->ioctl_lock);
>>> +       if (pm8001_ha->ioctl_completion != NULL) {
>>> +               ret = del_timer(&pm8001_ha->ioctl_timer);
>>> +               if (ret)
>>> +                       PM8001_MSG_DBG(pm8001_ha,
>>> +                               pm8001_printk("The timer was still in
>>> use.\n"));
>>> +               pm8001_ha->ioctl_timer_expired = 0;
>>> +               pgpio_resp = &pm8001_ha->gpio_resp;
>>> +               pgpio_resp->gpio_rd_val =
>>> le32_to_cpu(ppayload->gpio_rd_val);
>>> +               pgpio_resp->gpio_in_enabled =
>>> +                       le32_to_cpu(ppayload->gpio_in_enabled);
>>> +               pgpio_resp->gpio_pinsetup1 =
>>> +                       le32_to_cpu(ppayload->gpio_pinsetup1);
>>> +               pgpio_resp->gpio_pinsetup2 =
>>> +                       le32_to_cpu(ppayload->gpio_pinsetup2);
>>> +               pgpio_resp->gpio_evt_change =
>>> +                       le32_to_cpu(ppayload->gpio_evt_change);
>>> +               pgpio_resp->gpio_evt_rise =
>>> +                       le32_to_cpu(ppayload->gpio_evt_rise);
>>> +               pgpio_resp->gpio_evt_fall =
>>> +                       le32_to_cpu(ppayload->gpio_evt_fall);
>>> +
>>> +               complete(pm8001_ha->ioctl_completion);
>>> +               pm8001_ha->ioctl_completion = NULL;
>>> +       }
>>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>>> +       return 0;
>>> +}
>>> +
>>> +static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void 
>>> +*piomb) {
>>> +
>>> +       u32 gpio_event = 0;
>>> +       struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
>>> +       gpio_event = le32_to_cpu(ppayload->gpio_event);
>>> +       PM8001_MSG_DBG(pm8001_ha,
>>> +                      pm8001_printk("GPIO event: 0x%X\n", gpio_event));
>>> +       pm8001_ha->gpio_event_occured = 1;
>>> +       wake_up_interruptible(&pm8001_ha->pollq);
>>> +       return 0;
>>> +}
>>> +
>>>  /**
>>>   * process_one_iomb - process one outbound Queue memory block
>>>   * @pm8001_ha: our hba card information @@ -3567,10 +3615,12 @@ 
>>> static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void 
>>> *piomb)
>>>         case OPC_OUB_GPIO_RESPONSE:
>>>                 PM8001_MSG_DBG(pm8001_ha,
>>>                         pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
>>> +               mpi_gpio_resp(pm8001_ha, piomb);
>>>                 break;
>>>         case OPC_OUB_GPIO_EVENT:
>>>                 PM8001_MSG_DBG(pm8001_ha,
>>>                         pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
>>> +               mpi_gpio_event(pm8001_ha, piomb);
>>>                 break;
>>>         case OPC_OUB_GENERAL_EVENT:
>>>                 PM8001_MSG_DBG(pm8001_ha, @@ -4510,6 +4560,61 @@ 
>>> static u32 pm80xx_chip_is_our_interupt(struct
>>> pm8001_hba_info *pm8001_ha)
>>>  }
>>>
>>>  /**
>>> + * pm80xx_chip_gpio_req - support for GPIO operation
>>> + * @pm8001_ha: our hba card information.
>>> + * @ioctl_payload: the payload for the GPIO operation */
>>> +
>>> +int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
>>> +                               struct pm8001_gpio *gpio_payload) {
>>> +       struct gpio_req payload;
>>> +       struct inbound_queue_table *circularQ;
>>> +       int ret;
>>> +       u32 tag;
>>> +       u32 opc = OPC_INB_GPIO;
>>> +
>>> +       if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
>>> +               return ADPT_IOCTL_CALL_INVALID_DEVICE;
>>> +
>>> +       ret = pm8001_tag_alloc(pm8001_ha, &tag);
>>> +       if (ret)
>>> +               return -1;
>>> +
>>> +       memset(&payload, 0, sizeof(payload));
>>> +       circularQ = &pm8001_ha->inbnd_q_tbl[0];
>>> +       payload.tag = cpu_to_le32(tag);
>>> +       switch (gpio_payload->operation) {
>>> +       case GPIO_READ:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
>>> +               break;
>>> +       case GPIO_WRITE:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
>>> +               payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
>>> +               payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
>>> +               break;
>>> +       case GPIO_PINSETUP:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
>>> +               payload.gpio_in_enabled =
>>> +                       cpu_to_le32(gpio_payload->input_enable);
>>> +               payload.gpio_pinsetup1 =
>>> cpu_to_le32(gpio_payload->pinsetup1);
>>> +               payload.gpio_pinsetup2 =
>>> cpu_to_le32(gpio_payload->pinsetup2);
>>> +               break;
>>> +       case GPIO_EVENTSETUP:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
>>> +               payload.gpio_evt_change =
>>> +                       cpu_to_le32(gpio_payload->event_level);
>>> +               payload.gpio_evt_rise =
>>> +                       cpu_to_le32(gpio_payload->event_rising_edge);
>>> +               payload.gpio_evt_fall =
>>> +                       cpu_to_le32(gpio_payload->event_falling_edge);
>>> +               break;
>>> +       }
>>> +       ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>>   * pm8001_chip_isr - PM8001 isr handler.
>>>   * @pm8001_ha: our hba card information.
>>>   * @irq: irq number.
>>> @@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
>>>         .set_nvmd_req           = pm8001_chip_set_nvmd_req,
>>>         .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
>>>         .set_dev_state_req      = pm8001_chip_set_dev_state_req,
>>> +       .gpio_req               = pm80xx_chip_gpio_req,
>>>  };
>>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h
>>> b/drivers/scsi/pm8001/pm80xx_hwi.h
>>> index c86816b..671940a 100644
>>> --- a/drivers/scsi/pm8001/pm80xx_hwi.h
>>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.h
>>> @@ -422,6 +422,55 @@ struct hw_event_ack_req {  } 
>>> __attribute__((packed, aligned(4)));
>>>
>>>  /*
>>> + * brief the data structure of GPIO Commannd
>>> + * use to control MPI GPIOs (64 bytes)  */ struct gpio_req {
>>> +       __le32  tag;
>>> +       __le32  eobid_ge_gs_gr_gw;
>>> +       __le32  gpio_wr_msk;
>>> +       __le32  gpio_wr_val;
>>> +       __le32  gpio_in_enabled;
>>> +       __le32  gpio_pinsetup1;
>>> +       __le32  gpio_pinsetup2;
>>> +       __le32  gpio_evt_change;
>>> +       __le32  gpio_evt_rise;
>>> +       __le32  gpio_evt_fall;
>>> +       u32     reserved[5];
>>> +} __attribute__((packed, aligned(4)));
>>> +
>>> +#define GPIO_GW_BIT 0x1
>>> +#define GPIO_GR_BIT 0x2
>>> +#define GPIO_GS_BIT 0x4
>>> +#define GPIO_GE_BIT 0x8
>>> +
>>> +/*
>>> + * brief the data structure of GPIO Response
>>> + * indicates the completion of GPIO command (64 bytes)  */ struct 
>>> +gpio_resp {
>>> +       __le32  tag;
>>> +       u32     reserved[2];
>>> +       __le32  gpio_rd_val;
>>> +       __le32  gpio_in_enabled;
>>> +       __le32  gpio_pinsetup1;
>>> +       __le32  gpio_pinsetup2;
>>> +       __le32  gpio_evt_change;
>>> +       __le32  gpio_evt_rise;
>>> +       __le32  gpio_evt_fall;
>>> +       u32     reserved1[5];
>>> +} __attribute__((packed, aligned(4)));
>>> +
>>> +/*
>>> + * brief the data structure of GPIO Event
>>> + * indicates the generation of GPIO event (64 bytes)  */ struct 
>>> +gpio_event {
>>> +       __le32  gpio_event;
>>> +       u32     reserved[14];
>>> +} __attribute__((packed, aligned(4)));
>>> +
>>> +/*
>>>   * brief the data structure of PHY_START Response Command
>>>   * indicates the completion of PHY_START command (64 bytes)
>>>   */
>>> --
>>> 1.7.1
>>>
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-11-11  8:58         ` Jack Wang
@ 2013-11-15  6:14           ` Viswas G
  2013-11-16 18:25           ` James Bottomley
  1 sibling, 0 replies; 8+ messages in thread
From: Viswas G @ 2013-11-15  6:14 UTC (permalink / raw)
  To: Jack Wang, James Bottomley
  Cc: linux-scsi, Suresh Thiagarajan, Vasanthalakshmi Tharmarajan

Hi James,

Can you please share your opinion on this ?

Regards
Viswas G


-----Original Message-----
From: Jack Wang [mailto:xjtuwjp@gmail.com] 
Sent: Monday, November 11, 2013 2:28 PM
To: Viswas G; James Bottomley
Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith Ganigarakoppal; Anand Kumar Santhanam; Suresh Thiagarajan
Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers

Hi James,

About this gpio feature, do you think it's OK to implement with IOCTL or we can use exist bsg interface in libsas and add another function call?

Regards,
Jack

On 11/11/2013 06:57 AM, Viswas G wrote:
> Hi Jack,
> 
> The GPIO feature we implemented here is for controlling and configuring the GPIO pins present in the HBA and it is not related to the GPIO registers present in the SGPIO. Following is one of the GPIO operations we do from application.
> 
> When application wants to know the insertion/removal of a SAS cable in any of the port, it configures the GPIO for corresponding port to generate event for SAS cable insertion or removal using the IOCTL to the HBA driver and waits by calling poll function. When driver receives the GPIO event for the SAS cable insertion or removal then it intimates the application.
> 
> We are using IOCTL instead of sysfs interface since we have to pass structures between user space and kernel space. Again, in the kernel space, we have to parse user buffer from application and convert it to the corresponding data structure. We wanted to avoid the parsing complexity by using ioctl interface.
> 
> Regards,
> Viswas G
> 
> 
> -----Original Message-----
> From: Jack Wang [mailto:xjtuwjp@gmail.com]
> Sent: Monday, November 04, 2013 4:00 PM
> To: Viswas G
> Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith 
> Ganigarakoppal; Anand Kumar Santhanam; Suresh Thiagarajan
> Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for 
> motherboard controllers
> 
> On 11/04/2013 11:13 AM, Viswas G wrote:
>> Hi Jack,
>>
>> We wanted to control the GPIO in the HBA only. Bsg interface gets created only for enclosure or expander. 
>>
>> For our HBA, bsg interface will not be created since it does not have an enclosure target inside. That's why we wanted to use IOCTL. Please advise.
>>
>> Best Regards,
>> Viswas G
>>
> Hi Viswas,
> 
> No, bsg interface also support HBA.
> Here is two example output from LSI mpt2sas:
> 
> smp_rep_manufacturer /dev/bsg/sas_host0 Report manufacturer response:
>   SAS-1.1 format: 0
>   vendor identification: LSI
>   product identification: Virtual SMP Port
>   product revision level:
> smp_read_gpio /dev/bsg/sas_host0
> Read GPIO register response:
>   GPIO_CFG[0]:
>     version: 0
>     GPIO enable: 1
>     cfg register count: 2
>     gp register count: 1
>     supported drive count: 16
> 
> Regards,
> Jack
> 
>> -----Original Message-----
>> From: Jack Wang [mailto:xjtuwjp@gmail.com]
>> Sent: Tuesday, October 29, 2013 3:49 PM
>> To: Viswas G
>> Cc: linux-scsi@vger.kernel.org; Sangeetha Gnanasekaran; Nikith 
>> Ganigarakoppal; Anand Kumar Santhanam
>> Subject: Re: [PATCH 11/11] pm80xx : gpio feature support for 
>> motherboard controllers
>>
>> Hi Viswas,
>>
>> As ioctl interface is not welcome for new feature, that's why we removed ioctl interface when pm8001 accepted into mainline.
>>
>> I suggest you use bsg interface for this, see sas_host_smp.c for details.
>>
>> Regards,
>> Jack
>>
>> On 10/22/2013 02:20 PM, Viswas G wrote:
>>>
>>> Signed-off-by: Viswas G <Viswas.G@pmcs.com>
>>> ---
>>>  drivers/scsi/pm8001/pm8001_ctl.c  |  248
>>> ++++++++++++++++++++++++++++++++++++-
>>>  drivers/scsi/pm8001/pm8001_ctl.h  |   55 ++++++++
>>>  drivers/scsi/pm8001/pm8001_init.c |   37 ++++++
>>>  drivers/scsi/pm8001/pm8001_sas.h  |   30 +++++
>>>  drivers/scsi/pm8001/pm80xx_hwi.c  |  106 ++++++++++++++++
>>>  drivers/scsi/pm8001/pm80xx_hwi.h  |   49 +++++++
>>>  6 files changed, 524 insertions(+), 1 deletions(-)
>>>
>>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.c
>>> b/drivers/scsi/pm8001/pm8001_ctl.c
>>> index 247cb1c..3c9ca21 100644
>>> --- a/drivers/scsi/pm8001/pm8001_ctl.c
>>> +++ b/drivers/scsi/pm8001/pm8001_ctl.c
>>> @@ -40,7 +40,8 @@
>>>  #include <linux/firmware.h>
>>>  #include <linux/slab.h>
>>>  #include "pm8001_sas.h"
>>> -#include "pm8001_ctl.h"
>>> +
>>> +int pm8001_major = -1;
>>>
>>>  /* scsi host attributes */
>>>
>>> @@ -759,3 +760,248 @@ struct device_attribute *pm8001_host_attrs[] = {
>>>         NULL,
>>>  };
>>>
>>> +/**
>>> + * pm8001_open - open the configuration file
>>> + * @inode: inode being opened
>>> + * @file: file handle attached
>>> + *
>>> + * Called when the configuration device is opened. Does the needed
>>> + * set up on the handle and then returns
>>> + *
>>> + */
>>> +static int pm8001_open(struct inode *inode, struct file *file) {
>>> +       struct pm8001_hba_info *pm8001_ha;
>>> +       unsigned minor_number = iminor(inode);
>>> +       int err = -ENODEV;
>>> +
>>> +       list_for_each_entry(pm8001_ha, &hba_list, list) {
>>> +               if (pm8001_ha->id == minor_number) {
>>> +                       file->private_data = pm8001_ha;
>>> +                       err = 0;
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       return err;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_close - close the configuration file
>>> + * @inode: inode being opened
>>> + * @file: file handle attached
>>> + *
>>> + * Called when the configuration device is closed. Does the needed
>>> + * set up on the handle and then returns
>>> + *
>>> + */
>>> +static int pm8001_close(struct inode *inode, struct file *file) {
>>> +       return 0;
>>> +}
>>> +
>>> +static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha,
>>> +                             unsigned long arg) {
>>> +       u32 ret = 0;
>>> +       struct ioctl_info_buffer info_buf;
>>> +       union main_cfg_table main_tbl =  pm8001_ha->main_cfg_tbl;
>>> +
>>> +       strcpy(info_buf.information.sz_name, DRV_NAME);
>>> +
>>> +       info_buf.information.usmajor_revision = DRV_MAJOR;
>>> +       info_buf.information.usminor_revision = DRV_MINOR;
>>> +       info_buf.information.usbuild_revision = DRV_BUILD;
>>> +       if (pm8001_ha->chip_id == chip_8001) {
>>> +               info_buf.information.maxoutstandingIO =
>>> +                       main_tbl.pm8001_tbl.max_out_io;
>>> +               info_buf.information.maxdevices =
>>> +                       (main_tbl.pm8001_tbl.max_sgl >> 16) & 0xFFFF;
>>> +       } else {
>>> +               info_buf.information.maxoutstandingIO =
>>> +                       main_tbl.pm80xx_tbl.max_out_io;
>>> +               info_buf.information.maxdevices =
>>> +                       (main_tbl.pm80xx_tbl.max_sgl >> 16) & 0xFFFF;
>>> +       }
>>> +       info_buf.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>>> +
>>> +       if (copy_to_user((void *)arg, (void *)&info_buf,
>>> +                        sizeof(struct ioctl_info_buffer))) {
>>> +               ret = ADPT_IOCTL_CALL_FAILED;
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
>>> +                             unsigned long arg) {
>>> +       struct gpio_buffer buffer;
>>> +       struct pm8001_gpio *payload;
>>> +       struct gpio_ioctl_resp *gpio_resp;
>>> +       DECLARE_COMPLETION_ONSTACK(completion);
>>> +       unsigned long timeout;
>>> +       u32 ret = 0, operation;
>>> +
>>> +       mutex_lock(&pm8001_ha->ioctl_mutex);
>>> +
>>> +       if (copy_from_user(&buffer, (struct gpio_buffer *)arg,
>>> +               sizeof(struct gpio_buffer))) {
>>> +               ret = ADPT_IOCTL_CALL_FAILED;
>>> +               goto exit;
>>> +       }
>>> +
>>> +       pm8001_ha->ioctl_completion = &completion;
>>> +       payload = &buffer.gpio_payload;
>>> +       operation = payload->operation;
>>> +       ret = PM8001_CHIP_DISP->gpio_req(pm8001_ha, payload);
>>> +       if (ret != 0) {
>>> +               ret = ADPT_IOCTL_CALL_FAILED;
>>> +               goto exit;
>>> +       }
>>> +
>>> +       timeout = (unsigned long)buffer.header.timeout * 1000;
>>> +
>>> +       mod_timer(&pm8001_ha->ioctl_timer, jiffies +
>>> msecs_to_jiffies(timeout));
>>> +
>>> +       wait_for_completion(&completion);
>>> +
>>> +       if (pm8001_ha->ioctl_timer_expired) {
>>> +               ret = ADPT_IOCTL_CALL_TIMEOUT;
>>> +               goto exit;
>>> +       }
>>> +       gpio_resp = &pm8001_ha->gpio_resp;
>>> +
>>> +       buffer.header.return_code = ADPT_IOCTL_CALL_SUCCESS;
>>> +
>>> +       if (operation == GPIO_READ) {
>>> +               payload->rd_wr_val              = gpio_resp->gpio_rd_val;
>>> +               payload->input_enable           =
>>> gpio_resp->gpio_in_enabled;
>>> +               payload->pinsetup1              = gpio_resp->gpio_pinsetup1;
>>> +               payload->pinsetup2              = gpio_resp->gpio_pinsetup2;
>>> +               payload->event_level            =
>>> gpio_resp->gpio_evt_change;
>>> +               payload->event_rising_edge      = gpio_resp->gpio_evt_rise;
>>> +               payload->event_falling_edge     = gpio_resp->gpio_evt_fall;
>>> +
>>> +               if (copy_to_user((void *)arg, (void *)&buffer,
>>> +                                sizeof(struct gpio_buffer))) {
>>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>>> +               }
>>> +       } else {
>>> +               if (copy_to_user((void *)arg, (void *)&buffer.header,
>>> +                                sizeof(struct ioctl_header))) {
>>> +                       ret = ADPT_IOCTL_CALL_FAILED;
>>> +               }
>>> +       }
>>> +exit:
>>> +       pm8001_ha->ioctl_timer_expired = 0;
>>> +       mutex_unlock(&pm8001_ha->ioctl_mutex);
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_ioctl - pm8001 configuration request
>>> + * @inode: inode of device
>>> + * @file: file handle
>>> + * @cmd: ioctl command code
>>> + * @arg: argument
>>> + *
>>> + * Handles a configuration ioctl.
>>> + *
>>> + */
>>> +static long pm8001_ioctl(struct file *file,
>>> +                        unsigned int cmd, unsigned long arg) {
>>> +       u32 ret = -EACCES;
>>> +       struct pm8001_hba_info *pm8001_ha;
>>> +       struct ioctl_header header;
>>> +
>>> +       pm8001_ha = file->private_data;
>>> +
>>> +       switch (cmd) {
>>> +
>>> +       case ADPT_IOCTL_GPIO:
>>> +               ret = pm8001_gpio_ioctl(pm8001_ha, arg);
>>> +               break;
>>> +       case ADPT_IOCTL_INFO:
>>> +               ret = pm8001_info_ioctl(pm8001_ha, arg);
>>> +               break;
>>> +       default:
>>> +               ret = ADPT_IOCTL_CALL_INVALID_CODE;
>>> +       }
>>> +       if (ret == 0)
>>> +               return ret;
>>> +       header.return_code = ret;
>>> +       ret = -EACCES;
>>> +       if (copy_to_user((void *)arg, (void *)&header,
>>> +                       sizeof(struct ioctl_header))) {
>>> +               return -EFAULT;
>>> +       }
>>> +       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("IOCTL failed\n"));
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_poll - pm8001 poll request function
>>> + * @file: file handle
>>> + * @wait: poll table to wait
>>> + *
>>> + * Handles a poll request.
>>> + *
>>> + */
>>> +unsigned int pm8001_poll(struct file *file, poll_table *wait) {
>>> +       struct pm8001_hba_info *pm8001_ha;
>>> +       unsigned int mask = 0;
>>> +
>>> +       pm8001_ha = file->private_data;
>>> +
>>> +       poll_wait(file, &pm8001_ha->pollq, wait);
>>> +
>>> +       if (pm8001_ha->gpio_event_occured == 1) {
>>> +               pm8001_ha->gpio_event_occured = 0;
>>> +               mask |= POLLIN | POLLRDNORM;
>>> +       }
>>> +
>>> +       return mask;
>>> +}
>>> +
>>> +static const struct file_operations pm8001_fops = {
>>> +       .owner          = THIS_MODULE,
>>> +       .open           = pm8001_open,
>>> +       .release        = pm8001_close,
>>> +       .unlocked_ioctl = pm8001_ioctl,
>>> +       .poll           = pm8001_poll,
>>> +};
>>> +
>>> +/**
>>> + * pm8001_setup_chrdev - registers a char device
>>> + *
>>> + * Return value
>>> + *     0 in case of success, otherwise non-zero
>>> + */
>>> +int pm8001_setup_chrdev(void)
>>> +{
>>> +       pm8001_major = register_chrdev(0, DRV_NAME, &pm8001_fops);
>>> +       if (pm8001_major < 0) {
>>> +               pr_warn("pm8001 : unable to register \"%s\" device\n",
>>> +                               DRV_NAME);
>>> +               return pm8001_major;
>>> +       }
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * pm8001_release_chrdev - unregisters per-adapter management 
>>> +interface
>>> + *
>>> + * Return value
>>> + *     none
>>> + */
>>> +void pm8001_release_chrdev(void)
>>> +{
>>> +       if (pm8001_major > -1) {
>>> +               unregister_chrdev(pm8001_major, DRV_NAME);
>>> +               pm8001_major = -1;
>>> +       }
>>> +}
>>> +
>>> diff --git a/drivers/scsi/pm8001/pm8001_ctl.h
>>> b/drivers/scsi/pm8001/pm8001_ctl.h
>>> index d0d43a2..0064dd3 100644
>>> --- a/drivers/scsi/pm8001/pm8001_ctl.h
>>> +++ b/drivers/scsi/pm8001/pm8001_ctl.h
>>> @@ -55,6 +55,61 @@
>>>  #define FAIL_OUT_MEMORY                 0x000c00
>>>  #define FLASH_IN_PROGRESS               0x001000
>>>
>>> +#define ADPT_IOCTL_CALL_SUCCESS                0x00
>>> +#define ADPT_IOCTL_CALL_FAILED         0x01
>>> +#define ADPT_IOCTL_CALL_INVALID_CODE   0x03
>>> +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04
>>> +#define ADPT_IOCTL_CALL_TIMEOUT                0x08
>>> +
>>> +#define GPIO_READ      0
>>> +#define GPIO_WRITE     1
>>> +#define GPIO_PINSETUP  2
>>> +#define GPIO_EVENTSETUP        3
>>> +
>>> +struct ioctl_header {
>>> +       u32 io_controller_num;
>>> +       u32 length;
>>> +       u32 return_code;
>>> +       u32 timeout;
>>> +       u32 direction;
>>> +};
>>> +
>>> +struct ioctl_drv_info {
>>> +       u8      sz_name[64];
>>> +       u16     usmajor_revision;
>>> +       u16     usminor_revision;
>>> +       u16     usbuild_revision;
>>> +       u16     reserved0;
>>> +       u32     maxdevices;
>>> +       u32     maxoutstandingIO;
>>> +       u32     reserved[16];
>>> +};
>>> +
>>> +struct pm8001_gpio {
>>> +       u32 operation;
>>> +       u32 mask;
>>> +       u32 rd_wr_val;
>>> +       u32 input_enable;
>>> +       u32 pinsetup1;
>>> +       u32 pinsetup2;
>>> +       u32 event_level;
>>> +       u32 event_rising_edge;
>>> +       u32 event_falling_edge;
>>> +};
>>> +
>>> +struct ioctl_info_buffer {
>>> +       struct ioctl_header     header;
>>> +       struct ioctl_drv_info   information;
>>> +};
>>> +
>>> +struct gpio_buffer {
>>> +       struct ioctl_header     header;
>>> +       struct pm8001_gpio      gpio_payload;
>>> +};
>>> +
>>> +#define ADPT_IOCTL_INFO                _IOR(0, 0, struct
>>> ioctl_info_buffer *)
>>> +#define ADPT_IOCTL_GPIO                _IOWR(0, 1, struct  gpio_buffer *)
>>> +
>>>  #define IB_OB_READ_TIMES                256
>>>  #define SYSFS_OFFSET                    1024
>>>  #define PM80XX_IB_OB_QUEUE_SIZE         (32 * 1024)
>>> diff --git a/drivers/scsi/pm8001/pm8001_init.c
>>> b/drivers/scsi/pm8001/pm8001_init.c
>>> index 662bf13..ba2e659 100644
>>> --- a/drivers/scsi/pm8001/pm8001_init.c
>>> +++ b/drivers/scsi/pm8001/pm8001_init.c
>>> @@ -148,6 +148,8 @@ static void pm8001_free(struct pm8001_hba_info
>>> *pm8001_ha)
>>>         if (!pm8001_ha)
>>>                 return;
>>>
>>> +       del_timer(&pm8001_ha->ioctl_timer);
>>> +
>>>         for (i = 0; i < USI_MAX_MEMCNT; i++) {
>>>                 if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
>>>                         pci_free_consistent(pm8001_ha->pdev,
>>> @@ -444,6 +446,24 @@ static int pm8001_ioremap(struct 
>>> pm8001_hba_info
>>> *pm8001_ha)
>>>         return 0;
>>>  }
>>>
>>> +void pm8001_ioctl_timer_callback(unsigned long data) {
>>> +       struct pm8001_hba_info *pm8001_ha = (struct pm8001_hba_info 
>>> +*)data;
>>> +
>>> +       PM8001_FAIL_DBG(pm8001_ha,
>>> +               pm8001_printk("Timer expired for GPIO response\n"));
>>> +
>>> +       spin_lock(&pm8001_ha->ioctl_lock);
>>> +
>>> +       if (pm8001_ha->ioctl_completion != NULL) {
>>> +               pm8001_ha->ioctl_timer_expired = 1;
>>> +               complete(pm8001_ha->ioctl_completion);
>>> +               pm8001_ha->ioctl_completion = NULL;
>>> +       }
>>> +
>>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>>> +}
>>> +
>>>  /**
>>>   * pm8001_pci_alloc - initialize our ha card structure
>>>   * @pdev: pci device.
>>> @@ -479,6 +499,15 @@ static struct pm8001_hba_info 
>>> *pm8001_pci_alloc(struct pci_dev *pdev,
>>>         else
>>>                 pm8001_ha->iomb_size = IOMB_SIZE_SPC;
>>>
>>> +       mutex_init(&pm8001_ha->ioctl_mutex);
>>> +       pm8001_ha->ioctl_completion = NULL;
>>> +       init_waitqueue_head(&pm8001_ha->pollq);
>>> +       pm8001_ha->gpio_event_occured = 0;
>>> +       spin_lock_init(&pm8001_ha->ioctl_lock);
>>> +       setup_timer(&pm8001_ha->ioctl_timer, pm8001_ioctl_timer_callback,
>>> +                    (unsigned long)pm8001_ha);
>>> +
>>> +
>>>  #ifdef PM8001_USE_TASKLET
>>>         /**
>>>         * default tasklet for non msi-x interrupt handler/first 
>>> msi-x @@ -1147,8 +1176,15 @@ static int __init pm8001_init(void)
>>>         rc = pci_register_driver(&pm8001_pci_driver);
>>>         if (rc)
>>>                 goto err_tp;
>>> +
>>> +       rc = pm8001_setup_chrdev();
>>> +       if (rc)
>>> +               goto err_ctl;
>>> +
>>>         return 0;
>>>
>>> +err_ctl:
>>> +       pci_unregister_driver(&pm8001_pci_driver);
>>>  err_tp:
>>>         sas_release_transport(pm8001_stt);
>>>  err_wq:
>>> @@ -1162,6 +1198,7 @@ static void __exit pm8001_exit(void)
>>>         pci_unregister_driver(&pm8001_pci_driver);
>>>         sas_release_transport(pm8001_stt);
>>>         destroy_workqueue(pm8001_wq);
>>> +       pm8001_release_chrdev();
>>>  }
>>>
>>>  module_init(pm8001_init);
>>> diff --git a/drivers/scsi/pm8001/pm8001_sas.h
>>> b/drivers/scsi/pm8001/pm8001_sas.h
>>> index 9241c04..9171f0a 100644
>>> --- a/drivers/scsi/pm8001/pm8001_sas.h
>>> +++ b/drivers/scsi/pm8001/pm8001_sas.h
>>> @@ -51,14 +51,21 @@
>>>  #include <linux/pci.h>
>>>  #include <linux/interrupt.h>
>>>  #include <linux/workqueue.h>
>>> +#include <linux/poll.h>
>>> +#include <linux/poll.h>
>>> +#include <linux/timer.h>
>>>  #include <scsi/libsas.h>
>>>  #include <scsi/scsi_tcq.h>
>>>  #include <scsi/sas_ata.h>
>>>  #include <linux/atomic.h>
>>>  #include "pm8001_defs.h"
>>> +#include "pm8001_ctl.h"
>>>
>>>  #define DRV_NAME               "pm80xx"
>>>  #define DRV_VERSION            "0.1.37"
>>> +#define DRV_MAJOR              1
>>> +#define DRV_MINOR              0
>>> +#define DRV_BUILD              15
>>>  #define PM8001_FAIL_LOGGING    0x01 /* Error message logging */
>>>  #define PM8001_INIT_LOGGING    0x02 /* driver init logging */
>>>  #define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
>>> @@ -132,6 +139,17 @@ struct pm8001_ioctl_payload {
>>>         u8      *func_specific;
>>>  };
>>>
>>> +struct gpio_ioctl_resp {
>>> +       u32 tag;
>>> +       u32 gpio_rd_val;
>>> +       u32 gpio_in_enabled;
>>> +       u32 gpio_pinsetup1;
>>> +       u32 gpio_pinsetup2;
>>> +       u32 gpio_evt_change;
>>> +       u32 gpio_evt_rise;
>>> +       u32 gpio_evt_fall;
>>> +};
>>> +
>>>  #define MPI_FATAL_ERROR_TABLE_OFFSET_MASK 0xFFFFFF  #define
>>> MPI_FATAL_ERROR_TABLE_SIZE(value) ((0xFF000000 & value) >> SHIFT24)
>>>  #define MPI_FATAL_EDUMP_TABLE_LO_OFFSET            0x00     /* HNFBUFL */
>>> @@ -229,6 +247,8 @@ struct pm8001_dispatch {
>>>         int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
>>>                 u32 state);
>>>         int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
>>> +       int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
>>> +               struct pm8001_gpio *gpio_payload);
>>>  };
>>>
>>>  struct pm8001_chip_info {
>>> @@ -527,6 +547,14 @@ struct pm8001_hba_info {
>>>         u32                     int_vector;
>>>         const struct firmware   *fw_image;
>>>         u8                      outq[PM8001_MAX_MSIX_VEC];
>>> +       struct completion       *ioctl_completion;
>>> +       struct mutex            ioctl_mutex;
>>> +       spinlock_t              ioctl_lock;
>>> +       u32                     gpio_event_occured;
>>> +       u32                     ioctl_timer_expired;
>>> +       struct timer_list       ioctl_timer;
>>> +       struct gpio_ioctl_resp  gpio_resp;
>>> +       wait_queue_head_t       pollq;
>>>  };
>>>
>>>  struct pm8001_work {
>>> @@ -705,5 +733,7 @@ ssize_t pm8001_get_gsm_dump(struct device *cdev, 
>>> u32, char *buf);
>>>  /* ctl shared API */
>>>  extern struct device_attribute *pm8001_host_attrs[];
>>>
>>> +int pm8001_setup_chrdev(void);
>>> +void pm8001_release_chrdev(void);
>>>  #endif
>>>
>>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c
>>> b/drivers/scsi/pm8001/pm80xx_hwi.c
>>> index 4ebc79b..5423844 100644
>>> --- a/drivers/scsi/pm8001/pm80xx_hwi.c
>>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.c
>>> @@ -3481,6 +3481,54 @@ static int ssp_coalesced_comp_resp(struct 
>>> pm8001_hba_info *pm8001_ha,
>>>         return 0;
>>>  }
>>>
>>> +static int mpi_gpio_resp(struct pm8001_hba_info *pm8001_ha, void
>>> +*piomb) {
>>> +       int ret;
>>> +       struct gpio_ioctl_resp *pgpio_resp;
>>> +       struct gpio_resp *ppayload = (struct gpio_resp *)(piomb + 
>>> +4);
>>> +
>>> +       spin_lock(&pm8001_ha->ioctl_lock);
>>> +       if (pm8001_ha->ioctl_completion != NULL) {
>>> +               ret = del_timer(&pm8001_ha->ioctl_timer);
>>> +               if (ret)
>>> +                       PM8001_MSG_DBG(pm8001_ha,
>>> +                               pm8001_printk("The timer was still 
>>> + in
>>> use.\n"));
>>> +               pm8001_ha->ioctl_timer_expired = 0;
>>> +               pgpio_resp = &pm8001_ha->gpio_resp;
>>> +               pgpio_resp->gpio_rd_val =
>>> le32_to_cpu(ppayload->gpio_rd_val);
>>> +               pgpio_resp->gpio_in_enabled =
>>> +                       le32_to_cpu(ppayload->gpio_in_enabled);
>>> +               pgpio_resp->gpio_pinsetup1 =
>>> +                       le32_to_cpu(ppayload->gpio_pinsetup1);
>>> +               pgpio_resp->gpio_pinsetup2 =
>>> +                       le32_to_cpu(ppayload->gpio_pinsetup2);
>>> +               pgpio_resp->gpio_evt_change =
>>> +                       le32_to_cpu(ppayload->gpio_evt_change);
>>> +               pgpio_resp->gpio_evt_rise =
>>> +                       le32_to_cpu(ppayload->gpio_evt_rise);
>>> +               pgpio_resp->gpio_evt_fall =
>>> +                       le32_to_cpu(ppayload->gpio_evt_fall);
>>> +
>>> +               complete(pm8001_ha->ioctl_completion);
>>> +               pm8001_ha->ioctl_completion = NULL;
>>> +       }
>>> +       spin_unlock(&pm8001_ha->ioctl_lock);
>>> +       return 0;
>>> +}
>>> +
>>> +static int mpi_gpio_event(struct pm8001_hba_info *pm8001_ha, void
>>> +*piomb) {
>>> +
>>> +       u32 gpio_event = 0;
>>> +       struct gpio_event *ppayload = (struct gpio_event *)(piomb + 4);
>>> +       gpio_event = le32_to_cpu(ppayload->gpio_event);
>>> +       PM8001_MSG_DBG(pm8001_ha,
>>> +                      pm8001_printk("GPIO event: 0x%X\n", gpio_event));
>>> +       pm8001_ha->gpio_event_occured = 1;
>>> +       wake_up_interruptible(&pm8001_ha->pollq);
>>> +       return 0;
>>> +}
>>> +
>>>  /**
>>>   * process_one_iomb - process one outbound Queue memory block
>>>   * @pm8001_ha: our hba card information @@ -3567,10 +3615,12 @@ 
>>> static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void
>>> *piomb)
>>>         case OPC_OUB_GPIO_RESPONSE:
>>>                 PM8001_MSG_DBG(pm8001_ha,
>>>                         pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
>>> +               mpi_gpio_resp(pm8001_ha, piomb);
>>>                 break;
>>>         case OPC_OUB_GPIO_EVENT:
>>>                 PM8001_MSG_DBG(pm8001_ha,
>>>                         pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
>>> +               mpi_gpio_event(pm8001_ha, piomb);
>>>                 break;
>>>         case OPC_OUB_GENERAL_EVENT:
>>>                 PM8001_MSG_DBG(pm8001_ha, @@ -4510,6 +4560,61 @@ 
>>> static u32 pm80xx_chip_is_our_interupt(struct
>>> pm8001_hba_info *pm8001_ha)
>>>  }
>>>
>>>  /**
>>> + * pm80xx_chip_gpio_req - support for GPIO operation
>>> + * @pm8001_ha: our hba card information.
>>> + * @ioctl_payload: the payload for the GPIO operation */
>>> +
>>> +int pm80xx_chip_gpio_req(struct pm8001_hba_info *pm8001_ha,
>>> +                               struct pm8001_gpio *gpio_payload) {
>>> +       struct gpio_req payload;
>>> +       struct inbound_queue_table *circularQ;
>>> +       int ret;
>>> +       u32 tag;
>>> +       u32 opc = OPC_INB_GPIO;
>>> +
>>> +       if (pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ADAPTEC2)
>>> +               return ADPT_IOCTL_CALL_INVALID_DEVICE;
>>> +
>>> +       ret = pm8001_tag_alloc(pm8001_ha, &tag);
>>> +       if (ret)
>>> +               return -1;
>>> +
>>> +       memset(&payload, 0, sizeof(payload));
>>> +       circularQ = &pm8001_ha->inbnd_q_tbl[0];
>>> +       payload.tag = cpu_to_le32(tag);
>>> +       switch (gpio_payload->operation) {
>>> +       case GPIO_READ:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GR_BIT);
>>> +               break;
>>> +       case GPIO_WRITE:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GW_BIT);
>>> +               payload.gpio_wr_msk = cpu_to_le32(gpio_payload->mask);
>>> +               payload.gpio_wr_val = cpu_to_le32(gpio_payload->rd_wr_val);
>>> +               break;
>>> +       case GPIO_PINSETUP:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GS_BIT);
>>> +               payload.gpio_in_enabled =
>>> +                       cpu_to_le32(gpio_payload->input_enable);
>>> +               payload.gpio_pinsetup1 =
>>> cpu_to_le32(gpio_payload->pinsetup1);
>>> +               payload.gpio_pinsetup2 =
>>> cpu_to_le32(gpio_payload->pinsetup2);
>>> +               break;
>>> +       case GPIO_EVENTSETUP:
>>> +               payload.eobid_ge_gs_gr_gw = cpu_to_le32(GPIO_GE_BIT);
>>> +               payload.gpio_evt_change =
>>> +                       cpu_to_le32(gpio_payload->event_level);
>>> +               payload.gpio_evt_rise =
>>> +                       cpu_to_le32(gpio_payload->event_rising_edge);
>>> +               payload.gpio_evt_fall =
>>> +                       cpu_to_le32(gpio_payload->event_falling_edge);
>>> +               break;
>>> +       }
>>> +       ret = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>>   * pm8001_chip_isr - PM8001 isr handler.
>>>   * @pm8001_ha: our hba card information.
>>>   * @irq: irq number.
>>> @@ -4589,4 +4694,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
>>>         .set_nvmd_req           = pm8001_chip_set_nvmd_req,
>>>         .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
>>>         .set_dev_state_req      = pm8001_chip_set_dev_state_req,
>>> +       .gpio_req               = pm80xx_chip_gpio_req,
>>>  };
>>> diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h
>>> b/drivers/scsi/pm8001/pm80xx_hwi.h
>>> index c86816b..671940a 100644
>>> --- a/drivers/scsi/pm8001/pm80xx_hwi.h
>>> +++ b/drivers/scsi/pm8001/pm80xx_hwi.h
>>> @@ -422,6 +422,55 @@ struct hw_event_ack_req {  } 
>>> __attribute__((packed, aligned(4)));
>>>
>>>  /*
>>> + * brief the data structure of GPIO Commannd
>>> + * use to control MPI GPIOs (64 bytes)  */ struct gpio_req {
>>> +       __le32  tag;
>>> +       __le32  eobid_ge_gs_gr_gw;
>>> +       __le32  gpio_wr_msk;
>>> +       __le32  gpio_wr_val;
>>> +       __le32  gpio_in_enabled;
>>> +       __le32  gpio_pinsetup1;
>>> +       __le32  gpio_pinsetup2;
>>> +       __le32  gpio_evt_change;
>>> +       __le32  gpio_evt_rise;
>>> +       __le32  gpio_evt_fall;
>>> +       u32     reserved[5];
>>> +} __attribute__((packed, aligned(4)));
>>> +
>>> +#define GPIO_GW_BIT 0x1
>>> +#define GPIO_GR_BIT 0x2
>>> +#define GPIO_GS_BIT 0x4
>>> +#define GPIO_GE_BIT 0x8
>>> +
>>> +/*
>>> + * brief the data structure of GPIO Response
>>> + * indicates the completion of GPIO command (64 bytes)  */ struct 
>>> +gpio_resp {
>>> +       __le32  tag;
>>> +       u32     reserved[2];
>>> +       __le32  gpio_rd_val;
>>> +       __le32  gpio_in_enabled;
>>> +       __le32  gpio_pinsetup1;
>>> +       __le32  gpio_pinsetup2;
>>> +       __le32  gpio_evt_change;
>>> +       __le32  gpio_evt_rise;
>>> +       __le32  gpio_evt_fall;
>>> +       u32     reserved1[5];
>>> +} __attribute__((packed, aligned(4)));
>>> +
>>> +/*
>>> + * brief the data structure of GPIO Event
>>> + * indicates the generation of GPIO event (64 bytes)  */ struct 
>>> +gpio_event {
>>> +       __le32  gpio_event;
>>> +       u32     reserved[14];
>>> +} __attribute__((packed, aligned(4)));
>>> +
>>> +/*
>>>   * brief the data structure of PHY_START Response Command
>>>   * indicates the completion of PHY_START command (64 bytes)
>>>   */
>>> --
>>> 1.7.1
>>>
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers
  2013-11-11  8:58         ` Jack Wang
  2013-11-15  6:14           ` Viswas G
@ 2013-11-16 18:25           ` James Bottomley
  1 sibling, 0 replies; 8+ messages in thread
From: James Bottomley @ 2013-11-16 18:25 UTC (permalink / raw)
  To: Jack Wang
  Cc: Viswas G, linux-scsi, Sangeetha Gnanasekaran,
	Nikith Ganigarakoppal, Anand Kumar Santhanam, Suresh Thiagarajan

On Mon, 2013-11-11 at 09:58 +0100, Jack Wang wrote:
> Hi James,
> 
> About this gpio feature, do you think it's OK to implement with IOCTL

Definitely not via another strange ioctl

> or we can use exist bsg interface in libsas and add another function call?

My default thought is that it should go via bsg and libsas if you can
find a SES expander type emulation, which would be nice, but perhaps
complex.  Otherwise it should probably go via the gpio subsystem,
possibly via a SFF-8485 driver (which I don't think exists).

James


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-11-16 18:26 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-22 12:20 [PATCH 11/11] pm80xx : gpio feature support for motherboard controllers Viswas G
2013-10-29 10:19 ` Jack Wang
2013-11-04 10:13   ` Viswas G
2013-11-04 10:30     ` Jack Wang
2013-11-11  5:57       ` Viswas G
2013-11-11  8:58         ` Jack Wang
2013-11-15  6:14           ` Viswas G
2013-11-16 18:25           ` James Bottomley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.