On 11/12/2013 05:27 PM, Jackey Shen wrote: > On Tue, Nov 12, 2013 at 03:44:40PM +0800, Aaron Lu wrote: >> On 11/12/2013 02:56 PM, Jackey Shen wrote: >>> Enable MSI support in sdhci-pci driver and provide the mechanism >>> to fall back to Legacy Pin-based Interrupt if MSI register fails. >>> >>> Tested with SD cards on AMD platform. >>> >> >> -> >> >>> Change from V1: >>> - implement this PCI MSI feature in sdhci-pci.c instead of sdhci.c >> >> Such words can be placed under the --- below so that it will not appear >> in git log. >> >>> >>> Signed-off-by: Jackey Shen <Jackey.Shen@xxxxxxx> >>> Reviewed-by: Huang Rui <ray.huang@xxxxxxx> >>> --- >> >> -> >> Put change related info Here. > > OK. I will update my patch. > >> >>> drivers/mmc/host/Kconfig | 11 +++++++++ >>> drivers/mmc/host/sdhci-pci.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ >>> drivers/mmc/host/sdhci.c | 42 ++++++++++++++++++++++++++------ >>> drivers/mmc/host/sdhci.h | 2 ++ >>> include/linux/mmc/sdhci.h | 2 ++ >>> 5 files changed, 107 insertions(+), 8 deletions(-) >>> >>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig >>> index 7fc5099..a56cf27 100644 >>> --- a/drivers/mmc/host/Kconfig >>> +++ b/drivers/mmc/host/Kconfig >>> @@ -68,6 +68,17 @@ config MMC_SDHCI_PCI >>> >>> If unsure, say N. >>> >>> +config MMC_SDHCI_PCI_MSI >>> + bool "SDHCI support with MSI on PCI bus" >>> + depends on MMC_SDHCI_PCI && PCI_MSI >>> + help >>> + This enables PCI MSI (Message Signaled Interrupts) for Secure >>> + Digital Host Controller Interface. >>> + >>> + If you have a controller with this capability, say Y or M here. >>> + >>> + If unsure, say N. >>> + >>> config MMC_RICOH_MMC >>> bool "Ricoh MMC Controller Disabler" >>> depends on MMC_SDHCI_PCI >>> diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c >>> index 8f75381..2ca29c0 100644 >>> --- a/drivers/mmc/host/sdhci-pci.c >>> +++ b/drivers/mmc/host/sdhci-pci.c >>> @@ -1143,10 +1143,68 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) >>> slot->hw_reset(host); >>> } >>> >>> +#ifdef CONFIG_MMC_SDHCI_PCI_MSI >>> +static int sdhci_pci_setup_msi(struct sdhci_host *host, irq_handler_t handler) >>> +{ >>> + int ret; >>> + struct pci_dev *pdev = to_pci_dev(host->mmc->parent); >>> + >>> + host->msi_enabled = false; >>> + >>> + ret = pci_enable_msi(pdev); >>> + if (ret) { >>> + pr_warn("%s: Failed to allocate MSI entry\n", >>> + mmc_hostname(host->mmc)); >>> + return ret; >>> + } >>> + >>> + ret = request_irq(pdev->irq, handler, 0, >>> + mmc_hostname(host->mmc), host); >>> + if (ret) { >>> + pr_warn("%s: Failed to request MSI IRQ %d: %d\n", >>> + mmc_hostname(host->mmc), pdev->irq, ret); >>> + pci_disable_msi(pdev); >>> + return ret; >>> + } >>> + >>> + host->irq = pdev->irq; >>> + host->msi_enabled = true; >>> + return ret; >>> +} >> >> What about we only call pci_enable_msi in sdhci-pci.c and then assign >> host->irq appropriately before calling sdhci_add_host, will that work? >> The only difference would be, request_irq will have SHARED flag, but I >> suppose that's not a problem. >> > There are 2 points for this part: > 1. fall back to legacy interrupt right after MSI request fails; If we call pci_enable_msi somewhere in sdhci_pci_probe_slot, then if it failed, we can also fall back I suppose? > 2. prompt user more information about where error/warning occur. > >>> + >>> +static int sdhci_pci_enable_msi(struct sdhci_host *host, irq_handler_t handler) >>> +{ >>> + return sdhci_pci_setup_msi(host, handler); >>> +} >>> + >>> +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->msi_enabled = false; >>> + } >>> +} >>> + >>> +#else >>> + >>> +static int sdhci_pci_enable_msi(struct sdhci_host *host, irq_handler_t handler) >>> +{ >>> + return 0; >>> +} >>> + >>> +static void sdhci_pci_disable_msi(struct sdhci_host *host) >>> +{ >>> +} >>> +#endif /* CONFIG_MMC_SDHCI_PCI_MSI */ >>> + >>> static const struct sdhci_ops sdhci_pci_ops = { >>> .enable_dma = sdhci_pci_enable_dma, >>> .platform_bus_width = sdhci_pci_bus_width, >>> .hw_reset = sdhci_pci_hw_reset, >>> + .enable_msi = sdhci_pci_enable_msi, >>> + .disable_msi = sdhci_pci_disable_msi, >>> }; >>> >>> /*****************************************************************************\ >>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c >>> index 6785fb1..4c2a1ac 100644 >>> --- a/drivers/mmc/host/sdhci.c >>> +++ b/drivers/mmc/host/sdhci.c >>> @@ -2554,6 +2554,31 @@ void sdhci_disable_irq_wakeups(struct sdhci_host *host) >>> } >>> EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); >>> >>> +static int sdhci_request_irq(struct sdhci_host *host) >>> +{ >>> + int ret = 0; >>> + >>> + /* try to enable MSI */ >>> + if (host->ops->enable_msi) { >>> + ret = host->ops->enable_msi(host, sdhci_irq); >>> + if (ret) >>> + pr_warn("%s: Fall back to Pin-based Interrupt: %d\n", >>> + mmc_hostname(host->mmc), ret); >>> + } >>> + >>> + if (!host->msi_enabled) { >>> + /* fall back to legacy pin-based interrupt */ >>> + 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(host->mmc), host->irq, ret); >>> + return ret; >>> + } >>> + } >>> + >>> + return ret; >>> +} >>> int sdhci_suspend_host(struct sdhci_host *host) >>> { >>> if (host->ops->platform_suspend) >>> @@ -2570,6 +2595,8 @@ int sdhci_suspend_host(struct sdhci_host *host) >>> if (!device_may_wakeup(mmc_dev(host->mmc))) { >>> sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); >>> free_irq(host->irq, host); >>> + if (host->ops->disable_msi) >>> + host->ops->disable_msi(host); >> >> What would happen if we do not disable/enable msi in suspend/resume host? >> We have already masked all irqs with sdhci_mask_irqs anyway. >> > We should also remove request|free_irq at the same time, right? > I will do some tests. The comments above free_irq says: * on the card it drives before calling this function. The function * does not return until any executing interrupts for this IRQ * have completed. It feels like a guard for the interrupt handler: after we call free_irq, we are sure no interrupt hander is running(and since we have masked irq, no more interrupt will occur either), so I suggest we keep it. Thanks, Aaron >>> } else { >>> sdhci_enable_irq_wakeups(host); >>> enable_irq_wake(host->irq); >>> @@ -2589,8 +2616,7 @@ 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); >>> + ret = sdhci_request_irq(host); >>> if (ret) >>> return ret; >>> } else { >>> @@ -3212,13 +3238,9 @@ 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); >>> + ret = sdhci_request_irq(host); >>> + if (ret) >>> goto untasklet; >>> - } >>> >>> #ifdef CONFIG_MMC_DEBUG >>> sdhci_dumpregs(host); >>> @@ -3258,6 +3280,8 @@ reset: >>> sdhci_reset(host, SDHCI_RESET_ALL); >>> sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); >>> free_irq(host->irq, host); >>> + if (host->ops->disable_msi) >>> + host->ops->disable_msi(host); >>> #endif >>> untasklet: >>> tasklet_kill(&host->card_tasklet); >>> @@ -3301,6 +3325,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) >>> >>> sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); >>> free_irq(host->irq, host); >>> + if (host->ops->disable_msi) >>> + host->ops->disable_msi(host); >>> >>> del_timer_sync(&host->timer); >>> >>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h >>> index 0a3ed01..cbee843 100644 >>> --- a/drivers/mmc/host/sdhci.h >>> +++ b/drivers/mmc/host/sdhci.h >>> @@ -296,6 +296,8 @@ struct sdhci_ops { >>> void (*adma_workaround)(struct sdhci_host *host, u32 intmask); >>> void (*platform_init)(struct sdhci_host *host); >>> void (*card_event)(struct sdhci_host *host); >>> + int (*enable_msi)(struct sdhci_host *host, irq_handler_t handler); >>> + void (*disable_msi)(struct sdhci_host *host); >>> }; >>> >>> #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS >>> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h >>> index 3e781b8..3812479 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; /* SD host controller with PCI MSI enabled */ >>> + >>> int irq; /* Device IRQ */ >>> void __iomem *ioaddr; /* Mapped address */ >>> >>> >> >> -- >> 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 >> > -- 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