[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1363942307-9327-3-git-send-email-thierry.reding@avionic-design.de>
Date: Fri, 22 Mar 2013 09:51:47 +0100
From: Thierry Reding <thierry.reding@...onic-design.de>
To: Bjorn Helgaas <bhelgaas@...gle.com>
Cc: Arnd Bergmann <arnd@...db.de>, linux-pci@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [RFC 2/2] PCI: tegra: Use new MSI chip infrastructure
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@...onic-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 majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists