Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9bc6f39f authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: pcie: Enable GICv2m based MSI"

parents 4d25a475 18fe59ae
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ Optional Properties:
  - qti,l1ss-supported: L1 sub-states (L1ss) is supported.
  - qti,aux-clk-sync: The AUX clock is synchronous to the Core clock to
    support L1ss.
  - qcom,msi-gicm-addr: MSI address for GICv2m.
  - qcom,msi-gicm-base: MSI IRQ base for GICv2m.

Example:

@@ -107,4 +109,6 @@ Example:
						<0>, <0>, <0>, <0>;
		qti,l1ss-supported;
		qti,aux-clk-sync;
		qcom,msi-gicm-addr = <0xf9040040>;
		qcom,msi-gicm-base = <0x160>;
	};
+27 −1
Original line number Diff line number Diff line
@@ -1044,6 +1044,7 @@ static int msm_pcie_enable(u32 rc_idx, u32 options)

	msm_pcie_config_controller(rc_idx);

	if (!dev->msi_gicm_addr)
		msm_pcie_config_msi_controller(dev);

	if (dev->l1ss_supported)
@@ -1220,6 +1221,31 @@ static int msm_pcie_probe(struct platform_device *pdev)
	PCIE_DBG("AUX clock is %s synchronous to Core clock.\n",
		msm_pcie_dev[rc_idx].aux_clk_sync ? "" : "not");

	msm_pcie_dev[rc_idx].msi_gicm_addr = 0;
	msm_pcie_dev[rc_idx].msi_gicm_base = 0;
	ret = of_property_read_u32((&pdev->dev)->of_node,
				"qcom,msi-gicm-addr",
				&msm_pcie_dev[rc_idx].msi_gicm_addr);

	if (ret) {
		PCIE_DBG("msi-gicm-addr does not exist.\n");
	} else {
		PCIE_DBG("msi-gicm-addr: 0x%x.\n",
				msm_pcie_dev[rc_idx].msi_gicm_addr);

		ret = of_property_read_u32((&pdev->dev)->of_node,
				"qcom,msi-gicm-base",
				&msm_pcie_dev[rc_idx].msi_gicm_base);

		if (ret) {
			pr_err("msi-gicm-base does not exist.\n");
			goto decrease_rc_num;
		} else {
			PCIE_DBG("msi-gicm-base: 0x%x.\n",
					msm_pcie_dev[rc_idx].msi_gicm_base);
		}
	}

	msm_pcie_dev[rc_idx].pdev = pdev;
	msm_pcie_dev[rc_idx].vreg_n = 0;
	msm_pcie_dev[rc_idx].gpio_n = 0;
+2 −0
Original line number Diff line number Diff line
@@ -162,6 +162,8 @@ struct msm_pcie_dev_t {

	struct irq_domain            *irq_domain;
	DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_NR_IRQS);
	uint32_t                     msi_gicm_addr;
	uint32_t                     msi_gicm_base;

	enum msm_pcie_link_status    link_status;
	bool                         user_suspend;
+128 −5
Original line number Diff line number Diff line
@@ -97,7 +97,13 @@ void msm_pcie_destroy_irq(unsigned int irq)
	int pos;
	struct msm_pcie_dev_t *dev = irq_get_chip_data(irq);

	if (dev->msi_gicm_addr) {
		PCIE_DBG("destroy QGIC based irq\n");
		pos = irq - dev->msi_gicm_base;
	} else {
		PCIE_DBG("destroy default MSI irq\n");
		pos = irq - irq_find_mapping(dev->irq_domain, 0);
	}

	PCIE_DBG("\n");

@@ -150,8 +156,8 @@ again:
	return irq;
}

/* hookup to linux pci msi framework */
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
static int arch_setup_msi_irq_default(struct pci_dev *pdev,
		struct msi_desc *desc, int nvec)
{
	int irq;
	struct msi_msg msg;
@@ -160,6 +166,9 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
	PCIE_DBG("\n");

	irq = msm_pcie_create_irq(dev);

	PCIE_DBG("IRQ %d is allocated.\n", irq);

	if (irq < 0)
		return irq;

@@ -173,11 +182,125 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
	msg.data = irq - irq_find_mapping(dev->irq_domain, 0);
	write_msi_msg(irq, &msg);

	irq_set_chip_and_handler(irq, &pcie_msi_chip, handle_simple_irq);
	set_irq_flags(irq, IRQF_VALID);
	return 0;
}

static int msm_pcie_create_irq_qgic(struct msm_pcie_dev_t *dev)
{
	int irq, pos;

	PCIE_DBG("\n");

again:
	pos = find_first_zero_bit(dev->msi_irq_in_use, PCIE_MSI_NR_IRQS);

	if (pos >= PCIE_MSI_NR_IRQS)
		return -ENOSPC;

	if (test_and_set_bit(pos, dev->msi_irq_in_use))
		goto again;

	irq = dev->msi_gicm_base + pos;
	if (!irq) {
		pr_err("PCIe: failed to create QGIC MSI IRQ.\n");
		return -EINVAL;
	}

	return irq;
}

static int arch_setup_msi_irq_qgic(struct pci_dev *pdev,
		struct msi_desc *desc, int nvec)
{
	int irq, index, firstirq = 0;
	struct msi_msg msg;
	struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev);

	PCIE_DBG("\n");

	for (index = 0; index < nvec; index++) {
		irq = msm_pcie_create_irq_qgic(dev);
		PCIE_DBG("irq %d is allocated\n", irq);

		if (irq < 0)
			return irq;

		if (index == 0)
			firstirq = irq;

		irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
	}

	/* write msi vector and data */
	irq_set_msi_desc(firstirq, desc);
	msg.address_hi = 0;
	msg.address_lo = dev->msi_gicm_addr;
	msg.data = firstirq;
	write_msi_msg(firstirq, &msg);

	return 0;
}

int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
	struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev);

	PCIE_DBG("\n");

	if (dev->msi_gicm_addr)
		return arch_setup_msi_irq_qgic(pdev, desc, 1);
	else
		return arch_setup_msi_irq_default(pdev, desc, 1);
}

static int msm_pcie_get_msi_multiple(int nvec)
{
	int msi_multiple = 0;

	PCIE_DBG("\n");

	while (nvec) {
		nvec = nvec >> 1;
		msi_multiple++;
	}
	PCIE_DBG("log2 number of MSI multiple:%d\n",
		msi_multiple - 1);

	return msi_multiple - 1;
}

int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
	struct msi_desc *entry;
	int ret;
	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev);

	PCIE_DBG("\n");

	if (type != PCI_CAP_ID_MSI || nvec > 32)
		return -ENOSPC;

	PCIE_DBG("nvec = %d\n", nvec);

	list_for_each_entry(entry, &dev->msi_list, list) {
		entry->msi_attrib.multiple =
				msm_pcie_get_msi_multiple(nvec);

		if (pcie_dev->msi_gicm_addr)
			ret = arch_setup_msi_irq_qgic(dev, entry, nvec);
		else
			ret = arch_setup_msi_irq_default(dev, entry, nvec);

		PCIE_DBG("ret from msi_irq: %d\n", ret);

		if (ret < 0)
			return ret;
		if (ret > 0)
			return -ENOSPC;
	}

	return 0;
}

static int msm_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
	   irq_hw_number_t hwirq)