Commit 1245ee59 authored by Sakthivel K's avatar Sakthivel K Committed by James Bottomley

[SCSI] pm80xx: MSI-X implementation for using 64 interrupts

Implementation of interrupt handlers and tasklets to support
upto 64 interrupt for the device.
Signed-off-by: default avatarSakthivel K <Sakthivel.SaravananKamalRaju@pmcs.com>
Signed-off-by: default avatarAnand Kumar S <AnandKumar.Santhanam@pmcs.com>
Acked-by: default avatarJack Wang <jack_wang@usish.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent f74cf271
...@@ -159,33 +159,71 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha) ...@@ -159,33 +159,71 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
} }
#ifdef PM8001_USE_TASKLET #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) static void pm8001_tasklet(unsigned long opaque)
{ {
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
u32 vec;
pm8001_ha = (struct pm8001_hba_info *)opaque; pm8001_ha = (struct pm8001_hba_info *)opaque;
if (unlikely(!pm8001_ha)) if (unlikely(!pm8001_ha))
BUG_ON(1); BUG_ON(1);
PM8001_CHIP_DISP->isr(pm8001_ha, 0); vec = pm8001_ha->int_vector;
PM8001_CHIP_DISP->isr(pm8001_ha, vec);
} }
#endif #endif
static struct pm8001_hba_info *outq_to_hba(u8 *outq)
{
return container_of((outq - *outq), struct pm8001_hba_info, outq[0]);
}
/** /**
* pm8001_interrupt - when HBA originate a interrupt,we should invoke this * pm8001_interrupt_handler_msix - main MSIX interrupt handler.
* dispatcher to handle each case. * It obtains the vector number and calls the equivalent bottom
* @irq: irq number. * half or services directly.
* @opaque: the passed general host adapter struct * @opaque: the passed outbound queue/vector. Host structure is
* retrieved from the same.
*/ */
static irqreturn_t pm8001_interrupt(int irq, void *opaque) 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.
*/
static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id)
{ {
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
irqreturn_t ret = IRQ_HANDLED; irqreturn_t ret = IRQ_HANDLED;
struct sas_ha_struct *sha = opaque; struct sas_ha_struct *sha = dev_id;
pm8001_ha = sha->lldd_ha; pm8001_ha = sha->lldd_ha;
if (unlikely(!pm8001_ha)) if (unlikely(!pm8001_ha))
return IRQ_NONE; return IRQ_NONE;
if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
return IRQ_NONE; return IRQ_NONE;
pm8001_ha->int_vector = 0;
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
tasklet_schedule(&pm8001_ha->tasklet); tasklet_schedule(&pm8001_ha->tasklet);
#else #else
...@@ -427,6 +465,10 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, ...@@ -427,6 +465,10 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
pm8001_ha->iomb_size = IOMB_SIZE_SPC; pm8001_ha->iomb_size = IOMB_SIZE_SPC;
#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, tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
(unsigned long)pm8001_ha); (unsigned long)pm8001_ha);
#endif #endif
...@@ -591,31 +633,50 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) ...@@ -591,31 +633,50 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
* @chip_info: our ha struct. * @chip_info: our ha struct.
* @irq_handler: irq_handler * @irq_handler: irq_handler
*/ */
static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha, static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha)
irq_handler_t irq_handler)
{ {
u32 i = 0, j = 0; u32 i = 0, j = 0;
u32 number_of_intr = 1; u32 number_of_intr;
int flag = 0; int flag = 0;
u32 max_entry; u32 max_entry;
int rc; 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) / max_entry = sizeof(pm8001_ha->msix_entries) /
sizeof(pm8001_ha->msix_entries[0]); sizeof(pm8001_ha->msix_entries[0]);
flag |= IRQF_DISABLED;
for (i = 0; i < max_entry ; i++) for (i = 0; i < max_entry ; i++)
pm8001_ha->msix_entries[i].entry = i; pm8001_ha->msix_entries[i].entry = i;
rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries, rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries,
number_of_intr); number_of_intr);
pm8001_ha->number_of_intr = number_of_intr; pm8001_ha->number_of_intr = number_of_intr;
if (!rc) { 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++) { 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, if (request_irq(pm8001_ha->msix_entries[i].vector,
irq_handler, flag, DRV_NAME, pm8001_interrupt_handler_msix, flag,
SHOST_TO_SAS_HA(pm8001_ha->shost))) { intr_drvname[i], &pm8001_ha->outq[i])) {
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
free_irq( free_irq(
pm8001_ha->msix_entries[j].vector, pm8001_ha->msix_entries[j].vector,
SHOST_TO_SAS_HA(pm8001_ha->shost)); &pm8001_ha->outq[j]);
pci_disable_msix(pm8001_ha->pdev); pci_disable_msix(pm8001_ha->pdev);
break; break;
} }
...@@ -632,22 +693,24 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha, ...@@ -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) static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
{ {
struct pci_dev *pdev; struct pci_dev *pdev;
irq_handler_t irq_handler = pm8001_interrupt;
int rc; int rc;
pdev = pm8001_ha->pdev; pdev = pm8001_ha->pdev;
#ifdef PM8001_USE_MSIX #ifdef PM8001_USE_MSIX
if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
return pm8001_setup_msix(pm8001_ha, irq_handler); return pm8001_setup_msix(pm8001_ha);
else else {
PM8001_INIT_DBG(pm8001_ha,
pm8001_printk("MSIX not supported!!!\n"));
goto intx; goto intx;
}
#endif #endif
intx: intx:
/* initialize the INT-X interrupt */ /* initialize the INT-X interrupt */
rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED,
SHOST_TO_SAS_HA(pm8001_ha->shost)); DRV_NAME, SHOST_TO_SAS_HA(pm8001_ha->shost));
return rc; return rc;
} }
...@@ -665,6 +728,7 @@ static int pm8001_pci_probe(struct pci_dev *pdev, ...@@ -665,6 +728,7 @@ static int pm8001_pci_probe(struct pci_dev *pdev,
{ {
unsigned int rc; unsigned int rc;
u32 pci_reg; u32 pci_reg;
u8 i = 0;
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
struct Scsi_Host *shost = NULL; struct Scsi_Host *shost = NULL;
const struct pm8001_chip_info *chip; const struct pm8001_chip_info *chip;
...@@ -729,6 +793,11 @@ static int pm8001_pci_probe(struct pci_dev *pdev, ...@@ -729,6 +793,11 @@ static int pm8001_pci_probe(struct pci_dev *pdev,
goto err_out_shost; goto err_out_shost;
PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); 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_init_sas_add(pm8001_ha);
pm8001_post_sas_ha_init(shost, chip); pm8001_post_sas_ha_init(shost, chip);
rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
...@@ -764,14 +833,15 @@ static void pm8001_pci_remove(struct pci_dev *pdev) ...@@ -764,14 +833,15 @@ static void pm8001_pci_remove(struct pci_dev *pdev)
sas_remove_host(pm8001_ha->shost); sas_remove_host(pm8001_ha->shost);
list_del(&pm8001_ha->list); list_del(&pm8001_ha->list);
scsi_remove_host(pm8001_ha->shost); 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); PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
#ifdef PM8001_USE_MSIX #ifdef PM8001_USE_MSIX
for (i = 0; i < pm8001_ha->number_of_intr; i++) for (i = 0; i < pm8001_ha->number_of_intr; i++)
synchronize_irq(pm8001_ha->msix_entries[i].vector); synchronize_irq(pm8001_ha->msix_entries[i].vector);
for (i = 0; i < pm8001_ha->number_of_intr; i++) 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); pci_disable_msix(pdev);
#else #else
free_irq(pm8001_ha->irq, sha); free_irq(pm8001_ha->irq, sha);
...@@ -808,13 +878,14 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -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"); printk(KERN_ERR " PCI PM not supported\n");
return -ENODEV; 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); PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
#ifdef PM8001_USE_MSIX #ifdef PM8001_USE_MSIX
for (i = 0; i < pm8001_ha->number_of_intr; i++) for (i = 0; i < pm8001_ha->number_of_intr; i++)
synchronize_irq(pm8001_ha->msix_entries[i].vector); synchronize_irq(pm8001_ha->msix_entries[i].vector);
for (i = 0; i < pm8001_ha->number_of_intr; i++) 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); pci_disable_msix(pdev);
#else #else
free_irq(pm8001_ha->irq, sha); free_irq(pm8001_ha->irq, sha);
...@@ -843,6 +914,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev) ...@@ -843,6 +914,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
struct sas_ha_struct *sha = pci_get_drvdata(pdev); struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
int rc; int rc;
u8 i = 0;
u32 device_state; u32 device_state;
pm8001_ha = sha->lldd_ha; pm8001_ha = sha->lldd_ha;
device_state = pdev->current_state; device_state = pdev->current_state;
...@@ -869,15 +941,24 @@ static int pm8001_pci_resume(struct pci_dev *pdev) ...@@ -869,15 +941,24 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
if (rc) if (rc)
goto err_out_disable; 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); rc = pm8001_request_irq(pm8001_ha);
if (rc) if (rc)
goto err_out_disable; 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, tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
(unsigned long)pm8001_ha); (unsigned long)pm8001_ha);
#endif #endif
PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); 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); scsi_unblock_requests(pm8001_ha->shost);
return 0; return 0;
......
...@@ -453,7 +453,9 @@ struct pm8001_hba_info { ...@@ -453,7 +453,9 @@ struct pm8001_hba_info {
#endif #endif
u32 logging_level; u32 logging_level;
u32 fw_status; u32 fw_status;
u32 int_vector;
const struct firmware *fw_image; const struct firmware *fw_image;
u8 outq[PM8001_MAX_MSIX_VEC];
}; };
struct pm8001_work { struct pm8001_work {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment