Skip to content

Commit

Permalink
[SCSI] pm80xx: MSI-X implementation for using 64 interrupts
Browse files Browse the repository at this point in the history
Implementation of interrupt handlers and tasklets to support
upto 64 interrupt for the device.

Signed-off-by: Sakthivel K <[email protected]>
Signed-off-by: Anand Kumar S <[email protected]>
Acked-by: Jack Wang <[email protected]>
Reviewed-by: Hannes Reinecke <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
  • Loading branch information
Sakthivel K authored and James Bottomley committed May 10, 2013
1 parent f74cf27 commit 1245ee5
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 30 deletions.
141 changes: 111 additions & 30 deletions drivers/scsi/pm8001/pm8001_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,33 +159,71 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
}

#ifdef PM8001_USE_TASKLET

/**
* tasklet for 64 msi-x interrupt handler
* @opaque: the passed general host adapter struct
* Note: pm8001_tasklet is common for pm8001 & pm80xx
*/
static void pm8001_tasklet(unsigned long opaque)
{
struct pm8001_hba_info *pm8001_ha;
u32 vec;
pm8001_ha = (struct pm8001_hba_info *)opaque;
if (unlikely(!pm8001_ha))
BUG_ON(1);
PM8001_CHIP_DISP->isr(pm8001_ha, 0);
vec = pm8001_ha->int_vector;
PM8001_CHIP_DISP->isr(pm8001_ha, vec);
}
#endif

static struct pm8001_hba_info *outq_to_hba(u8 *outq)
{
return container_of((outq - *outq), struct pm8001_hba_info, outq[0]);
}

/**
* pm8001_interrupt_handler_msix - main MSIX interrupt handler.
* It obtains the vector number and calls the equivalent bottom
* half or services directly.
* @opaque: the passed outbound queue/vector. Host structure is
* retrieved from the same.
*/
static irqreturn_t pm8001_interrupt_handler_msix(int irq, void *opaque)
{
struct pm8001_hba_info *pm8001_ha = outq_to_hba(opaque);
u8 outq = *(u8 *)opaque;
irqreturn_t ret = IRQ_HANDLED;
if (unlikely(!pm8001_ha))
return IRQ_NONE;
if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
return IRQ_NONE;
pm8001_ha->int_vector = outq;
#ifdef PM8001_USE_TASKLET
tasklet_schedule(&pm8001_ha->tasklet);
#else
ret = PM8001_CHIP_DISP->isr(pm8001_ha, outq);
#endif
return ret;
}

/**
* pm8001_interrupt_handler_intx - main INTx interrupt handler.
* @dev_id: sas_ha structure. The HBA is retrieved from sas_has structure.
*/

/**
* pm8001_interrupt - when HBA originate a interrupt,we should invoke this
* dispatcher to handle each case.
* @irq: irq number.
* @opaque: the passed general host adapter struct
*/
static irqreturn_t pm8001_interrupt(int irq, void *opaque)
static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id)
{
struct pm8001_hba_info *pm8001_ha;
irqreturn_t ret = IRQ_HANDLED;
struct sas_ha_struct *sha = opaque;
struct sas_ha_struct *sha = dev_id;
pm8001_ha = sha->lldd_ha;
if (unlikely(!pm8001_ha))
return IRQ_NONE;
if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
return IRQ_NONE;

pm8001_ha->int_vector = 0;
#ifdef PM8001_USE_TASKLET
tasklet_schedule(&pm8001_ha->tasklet);
#else
Expand Down Expand Up @@ -427,8 +465,12 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
pm8001_ha->iomb_size = IOMB_SIZE_SPC;

#ifdef PM8001_USE_TASKLET
/**
* default tasklet for non msi-x interrupt handler/first msi-x
* interrupt handler
**/
tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
(unsigned long)pm8001_ha);
(unsigned long)pm8001_ha);
#endif
pm8001_ioremap(pm8001_ha);
if (!pm8001_alloc(pm8001_ha, ent))
Expand Down Expand Up @@ -591,31 +633,50 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
* @chip_info: our ha struct.
* @irq_handler: irq_handler
*/
static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
irq_handler_t irq_handler)
static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha)
{
u32 i = 0, j = 0;
u32 number_of_intr = 1;
u32 number_of_intr;
int flag = 0;
u32 max_entry;
int rc;
static char intr_drvname[PM8001_MAX_MSIX_VEC][sizeof(DRV_NAME)+3];

/* SPCv controllers supports 64 msi-x */
if (pm8001_ha->chip_id == chip_8001) {
number_of_intr = 1;
flag |= IRQF_DISABLED;
} else {
number_of_intr = PM8001_MAX_MSIX_VEC;
flag &= ~IRQF_SHARED;
flag |= IRQF_DISABLED;
}

max_entry = sizeof(pm8001_ha->msix_entries) /
sizeof(pm8001_ha->msix_entries[0]);
flag |= IRQF_DISABLED;
for (i = 0; i < max_entry ; i++)
pm8001_ha->msix_entries[i].entry = i;
rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries,
number_of_intr);
pm8001_ha->number_of_intr = number_of_intr;
if (!rc) {
PM8001_INIT_DBG(pm8001_ha, pm8001_printk(
"pci_enable_msix request ret:%d no of intr %d\n",
rc, pm8001_ha->number_of_intr));

for (i = 0; i < number_of_intr; i++)
pm8001_ha->outq[i] = i;

for (i = 0; i < number_of_intr; i++) {
snprintf(intr_drvname[i], sizeof(intr_drvname[0]),
DRV_NAME"%d", i);
if (request_irq(pm8001_ha->msix_entries[i].vector,
irq_handler, flag, DRV_NAME,
SHOST_TO_SAS_HA(pm8001_ha->shost))) {
pm8001_interrupt_handler_msix, flag,
intr_drvname[i], &pm8001_ha->outq[i])) {
for (j = 0; j < i; j++)
free_irq(
pm8001_ha->msix_entries[j].vector,
SHOST_TO_SAS_HA(pm8001_ha->shost));
&pm8001_ha->outq[j]);
pci_disable_msix(pm8001_ha->pdev);
break;
}
Expand All @@ -632,22 +693,24 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
{
struct pci_dev *pdev;
irq_handler_t irq_handler = pm8001_interrupt;
int rc;

pdev = pm8001_ha->pdev;

#ifdef PM8001_USE_MSIX
if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
return pm8001_setup_msix(pm8001_ha, irq_handler);
else
return pm8001_setup_msix(pm8001_ha);
else {
PM8001_INIT_DBG(pm8001_ha,
pm8001_printk("MSIX not supported!!!\n"));
goto intx;
}
#endif

intx:
/* initialize the INT-X interrupt */
rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
SHOST_TO_SAS_HA(pm8001_ha->shost));
rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED,
DRV_NAME, SHOST_TO_SAS_HA(pm8001_ha->shost));
return rc;
}

Expand All @@ -665,6 +728,7 @@ static int pm8001_pci_probe(struct pci_dev *pdev,
{
unsigned int rc;
u32 pci_reg;
u8 i = 0;
struct pm8001_hba_info *pm8001_ha;
struct Scsi_Host *shost = NULL;
const struct pm8001_chip_info *chip;
Expand Down Expand Up @@ -729,6 +793,11 @@ static int pm8001_pci_probe(struct pci_dev *pdev,
goto err_out_shost;

PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0);
if (pm8001_ha->chip_id != chip_8001) {
for (i = 1; i < pm8001_ha->number_of_intr; i++)
PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i);
}

pm8001_init_sas_add(pm8001_ha);
pm8001_post_sas_ha_init(shost, chip);
rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
Expand Down Expand Up @@ -764,14 +833,15 @@ static void pm8001_pci_remove(struct pci_dev *pdev)
sas_remove_host(pm8001_ha->shost);
list_del(&pm8001_ha->list);
scsi_remove_host(pm8001_ha->shost);
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0);
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);

#ifdef PM8001_USE_MSIX
for (i = 0; i < pm8001_ha->number_of_intr; i++)
synchronize_irq(pm8001_ha->msix_entries[i].vector);
for (i = 0; i < pm8001_ha->number_of_intr; i++)
free_irq(pm8001_ha->msix_entries[i].vector, sha);
free_irq(pm8001_ha->msix_entries[i].vector,
&pm8001_ha->outq[i]);
pci_disable_msix(pdev);
#else
free_irq(pm8001_ha->irq, sha);
Expand Down Expand Up @@ -808,13 +878,14 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
printk(KERN_ERR " PCI PM not supported\n");
return -ENODEV;
}
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0);
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
#ifdef PM8001_USE_MSIX
for (i = 0; i < pm8001_ha->number_of_intr; i++)
synchronize_irq(pm8001_ha->msix_entries[i].vector);
for (i = 0; i < pm8001_ha->number_of_intr; i++)
free_irq(pm8001_ha->msix_entries[i].vector, sha);
free_irq(pm8001_ha->msix_entries[i].vector,
&pm8001_ha->outq[i]);
pci_disable_msix(pdev);
#else
free_irq(pm8001_ha->irq, sha);
Expand Down Expand Up @@ -843,6 +914,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha;
int rc;
u8 i = 0;
u32 device_state;
pm8001_ha = sha->lldd_ha;
device_state = pdev->current_state;
Expand All @@ -869,15 +941,24 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
if (rc)
goto err_out_disable;
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0);

/* disable all the interrupt bits */
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);

rc = pm8001_request_irq(pm8001_ha);
if (rc)
goto err_out_disable;
#ifdef PM8001_USE_TASKLET
#ifdef PM8001_USE_TASKLET
/* default tasklet for non msi-x interrupt handler/first msi-x
* interrupt handler */
tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
(unsigned long)pm8001_ha);
#endif
(unsigned long)pm8001_ha);
#endif
PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0);
if (pm8001_ha->chip_id != chip_8001) {
for (i = 1; i < pm8001_ha->number_of_intr; i++)
PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i);
}
scsi_unblock_requests(pm8001_ha->shost);
return 0;

Expand Down
2 changes: 2 additions & 0 deletions drivers/scsi/pm8001/pm8001_sas.h
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,9 @@ struct pm8001_hba_info {
#endif
u32 logging_level;
u32 fw_status;
u32 int_vector;
const struct firmware *fw_image;
u8 outq[PM8001_MAX_MSIX_VEC];
};

struct pm8001_work {
Expand Down

0 comments on commit 1245ee5

Please sign in to comment.