mirror of https://github.com/OpenIPC/firmware.git
178 lines
4.8 KiB
Diff
178 lines
4.8 KiB
Diff
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;
|