[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: Sakthivel K <Sakthivel.SaravananKamalRaju@pmcs.com> Signed-off-by: Anand Kumar S <AnandKumar.Santhanam@pmcs.com> Acked-by: Jack Wang <jack_wang@usish.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
committed by
James Bottomley
parent
f74cf271e6
commit
1245ee5996
@@ -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,8 +465,12 @@ 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
|
||||||
pm8001_ioremap(pm8001_ha);
|
pm8001_ioremap(pm8001_ha);
|
||||||
if (!pm8001_alloc(pm8001_ha, ent))
|
if (!pm8001_alloc(pm8001_ha, ent))
|
||||||
@@ -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,
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 {
|
|||||||
#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 {
|
||||||
|
Reference in New Issue
Block a user