-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 336630 b: refs/heads/master c: c4e0503 h: refs/heads/master v: v3
- Loading branch information
Adrian Hunter
authored and
Rafael J. Wysocki
committed
Nov 23, 2012
1 parent
c1880a4
commit 53ea835
Showing
4 changed files
with
318 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: 142b007b65aa763957627ea5c343a1001d5ed449 | ||
refs/heads/master: c4e050376c69bb9d67895842665264df2a2004d9 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,304 @@ | ||
/* | ||
* Secure Digital Host Controller Interface ACPI driver. | ||
* | ||
* Copyright (c) 2012, Intel Corporation. | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms and conditions of the GNU General Public License, | ||
* version 2, as published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
* more details. | ||
* | ||
* You should have received a copy of the GNU General Public License along with | ||
* this program; if not, write to the Free Software Foundation, Inc., | ||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
*/ | ||
|
||
#include <linux/init.h> | ||
#include <linux/export.h> | ||
#include <linux/module.h> | ||
#include <linux/device.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/ioport.h> | ||
#include <linux/io.h> | ||
#include <linux/dma-mapping.h> | ||
#include <linux/compiler.h> | ||
#include <linux/stddef.h> | ||
#include <linux/bitops.h> | ||
#include <linux/types.h> | ||
#include <linux/err.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/acpi.h> | ||
#include <linux/pm.h> | ||
#include <linux/pm_runtime.h> | ||
|
||
#include <linux/mmc/host.h> | ||
#include <linux/mmc/pm.h> | ||
#include <linux/mmc/sdhci.h> | ||
|
||
#include "sdhci.h" | ||
|
||
enum { | ||
SDHCI_ACPI_SD_CD = BIT(0), | ||
SDHCI_ACPI_RUNTIME_PM = BIT(1), | ||
}; | ||
|
||
struct sdhci_acpi_chip { | ||
const struct sdhci_ops *ops; | ||
unsigned int quirks; | ||
unsigned int quirks2; | ||
unsigned long caps; | ||
unsigned int caps2; | ||
mmc_pm_flag_t pm_caps; | ||
}; | ||
|
||
struct sdhci_acpi_slot { | ||
const struct sdhci_acpi_chip *chip; | ||
unsigned int quirks; | ||
unsigned int quirks2; | ||
unsigned long caps; | ||
unsigned int caps2; | ||
mmc_pm_flag_t pm_caps; | ||
unsigned int flags; | ||
}; | ||
|
||
struct sdhci_acpi_host { | ||
struct sdhci_host *host; | ||
const struct sdhci_acpi_slot *slot; | ||
struct platform_device *pdev; | ||
bool use_runtime_pm; | ||
}; | ||
|
||
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) | ||
{ | ||
return c->slot && (c->slot->flags & flag); | ||
} | ||
|
||
static int sdhci_acpi_enable_dma(struct sdhci_host *host) | ||
{ | ||
return 0; | ||
} | ||
|
||
static const struct sdhci_ops sdhci_acpi_ops_dflt = { | ||
.enable_dma = sdhci_acpi_enable_dma, | ||
}; | ||
|
||
static const struct acpi_device_id sdhci_acpi_ids[] = { | ||
{ "PNP0D40" }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids); | ||
|
||
static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid) | ||
{ | ||
const struct acpi_device_id *id; | ||
|
||
for (id = sdhci_acpi_ids; id->id[0]; id++) | ||
if (!strcmp(id->id, hid)) | ||
return (const struct sdhci_acpi_slot *)id->driver_data; | ||
return NULL; | ||
} | ||
|
||
static int __devinit sdhci_acpi_probe(struct platform_device *pdev) | ||
{ | ||
struct device *dev = &pdev->dev; | ||
acpi_handle handle = ACPI_HANDLE(dev); | ||
struct acpi_device *device; | ||
struct sdhci_acpi_host *c; | ||
struct sdhci_host *host; | ||
struct resource *iomem; | ||
resource_size_t len; | ||
const char *hid; | ||
int err; | ||
|
||
if (acpi_bus_get_device(handle, &device)) | ||
return -ENODEV; | ||
|
||
if (acpi_bus_get_status(device) || !device->status.present) | ||
return -ENODEV; | ||
|
||
hid = acpi_device_hid(device); | ||
|
||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
if (!iomem) | ||
return -ENOMEM; | ||
|
||
len = resource_size(iomem); | ||
if (len < 0x100) | ||
dev_err(dev, "Invalid iomem size!\n"); | ||
|
||
if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) | ||
return -ENOMEM; | ||
|
||
host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host)); | ||
if (IS_ERR(host)) | ||
return PTR_ERR(host); | ||
|
||
c = sdhci_priv(host); | ||
c->host = host; | ||
c->slot = sdhci_acpi_get_slot(hid); | ||
c->pdev = pdev; | ||
c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); | ||
|
||
platform_set_drvdata(pdev, c); | ||
|
||
host->hw_name = "ACPI"; | ||
host->ops = &sdhci_acpi_ops_dflt; | ||
host->irq = platform_get_irq(pdev, 0); | ||
|
||
host->ioaddr = devm_ioremap_nocache(dev, iomem->start, | ||
resource_size(iomem)); | ||
if (host->ioaddr == NULL) { | ||
err = -ENOMEM; | ||
goto err_free; | ||
} | ||
|
||
if (!dev->dma_mask) { | ||
u64 dma_mask; | ||
|
||
if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) { | ||
/* 64-bit DMA is not supported at present */ | ||
dma_mask = DMA_BIT_MASK(32); | ||
} else { | ||
dma_mask = DMA_BIT_MASK(32); | ||
} | ||
|
||
dev->dma_mask = &dev->coherent_dma_mask; | ||
dev->coherent_dma_mask = dma_mask; | ||
} | ||
|
||
if (c->slot) { | ||
if (c->slot->chip) { | ||
host->ops = c->slot->chip->ops; | ||
host->quirks |= c->slot->chip->quirks; | ||
host->quirks2 |= c->slot->chip->quirks2; | ||
host->mmc->caps |= c->slot->chip->caps; | ||
host->mmc->caps2 |= c->slot->chip->caps2; | ||
host->mmc->pm_caps |= c->slot->chip->pm_caps; | ||
} | ||
host->quirks |= c->slot->quirks; | ||
host->quirks2 |= c->slot->quirks2; | ||
host->mmc->caps |= c->slot->caps; | ||
host->mmc->caps2 |= c->slot->caps2; | ||
host->mmc->pm_caps |= c->slot->pm_caps; | ||
} | ||
|
||
err = sdhci_add_host(host); | ||
if (err) | ||
goto err_free; | ||
|
||
if (c->use_runtime_pm) { | ||
pm_suspend_ignore_children(dev, 1); | ||
pm_runtime_set_autosuspend_delay(dev, 50); | ||
pm_runtime_use_autosuspend(dev); | ||
pm_runtime_enable(dev); | ||
} | ||
|
||
return 0; | ||
|
||
err_free: | ||
platform_set_drvdata(pdev, NULL); | ||
sdhci_free_host(c->host); | ||
return err; | ||
} | ||
|
||
static int __devexit sdhci_acpi_remove(struct platform_device *pdev) | ||
{ | ||
struct sdhci_acpi_host *c = platform_get_drvdata(pdev); | ||
struct device *dev = &pdev->dev; | ||
int dead; | ||
|
||
if (c->use_runtime_pm) { | ||
pm_runtime_get_sync(dev); | ||
pm_runtime_disable(dev); | ||
pm_runtime_put_noidle(dev); | ||
} | ||
|
||
dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); | ||
sdhci_remove_host(c->host, dead); | ||
platform_set_drvdata(pdev, NULL); | ||
sdhci_free_host(c->host); | ||
|
||
return 0; | ||
} | ||
|
||
#ifdef CONFIG_PM_SLEEP | ||
|
||
static int sdhci_acpi_suspend(struct device *dev) | ||
{ | ||
struct sdhci_acpi_host *c = dev_get_drvdata(dev); | ||
|
||
return sdhci_suspend_host(c->host); | ||
} | ||
|
||
static int sdhci_acpi_resume(struct device *dev) | ||
{ | ||
struct sdhci_acpi_host *c = dev_get_drvdata(dev); | ||
|
||
return sdhci_resume_host(c->host); | ||
} | ||
|
||
#else | ||
|
||
#define sdhci_acpi_suspend NULL | ||
#define sdhci_acpi_resume NULL | ||
|
||
#endif | ||
|
||
#ifdef CONFIG_PM_RUNTIME | ||
|
||
static int sdhci_acpi_runtime_suspend(struct device *dev) | ||
{ | ||
struct sdhci_acpi_host *c = dev_get_drvdata(dev); | ||
|
||
return sdhci_runtime_suspend_host(c->host); | ||
} | ||
|
||
static int sdhci_acpi_runtime_resume(struct device *dev) | ||
{ | ||
struct sdhci_acpi_host *c = dev_get_drvdata(dev); | ||
|
||
return sdhci_runtime_resume_host(c->host); | ||
} | ||
|
||
static int sdhci_acpi_runtime_idle(struct device *dev) | ||
{ | ||
return 0; | ||
} | ||
|
||
#else | ||
|
||
#define sdhci_acpi_runtime_suspend NULL | ||
#define sdhci_acpi_runtime_resume NULL | ||
#define sdhci_acpi_runtime_idle NULL | ||
|
||
#endif | ||
|
||
static const struct dev_pm_ops sdhci_acpi_pm_ops = { | ||
.suspend = sdhci_acpi_suspend, | ||
.resume = sdhci_acpi_resume, | ||
.runtime_suspend = sdhci_acpi_runtime_suspend, | ||
.runtime_resume = sdhci_acpi_runtime_resume, | ||
.runtime_idle = sdhci_acpi_runtime_idle, | ||
}; | ||
|
||
static struct platform_driver sdhci_acpi_driver = { | ||
.driver = { | ||
.name = "sdhci-acpi", | ||
.owner = THIS_MODULE, | ||
.acpi_match_table = sdhci_acpi_ids, | ||
.pm = &sdhci_acpi_pm_ops, | ||
}, | ||
.probe = sdhci_acpi_probe, | ||
.remove = __devexit_p(sdhci_acpi_remove), | ||
}; | ||
|
||
module_platform_driver(sdhci_acpi_driver); | ||
|
||
MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver"); | ||
MODULE_AUTHOR("Adrian Hunter"); | ||
MODULE_LICENSE("GPL v2"); |