[RFC 2/2] PCI: tegra: Use new MSI chip infrastructure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Implement an MSI chip that uses the Tegra PCIe controller's built-in
support to provide MSI services to the root bus and its children.

Signed-off-by: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>
---
 drivers/pci/host/pci-tegra.c | 105 ++++++++++++++++++++++++-------------------
 1 file changed, 60 insertions(+), 45 deletions(-)

diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 1efd746..19c250f 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -183,14 +183,20 @@
 #define  PADS_PLL_CTL_TXCLKREF_DIV10		(0 << 20)
 #define  PADS_PLL_CTL_TXCLKREF_DIV5		(1 << 20)
 
-struct tegra_pcie_msi {
+struct tegra_msi {
 	DECLARE_BITMAP(used, INT_PCI_MSI_NR);
 	struct irq_domain *domain;
+	struct msi_chip chip;
 	unsigned long pages;
 	struct mutex lock;
 	int irq;
 };
 
+static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
+{
+	return container_of(chip, struct tegra_msi, chip);
+}
+
 struct tegra_pcie {
 	struct device *dev;
 
@@ -211,7 +217,7 @@ struct tegra_pcie {
 	struct clk *pcie_xclk;
 	struct clk *pll_e;
 
-	struct tegra_pcie_msi msi;
+	struct tegra_msi msi;
 
 	struct list_head ports;
 	unsigned int num_ports;
@@ -605,6 +611,9 @@ static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys)
 	if (!bus)
 		return NULL;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		bus->msi = &pcie->msi.chip;
+
 	pci_scan_child_bus(bus);
 
 	return bus;
@@ -1001,38 +1010,41 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
 	return 0;
 }
 
-static int tegra_pcie_msi_alloc(struct tegra_pcie *pcie)
+static int tegra_msi_alloc(struct tegra_msi *chip)
 {
 	int msi;
 
-	mutex_lock(&pcie->msi.lock);
+	mutex_lock(&chip->lock);
 
-	msi = find_first_zero_bit(pcie->msi.used, INT_PCI_MSI_NR);
+	msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
 	if (msi < INT_PCI_MSI_NR)
-		set_bit(msi, pcie->msi.used);
+		set_bit(msi, chip->used);
 	else
 		msi = -ENOSPC;
 
-	mutex_unlock(&pcie->msi.lock);
+	mutex_unlock(&chip->lock);
 
 	return msi;
 }
 
-static void tegra_pcie_msi_free(struct tegra_pcie *pcie, unsigned long irq)
+static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq)
 {
-	mutex_lock(&pcie->msi.lock);
+	struct device *dev = chip->chip.dev;
+
+	mutex_lock(&chip->lock);
 
-	if (!test_bit(irq, pcie->msi.used))
-		dev_err(pcie->dev, "trying to free unused MSI#%lu\n", irq);
+	if (!test_bit(irq, chip->used))
+		dev_err(dev, "trying to free unused MSI#%lu\n", irq);
 	else
-		clear_bit(irq, pcie->msi.used);
+		clear_bit(irq, chip->used);
 
-	mutex_unlock(&pcie->msi.lock);
+	mutex_unlock(&chip->lock);
 }
 
 static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
 {
 	struct tegra_pcie *pcie = data;
+	struct tegra_msi *msi = &pcie->msi;
 	unsigned int i;
 
 	for (i = 0; i < 8; i++) {
@@ -1046,9 +1058,9 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
 			/* clear the interrupt */
 			afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4);
 
-			irq = irq_find_mapping(pcie->msi.domain, index);
+			irq = irq_find_mapping(msi->domain, index);
 			if (irq) {
-				if (test_bit(index, pcie->msi.used))
+				if (test_bit(index, msi->used))
 					generic_handle_irq(irq);
 				else
 					dev_info(pcie->dev, "unhandled MSI\n");
@@ -1068,20 +1080,20 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-#ifdef CONFIG_PCI_MSI
-/* called by arch_setup_msi_irqs in drivers/pci/msi.c */
-int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+			       struct msi_desc *desc)
 {
-	struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
+	struct tegra_msi *msi = to_tegra_msi(chip);
+	struct tegra_pcie *pcie = container_of(chip, struct tegra_pcie, msi.chip);
 	struct msi_msg msg;
 	unsigned int irq;
 	int hwirq;
 
-	hwirq = tegra_pcie_msi_alloc(pcie);
+	hwirq = tegra_msi_alloc(msi);
 	if (hwirq < 0)
 		return hwirq;
 
-	irq = irq_create_mapping(pcie->msi.domain, hwirq);
+	irq = irq_create_mapping(msi->domain, hwirq);
 	if (!irq)
 		return -EINVAL;
 
@@ -1097,16 +1109,15 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
 	return 0;
 }
 
-void arch_teardown_msi_irq(unsigned int irq)
+static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq)
 {
-	struct tegra_pcie *pcie = irq_get_chip_data(irq);
+	struct tegra_msi *msi = to_tegra_msi(chip);
 	struct irq_data *d = irq_get_irq_data(irq);
 
-	tegra_pcie_msi_free(pcie, d->hwirq);
+	tegra_msi_free(msi, d->hwirq);
 }
-#endif
 
-static struct irq_chip tegra_pcie_msi_irq_chip = {
+static struct irq_chip tegra_msi_irq_chip = {
 	.name = "Tegra PCIe MSI",
 	.irq_enable = unmask_msi_irq,
 	.irq_disable = mask_msi_irq,
@@ -1114,11 +1125,10 @@ static struct irq_chip tegra_pcie_msi_irq_chip = {
 	.irq_unmask = unmask_msi_irq,
 };
 
-static int tegra_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
-			      irq_hw_number_t hwirq)
+static int tegra_msi_map(struct irq_domain *domain, unsigned int irq,
+			 irq_hw_number_t hwirq)
 {
-	irq_set_chip_and_handler(irq, &tegra_pcie_msi_irq_chip,
-				 handle_simple_irq);
+	irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq);
 	irq_set_chip_data(irq, domain->host_data);
 	set_irq_flags(irq, IRQF_VALID);
 
@@ -1126,22 +1136,26 @@ static int tegra_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
 }
 
 static const struct irq_domain_ops msi_domain_ops = {
-	.map = tegra_pcie_msi_map,
+	.map = tegra_msi_map,
 };
 
 static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 {
 	struct platform_device *pdev = to_platform_device(pcie->dev);
+	struct tegra_msi *msi = &pcie->msi;
 	unsigned long base;
 	int err;
 	u32 reg;
 
-	mutex_init(&pcie->msi.lock);
+	mutex_init(&msi->lock);
+
+	msi->chip.dev = pcie->dev;
+	msi->chip.setup_irq = tegra_msi_setup_irq;
+	msi->chip.teardown_irq = tegra_msi_teardown_irq;
 
-	pcie->msi.domain = irq_domain_add_linear(pcie->dev->of_node,
-						 INT_PCI_MSI_NR,
-						 &msi_domain_ops, pcie);
-	if (!pcie->msi.domain) {
+	msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+					    &msi_domain_ops, &msi->chip);
+	if (!msi->domain) {
 		dev_err(&pdev->dev, "failed to create IRQ domain\n");
 		return -ENOMEM;
 	}
@@ -1152,18 +1166,18 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 		goto err;
 	}
 
-	pcie->msi.irq = err;
+	msi->irq = err;
 
-	err = devm_request_irq(&pdev->dev, pcie->msi.irq, tegra_pcie_msi_irq,
-			       0, tegra_pcie_msi_irq_chip.name, pcie);
+	err = devm_request_irq(&pdev->dev, msi->irq, tegra_pcie_msi_irq,
+			       0, tegra_msi_irq_chip.name, pcie);
 	if (err < 0) {
 		dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
 		goto err;
 	}
 
 	/* setup AFI/FPCI range */
-	pcie->msi.pages = __get_free_pages(GFP_KERNEL, 3);
-	base = virt_to_phys((void *)pcie->msi.pages);
+	msi->pages = __get_free_pages(GFP_KERNEL, 3);
+	base = virt_to_phys((void *)msi->pages);
 
 	afi_writel(pcie, base, AFI_MSI_FPCI_BAR_ST);
 	afi_writel(pcie, base, AFI_MSI_AXI_BAR_ST);
@@ -1188,12 +1202,13 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 	return 0;
 
 err:
-	irq_domain_remove(pcie->msi.domain);
+	irq_domain_remove(msi->domain);
 	return err;
 }
 
 static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
 {
+	struct tegra_msi *msi = &pcie->msi;
 	unsigned int i, irq;
 	u32 value;
 
@@ -1212,15 +1227,15 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
 	afi_writel(pcie, 0, AFI_MSI_EN_VEC6);
 	afi_writel(pcie, 0, AFI_MSI_EN_VEC7);
 
-	free_pages(pcie->msi.pages, 3);
+	free_pages(msi->pages, 3);
 
 	for (i = 0; i < INT_PCI_MSI_NR; i++) {
-		irq = irq_find_mapping(pcie->msi.domain, i);
+		irq = irq_find_mapping(msi->domain, i);
 		if (irq > 0)
 			irq_dispose_mapping(irq);
 	}
 
-	irq_domain_remove(pcie->msi.domain);
+	irq_domain_remove(msi->domain);
 
 	return 0;
 }
-- 
1.8.1.5

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux