powerpc/powernv: Patch MSI EOI handler on P8
The EOI handler of MSI/MSI-X interrupts for P8 (PHB3) need additional steps to handle the P/Q bits in IVE before EOIing the corresponding interrupt. The patch changes the EOI handler to cover that. we have individual IRQ chip in each PHB instance. During the MSI IRQ setup time, the IRQ chip is copied over from the original one for that IRQ, and the EOI handler is patched with the one that will handle the P/Q bits (As Ben suggested). Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
a486bdb0e9
commit
137436c9a6
|
@ -117,6 +117,7 @@ extern int opal_enter_rtas(struct rtas_args *args,
|
||||||
#define OPAL_SET_SLOT_LED_STATUS 55
|
#define OPAL_SET_SLOT_LED_STATUS 55
|
||||||
#define OPAL_GET_EPOW_STATUS 56
|
#define OPAL_GET_EPOW_STATUS 56
|
||||||
#define OPAL_SET_SYSTEM_ATTENTION_LED 57
|
#define OPAL_SET_SYSTEM_ATTENTION_LED 57
|
||||||
|
#define OPAL_PCI_MSI_EOI 63
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
|
@ -506,6 +507,7 @@ int64_t opal_pci_get_xive_reissue(uint64_t phb_id, uint32_t xive_number,
|
||||||
uint8_t *p_bit, uint8_t *q_bit);
|
uint8_t *p_bit, uint8_t *q_bit);
|
||||||
int64_t opal_pci_set_xive_reissue(uint64_t phb_id, uint32_t xive_number,
|
int64_t opal_pci_set_xive_reissue(uint64_t phb_id, uint32_t xive_number,
|
||||||
uint8_t p_bit, uint8_t q_bit);
|
uint8_t p_bit, uint8_t q_bit);
|
||||||
|
int64_t opal_pci_msi_eoi(uint64_t phb_id, uint32_t hw_irq);
|
||||||
int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number,
|
int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number,
|
||||||
uint32_t xive_num);
|
uint32_t xive_num);
|
||||||
int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num,
|
int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num,
|
||||||
|
|
|
@ -150,6 +150,7 @@ extern void xics_register_ics(struct ics *ics);
|
||||||
extern void xics_teardown_cpu(void);
|
extern void xics_teardown_cpu(void);
|
||||||
extern void xics_kexec_teardown_cpu(int secondary);
|
extern void xics_kexec_teardown_cpu(int secondary);
|
||||||
extern void xics_migrate_irqs_away(void);
|
extern void xics_migrate_irqs_away(void);
|
||||||
|
extern void icp_native_eoi(struct irq_data *d);
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
extern int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask,
|
extern int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask,
|
||||||
unsigned int strict_check);
|
unsigned int strict_check);
|
||||||
|
|
|
@ -107,3 +107,4 @@ OPAL_CALL(opal_pci_mask_pe_error, OPAL_PCI_MASK_PE_ERROR);
|
||||||
OPAL_CALL(opal_set_slot_led_status, OPAL_SET_SLOT_LED_STATUS);
|
OPAL_CALL(opal_set_slot_led_status, OPAL_SET_SLOT_LED_STATUS);
|
||||||
OPAL_CALL(opal_get_epow_status, OPAL_GET_EPOW_STATUS);
|
OPAL_CALL(opal_get_epow_status, OPAL_GET_EPOW_STATUS);
|
||||||
OPAL_CALL(opal_set_system_attention_led, OPAL_SET_SYSTEM_ATTENTION_LED);
|
OPAL_CALL(opal_set_system_attention_led, OPAL_SET_SYSTEM_ATTENTION_LED);
|
||||||
|
OPAL_CALL(opal_pci_msi_eoi, OPAL_PCI_MSI_EOI);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <asm/opal.h>
|
#include <asm/opal.h>
|
||||||
#include <asm/iommu.h>
|
#include <asm/iommu.h>
|
||||||
#include <asm/tce.h>
|
#include <asm/tce.h>
|
||||||
|
#include <asm/xics.h>
|
||||||
|
|
||||||
#include "powernv.h"
|
#include "powernv.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
@ -589,11 +590,27 @@ static void pnv_ioda_setup_dma(struct pnv_phb *phb)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PCI_MSI
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
static void pnv_ioda2_msi_eoi(struct irq_data *d)
|
||||||
|
{
|
||||||
|
unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
|
||||||
|
struct irq_chip *chip = irq_data_get_irq_chip(d);
|
||||||
|
struct pnv_phb *phb = container_of(chip, struct pnv_phb,
|
||||||
|
ioda.irq_chip);
|
||||||
|
int64_t rc;
|
||||||
|
|
||||||
|
rc = opal_pci_msi_eoi(phb->opal_id, hw_irq);
|
||||||
|
WARN_ON_ONCE(rc);
|
||||||
|
|
||||||
|
icp_native_eoi(d);
|
||||||
|
}
|
||||||
|
|
||||||
static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
|
static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
|
||||||
unsigned int hwirq, unsigned int is_64,
|
unsigned int hwirq, unsigned int virq,
|
||||||
struct msi_msg *msg)
|
unsigned int is_64, struct msi_msg *msg)
|
||||||
{
|
{
|
||||||
struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev);
|
struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev);
|
||||||
|
struct irq_data *idata;
|
||||||
|
struct irq_chip *ichip;
|
||||||
unsigned int xive_num = hwirq - phb->msi_base;
|
unsigned int xive_num = hwirq - phb->msi_base;
|
||||||
uint64_t addr64;
|
uint64_t addr64;
|
||||||
uint32_t addr32, data;
|
uint32_t addr32, data;
|
||||||
|
@ -638,6 +655,23 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
|
||||||
}
|
}
|
||||||
msg->data = data;
|
msg->data = data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change the IRQ chip for the MSI interrupts on PHB3.
|
||||||
|
* The corresponding IRQ chip should be populated for
|
||||||
|
* the first time.
|
||||||
|
*/
|
||||||
|
if (phb->type == PNV_PHB_IODA2) {
|
||||||
|
if (!phb->ioda.irq_chip_init) {
|
||||||
|
idata = irq_get_irq_data(virq);
|
||||||
|
ichip = irq_data_get_irq_chip(idata);
|
||||||
|
phb->ioda.irq_chip_init = 1;
|
||||||
|
phb->ioda.irq_chip = *ichip;
|
||||||
|
phb->ioda.irq_chip.irq_eoi = pnv_ioda2_msi_eoi;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_set_chip(virq, &phb->ioda.irq_chip);
|
||||||
|
}
|
||||||
|
|
||||||
pr_devel("%s: %s-bit MSI on hwirq %x (xive #%d),"
|
pr_devel("%s: %s-bit MSI on hwirq %x (xive #%d),"
|
||||||
" address=%x_%08x data=%x PE# %d\n",
|
" address=%x_%08x data=%x PE# %d\n",
|
||||||
pci_name(dev), is_64 ? "64" : "32", hwirq, xive_num,
|
pci_name(dev), is_64 ? "64" : "32", hwirq, xive_num,
|
||||||
|
|
|
@ -42,8 +42,8 @@
|
||||||
|
|
||||||
#ifdef CONFIG_PCI_MSI
|
#ifdef CONFIG_PCI_MSI
|
||||||
static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
|
static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
|
||||||
unsigned int hwirq, unsigned int is_64,
|
unsigned int hwirq, unsigned int virq,
|
||||||
struct msi_msg *msg)
|
unsigned int is_64, struct msi_msg *msg)
|
||||||
{
|
{
|
||||||
if (WARN_ON(!is_64))
|
if (WARN_ON(!is_64))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
|
@ -84,7 +84,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
rc = phb->msi_setup(phb, pdev, phb->msi_base + hwirq,
|
rc = phb->msi_setup(phb, pdev, phb->msi_base + hwirq,
|
||||||
entry->msi_attrib.is_64, &msg);
|
virq, entry->msi_attrib.is_64, &msg);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_warn("%s: Failed to setup MSI\n", pci_name(pdev));
|
pr_warn("%s: Failed to setup MSI\n", pci_name(pdev));
|
||||||
irq_dispose_mapping(virq);
|
irq_dispose_mapping(virq);
|
||||||
|
|
|
@ -79,8 +79,8 @@ struct pnv_phb {
|
||||||
struct msi_bitmap msi_bmp;
|
struct msi_bitmap msi_bmp;
|
||||||
#endif
|
#endif
|
||||||
int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev,
|
int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev,
|
||||||
unsigned int hwirq, unsigned int is_64,
|
unsigned int hwirq, unsigned int virq,
|
||||||
struct msi_msg *msg);
|
unsigned int is_64, struct msi_msg *msg);
|
||||||
void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
|
void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
|
||||||
void (*fixup_phb)(struct pci_controller *hose);
|
void (*fixup_phb)(struct pci_controller *hose);
|
||||||
u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
|
u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
|
||||||
|
@ -108,6 +108,10 @@ struct pnv_phb {
|
||||||
unsigned int *io_segmap;
|
unsigned int *io_segmap;
|
||||||
struct pnv_ioda_pe *pe_array;
|
struct pnv_ioda_pe *pe_array;
|
||||||
|
|
||||||
|
/* IRQ chip */
|
||||||
|
int irq_chip_init;
|
||||||
|
struct irq_chip irq_chip;
|
||||||
|
|
||||||
/* Sorted list of used PE's based
|
/* Sorted list of used PE's based
|
||||||
* on the sequence of creation
|
* on the sequence of creation
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -81,7 +81,7 @@ static void icp_native_set_cpu_priority(unsigned char cppr)
|
||||||
iosync();
|
iosync();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void icp_native_eoi(struct irq_data *d)
|
void icp_native_eoi(struct irq_data *d)
|
||||||
{
|
{
|
||||||
unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
|
unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue