Skip to content

Commit

Permalink
mmc: sdhci-pci: add runtime pm support
Browse files Browse the repository at this point in the history
Ths patch allows runtime PM for sdhci-pci, runtime suspending after
inactivity of 50ms and ensuring runtime resume before SDHC registers
are accessed.  During runtime suspend, interrupts are masked.
The host controller state is restored at runtime resume.

For Medfield, the host controller's card detect mechanism is
supplanted by an always-on GPIO which provides for card detect wake-up.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
  • Loading branch information
Adrian Hunter authored and Chris Ball committed Oct 26, 2011
1 parent 08a7e1d commit 66fd8ad
Show file tree
Hide file tree
Showing 4 changed files with 405 additions and 45 deletions.
182 changes: 181 additions & 1 deletion drivers/mmc/host/sdhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/sfi.h>
#include <linux/pm_runtime.h>

#include "sdhci.h"

Expand Down Expand Up @@ -63,6 +64,8 @@ struct sdhci_pci_slot {

int pci_bar;
int rst_n_gpio;
int cd_gpio;
int cd_irq;
};

struct sdhci_pci_chip {
Expand Down Expand Up @@ -190,6 +193,70 @@ static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
return 0;
}

#ifdef CONFIG_PM_RUNTIME

static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
{
struct sdhci_pci_slot *slot = dev_id;
struct sdhci_host *host = slot->host;

mmc_detect_change(host->mmc, msecs_to_jiffies(200));
return IRQ_HANDLED;
}

#define MFLD_SD_CD_PIN 69

static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
{
int err, irq, gpio = MFLD_SD_CD_PIN;

slot->cd_gpio = -EINVAL;
slot->cd_irq = -EINVAL;

err = gpio_request(gpio, "sd_cd");
if (err < 0)
goto out;

err = gpio_direction_input(gpio);
if (err < 0)
goto out_free;

irq = gpio_to_irq(gpio);
if (irq < 0)
goto out_free;

err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING, "sd_cd", slot);
if (err)
goto out_free;

slot->cd_gpio = gpio;
slot->cd_irq = irq;
slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION;

return 0;

out_free:
gpio_free(gpio);
out:
dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
return 0;
}

static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead)
{
if (slot->cd_irq >= 0)
free_irq(slot->cd_irq, slot);
gpio_free(slot->cd_gpio);
}

#else

#define mfd_sd_probe_slot NULL
#define mfd_sd_remove_slot NULL

#endif

static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
const char *name = NULL;
Expand All @@ -214,7 +281,7 @@ static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
}

slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;

slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;

Expand All @@ -238,6 +305,8 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {

static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.probe_slot = mfd_sd_probe_slot,
.remove_slot = mfd_sd_remove_slot,
};

static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
Expand Down Expand Up @@ -1018,6 +1087,95 @@ static int sdhci_pci_resume(struct pci_dev *pdev)

#endif /* CONFIG_PM */

#ifdef CONFIG_PM_RUNTIME

static int sdhci_pci_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
pm_message_t state = { .event = PM_EVENT_SUSPEND };
int i, ret;

chip = pci_get_drvdata(pdev);
if (!chip)
return 0;

for (i = 0; i < chip->num_slots; i++) {
slot = chip->slots[i];
if (!slot)
continue;

ret = sdhci_runtime_suspend_host(slot->host);

if (ret) {
for (i--; i >= 0; i--)
sdhci_runtime_resume_host(chip->slots[i]->host);
return ret;
}
}

if (chip->fixes && chip->fixes->suspend) {
ret = chip->fixes->suspend(chip, state);
if (ret) {
for (i = chip->num_slots - 1; i >= 0; i--)
sdhci_runtime_resume_host(chip->slots[i]->host);
return ret;
}
}

return 0;
}

static int sdhci_pci_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
int i, ret;

chip = pci_get_drvdata(pdev);
if (!chip)
return 0;

if (chip->fixes && chip->fixes->resume) {
ret = chip->fixes->resume(chip);
if (ret)
return ret;
}

for (i = 0; i < chip->num_slots; i++) {
slot = chip->slots[i];
if (!slot)
continue;

ret = sdhci_runtime_resume_host(slot->host);
if (ret)
return ret;
}

return 0;
}

static int sdhci_pci_runtime_idle(struct device *dev)
{
return 0;
}

#else

#define sdhci_pci_runtime_suspend NULL
#define sdhci_pci_runtime_resume NULL
#define sdhci_pci_runtime_idle NULL

#endif

static const struct dev_pm_ops sdhci_pci_pm_ops = {
.runtime_suspend = sdhci_pci_runtime_suspend,
.runtime_resume = sdhci_pci_runtime_resume,
.runtime_idle = sdhci_pci_runtime_idle,
};

/*****************************************************************************\
* *
* Device probing/removal *
Expand Down Expand Up @@ -1133,6 +1291,21 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_free_host(slot->host);
}

static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev)
{
pm_runtime_put_noidle(dev);
pm_runtime_allow(dev);
pm_runtime_set_autosuspend_delay(dev, 50);
pm_runtime_use_autosuspend(dev);
pm_suspend_ignore_children(dev, 1);
}

static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev)
{
pm_runtime_forbid(dev);
pm_runtime_get_noresume(dev);
}

static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
Expand Down Expand Up @@ -1208,6 +1381,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->slots[i] = slot;
}

sdhci_pci_runtime_pm_allow(&pdev->dev);

return 0;

free:
Expand All @@ -1224,6 +1399,8 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
int i;
struct sdhci_pci_chip *chip;

sdhci_pci_runtime_pm_forbid(&pdev->dev);

chip = pci_get_drvdata(pdev);

if (chip) {
Expand All @@ -1244,6 +1421,9 @@ static struct pci_driver sdhci_driver = {
.remove = __devexit_p(sdhci_pci_remove),
.suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume,
.driver = {
.pm = &sdhci_pci_pm_ops
},
};

/*****************************************************************************\
Expand Down
Loading

0 comments on commit 66fd8ad

Please sign in to comment.