diff -drupN a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c --- a/drivers/pci/host/pcie-designware.c 2018-08-06 17:23:04.000000000 +0300 +++ b/drivers/pci/host/pcie-designware.c 2022-06-12 05:28:14.000000000 +0300 @@ -101,6 +101,12 @@ #define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) #define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) +#define PCIE_MISC_CONTROL_1_CFG 0x8bc + +#ifdef CONFIG_ARCH_SUN50IW6 +extern void __iomem *dbi_base; +#endif + static struct pci_ops dw_pcie_ops; int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val) @@ -335,6 +341,29 @@ static void dw_pcie_msi_set_irq(struct p dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); } +static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) +{ + int flag = 1; + + do { + pos = find_next_zero_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos); + /*if you have reached to the end then get out from here.*/ + if (pos == MAX_MSI_IRQS) + return -ENOSPC; + /* + * Check if this position is at correct offset.nvec is always a + * power of two. pos0 must be nvec bit aligned. + */ + if (pos % msgvec) + pos += msgvec - (pos % msgvec); + else + flag = 0; + } while (flag); + + *pos0 = pos; + return 0; +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int irq, pos0, i; @@ -379,6 +408,68 @@ no_valid_irq: return -ENOSPC; } +static int assign_irq_msix(int no_irqs, struct msi_desc *desc, int *pos) +{ + int irq, pos0, pos1, i; + struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(desc); + + pos0 = find_first_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS); + if (pos0 % no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) + goto no_valid_irq; + } + if (no_irqs > 1) { + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + /* there must be nvec number of consecutive free bits */ + while ((pos1 - pos0) < no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) + goto no_valid_irq; + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + } + } + + if (pos0 < 0) + goto no_valid_irq; + + irq = irq_find_mapping(pp->irq_domain, pos0); + if (!irq) + goto no_valid_irq; + + /* + * irq_create_mapping (called from dw_pcie_host_init) pre-allocates + * descs so there is no need to allocate descs here. We can therefore + * assume that if irq_find_mapping above returns non-zero, then the + * descs are also successfully allocated. + */ + + for (i = 0; i < no_irqs; i++) { + if (irq_set_msi_desc_off(irq, i, desc) != 0) { + clear_irq_range(pp, irq, i, pos0); + goto no_valid_irq; + } + set_bit(pos0 + i, pp->msi_irq_in_use); + /*Enable corresponding interrupt in MSI interrupt controller */ + + if (pp->ops->msi_set_irq) + pp->ops->msi_set_irq(pp, pos0 + i); + else + dw_pcie_msi_set_irq(pp, pos0 + i); + + } + desc->nvec_used = no_irqs; + desc->msi_attrib.multiple = order_base_2(no_irqs); + + *pos = pos0; + return irq; + +no_valid_irq: + *pos = pos0; + return -ENOSPC; +} + static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos) { struct msi_msg msg; @@ -406,8 +497,14 @@ static int dw_msi_setup_irq(struct msi_c int irq, pos; struct pcie_port *pp = pdev->bus->sysdata; - if (desc->msi_attrib.is_msix) - return -EINVAL; + if (desc->msi_attrib.is_msix) { + irq = assign_irq_msix(1, desc, &pos); + if (irq < 0) + return irq; + dw_msi_setup_msg(pp, irq, pos); + + return 0; + } irq = assign_irq(1, desc, &pos); if (irq < 0) @@ -427,8 +524,18 @@ static int dw_msi_setup_irqs(struct msi_ struct pcie_port *pp = pdev->bus->sysdata; /* MSI-X interrupts are not supported */ - if (type == PCI_CAP_ID_MSIX) - return -EINVAL; + + if (type == PCI_CAP_ID_MSIX) { + for_each_pci_msi_entry(desc, pdev) { + irq = assign_irq_msix(1, desc, &pos); + if (irq < 0) + return irq; + + dw_msi_setup_msg(pp, irq, pos); + } + + return 0; + } WARN_ON(!list_is_singular(&pdev->dev.msi_list)); desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list); @@ -586,7 +693,7 @@ int dw_pcie_host_init(struct pcie_port * goto error; } } - + dbi_base = pp->dbi_base; pp->mem_base = pp->mem->start; if (!pp->va_cfg0_base) { @@ -893,8 +1000,14 @@ void dw_pcie_setup_rc(struct pcie_port * dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); + dw_pcie_rd_own_conf(pp, PCIE_MISC_CONTROL_1_CFG, 4, &val); + val |= 0x1; + dw_pcie_wr_own_conf(pp, PCIE_MISC_CONTROL_1_CFG, 4, val); /* program correct class for RC */ dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); + dw_pcie_rd_own_conf(pp, PCIE_MISC_CONTROL_1_CFG, 4, &val); + val &= ~(0x1<<0); + dw_pcie_wr_own_conf(pp, PCIE_MISC_CONTROL_1_CFG, 4, val); dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); val |= PORT_LOGIC_SPEED_CHANGE;