[PATCH V3] mmc: sdhci: supporting PCI MSI

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

 



Enable MSI support in sdhci-pci driver and provide the mechanism
to fall back to Legacy Pin-based Interrupt if MSI register fails.
That is, sdhci-pci driver first checks and enables MSI. If error occurs,
it will fall to use Legacy Pin-based Interrupt.

Tested with SD cards on AMD platform.

Signed-off-by: Jackey Shen <Jackey.Shen@xxxxxxx>
---
This patch is based on the patches e6039832 and 210b7d28 and applies to
the mmc-next branch.

NOTE:
If SD host controllers support and enable PCI MSI successfully, but doesn't
function well, the kind of patch like patch 210b7d28 should be added.

V3:
- clarify and tidy source code logic
- export sdhci_irq function to be used in sdhci-pci driver

V2:
- implement this PCI MSI feature in sdhci-pci.c instead of sdhci.c
---
 drivers/mmc/host/sdhci-pci.c | 53 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/mmc/host/sdhci.c     | 27 +++++++++++++---------
 drivers/mmc/host/sdhci.h     |  1 +
 include/linux/mmc/sdhci.h    |  2 ++
 4 files changed, 70 insertions(+), 13 deletions(-)

diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 8f75381..0337dc4 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -1143,6 +1143,45 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
 		slot->hw_reset(host);
 }
 
+static int sdhci_pci_enable_msi(struct sdhci_host *host)
+{
+	int ret;
+	struct pci_dev *pdev = to_pci_dev(host->mmc->parent);
+
+	if (!host->msi_enabled) {
+		ret = pci_enable_msi(pdev);
+		if (ret) {
+			dev_warn(&pdev->dev, "Fail to allocate MSI entry\n");
+			host->msi_enabled = false;
+			return ret;
+		}
+	}
+
+	ret = request_irq(pdev->irq, sdhci_irq, 0,
+		mmc_hostname(host->mmc), host);
+	if (ret) {
+		dev_warn(&pdev->dev, "Fail to request MSI IRQ %d: %d\n",
+		       pdev->irq, ret);
+		pci_disable_msi(pdev);
+		return ret;
+	}
+
+	host->irq = pdev->irq;
+	host->msi_enabled = true;
+	return ret;
+}
+
+static void sdhci_pci_disable_msi(struct sdhci_host *host)
+{
+	struct pci_dev *pdev = to_pci_dev(host->mmc->parent);
+
+	if (host->msi_enabled) {
+		pci_disable_msi(pdev);
+		host->irq = pdev->irq;
+		host->msi_enabled = false;
+	}
+}
+
 static const struct sdhci_ops sdhci_pci_ops = {
 	.enable_dma	= sdhci_pci_enable_dma,
 	.platform_bus_width	= sdhci_pci_bus_width,
@@ -1242,6 +1281,10 @@ static int sdhci_pci_resume(struct device *dev)
 		if (!slot)
 			continue;
 
+		ret = sdhci_pci_enable_msi(slot->host);
+		if (ret)
+			dev_warn(&pdev->dev, "Fall back to Pin-based Interrupt: %d\n", ret);
+
 		ret = sdhci_resume_host(slot->host);
 		if (ret)
 			return ret;
@@ -1415,8 +1458,6 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
 	host->quirks = chip->quirks;
 	host->quirks2 = chip->quirks2;
 
-	host->irq = pdev->irq;
-
 	ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
 	if (ret) {
 		dev_err(&pdev->dev, "cannot request region\n");
@@ -1436,6 +1477,10 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
 			goto unmap;
 	}
 
+	ret = sdhci_pci_enable_msi(host);
+	if (ret)
+		dev_warn(&pdev->dev, "Fall back to Pin-based Interrupt: %d\n", ret);
+
 	if (gpio_is_valid(slot->rst_n_gpio)) {
 		if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) {
 			gpio_direction_output(slot->rst_n_gpio, 1);
@@ -1463,6 +1508,8 @@ remove:
 	if (gpio_is_valid(slot->rst_n_gpio))
 		gpio_free(slot->rst_n_gpio);
 
+	sdhci_pci_disable_msi(host);
+
 	if (chip->fixes && chip->fixes->remove_slot)
 		chip->fixes->remove_slot(slot, 0);
 
@@ -1499,6 +1546,8 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
 	if (gpio_is_valid(slot->rst_n_gpio))
 		gpio_free(slot->rst_n_gpio);
 
+	sdhci_pci_disable_msi(slot->host);
+
 	if (slot->chip->fixes && slot->chip->fixes->remove_slot)
 		slot->chip->fixes->remove_slot(slot, dead);
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index bd8a098..bd64825 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2412,7 +2412,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 	}
 }
 
-static irqreturn_t sdhci_irq(int irq, void *dev_id)
+irqreturn_t sdhci_irq(int irq, void *dev_id)
 {
 	irqreturn_t result;
 	struct sdhci_host *host = dev_id;
@@ -2527,6 +2527,7 @@ out:
 
 	return result;
 }
+EXPORT_SYMBOL_GPL(sdhci_irq);
 
 /*****************************************************************************\
  *                                                                           *
@@ -2597,10 +2598,12 @@ int sdhci_resume_host(struct sdhci_host *host)
 	}
 
 	if (!device_may_wakeup(mmc_dev(host->mmc))) {
-		ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
-				  mmc_hostname(host->mmc), host);
-		if (ret)
-			return ret;
+		if (!host->msi_enabled) {
+			ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+					  mmc_hostname(host->mmc), host);
+			if (ret)
+				return ret;
+		}
 	} else {
 		sdhci_disable_irq_wakeups(host);
 		disable_irq_wake(host->irq);
@@ -3220,12 +3223,14 @@ int sdhci_add_host(struct sdhci_host *host)
 
 	sdhci_init(host, 0);
 
-	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
-		mmc_hostname(mmc), host);
-	if (ret) {
-		pr_err("%s: Failed to request IRQ %d: %d\n",
-		       mmc_hostname(mmc), host->irq, ret);
-		goto untasklet;
+	if (!host->msi_enabled) {
+		ret = request_irq(host->irq, sdhci_irq,	IRQF_SHARED,
+				mmc_hostname(host->mmc), host);
+		if (ret) {
+			pr_err("%s: Failed to request IRQ %d: %d\n",
+				mmc_hostname(mmc), host->irq, ret);
+			goto untasklet;
+		}
 	}
 
 #ifdef CONFIG_MMC_DEBUG
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 0a3ed01..597e53d 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -396,6 +396,7 @@ extern int sdhci_add_host(struct sdhci_host *host);
 extern void sdhci_remove_host(struct sdhci_host *host, int dead);
 extern void sdhci_send_command(struct sdhci_host *host,
 				struct mmc_command *cmd);
+extern irqreturn_t sdhci_irq(int irq, void *dev_id);
 
 #ifdef CONFIG_PM
 extern int sdhci_suspend_host(struct sdhci_host *host);
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 3e781b8..e36b74f 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -99,6 +99,8 @@ struct sdhci_host {
 /* Controller has a non-standard host control register */
 #define SDHCI_QUIRK2_BROKEN_HOST_CONTROL		(1<<5)
 
+	bool msi_enabled;	/* PCI MSI is enabled or not */
+
 	int irq;		/* Device IRQ */
 	void __iomem *ioaddr;	/* Mapped address */
 
-- 
1.8.1.2


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




[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux