mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-22 17:01:14 +00:00
Merge branch 'remotes/lorenzo/pci/aardvark'
- Use PCI_INTERRUPT_* definitions from PCI core instead of custom ones (Pali Rohár) - Derive MSI number from bit(s) set in PCIE_MSI_STATUS_REG, not from PCIE_MSI_PAYLOAD_REG (Pali Rohár) - Align multi-MSI vectors to power of two (Pali Rohár) - Rewrite IRQ code to use chained IRQ handler (Pali Rohár) - Check return value of generic_handle_domain_irq() and warn about spurious interrupts (Pali Rohár) - Make MSI irq_chip structures static to driver (Marek Behún) - Make msi_domain_info structure static to driver (Marek Behún) - Use dev_fwnode() instead of of_node_to_fwnode(dev->of_node) (Marek Behún) - Refactor unmasking of summary MSI interrupt (Pali Rohár) - Add support for masking MSI interrupts and leave them masked at setup (Pali Rohár) - Set MSI doorbell address to address of struct advk_pcie (Pali Rohár) - Enable MSI-X support (Pali Rohár) - Add support for ERR interrupt on emulated bridge (Pali Rohár) - Fix read of PCI_EXP_RTSTA_PME bit on emulated bridge (Pali Rohár) - Optimize writing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME on emulated bridge (Pali Rohár) - Add support for PME interrupts (Pali Rohár) - Fix support for PME requester on emulated bridge (Pali Rohár) - Use separate INTA interrupt for emulated Root Port so PME and AER interrupt is not shared with downstream devices (Pali Rohár) - Remove irq_mask_ack() callback for INTx interrupts (Pali Rohár) - Don't mask legacy INTx interrupts when mapping (Pali Rohár) - Drop unnecessary "__maybe_unused" from advk_pcie_disable_phy() (Marek Behún) - Update comment about why we check for link being up before issuing a config request (Marek Behún) * remotes/lorenzo/pci/aardvark: PCI: aardvark: Update comment about link going down after link-up PCI: aardvark: Drop __maybe_unused from advk_pcie_disable_phy() PCI: aardvark: Don't mask irq when mapping PCI: aardvark: Remove irq_mask_ack() callback for INTx interrupts PCI: aardvark: Use separate INTA interrupt for emulated root bridge PCI: aardvark: Fix support for PME requester on emulated bridge PCI: aardvark: Add support for PME interrupts PCI: aardvark: Optimize writing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME on emulated bridge PCI: aardvark: Fix reading PCI_EXP_RTSTA_PME bit on emulated bridge PCI: aardvark: Add support for ERR interrupt on emulated bridge PCI: aardvark: Enable MSI-X support PCI: aardvark: Fix setting MSI address PCI: aardvark: Add support for masking MSI interrupts PCI: aardvark: Refactor unmasking summary MSI interrupt PCI: aardvark: Use dev_fwnode() instead of of_node_to_fwnode(dev->of_node) PCI: aardvark: Make msi_domain_info structure a static driver structure PCI: aardvark: Make MSI irq_chip structures static driver structures PCI: aardvark: Check return value of generic_handle_domain_irq() when processing INTx IRQ PCI: aardvark: Rewrite IRQ code to chained IRQ handler PCI: aardvark: Fix support for MSI interrupts PCI: aardvark: Fix reading MSI interrupt number PCI: aardvark: Replace custom PCIE_CORE_INT_* macros with PCI_INTERRUPT_*
This commit is contained in:
commit
7ec9ff94f5
1 changed files with 274 additions and 116 deletions
|
@ -38,10 +38,6 @@
|
|||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6)
|
||||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7)
|
||||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8)
|
||||
#define PCIE_CORE_INT_A_ASSERT_ENABLE 1
|
||||
#define PCIE_CORE_INT_B_ASSERT_ENABLE 2
|
||||
#define PCIE_CORE_INT_C_ASSERT_ENABLE 3
|
||||
#define PCIE_CORE_INT_D_ASSERT_ENABLE 4
|
||||
/* PIO registers base address and register offsets */
|
||||
#define PIO_BASE_ADDR 0x4000
|
||||
#define PIO_CTRL (PIO_BASE_ADDR + 0x0)
|
||||
|
@ -102,6 +98,10 @@
|
|||
#define PCIE_MSG_PM_PME_MASK BIT(7)
|
||||
#define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44)
|
||||
#define PCIE_ISR0_MSI_INT_PENDING BIT(24)
|
||||
#define PCIE_ISR0_CORR_ERR BIT(11)
|
||||
#define PCIE_ISR0_NFAT_ERR BIT(12)
|
||||
#define PCIE_ISR0_FAT_ERR BIT(13)
|
||||
#define PCIE_ISR0_ERR_MASK GENMASK(13, 11)
|
||||
#define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val))
|
||||
#define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val))
|
||||
#define PCIE_ISR0_ALL_MASK GENMASK(31, 0)
|
||||
|
@ -272,17 +272,16 @@ struct advk_pcie {
|
|||
u32 actions;
|
||||
} wins[OB_WIN_COUNT];
|
||||
u8 wins_count;
|
||||
int irq;
|
||||
struct irq_domain *rp_irq_domain;
|
||||
struct irq_domain *irq_domain;
|
||||
struct irq_chip irq_chip;
|
||||
raw_spinlock_t irq_lock;
|
||||
struct irq_domain *msi_domain;
|
||||
struct irq_domain *msi_inner_domain;
|
||||
struct irq_chip msi_bottom_irq_chip;
|
||||
struct irq_chip msi_irq_chip;
|
||||
struct msi_domain_info msi_domain_info;
|
||||
raw_spinlock_t msi_irq_lock;
|
||||
DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
|
||||
struct mutex msi_used_lock;
|
||||
u16 msi_msg;
|
||||
int link_gen;
|
||||
struct pci_bridge_emul bridge;
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
@ -477,6 +476,7 @@ static void advk_pcie_disable_ob_win(struct advk_pcie *pcie, u8 win_num)
|
|||
|
||||
static void advk_pcie_setup_hw(struct advk_pcie *pcie)
|
||||
{
|
||||
phys_addr_t msi_addr;
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
|
@ -565,6 +565,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
|
|||
reg |= LANE_COUNT_1;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
|
||||
|
||||
/* Set MSI address */
|
||||
msi_addr = virt_to_phys(pcie);
|
||||
advk_writel(pcie, lower_32_bits(msi_addr), PCIE_MSI_ADDR_LOW_REG);
|
||||
advk_writel(pcie, upper_32_bits(msi_addr), PCIE_MSI_ADDR_HIGH_REG);
|
||||
|
||||
/* Enable MSI */
|
||||
reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
|
||||
reg |= PCIE_CORE_CTRL2_MSI_ENABLE;
|
||||
|
@ -576,15 +581,20 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
|
|||
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
|
||||
advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
|
||||
|
||||
/* Disable All ISR0/1 Sources */
|
||||
reg = PCIE_ISR0_ALL_MASK;
|
||||
/* Disable All ISR0/1 and MSI Sources */
|
||||
advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
|
||||
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
|
||||
advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
|
||||
|
||||
/* Unmask summary MSI interrupt */
|
||||
reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
|
||||
reg &= ~PCIE_ISR0_MSI_INT_PENDING;
|
||||
advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
|
||||
|
||||
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
|
||||
|
||||
/* Unmask all MSIs */
|
||||
advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
|
||||
/* Unmask PME interrupt for processing of PME requester */
|
||||
reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
|
||||
reg &= ~PCIE_MSG_PM_PME_MASK;
|
||||
advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
|
||||
|
||||
/* Enable summary interrupt for GIC SPI source */
|
||||
reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
|
||||
|
@ -778,11 +788,15 @@ advk_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge,
|
|||
case PCI_INTERRUPT_LINE: {
|
||||
/*
|
||||
* From the whole 32bit register we support reading from HW only
|
||||
* one bit: PCI_BRIDGE_CTL_BUS_RESET.
|
||||
* two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR.
|
||||
* Other bits are retrieved only from emulated config buffer.
|
||||
*/
|
||||
__le32 *cfgspace = (__le32 *)&bridge->conf;
|
||||
u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]);
|
||||
if (advk_readl(pcie, PCIE_ISR0_MASK_REG) & PCIE_ISR0_ERR_MASK)
|
||||
val &= ~(PCI_BRIDGE_CTL_SERR << 16);
|
||||
else
|
||||
val |= PCI_BRIDGE_CTL_SERR << 16;
|
||||
if (advk_readl(pcie, PCIE_CORE_CTRL1_REG) & HOT_RESET_GEN)
|
||||
val |= PCI_BRIDGE_CTL_BUS_RESET << 16;
|
||||
else
|
||||
|
@ -808,6 +822,19 @@ advk_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
|
|||
break;
|
||||
|
||||
case PCI_INTERRUPT_LINE:
|
||||
/*
|
||||
* According to Figure 6-3: Pseudo Logic Diagram for Error
|
||||
* Message Controls in PCIe base specification, SERR# Enable bit
|
||||
* in Bridge Control register enable receiving of ERR_* messages
|
||||
*/
|
||||
if (mask & (PCI_BRIDGE_CTL_SERR << 16)) {
|
||||
u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
|
||||
if (new & (PCI_BRIDGE_CTL_SERR << 16))
|
||||
val &= ~PCIE_ISR0_ERR_MASK;
|
||||
else
|
||||
val |= PCIE_ISR0_ERR_MASK;
|
||||
advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
|
||||
}
|
||||
if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) {
|
||||
u32 val = advk_readl(pcie, PCIE_CORE_CTRL1_REG);
|
||||
if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16))
|
||||
|
@ -835,20 +862,11 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
|
|||
*value = PCI_EXP_SLTSTA_PDS << 16;
|
||||
return PCI_BRIDGE_EMUL_HANDLED;
|
||||
|
||||
case PCI_EXP_RTCTL: {
|
||||
u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
|
||||
*value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE;
|
||||
*value |= le16_to_cpu(bridge->pcie_conf.rootctl) & PCI_EXP_RTCTL_CRSSVE;
|
||||
*value |= PCI_EXP_RTCAP_CRSVIS << 16;
|
||||
return PCI_BRIDGE_EMUL_HANDLED;
|
||||
}
|
||||
|
||||
case PCI_EXP_RTSTA: {
|
||||
u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
|
||||
u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
|
||||
*value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16);
|
||||
return PCI_BRIDGE_EMUL_HANDLED;
|
||||
}
|
||||
/*
|
||||
* PCI_EXP_RTCTL and PCI_EXP_RTSTA are also supported, but do not need
|
||||
* to be handled here, because their values are stored in emulated
|
||||
* config space buffer, and we read them from there when needed.
|
||||
*/
|
||||
|
||||
case PCI_EXP_LNKCAP: {
|
||||
u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
|
||||
|
@ -903,19 +921,18 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
|
|||
break;
|
||||
|
||||
case PCI_EXP_RTCTL: {
|
||||
/* Only mask/unmask PME interrupt */
|
||||
u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG) &
|
||||
~PCIE_MSG_PM_PME_MASK;
|
||||
if ((new & PCI_EXP_RTCTL_PMEIE) == 0)
|
||||
val |= PCIE_MSG_PM_PME_MASK;
|
||||
advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
|
||||
u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl);
|
||||
/* Only emulation of PMEIE and CRSSVE bits is provided */
|
||||
rootctl &= PCI_EXP_RTCTL_PMEIE | PCI_EXP_RTCTL_CRSSVE;
|
||||
bridge->pcie_conf.rootctl = cpu_to_le16(rootctl);
|
||||
break;
|
||||
}
|
||||
|
||||
case PCI_EXP_RTSTA:
|
||||
new = (new & PCI_EXP_RTSTA_PME) >> 9;
|
||||
advk_writel(pcie, new, PCIE_ISR0_REG);
|
||||
break;
|
||||
/*
|
||||
* PCI_EXP_RTSTA is also supported, but does not need to be handled
|
||||
* here, because its value is stored in emulated config space buffer,
|
||||
* and we write it there when needed.
|
||||
*/
|
||||
|
||||
case PCI_EXP_DEVCTL:
|
||||
case PCI_EXP_DEVCTL2:
|
||||
|
@ -959,7 +976,7 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
|
|||
bridge->conf.pref_mem_limit = cpu_to_le16(PCI_PREF_RANGE_TYPE_64);
|
||||
|
||||
/* Support interrupt A for MSI feature */
|
||||
bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
|
||||
bridge->conf.intpin = PCI_INTERRUPT_INTA;
|
||||
|
||||
/* Aardvark HW provides PCIe Capability structure in version 2 */
|
||||
bridge->pcie_conf.cap = cpu_to_le16(2);
|
||||
|
@ -981,8 +998,12 @@ static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus,
|
|||
return false;
|
||||
|
||||
/*
|
||||
* If the link goes down after we check for link-up, nothing bad
|
||||
* happens but the config access times out.
|
||||
* If the link goes down after we check for link-up, we have a problem:
|
||||
* if a PIO request is executed while link-down, the whole controller
|
||||
* gets stuck in a non-functional state, and even after link comes up
|
||||
* again, PIO requests won't work anymore, and a reset of the whole PCIe
|
||||
* controller is needed. Therefore we need to prevent sending PIO
|
||||
* requests while the link is down.
|
||||
*/
|
||||
if (!pci_is_root_bus(bus) && !advk_pcie_link_up(pcie))
|
||||
return false;
|
||||
|
@ -1180,11 +1201,11 @@ static void advk_msi_irq_compose_msi_msg(struct irq_data *data,
|
|||
struct msi_msg *msg)
|
||||
{
|
||||
struct advk_pcie *pcie = irq_data_get_irq_chip_data(data);
|
||||
phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg);
|
||||
phys_addr_t msi_addr = virt_to_phys(pcie);
|
||||
|
||||
msg->address_lo = lower_32_bits(msi_msg);
|
||||
msg->address_hi = upper_32_bits(msi_msg);
|
||||
msg->data = data->irq;
|
||||
msg->address_lo = lower_32_bits(msi_addr);
|
||||
msg->address_hi = upper_32_bits(msi_addr);
|
||||
msg->data = data->hwirq;
|
||||
}
|
||||
|
||||
static int advk_msi_set_affinity(struct irq_data *irq_data,
|
||||
|
@ -1193,6 +1214,54 @@ static int advk_msi_set_affinity(struct irq_data *irq_data,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void advk_msi_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct advk_pcie *pcie = d->domain->host_data;
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags);
|
||||
mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
|
||||
mask |= BIT(hwirq);
|
||||
advk_writel(pcie, mask, PCIE_MSI_MASK_REG);
|
||||
raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags);
|
||||
}
|
||||
|
||||
static void advk_msi_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct advk_pcie *pcie = d->domain->host_data;
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags);
|
||||
mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
|
||||
mask &= ~BIT(hwirq);
|
||||
advk_writel(pcie, mask, PCIE_MSI_MASK_REG);
|
||||
raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags);
|
||||
}
|
||||
|
||||
static void advk_msi_top_irq_mask(struct irq_data *d)
|
||||
{
|
||||
pci_msi_mask_irq(d);
|
||||
irq_chip_mask_parent(d);
|
||||
}
|
||||
|
||||
static void advk_msi_top_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
pci_msi_unmask_irq(d);
|
||||
irq_chip_unmask_parent(d);
|
||||
}
|
||||
|
||||
static struct irq_chip advk_msi_bottom_irq_chip = {
|
||||
.name = "MSI",
|
||||
.irq_compose_msi_msg = advk_msi_irq_compose_msi_msg,
|
||||
.irq_set_affinity = advk_msi_set_affinity,
|
||||
.irq_mask = advk_msi_irq_mask,
|
||||
.irq_unmask = advk_msi_irq_unmask,
|
||||
};
|
||||
|
||||
static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
|
||||
unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
|
@ -1201,19 +1270,15 @@ static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
|
|||
int hwirq, i;
|
||||
|
||||
mutex_lock(&pcie->msi_used_lock);
|
||||
hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM,
|
||||
0, nr_irqs, 0);
|
||||
if (hwirq >= MSI_IRQ_NUM) {
|
||||
mutex_unlock(&pcie->msi_used_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
bitmap_set(pcie->msi_used, hwirq, nr_irqs);
|
||||
hwirq = bitmap_find_free_region(pcie->msi_used, MSI_IRQ_NUM,
|
||||
order_base_2(nr_irqs));
|
||||
mutex_unlock(&pcie->msi_used_lock);
|
||||
if (hwirq < 0)
|
||||
return -ENOSPC;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
irq_domain_set_info(domain, virq + i, hwirq + i,
|
||||
&pcie->msi_bottom_irq_chip,
|
||||
&advk_msi_bottom_irq_chip,
|
||||
domain->host_data, handle_simple_irq,
|
||||
NULL, NULL);
|
||||
|
||||
|
@ -1227,7 +1292,7 @@ static void advk_msi_irq_domain_free(struct irq_domain *domain,
|
|||
struct advk_pcie *pcie = domain->host_data;
|
||||
|
||||
mutex_lock(&pcie->msi_used_lock);
|
||||
bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs);
|
||||
bitmap_release_region(pcie->msi_used, d->hwirq, order_base_2(nr_irqs));
|
||||
mutex_unlock(&pcie->msi_used_lock);
|
||||
}
|
||||
|
||||
|
@ -1269,7 +1334,6 @@ static int advk_pcie_irq_map(struct irq_domain *h,
|
|||
{
|
||||
struct advk_pcie *pcie = h->host_data;
|
||||
|
||||
advk_pcie_irq_mask(irq_get_irq_data(virq));
|
||||
irq_set_status_flags(virq, IRQ_LEVEL);
|
||||
irq_set_chip_and_handler(virq, &pcie->irq_chip,
|
||||
handle_level_irq);
|
||||
|
@ -1283,37 +1347,25 @@ static const struct irq_domain_ops advk_pcie_irq_domain_ops = {
|
|||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static struct irq_chip advk_msi_irq_chip = {
|
||||
.name = "advk-MSI",
|
||||
.irq_mask = advk_msi_top_irq_mask,
|
||||
.irq_unmask = advk_msi_top_irq_unmask,
|
||||
};
|
||||
|
||||
static struct msi_domain_info advk_msi_domain_info = {
|
||||
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
|
||||
.chip = &advk_msi_irq_chip,
|
||||
};
|
||||
|
||||
static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
|
||||
{
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct irq_chip *bottom_ic, *msi_ic;
|
||||
struct msi_domain_info *msi_di;
|
||||
phys_addr_t msi_msg_phys;
|
||||
|
||||
raw_spin_lock_init(&pcie->msi_irq_lock);
|
||||
mutex_init(&pcie->msi_used_lock);
|
||||
|
||||
bottom_ic = &pcie->msi_bottom_irq_chip;
|
||||
|
||||
bottom_ic->name = "MSI";
|
||||
bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg;
|
||||
bottom_ic->irq_set_affinity = advk_msi_set_affinity;
|
||||
|
||||
msi_ic = &pcie->msi_irq_chip;
|
||||
msi_ic->name = "advk-MSI";
|
||||
|
||||
msi_di = &pcie->msi_domain_info;
|
||||
msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_MULTI_PCI_MSI;
|
||||
msi_di->chip = msi_ic;
|
||||
|
||||
msi_msg_phys = virt_to_phys(&pcie->msi_msg);
|
||||
|
||||
advk_writel(pcie, lower_32_bits(msi_msg_phys),
|
||||
PCIE_MSI_ADDR_LOW_REG);
|
||||
advk_writel(pcie, upper_32_bits(msi_msg_phys),
|
||||
PCIE_MSI_ADDR_HIGH_REG);
|
||||
|
||||
pcie->msi_inner_domain =
|
||||
irq_domain_add_linear(NULL, MSI_IRQ_NUM,
|
||||
&advk_msi_domain_ops, pcie);
|
||||
|
@ -1321,8 +1373,9 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
|
|||
return -ENOMEM;
|
||||
|
||||
pcie->msi_domain =
|
||||
pci_msi_create_irq_domain(of_node_to_fwnode(node),
|
||||
msi_di, pcie->msi_inner_domain);
|
||||
pci_msi_create_irq_domain(dev_fwnode(dev),
|
||||
&advk_msi_domain_info,
|
||||
pcie->msi_inner_domain);
|
||||
if (!pcie->msi_domain) {
|
||||
irq_domain_remove(pcie->msi_inner_domain);
|
||||
return -ENOMEM;
|
||||
|
@ -1363,7 +1416,6 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
|
|||
}
|
||||
|
||||
irq_chip->irq_mask = advk_pcie_irq_mask;
|
||||
irq_chip->irq_mask_ack = advk_pcie_irq_mask;
|
||||
irq_chip->irq_unmask = advk_pcie_irq_unmask;
|
||||
|
||||
pcie->irq_domain =
|
||||
|
@ -1385,10 +1437,73 @@ static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie)
|
|||
irq_domain_remove(pcie->irq_domain);
|
||||
}
|
||||
|
||||
static struct irq_chip advk_rp_irq_chip = {
|
||||
.name = "advk-RP",
|
||||
};
|
||||
|
||||
static int advk_pcie_rp_irq_map(struct irq_domain *h,
|
||||
unsigned int virq, irq_hw_number_t hwirq)
|
||||
{
|
||||
struct advk_pcie *pcie = h->host_data;
|
||||
|
||||
irq_set_chip_and_handler(virq, &advk_rp_irq_chip, handle_simple_irq);
|
||||
irq_set_chip_data(virq, pcie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops advk_pcie_rp_irq_domain_ops = {
|
||||
.map = advk_pcie_rp_irq_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie)
|
||||
{
|
||||
pcie->rp_irq_domain = irq_domain_add_linear(NULL, 1,
|
||||
&advk_pcie_rp_irq_domain_ops,
|
||||
pcie);
|
||||
if (!pcie->rp_irq_domain) {
|
||||
dev_err(&pcie->pdev->dev, "Failed to add Root Port IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie)
|
||||
{
|
||||
irq_domain_remove(pcie->rp_irq_domain);
|
||||
}
|
||||
|
||||
static void advk_pcie_handle_pme(struct advk_pcie *pcie)
|
||||
{
|
||||
u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
|
||||
|
||||
advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
|
||||
|
||||
/*
|
||||
* PCIE_MSG_LOG_REG contains the last inbound message, so store
|
||||
* the requester ID only when PME was not asserted yet.
|
||||
* Also do not trigger PME interrupt when PME is still asserted.
|
||||
*/
|
||||
if (!(le32_to_cpu(pcie->bridge.pcie_conf.rootsta) & PCI_EXP_RTSTA_PME)) {
|
||||
pcie->bridge.pcie_conf.rootsta = cpu_to_le32(requester | PCI_EXP_RTSTA_PME);
|
||||
|
||||
/*
|
||||
* Trigger PME interrupt only if PMEIE bit in Root Control is set.
|
||||
* Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe interrupt 0.
|
||||
*/
|
||||
if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
|
||||
return;
|
||||
|
||||
if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL)
|
||||
dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void advk_pcie_handle_msi(struct advk_pcie *pcie)
|
||||
{
|
||||
u32 msi_val, msi_mask, msi_status, msi_idx;
|
||||
u16 msi_data;
|
||||
|
||||
msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
|
||||
msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG);
|
||||
|
@ -1398,13 +1513,9 @@ static void advk_pcie_handle_msi(struct advk_pcie *pcie)
|
|||
if (!(BIT(msi_idx) & msi_status))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* msi_idx contains bits [4:0] of the msi_data and msi_data
|
||||
* contains 16bit MSI interrupt number
|
||||
*/
|
||||
advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG);
|
||||
msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & PCIE_MSI_DATA_MASK;
|
||||
generic_handle_irq(msi_data);
|
||||
if (generic_handle_domain_irq(pcie->msi_inner_domain, msi_idx) == -EINVAL)
|
||||
dev_err_ratelimited(&pcie->pdev->dev, "unexpected MSI 0x%02x\n", msi_idx);
|
||||
}
|
||||
|
||||
advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING,
|
||||
|
@ -1425,6 +1536,22 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
|
|||
isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
|
||||
isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
|
||||
|
||||
/* Process PME interrupt as the first one to do not miss PME requester id */
|
||||
if (isr0_status & PCIE_MSG_PM_PME_MASK)
|
||||
advk_pcie_handle_pme(pcie);
|
||||
|
||||
/* Process ERR interrupt */
|
||||
if (isr0_status & PCIE_ISR0_ERR_MASK) {
|
||||
advk_writel(pcie, PCIE_ISR0_ERR_MASK, PCIE_ISR0_REG);
|
||||
|
||||
/*
|
||||
* Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use
|
||||
* PCIe interrupt 0
|
||||
*/
|
||||
if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL)
|
||||
dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
|
||||
}
|
||||
|
||||
/* Process MSI interrupts */
|
||||
if (isr0_status & PCIE_ISR0_MSI_INT_PENDING)
|
||||
advk_pcie_handle_msi(pcie);
|
||||
|
@ -1437,28 +1564,50 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
|
|||
advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i),
|
||||
PCIE_ISR1_REG);
|
||||
|
||||
generic_handle_domain_irq(pcie->irq_domain, i);
|
||||
if (generic_handle_domain_irq(pcie->irq_domain, i) == -EINVAL)
|
||||
dev_err_ratelimited(&pcie->pdev->dev, "unexpected INT%c IRQ\n",
|
||||
(char)i + 'A');
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
|
||||
static void advk_pcie_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct advk_pcie *pcie = arg;
|
||||
u32 status;
|
||||
struct advk_pcie *pcie = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
u32 val, mask, status;
|
||||
|
||||
status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
|
||||
if (!(status & PCIE_IRQ_CORE_INT))
|
||||
return IRQ_NONE;
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
advk_pcie_handle_int(pcie);
|
||||
val = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
|
||||
mask = advk_readl(pcie, HOST_CTRL_INT_MASK_REG);
|
||||
status = val & ((~mask) & PCIE_IRQ_ALL_MASK);
|
||||
|
||||
/* Clear interrupt */
|
||||
advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
|
||||
if (status & PCIE_IRQ_CORE_INT) {
|
||||
advk_pcie_handle_int(pcie);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
/* Clear interrupt */
|
||||
advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
|
||||
static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
{
|
||||
struct advk_pcie *pcie = dev->bus->sysdata;
|
||||
|
||||
/*
|
||||
* Emulated root bridge has its own emulated irq chip and irq domain.
|
||||
* Argument pin is the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and
|
||||
* hwirq for irq_create_mapping() is indexed from zero.
|
||||
*/
|
||||
if (pci_is_root_bus(dev->bus))
|
||||
return irq_create_mapping(pcie->rp_irq_domain, pin - 1);
|
||||
else
|
||||
return of_irq_parse_and_map_pci(dev, slot, pin);
|
||||
}
|
||||
|
||||
static void advk_pcie_disable_phy(struct advk_pcie *pcie)
|
||||
{
|
||||
phy_power_off(pcie->phy);
|
||||
phy_exit(pcie->phy);
|
||||
|
@ -1522,7 +1671,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
|
|||
struct advk_pcie *pcie;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct resource_entry *entry;
|
||||
int ret, irq;
|
||||
int ret;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie));
|
||||
if (!bridge)
|
||||
|
@ -1608,17 +1757,9 @@ static int advk_pcie_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
|
||||
IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
|
||||
pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register interrupt\n");
|
||||
return ret;
|
||||
}
|
||||
pcie->irq = platform_get_irq(pdev, 0);
|
||||
if (pcie->irq < 0)
|
||||
return pcie->irq;
|
||||
|
||||
pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node,
|
||||
"reset-gpios", 0,
|
||||
|
@ -1667,11 +1808,24 @@ static int advk_pcie_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = advk_pcie_init_rp_irq_domain(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize irq\n");
|
||||
advk_pcie_remove_msi_irq_domain(pcie);
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_set_chained_handler_and_data(pcie->irq, advk_pcie_irq_handler, pcie);
|
||||
|
||||
bridge->sysdata = pcie;
|
||||
bridge->ops = &advk_pcie_ops;
|
||||
bridge->map_irq = advk_pcie_map_irq;
|
||||
|
||||
ret = pci_host_probe(bridge);
|
||||
if (ret < 0) {
|
||||
irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
|
||||
advk_pcie_remove_rp_irq_domain(pcie);
|
||||
advk_pcie_remove_msi_irq_domain(pcie);
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
return ret;
|
||||
|
@ -1719,7 +1873,11 @@ static int advk_pcie_remove(struct platform_device *pdev)
|
|||
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
|
||||
advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
|
||||
|
||||
/* Remove IRQ handler */
|
||||
irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
|
||||
|
||||
/* Remove IRQ domains */
|
||||
advk_pcie_remove_rp_irq_domain(pcie);
|
||||
advk_pcie_remove_msi_irq_domain(pcie);
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
|
||||
|
|
Loading…
Reference in a new issue