mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'mmc-merge-for-3.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball: Core: * Support for MMC 4.5 Data Tag feature -- we tag REQ_META, so devices that support Data Tag will provide increased throughput for metadata. * Faster detection of card removal on I/O errors. Drivers: * dw_mmc now supports eMMC Power Off Notify, has PCI support, and implements pre_req and post_req for asynchronous requests. * omap_hsmmc now supports device tree. * esdhc now has power management support. * sdhci-tegra now supports Tegra30 devices. * sdhci-spear now supports hibernation. * tmio_mmc now supports using a GPIO for card detection. * Intel PCH now supports 8-bit bus transfers. * tag 'mmc-merge-for-3.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (53 commits) mmc: sh_mmcif: simplify bitmask macros mmc: sh_mobile_sdhi: support modular mmc-core with non-standard hotplug mmc: sh_mobile_sdhi: add a callback for board specific init code mmc: tmio: cosmetic: prettify the tmio_mmc_set_ios() function mmc: sh_mobile_sdhi: do not manage PM clocks manually mmc: tmio_mmc: remove unused sdio_irq_enabled flag mmc: tmio_mmc: power status flag doesn't have to be exposed in platform data mmc: sh_mobile_sdhi: pass card hotplug GPIO number to TMIO MMC mmc: tmio_mmc: support the generic MMC GPIO card hotplug helper mmc: tmio: calculate the native hotplug condition only once mmc: simplify mmc_cd_gpio_request() by removing two parameters mmc: sdhci-pci: allow 8-bit bus width for Intel PCH mmc: sdhci: check interrupt flags in ISR again mmc: sdhci-pci: Add MSI support mmc: core: warn when card doesn't support HPI mmc: davinci: Poll status for small size transfers mmc: davinci: Eliminate spurious interrupts mmc: omap_hsmmc: Avoid a regulator voltage change with dt mmc: omap_hsmmc: Convert hsmmc driver to use device tree mmc: sdhci-pci: add SDHCI_QUIRK2_HOST_OFF_CARD_ON for Medfield SDIO ...
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
* TI Highspeed MMC host controller for OMAP
|
||||
|
||||
The Highspeed MMC Host Controller on TI OMAP family
|
||||
provides an interface for MMC, SD, and SDIO types of memory cards.
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
Should be "ti,omap2-hsmmc", for OMAP2 controllers
|
||||
Should be "ti,omap3-hsmmc", for OMAP3 controllers
|
||||
Should be "ti,omap4-hsmmc", for OMAP4 controllers
|
||||
- ti,hwmods: Must be "mmc<n>", n is controller instance starting 1
|
||||
- reg : should contain hsmmc registers location and length
|
||||
|
||||
Optional properties:
|
||||
ti,dual-volt: boolean, supports dual voltage cards
|
||||
<supply-name>-supply: phandle to the regulator device tree node
|
||||
"supply-name" examples are "vmmc", "vmmc_aux" etc
|
||||
ti,bus-width: Number of data lines, default assumed is 1 if the property is missing.
|
||||
cd-gpios: GPIOs for card detection
|
||||
wp-gpios: GPIOs for write protection
|
||||
ti,non-removable: non-removable slot (like eMMC)
|
||||
ti,needs-special-reset: Requires a special softreset sequence
|
||||
|
||||
Example:
|
||||
mmc1: mmc@0x4809c000 {
|
||||
compatible = "ti,omap4-hsmmc";
|
||||
reg = <0x4809c000 0x400>;
|
||||
ti,hwmods = "mmc1";
|
||||
ti,dual-volt;
|
||||
ti,bus-width = <4>;
|
||||
vmmc-supply = <&vmmc>; /* phandle to regulator node */
|
||||
ti,non-removable;
|
||||
};
|
||||
@@ -111,7 +111,7 @@ static struct s3c_sdhci_platdata nuri_hsmmc0_data __initdata = {
|
||||
.max_width = 8,
|
||||
.host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_DISABLE | MMC_CAP_ERASE),
|
||||
MMC_CAP_ERASE),
|
||||
.cd_type = S3C_SDHCI_CD_PERMANENT,
|
||||
.clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL,
|
||||
};
|
||||
@@ -150,8 +150,7 @@ static struct platform_device emmc_fixed_voltage = {
|
||||
static struct s3c_sdhci_platdata nuri_hsmmc2_data __initdata = {
|
||||
.max_width = 4,
|
||||
.host_caps = MMC_CAP_4_BIT_DATA |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_DISABLE,
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
|
||||
.ext_cd_gpio = EXYNOS4_GPX3(3), /* XEINT_27 */
|
||||
.ext_cd_gpio_invert = 1,
|
||||
.cd_type = S3C_SDHCI_CD_GPIO,
|
||||
|
||||
@@ -745,8 +745,7 @@ static struct platform_device universal_gpio_keys = {
|
||||
static struct s3c_sdhci_platdata universal_hsmmc0_data __initdata = {
|
||||
.max_width = 8,
|
||||
.host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_DISABLE),
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED),
|
||||
.cd_type = S3C_SDHCI_CD_PERMANENT,
|
||||
.clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL,
|
||||
};
|
||||
@@ -784,8 +783,7 @@ static struct platform_device mmc0_fixed_voltage = {
|
||||
static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = {
|
||||
.max_width = 4,
|
||||
.host_caps = MMC_CAP_4_BIT_DATA |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_DISABLE,
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
|
||||
.ext_cd_gpio = EXYNOS4_GPX3(4), /* XEINT_28 */
|
||||
.ext_cd_gpio_invert = 1,
|
||||
.cd_type = S3C_SDHCI_CD_GPIO,
|
||||
@@ -796,8 +794,7 @@ static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = {
|
||||
static struct s3c_sdhci_platdata universal_hsmmc3_data __initdata = {
|
||||
.max_width = 4,
|
||||
.host_caps = MMC_CAP_4_BIT_DATA |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_DISABLE,
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
|
||||
.cd_type = S3C_SDHCI_CD_EXTERNAL,
|
||||
};
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
|
||||
mmc->slots[0].pm_caps = c->pm_caps;
|
||||
mmc->slots[0].internal_clock = !c->ext_clock;
|
||||
mmc->dma_mask = 0xffffffff;
|
||||
mmc->max_freq = c->max_freq;
|
||||
if (cpu_is_omap44xx())
|
||||
mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
|
||||
else
|
||||
|
||||
@@ -27,6 +27,8 @@ struct omap2_hsmmc_info {
|
||||
char *name; /* or NULL for default */
|
||||
struct platform_device *pdev; /* mmc controller instance */
|
||||
int ocr_mask; /* temporary HACK */
|
||||
int max_freq; /* maximum clock, if constrained by external
|
||||
* circuitry, or 0 for default */
|
||||
/* Remux (pad configuration) when powering on/off */
|
||||
void (*remux)(struct device *dev, int slot, int power_on);
|
||||
/* init some special card */
|
||||
|
||||
@@ -137,8 +137,6 @@ struct omap_mmc_platform_data {
|
||||
int (*set_power)(struct device *dev, int slot,
|
||||
int power_on, int vdd);
|
||||
int (*get_ro)(struct device *dev, int slot);
|
||||
int (*set_sleep)(struct device *dev, int slot, int sleep,
|
||||
int vdd, int cardsleep);
|
||||
void (*remux)(struct device *dev, int slot, int power_on);
|
||||
/* Call back before enabling / disabling regulators */
|
||||
void (*before_set_reg)(struct device *dev, int slot,
|
||||
|
||||
@@ -1079,6 +1079,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_blk_request *brq = &mqrq->brq;
|
||||
struct request *req = mqrq->req;
|
||||
struct mmc_blk_data *md = mq->data;
|
||||
bool do_data_tag;
|
||||
|
||||
/*
|
||||
* Reliable writes are used to implement Forced Unit Access and
|
||||
@@ -1154,6 +1155,16 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
if (do_rel_wr)
|
||||
mmc_apply_rel_rw(brq, card, req);
|
||||
|
||||
/*
|
||||
* Data tag is used only during writing meta data to speed
|
||||
* up write and any subsequent read of this meta data
|
||||
*/
|
||||
do_data_tag = (card->ext_csd.data_tag_unit_size) &&
|
||||
(req->cmd_flags & REQ_META) &&
|
||||
(rq_data_dir(req) == WRITE) &&
|
||||
((brq->data.blocks * brq->data.blksz) >=
|
||||
card->ext_csd.data_tag_unit_size);
|
||||
|
||||
/*
|
||||
* Pre-defined multi-block transfers are preferable to
|
||||
* open ended-ones (and necessary for reliable writes).
|
||||
@@ -1172,13 +1183,13 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
* We'll avoid using CMD23-bounded multiblock writes for
|
||||
* these, while retaining features like reliable writes.
|
||||
*/
|
||||
|
||||
if ((md->flags & MMC_BLK_CMD23) &&
|
||||
mmc_op_multi(brq->cmd.opcode) &&
|
||||
(do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) {
|
||||
if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) &&
|
||||
(do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) ||
|
||||
do_data_tag)) {
|
||||
brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
|
||||
brq->sbc.arg = brq->data.blocks |
|
||||
(do_rel_wr ? (1 << 31) : 0);
|
||||
(do_rel_wr ? (1 << 31) : 0) |
|
||||
(do_data_tag ? (1 << 29) : 0);
|
||||
brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
brq->mrq.sbc = &brq->sbc;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,17 @@ static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int irq, unsigned long flags)
|
||||
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio)
|
||||
{
|
||||
size_t len = strlen(dev_name(host->parent)) + 4;
|
||||
struct mmc_cd_gpio *cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
|
||||
struct mmc_cd_gpio *cd;
|
||||
int irq = gpio_to_irq(gpio);
|
||||
int ret;
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
|
||||
if (!cd)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -45,7 +49,8 @@ int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
|
||||
goto egpioreq;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
|
||||
flags, cd->label, host);
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
cd->label, host);
|
||||
if (ret < 0)
|
||||
goto eirqreq;
|
||||
|
||||
|
||||
+57
-195
@@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
struct scatterlist *sg;
|
||||
#endif
|
||||
|
||||
if (mrq->sbc) {
|
||||
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
|
||||
mmc_hostname(host), mrq->sbc->opcode,
|
||||
mrq->sbc->arg, mrq->sbc->flags);
|
||||
}
|
||||
|
||||
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
|
||||
mmc_hostname(host), mrq->cmd->opcode,
|
||||
mrq->cmd->arg, mrq->cmd->flags);
|
||||
@@ -243,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq)
|
||||
complete(&mrq->completion);
|
||||
}
|
||||
|
||||
static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
init_completion(&mrq->completion);
|
||||
mrq->done = mmc_wait_done;
|
||||
if (mmc_card_removed(host->card)) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
complete(&mrq->completion);
|
||||
return;
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
mmc_start_request(host, mrq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmc_wait_for_req_done(struct mmc_host *host,
|
||||
@@ -336,6 +343,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
||||
struct mmc_async_req *areq, int *error)
|
||||
{
|
||||
int err = 0;
|
||||
int start_err = 0;
|
||||
struct mmc_async_req *data = host->areq;
|
||||
|
||||
/* Prepare a new request */
|
||||
@@ -345,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
||||
if (host->areq) {
|
||||
mmc_wait_for_req_done(host, host->areq->mrq);
|
||||
err = host->areq->err_check(host->card, host->areq);
|
||||
if (err) {
|
||||
/* post process the completed failed request */
|
||||
mmc_post_req(host, host->areq->mrq, 0);
|
||||
if (areq)
|
||||
/*
|
||||
* Cancel the new prepared request, because
|
||||
* it can't run until the failed
|
||||
* request has been properly handled.
|
||||
*/
|
||||
mmc_post_req(host, areq->mrq, -EINVAL);
|
||||
|
||||
host->areq = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (areq)
|
||||
__mmc_start_req(host, areq->mrq);
|
||||
if (!err && areq)
|
||||
start_err = __mmc_start_req(host, areq->mrq);
|
||||
|
||||
if (host->areq)
|
||||
mmc_post_req(host, host->areq->mrq, 0);
|
||||
|
||||
host->areq = areq;
|
||||
out:
|
||||
/* Cancel a prepared request if it was not started. */
|
||||
if ((err || start_err) && areq)
|
||||
mmc_post_req(host, areq->mrq, -EINVAL);
|
||||
|
||||
if (err)
|
||||
host->areq = NULL;
|
||||
else
|
||||
host->areq = areq;
|
||||
|
||||
if (error)
|
||||
*error = err;
|
||||
return data;
|
||||
@@ -598,105 +599,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_align_data_size);
|
||||
|
||||
/**
|
||||
* mmc_host_enable - enable a host.
|
||||
* @host: mmc host to enable
|
||||
*
|
||||
* Hosts that support power saving can use the 'enable' and 'disable'
|
||||
* methods to exit and enter power saving states. For more information
|
||||
* see comments for struct mmc_host_ops.
|
||||
*/
|
||||
int mmc_host_enable(struct mmc_host *host)
|
||||
{
|
||||
if (!(host->caps & MMC_CAP_DISABLE))
|
||||
return 0;
|
||||
|
||||
if (host->en_dis_recurs)
|
||||
return 0;
|
||||
|
||||
if (host->nesting_cnt++)
|
||||
return 0;
|
||||
|
||||
cancel_delayed_work_sync(&host->disable);
|
||||
|
||||
if (host->enabled)
|
||||
return 0;
|
||||
|
||||
if (host->ops->enable) {
|
||||
int err;
|
||||
|
||||
host->en_dis_recurs = 1;
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->enable(host);
|
||||
mmc_host_clk_release(host);
|
||||
host->en_dis_recurs = 0;
|
||||
|
||||
if (err) {
|
||||
pr_debug("%s: enable error %d\n",
|
||||
mmc_hostname(host), err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
host->enabled = 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_host_enable);
|
||||
|
||||
static int mmc_host_do_disable(struct mmc_host *host, int lazy)
|
||||
{
|
||||
if (host->ops->disable) {
|
||||
int err;
|
||||
|
||||
host->en_dis_recurs = 1;
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->disable(host, lazy);
|
||||
mmc_host_clk_release(host);
|
||||
host->en_dis_recurs = 0;
|
||||
|
||||
if (err < 0) {
|
||||
pr_debug("%s: disable error %d\n",
|
||||
mmc_hostname(host), err);
|
||||
return err;
|
||||
}
|
||||
if (err > 0) {
|
||||
unsigned long delay = msecs_to_jiffies(err);
|
||||
|
||||
mmc_schedule_delayed_work(&host->disable, delay);
|
||||
}
|
||||
}
|
||||
host->enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_host_disable - disable a host.
|
||||
* @host: mmc host to disable
|
||||
*
|
||||
* Hosts that support power saving can use the 'enable' and 'disable'
|
||||
* methods to exit and enter power saving states. For more information
|
||||
* see comments for struct mmc_host_ops.
|
||||
*/
|
||||
int mmc_host_disable(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_DISABLE))
|
||||
return 0;
|
||||
|
||||
if (host->en_dis_recurs)
|
||||
return 0;
|
||||
|
||||
if (--host->nesting_cnt)
|
||||
return 0;
|
||||
|
||||
if (!host->enabled)
|
||||
return 0;
|
||||
|
||||
err = mmc_host_do_disable(host, 0);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_host_disable);
|
||||
|
||||
/**
|
||||
* __mmc_claim_host - exclusively claim a host
|
||||
* @host: mmc host to claim
|
||||
@@ -735,8 +637,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
|
||||
wake_up(&host->wq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
remove_wait_queue(&host->wq, &wait);
|
||||
if (!stop)
|
||||
mmc_host_enable(host);
|
||||
if (host->ops->enable && !stop && host->claim_cnt == 1)
|
||||
host->ops->enable(host);
|
||||
return stop;
|
||||
}
|
||||
|
||||
@@ -761,21 +663,28 @@ int mmc_try_claim_host(struct mmc_host *host)
|
||||
claimed_host = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
if (host->ops->enable && claimed_host && host->claim_cnt == 1)
|
||||
host->ops->enable(host);
|
||||
return claimed_host;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_try_claim_host);
|
||||
|
||||
/**
|
||||
* mmc_do_release_host - release a claimed host
|
||||
* mmc_release_host - release a host
|
||||
* @host: mmc host to release
|
||||
*
|
||||
* If you successfully claimed a host, this function will
|
||||
* release it again.
|
||||
* Release a MMC host, allowing others to claim the host
|
||||
* for their operations.
|
||||
*/
|
||||
void mmc_do_release_host(struct mmc_host *host)
|
||||
void mmc_release_host(struct mmc_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
if (host->ops->disable && host->claim_cnt == 1)
|
||||
host->ops->disable(host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (--host->claim_cnt) {
|
||||
/* Release for nested claim */
|
||||
@@ -787,67 +696,6 @@ void mmc_do_release_host(struct mmc_host *host)
|
||||
wake_up(&host->wq);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_do_release_host);
|
||||
|
||||
void mmc_host_deeper_disable(struct work_struct *work)
|
||||
{
|
||||
struct mmc_host *host =
|
||||
container_of(work, struct mmc_host, disable.work);
|
||||
|
||||
/* If the host is claimed then we do not want to disable it anymore */
|
||||
if (!mmc_try_claim_host(host))
|
||||
return;
|
||||
mmc_host_do_disable(host, 1);
|
||||
mmc_do_release_host(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_host_lazy_disable - lazily disable a host.
|
||||
* @host: mmc host to disable
|
||||
*
|
||||
* Hosts that support power saving can use the 'enable' and 'disable'
|
||||
* methods to exit and enter power saving states. For more information
|
||||
* see comments for struct mmc_host_ops.
|
||||
*/
|
||||
int mmc_host_lazy_disable(struct mmc_host *host)
|
||||
{
|
||||
if (!(host->caps & MMC_CAP_DISABLE))
|
||||
return 0;
|
||||
|
||||
if (host->en_dis_recurs)
|
||||
return 0;
|
||||
|
||||
if (--host->nesting_cnt)
|
||||
return 0;
|
||||
|
||||
if (!host->enabled)
|
||||
return 0;
|
||||
|
||||
if (host->disable_delay) {
|
||||
mmc_schedule_delayed_work(&host->disable,
|
||||
msecs_to_jiffies(host->disable_delay));
|
||||
return 0;
|
||||
} else
|
||||
return mmc_host_do_disable(host, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_host_lazy_disable);
|
||||
|
||||
/**
|
||||
* mmc_release_host - release a host
|
||||
* @host: mmc host to release
|
||||
*
|
||||
* Release a MMC host, allowing others to claim the host
|
||||
* for their operations.
|
||||
*/
|
||||
void mmc_release_host(struct mmc_host *host)
|
||||
{
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
mmc_host_lazy_disable(host);
|
||||
|
||||
mmc_do_release_host(host);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_release_host);
|
||||
|
||||
/*
|
||||
@@ -2115,18 +1963,36 @@ int _mmc_detect_card_removed(struct mmc_host *host)
|
||||
int mmc_detect_card_removed(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
int ret;
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
if (!card)
|
||||
return 1;
|
||||
|
||||
ret = mmc_card_removed(card);
|
||||
/*
|
||||
* The card will be considered unchanged unless we have been asked to
|
||||
* detect a change or host requires polling to provide card detection.
|
||||
*/
|
||||
if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
|
||||
return mmc_card_removed(card);
|
||||
if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) &&
|
||||
!(host->caps2 & MMC_CAP2_DETECT_ON_ERR))
|
||||
return ret;
|
||||
|
||||
host->detect_change = 0;
|
||||
if (!ret) {
|
||||
ret = _mmc_detect_card_removed(host);
|
||||
if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) {
|
||||
/*
|
||||
* Schedule a detect work as soon as possible to let a
|
||||
* rescan handle the card removal.
|
||||
*/
|
||||
cancel_delayed_work(&host->detect);
|
||||
mmc_detect_change(host, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return _mmc_detect_card_removed(host);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_detect_card_removed);
|
||||
|
||||
@@ -2203,8 +2069,6 @@ void mmc_stop_host(struct mmc_host *host)
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
|
||||
if (host->caps & MMC_CAP_DISABLE)
|
||||
cancel_delayed_work(&host->disable);
|
||||
cancel_delayed_work_sync(&host->detect);
|
||||
mmc_flush_scheduled_work();
|
||||
|
||||
@@ -2399,13 +2263,11 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (host->caps & MMC_CAP_DISABLE)
|
||||
cancel_delayed_work(&host->disable);
|
||||
cancel_delayed_work(&host->detect);
|
||||
mmc_flush_scheduled_work();
|
||||
if (mmc_try_claim_host(host)) {
|
||||
err = mmc_cache_ctrl(host, 0);
|
||||
mmc_do_release_host(host);
|
||||
mmc_release_host(host);
|
||||
} else {
|
||||
err = -EBUSY;
|
||||
}
|
||||
@@ -2426,7 +2288,7 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||
if (host->bus_ops->suspend) {
|
||||
err = host->bus_ops->suspend(host);
|
||||
}
|
||||
mmc_do_release_host(host);
|
||||
mmc_release_host(host);
|
||||
|
||||
if (err == -ENOSYS || !host->bus_ops->resume) {
|
||||
/*
|
||||
|
||||
@@ -330,7 +330,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||
spin_lock_init(&host->lock);
|
||||
init_waitqueue_head(&host->wq);
|
||||
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
|
||||
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
|
||||
#ifdef CONFIG_PM
|
||||
host->pm_notify.notifier_call = mmc_pm_notify;
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
int mmc_register_host_class(void);
|
||||
void mmc_unregister_host_class(void);
|
||||
void mmc_host_deeper_disable(struct work_struct *work);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+37
-18
@@ -519,6 +519,20 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 |
|
||||
ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
|
||||
ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24;
|
||||
|
||||
if (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 1)
|
||||
card->ext_csd.data_sector_size = 4096;
|
||||
else
|
||||
card->ext_csd.data_sector_size = 512;
|
||||
|
||||
if ((ext_csd[EXT_CSD_DATA_TAG_SUPPORT] & 1) &&
|
||||
(ext_csd[EXT_CSD_TAG_UNIT_SIZE] <= 8)) {
|
||||
card->ext_csd.data_tag_unit_size =
|
||||
((unsigned int) 1 << ext_csd[EXT_CSD_TAG_UNIT_SIZE]) *
|
||||
(card->ext_csd.data_sector_size);
|
||||
} else {
|
||||
card->ext_csd.data_tag_unit_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -938,7 +952,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
* If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF
|
||||
* bit. This bit will be lost every time after a reset or power off.
|
||||
*/
|
||||
if (card->ext_csd.enhanced_area_en) {
|
||||
if (card->ext_csd.enhanced_area_en ||
|
||||
(card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_ERASE_GROUP_DEF, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
@@ -1032,22 +1047,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable HPI feature (if supported)
|
||||
*/
|
||||
if (card->ext_csd.hpi) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HPI_MGMT, 1, 0);
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
if (err) {
|
||||
pr_warning("%s: Enabling HPI failed\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
} else
|
||||
card->ext_csd.hpi_en = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute bus speed.
|
||||
*/
|
||||
@@ -1097,9 +1096,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
* 4. execute tuning for HS200
|
||||
*/
|
||||
if ((host->caps2 & MMC_CAP2_HS200) &&
|
||||
card->host->ops->execute_tuning)
|
||||
card->host->ops->execute_tuning) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK_HS200);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
if (err) {
|
||||
pr_warning("%s: tuning execution failed\n",
|
||||
mmc_hostname(card->host));
|
||||
@@ -1218,6 +1220,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable HPI feature (if supported)
|
||||
*/
|
||||
if (card->ext_csd.hpi) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HPI_MGMT, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
if (err) {
|
||||
pr_warning("%s: Enabling HPI failed\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
} else
|
||||
card->ext_csd.hpi_en = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If cache size is higher than 0, this indicates
|
||||
* the existence of cache and it can be turned on.
|
||||
|
||||
@@ -553,18 +553,22 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned int opcode;
|
||||
unsigned int flags;
|
||||
int err;
|
||||
|
||||
if (!card->ext_csd.hpi) {
|
||||
pr_warning("%s: Card didn't support HPI command\n",
|
||||
mmc_hostname(card->host));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
opcode = card->ext_csd.hpi_cmd;
|
||||
if (opcode == MMC_STOP_TRANSMISSION)
|
||||
flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
else if (opcode == MMC_SEND_STATUS)
|
||||
flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = card->rca << 16 | 1;
|
||||
cmd.flags = flags;
|
||||
cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||
|
||||
@@ -533,6 +533,31 @@ config MMC_DW_IDMAC
|
||||
Designware Mobile Storage IP block. This disables the external DMA
|
||||
interface.
|
||||
|
||||
config MMC_DW_PLTFM
|
||||
tristate "Synopsys Designware MCI Support as platform device"
|
||||
depends on MMC_DW
|
||||
default y
|
||||
help
|
||||
This selects the common helper functions support for Host Controller
|
||||
Interface based platform driver. Please select this option if the IP
|
||||
is present as a platform device. This is the common interface for the
|
||||
Synopsys Designware IP.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config MMC_DW_PCI
|
||||
tristate "Synopsys Designware MCI support on PCI bus"
|
||||
depends on MMC_DW && PCI
|
||||
help
|
||||
This selects the PCI bus for the Synopsys Designware Mobile Storage IP.
|
||||
Select this option if the IP is present on PCI platform.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SH_MMCIF
|
||||
tristate "SuperH Internal MMCIF support"
|
||||
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
|
||||
|
||||
@@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
|
||||
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
|
||||
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
|
||||
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
||||
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
obj-$(CONFIG_MMC_VUB300) += vub300.o
|
||||
|
||||
@@ -1975,7 +1975,7 @@ static bool atmci_configure_dma(struct atmel_mci *host)
|
||||
return false;
|
||||
} else {
|
||||
dev_info(&host->pdev->dev,
|
||||
"Using %s for DMA transfers\n",
|
||||
"using %s for DMA transfers\n",
|
||||
dma_chan_name(host->dma.chan));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -160,6 +160,16 @@ module_param(rw_threshold, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(rw_threshold,
|
||||
"Read/Write threshold. Default = 32");
|
||||
|
||||
static unsigned poll_threshold = 128;
|
||||
module_param(poll_threshold, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(poll_threshold,
|
||||
"Polling transaction size threshold. Default = 128");
|
||||
|
||||
static unsigned poll_loopcount = 32;
|
||||
module_param(poll_loopcount, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(poll_loopcount,
|
||||
"Maximum polling loop count. Default = 32");
|
||||
|
||||
static unsigned __initdata use_dma = 1;
|
||||
module_param(use_dma, uint, 0);
|
||||
MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
|
||||
@@ -193,6 +203,7 @@ struct mmc_davinci_host {
|
||||
bool use_dma;
|
||||
bool do_dma;
|
||||
bool sdio_int;
|
||||
bool active_request;
|
||||
|
||||
/* Scatterlist DMA uses one or more parameter RAM entries:
|
||||
* the main one (associated with rxdma or txdma) plus zero or
|
||||
@@ -219,6 +230,7 @@ struct mmc_davinci_host {
|
||||
#endif
|
||||
};
|
||||
|
||||
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id);
|
||||
|
||||
/* PIO only */
|
||||
static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
|
||||
@@ -376,7 +388,20 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host,
|
||||
|
||||
writel(cmd->arg, host->base + DAVINCI_MMCARGHL);
|
||||
writel(cmd_reg, host->base + DAVINCI_MMCCMD);
|
||||
writel(im_val, host->base + DAVINCI_MMCIM);
|
||||
|
||||
host->active_request = true;
|
||||
|
||||
if (!host->do_dma && host->bytes_left <= poll_threshold) {
|
||||
u32 count = poll_loopcount;
|
||||
|
||||
while (host->active_request && count--) {
|
||||
mmc_davinci_irq(0, host);
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
if (host->active_request)
|
||||
writel(im_val, host->base + DAVINCI_MMCIM);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -915,6 +940,7 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
|
||||
if (!data->stop || (host->cmd && host->cmd->error)) {
|
||||
mmc_request_done(host->mmc, data->mrq);
|
||||
writel(0, host->base + DAVINCI_MMCIM);
|
||||
host->active_request = false;
|
||||
} else
|
||||
mmc_davinci_start_command(host, data->stop);
|
||||
}
|
||||
@@ -942,6 +968,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
|
||||
cmd->mrq->cmd->retries = 0;
|
||||
mmc_request_done(host->mmc, cmd->mrq);
|
||||
writel(0, host->base + DAVINCI_MMCIM);
|
||||
host->active_request = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1009,12 +1036,33 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
|
||||
* by read. So, it is not unbouned loop even in the case of
|
||||
* non-dma.
|
||||
*/
|
||||
while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) {
|
||||
davinci_fifo_data_trans(host, rw_threshold);
|
||||
status = readl(host->base + DAVINCI_MMCST0);
|
||||
if (!status)
|
||||
break;
|
||||
qstatus |= status;
|
||||
if (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) {
|
||||
unsigned long im_val;
|
||||
|
||||
/*
|
||||
* If interrupts fire during the following loop, they will be
|
||||
* handled by the handler, but the PIC will still buffer these.
|
||||
* As a result, the handler will be called again to serve these
|
||||
* needlessly. In order to avoid these spurious interrupts,
|
||||
* keep interrupts masked during the loop.
|
||||
*/
|
||||
im_val = readl(host->base + DAVINCI_MMCIM);
|
||||
writel(0, host->base + DAVINCI_MMCIM);
|
||||
|
||||
do {
|
||||
davinci_fifo_data_trans(host, rw_threshold);
|
||||
status = readl(host->base + DAVINCI_MMCST0);
|
||||
qstatus |= status;
|
||||
} while (host->bytes_left &&
|
||||
(status & (MMCST0_DXRDY | MMCST0_DRRDY)));
|
||||
|
||||
/*
|
||||
* If an interrupt is pending, it is assumed it will fire when
|
||||
* it is unmasked. This assumption is also taken when the MMCIM
|
||||
* is first set. Otherwise, writing to MMCIM after reading the
|
||||
* status is race-prone.
|
||||
*/
|
||||
writel(im_val, host->base + DAVINCI_MMCIM);
|
||||
}
|
||||
|
||||
if (qstatus & MMCST0_DATDNE) {
|
||||
@@ -1418,17 +1466,14 @@ static int davinci_mmcsd_suspend(struct device *dev)
|
||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
mmc_host_enable(host->mmc);
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
if (!ret) {
|
||||
writel(0, host->base + DAVINCI_MMCIM);
|
||||
mmc_davinci_reset_ctrl(host, 1);
|
||||
mmc_host_disable(host->mmc);
|
||||
clk_disable(host->clk);
|
||||
host->suspended = 1;
|
||||
} else {
|
||||
host->suspended = 0;
|
||||
mmc_host_disable(host->mmc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1444,7 +1489,6 @@ static int davinci_mmcsd_resume(struct device *dev)
|
||||
return 0;
|
||||
|
||||
clk_enable(host->clk);
|
||||
mmc_host_enable(host->mmc);
|
||||
|
||||
mmc_davinci_reset_ctrl(host, 0);
|
||||
ret = mmc_resume_host(host->mmc);
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Synopsys DesignWare Multimedia Card PCI Interface driver
|
||||
*
|
||||
* Copyright (C) 2012 Vayavya Labs Pvt. Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include "dw_mmc.h"
|
||||
|
||||
#define PCI_BAR_NO 2
|
||||
#define COMPLETE_BAR 0
|
||||
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
|
||||
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
|
||||
/* Defining the Capabilities */
|
||||
#define DW_MCI_CAPABILITIES (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |\
|
||||
MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\
|
||||
MMC_CAP_SDIO_IRQ)
|
||||
|
||||
static struct dw_mci_board pci_board_data = {
|
||||
.num_slots = 1,
|
||||
.caps = DW_MCI_CAPABILITIES,
|
||||
.bus_hz = 33 * 1000 * 1000,
|
||||
.detect_delay_ms = 200,
|
||||
.fifo_depth = 32,
|
||||
};
|
||||
|
||||
static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *entries)
|
||||
{
|
||||
struct dw_mci *host;
|
||||
int ret;
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (pci_request_regions(pdev, "dw_mmc_pci")) {
|
||||
ret = -ENODEV;
|
||||
goto err_disable_dev;
|
||||
}
|
||||
|
||||
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
host->irq = pdev->irq;
|
||||
host->irq_flags = IRQF_SHARED;
|
||||
host->dev = pdev->dev;
|
||||
host->pdata = &pci_board_data;
|
||||
|
||||
host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
|
||||
if (!host->regs) {
|
||||
ret = -EIO;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, host);
|
||||
ret = dw_mci_probe(host);
|
||||
if (ret)
|
||||
goto err_probe_failed;
|
||||
return ret;
|
||||
|
||||
err_probe_failed:
|
||||
pci_iounmap(pdev, host->regs);
|
||||
err_unmap:
|
||||
kfree(host);
|
||||
err_release:
|
||||
pci_release_regions(pdev);
|
||||
err_disable_dev:
|
||||
pci_disable_device(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit dw_mci_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct dw_mci *host = pci_get_drvdata(pdev);
|
||||
|
||||
dw_mci_remove(host);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
pci_release_regions(pdev);
|
||||
pci_iounmap(pdev, host->regs);
|
||||
kfree(host);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_mci_pci_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_mci *host = pci_get_drvdata(pdev);
|
||||
|
||||
ret = dw_mci_suspend(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dw_mci_pci_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_mci *host = pci_get_drvdata(pdev);
|
||||
|
||||
ret = dw_mci_resume(host);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define dw_mci_pci_suspend NULL
|
||||
#define dw_mci_pci_resume NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume);
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(dw_mci_pci_id) = {
|
||||
{ PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, dw_mci_pci_id);
|
||||
|
||||
static struct pci_driver dw_mci_pci_driver = {
|
||||
.name = "dw_mmc_pci",
|
||||
.id_table = dw_mci_pci_id,
|
||||
.probe = dw_mci_pci_probe,
|
||||
.remove = dw_mci_pci_remove,
|
||||
.driver = {
|
||||
.pm = &dw_mci_pci_pmops
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dw_mci_init(void)
|
||||
{
|
||||
return pci_register_driver(&dw_mci_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit dw_mci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&dw_mci_pci_driver);
|
||||
}
|
||||
|
||||
module_init(dw_mci_init);
|
||||
module_exit(dw_mci_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver");
|
||||
MODULE_AUTHOR("Shashidhar Hiremath <shashidharh@vayavyalabs.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Synopsys DesignWare Multimedia Card Interface driver
|
||||
*
|
||||
* Copyright (C) 2009 NXP Semiconductors
|
||||
* Copyright (C) 2009, 2010 Imagination Technologies Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include "dw_mmc.h"
|
||||
|
||||
static int dw_mci_pltfm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_mci *host;
|
||||
struct resource *regs;
|
||||
int ret;
|
||||
|
||||
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
ret = -ENXIO;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
if (host->irq < 0) {
|
||||
ret = host->irq;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
host->dev = pdev->dev;
|
||||
host->irq_flags = 0;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
ret = -ENOMEM;
|
||||
host->regs = ioremap(regs->start, resource_size(regs));
|
||||
if (!host->regs)
|
||||
goto err_free;
|
||||
platform_set_drvdata(pdev, host);
|
||||
ret = dw_mci_probe(host);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
return ret;
|
||||
err_out:
|
||||
iounmap(host->regs);
|
||||
err_free:
|
||||
kfree(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit dw_mci_pltfm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
dw_mci_remove(host);
|
||||
iounmap(host->regs);
|
||||
kfree(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* TODO: we should probably disable the clock to the card in the suspend path.
|
||||
*/
|
||||
static int dw_mci_pltfm_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_mci_suspend(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_pltfm_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_mci_resume(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define dw_mci_pltfm_suspend NULL
|
||||
#define dw_mci_pltfm_resume NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
|
||||
|
||||
static struct platform_driver dw_mci_pltfm_driver = {
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dw_mmc",
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dw_mci_init(void)
|
||||
{
|
||||
return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe);
|
||||
}
|
||||
|
||||
static void __exit dw_mci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&dw_mci_pltfm_driver);
|
||||
}
|
||||
|
||||
module_init(dw_mci_init);
|
||||
module_exit(dw_mci_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
|
||||
MODULE_AUTHOR("NXP Semiconductor VietNam");
|
||||
MODULE_AUTHOR("Imagination Technologies Ltd");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
+153
-129
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user