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.red...@avionic-design.de>
---
 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-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to