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@xxxxxxxxx] > Sent: Tuesday, October 29, 2013 3:49 PM > To: Viswas G > Cc: linux-scsi@xxxxxxxxxxxxxxx; 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@xxxxxxxx> >> --- >> 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 >> > -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html