Commit b5174fa3 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents afb9bd70 135111cc
* 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 = { ...@@ -111,7 +111,7 @@ static struct s3c_sdhci_platdata nuri_hsmmc0_data __initdata = {
.max_width = 8, .max_width = 8,
.host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | .host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_DISABLE | MMC_CAP_ERASE), MMC_CAP_ERASE),
.cd_type = S3C_SDHCI_CD_PERMANENT, .cd_type = S3C_SDHCI_CD_PERMANENT,
.clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL,
}; };
...@@ -150,8 +150,7 @@ static struct platform_device emmc_fixed_voltage = { ...@@ -150,8 +150,7 @@ static struct platform_device emmc_fixed_voltage = {
static struct s3c_sdhci_platdata nuri_hsmmc2_data __initdata = { static struct s3c_sdhci_platdata nuri_hsmmc2_data __initdata = {
.max_width = 4, .max_width = 4,
.host_caps = MMC_CAP_4_BIT_DATA | .host_caps = MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
MMC_CAP_DISABLE,
.ext_cd_gpio = EXYNOS4_GPX3(3), /* XEINT_27 */ .ext_cd_gpio = EXYNOS4_GPX3(3), /* XEINT_27 */
.ext_cd_gpio_invert = 1, .ext_cd_gpio_invert = 1,
.cd_type = S3C_SDHCI_CD_GPIO, .cd_type = S3C_SDHCI_CD_GPIO,
......
...@@ -745,8 +745,7 @@ static struct platform_device universal_gpio_keys = { ...@@ -745,8 +745,7 @@ static struct platform_device universal_gpio_keys = {
static struct s3c_sdhci_platdata universal_hsmmc0_data __initdata = { static struct s3c_sdhci_platdata universal_hsmmc0_data __initdata = {
.max_width = 8, .max_width = 8,
.host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | .host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED),
MMC_CAP_DISABLE),
.cd_type = S3C_SDHCI_CD_PERMANENT, .cd_type = S3C_SDHCI_CD_PERMANENT,
.clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL,
}; };
...@@ -784,8 +783,7 @@ static struct platform_device mmc0_fixed_voltage = { ...@@ -784,8 +783,7 @@ static struct platform_device mmc0_fixed_voltage = {
static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = { static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = {
.max_width = 4, .max_width = 4,
.host_caps = MMC_CAP_4_BIT_DATA | .host_caps = MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
MMC_CAP_DISABLE,
.ext_cd_gpio = EXYNOS4_GPX3(4), /* XEINT_28 */ .ext_cd_gpio = EXYNOS4_GPX3(4), /* XEINT_28 */
.ext_cd_gpio_invert = 1, .ext_cd_gpio_invert = 1,
.cd_type = S3C_SDHCI_CD_GPIO, .cd_type = S3C_SDHCI_CD_GPIO,
...@@ -796,8 +794,7 @@ static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = { ...@@ -796,8 +794,7 @@ static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = {
static struct s3c_sdhci_platdata universal_hsmmc3_data __initdata = { static struct s3c_sdhci_platdata universal_hsmmc3_data __initdata = {
.max_width = 4, .max_width = 4,
.host_caps = MMC_CAP_4_BIT_DATA | .host_caps = MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
MMC_CAP_DISABLE,
.cd_type = S3C_SDHCI_CD_EXTERNAL, .cd_type = S3C_SDHCI_CD_EXTERNAL,
}; };
......
...@@ -316,6 +316,7 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, ...@@ -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].pm_caps = c->pm_caps;
mmc->slots[0].internal_clock = !c->ext_clock; mmc->slots[0].internal_clock = !c->ext_clock;
mmc->dma_mask = 0xffffffff; mmc->dma_mask = 0xffffffff;
mmc->max_freq = c->max_freq;
if (cpu_is_omap44xx()) if (cpu_is_omap44xx())
mmc->reg_offset = OMAP4_MMC_REG_OFFSET; mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
else else
......
...@@ -27,6 +27,8 @@ struct omap2_hsmmc_info { ...@@ -27,6 +27,8 @@ struct omap2_hsmmc_info {
char *name; /* or NULL for default */ char *name; /* or NULL for default */
struct platform_device *pdev; /* mmc controller instance */ struct platform_device *pdev; /* mmc controller instance */
int ocr_mask; /* temporary HACK */ 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 */ /* Remux (pad configuration) when powering on/off */
void (*remux)(struct device *dev, int slot, int power_on); void (*remux)(struct device *dev, int slot, int power_on);
/* init some special card */ /* init some special card */
......
...@@ -137,8 +137,6 @@ struct omap_mmc_platform_data { ...@@ -137,8 +137,6 @@ struct omap_mmc_platform_data {
int (*set_power)(struct device *dev, int slot, int (*set_power)(struct device *dev, int slot,
int power_on, int vdd); int power_on, int vdd);
int (*get_ro)(struct device *dev, int slot); 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); void (*remux)(struct device *dev, int slot, int power_on);
/* Call back before enabling / disabling regulators */ /* Call back before enabling / disabling regulators */
void (*before_set_reg)(struct device *dev, int slot, 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, ...@@ -1079,6 +1079,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
struct mmc_blk_request *brq = &mqrq->brq; struct mmc_blk_request *brq = &mqrq->brq;
struct request *req = mqrq->req; struct request *req = mqrq->req;
struct mmc_blk_data *md = mq->data; struct mmc_blk_data *md = mq->data;
bool do_data_tag;
/* /*
* Reliable writes are used to implement Forced Unit Access and * 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, ...@@ -1154,6 +1155,16 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
if (do_rel_wr) if (do_rel_wr)
mmc_apply_rel_rw(brq, card, req); 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 * Pre-defined multi-block transfers are preferable to
* open ended-ones (and necessary for reliable writes). * open ended-ones (and necessary for reliable writes).
...@@ -1172,13 +1183,13 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, ...@@ -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 * We'll avoid using CMD23-bounded multiblock writes for
* these, while retaining features like reliable writes. * these, while retaining features like reliable writes.
*/ */
if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) &&
if ((md->flags & MMC_BLK_CMD23) && (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) ||
mmc_op_multi(brq->cmd.opcode) && do_data_tag)) {
(do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) {
brq->sbc.opcode = MMC_SET_BLOCK_COUNT; brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
brq->sbc.arg = brq->data.blocks | 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->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
brq->mrq.sbc = &brq->sbc; brq->mrq.sbc = &brq->sbc;
} }
......
...@@ -28,13 +28,17 @@ static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id) ...@@ -28,13 +28,17 @@ static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio)
unsigned int irq, unsigned long flags)
{ {
size_t len = strlen(dev_name(host->parent)) + 4; 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; int ret;
if (irq < 0)
return irq;
cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
if (!cd) if (!cd)
return -ENOMEM; return -ENOMEM;
...@@ -45,7 +49,8 @@ int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, ...@@ -45,7 +49,8 @@ int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
goto egpioreq; goto egpioreq;
ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt, 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) if (ret < 0)
goto eirqreq; goto eirqreq;
......
...@@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) ...@@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
struct scatterlist *sg; struct scatterlist *sg;
#endif #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", pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->cmd->opcode, mmc_hostname(host), mrq->cmd->opcode,
mrq->cmd->arg, mrq->cmd->flags); mrq->cmd->arg, mrq->cmd->flags);
...@@ -243,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq) ...@@ -243,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq)
complete(&mrq->completion); 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); init_completion(&mrq->completion);
mrq->done = mmc_wait_done; mrq->done = mmc_wait_done;
if (mmc_card_removed(host->card)) { if (mmc_card_removed(host->card)) {
mrq->cmd->error = -ENOMEDIUM; mrq->cmd->error = -ENOMEDIUM;
complete(&mrq->completion); complete(&mrq->completion);
return; return -ENOMEDIUM;
} }
mmc_start_request(host, mrq); mmc_start_request(host, mrq);
return 0;
} }
static void mmc_wait_for_req_done(struct mmc_host *host, 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, ...@@ -336,6 +343,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error) struct mmc_async_req *areq, int *error)
{ {
int err = 0; int err = 0;
int start_err = 0;
struct mmc_async_req *data = host->areq; struct mmc_async_req *data = host->areq;
/* Prepare a new request */ /* Prepare a new request */
...@@ -345,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, ...@@ -345,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
if (host->areq) { if (host->areq) {
mmc_wait_for_req_done(host, host->areq->mrq); mmc_wait_for_req_done(host, host->areq->mrq);
err = host->areq->err_check(host->card, host->areq); 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) if (!err && areq)
__mmc_start_req(host, areq->mrq); start_err = __mmc_start_req(host, areq->mrq);
if (host->areq) if (host->areq)
mmc_post_req(host, host->areq->mrq, 0); mmc_post_req(host, host->areq->mrq, 0);
/* 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; host->areq = areq;
out:
if (error) if (error)
*error = err; *error = err;
return data; return data;
...@@ -598,105 +599,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) ...@@ -598,105 +599,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
} }
EXPORT_SYMBOL(mmc_align_data_size); 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 * __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim * @host: mmc host to claim
...@@ -735,8 +637,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) ...@@ -735,8 +637,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
wake_up(&host->wq); wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait); remove_wait_queue(&host->wq, &wait);
if (!stop) if (host->ops->enable && !stop && host->claim_cnt == 1)
mmc_host_enable(host); host->ops->enable(host);
return stop; return stop;
} }
...@@ -761,21 +663,28 @@ int mmc_try_claim_host(struct mmc_host *host) ...@@ -761,21 +663,28 @@ int mmc_try_claim_host(struct mmc_host *host)
claimed_host = 1; claimed_host = 1;
} }
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
if (host->ops->enable && claimed_host && host->claim_cnt == 1)
host->ops->enable(host);
return claimed_host; return claimed_host;
} }
EXPORT_SYMBOL(mmc_try_claim_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 * @host: mmc host to release
* *
* If you successfully claimed a host, this function will * Release a MMC host, allowing others to claim the host
* release it again. * for their operations.
*/ */
void mmc_do_release_host(struct mmc_host *host) void mmc_release_host(struct mmc_host *host)
{ {
unsigned long flags; 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); spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) { if (--host->claim_cnt) {
/* Release for nested claim */ /* Release for nested claim */
...@@ -787,67 +696,6 @@ void mmc_do_release_host(struct mmc_host *host) ...@@ -787,67 +696,6 @@ void mmc_do_release_host(struct mmc_host *host)
wake_up(&host->wq); 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); EXPORT_SYMBOL(mmc_release_host);
/* /*
...@@ -2115,18 +1963,36 @@ int _mmc_detect_card_removed(struct mmc_host *host) ...@@ -2115,18 +1963,36 @@ int _mmc_detect_card_removed(struct mmc_host *host)
int mmc_detect_card_removed(struct mmc_host *host) int mmc_detect_card_removed(struct mmc_host *host)
{ {
struct mmc_card *card = host->card; struct mmc_card *card = host->card;
int ret;
WARN_ON(!host->claimed); 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 * The card will be considered unchanged unless we have been asked to
* detect a change or host requires polling to provide card detection. * detect a change or host requires polling to provide card detection.
*/ */
if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL)) if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) &&
return mmc_card_removed(card); !(host->caps2 & MMC_CAP2_DETECT_ON_ERR))
return ret;
host->detect_change = 0; 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); EXPORT_SYMBOL(mmc_detect_card_removed);
...@@ -2203,8 +2069,6 @@ void mmc_stop_host(struct mmc_host *host) ...@@ -2203,8 +2069,6 @@ void mmc_stop_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
#endif #endif
if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable);
cancel_delayed_work_sync(&host->detect); cancel_delayed_work_sync(&host->detect);
mmc_flush_scheduled_work(); mmc_flush_scheduled_work();
...@@ -2399,13 +2263,11 @@ int mmc_suspend_host(struct mmc_host *host) ...@@ -2399,13 +2263,11 @@ int mmc_suspend_host(struct mmc_host *host)
{ {
int err = 0; int err = 0;
if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect); cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work(); mmc_flush_scheduled_work();
if (mmc_try_claim_host(host)) { if (mmc_try_claim_host(host)) {
err = mmc_cache_ctrl(host, 0); err = mmc_cache_ctrl(host, 0);
mmc_do_release_host(host); mmc_release_host(host);
} else { } else {
err = -EBUSY; err = -EBUSY;
} }
...@@ -2426,7 +2288,7 @@ int mmc_suspend_host(struct mmc_host *host) ...@@ -2426,7 +2288,7 @@ int mmc_suspend_host(struct mmc_host *host)
if (host->bus_ops->suspend) { if (host->bus_ops->suspend) {
err = host->bus_ops->suspend(host); err = host->bus_ops->suspend(host);
} }
mmc_do_release_host(host); mmc_release_host(host);
if (err == -ENOSYS || !host->bus_ops->resume) { if (err == -ENOSYS || !host->bus_ops->resume) {
/* /*
......
...@@ -330,7 +330,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) ...@@ -330,7 +330,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq); init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
#ifdef CONFIG_PM #ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify; host->pm_notify.notifier_call = mmc_pm_notify;
#endif #endif
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
int mmc_register_host_class(void); int mmc_register_host_class(void);
void mmc_unregister_host_class(void); void mmc_unregister_host_class(void);
void mmc_host_deeper_disable(struct work_struct *work);
#endif #endif
...@@ -519,6 +519,20 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -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 + 1] << 8 |
ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 | ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24; 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: out:
...@@ -938,7 +952,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -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 * 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. * 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, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1, EXT_CSD_ERASE_GROUP_DEF, 1,
card->ext_csd.generic_cmd6_time); card->ext_csd.generic_cmd6_time);
...@@ -1032,22 +1047,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -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. * Compute bus speed.
*/ */
...@@ -1097,9 +1096,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1097,9 +1096,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* 4. execute tuning for HS200 * 4. execute tuning for HS200
*/ */
if ((host->caps2 & MMC_CAP2_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, err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK_HS200); MMC_SEND_TUNING_BLOCK_HS200);
mmc_host_clk_release(card->host);
}
if (err) { if (err) {
pr_warning("%s: tuning execution failed\n", pr_warning("%s: tuning execution failed\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
...@@ -1218,6 +1220,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -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 * If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on. * 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) ...@@ -553,18 +553,22 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
{ {
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
unsigned int opcode; unsigned int opcode;
unsigned int flags;
int err; 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; opcode = card->ext_csd.hpi_cmd;
if (opcode == MMC_STOP_TRANSMISSION) 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) 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.opcode = opcode;
cmd.arg = card->rca << 16 | 1; cmd.arg = card->rca << 16 | 1;
cmd.flags = flags;
cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time; cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
......
...@@ -533,6 +533,31 @@ config MMC_DW_IDMAC ...@@ -533,6 +533,31 @@ config MMC_DW_IDMAC
Designware Mobile Storage IP block. This disables the external DMA Designware Mobile Storage IP block. This disables the external DMA
interface. 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 config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support" tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
......
...@@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o ...@@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.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_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_VUB300) += vub300.o
......
...@@ -1975,7 +1975,7 @@ static bool atmci_configure_dma(struct atmel_mci *host) ...@@ -1975,7 +1975,7 @@ static bool atmci_configure_dma(struct atmel_mci *host)
return false; return false;
} else { } else {
dev_info(&host->pdev->dev, dev_info(&host->pdev->dev,
"Using %s for DMA transfers\n", "using %s for DMA transfers\n",
dma_chan_name(host->dma.chan)); dma_chan_name(host->dma.chan));
return true; return true;
} }
......
...@@ -160,6 +160,16 @@ module_param(rw_threshold, uint, S_IRUGO); ...@@ -160,6 +160,16 @@ module_param(rw_threshold, uint, S_IRUGO);
MODULE_PARM_DESC(rw_threshold, MODULE_PARM_DESC(rw_threshold,
"Read/Write threshold. Default = 32"); "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; static unsigned __initdata use_dma = 1;
module_param(use_dma, uint, 0); module_param(use_dma, uint, 0);
MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1"); MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
...@@ -193,6 +203,7 @@ struct mmc_davinci_host { ...@@ -193,6 +203,7 @@ struct mmc_davinci_host {
bool use_dma; bool use_dma;
bool do_dma; bool do_dma;
bool sdio_int; bool sdio_int;
bool active_request;
/* Scatterlist DMA uses one or more parameter RAM entries: /* Scatterlist DMA uses one or more parameter RAM entries:
* the main one (associated with rxdma or txdma) plus zero or * the main one (associated with rxdma or txdma) plus zero or
...@@ -219,6 +230,7 @@ struct mmc_davinci_host { ...@@ -219,6 +230,7 @@ struct mmc_davinci_host {
#endif #endif
}; };
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id);
/* PIO only */ /* PIO only */
static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host) static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
...@@ -376,6 +388,19 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host, ...@@ -376,6 +388,19 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host,
writel(cmd->arg, host->base + DAVINCI_MMCARGHL); writel(cmd->arg, host->base + DAVINCI_MMCARGHL);
writel(cmd_reg, host->base + DAVINCI_MMCCMD); writel(cmd_reg, host->base + DAVINCI_MMCCMD);
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); writel(im_val, host->base + DAVINCI_MMCIM);
} }
...@@ -915,6 +940,7 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) ...@@ -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)) { if (!data->stop || (host->cmd && host->cmd->error)) {
mmc_request_done(host->mmc, data->mrq); mmc_request_done(host->mmc, data->mrq);
writel(0, host->base + DAVINCI_MMCIM); writel(0, host->base + DAVINCI_MMCIM);
host->active_request = false;
} else } else
mmc_davinci_start_command(host, data->stop); mmc_davinci_start_command(host, data->stop);
} }
...@@ -942,6 +968,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host, ...@@ -942,6 +968,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
cmd->mrq->cmd->retries = 0; cmd->mrq->cmd->retries = 0;
mmc_request_done(host->mmc, cmd->mrq); mmc_request_done(host->mmc, cmd->mrq);
writel(0, host->base + DAVINCI_MMCIM); 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) ...@@ -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 * by read. So, it is not unbouned loop even in the case of
* non-dma. * non-dma.
*/ */
while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) { 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); davinci_fifo_data_trans(host, rw_threshold);
status = readl(host->base + DAVINCI_MMCST0); status = readl(host->base + DAVINCI_MMCST0);
if (!status)
break;
qstatus |= status; 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) { if (qstatus & MMCST0_DATDNE) {
...@@ -1418,17 +1466,14 @@ static int davinci_mmcsd_suspend(struct device *dev) ...@@ -1418,17 +1466,14 @@ static int davinci_mmcsd_suspend(struct device *dev)
struct mmc_davinci_host *host = platform_get_drvdata(pdev); struct mmc_davinci_host *host = platform_get_drvdata(pdev);
int ret; int ret;
mmc_host_enable(host->mmc);
ret = mmc_suspend_host(host->mmc); ret = mmc_suspend_host(host->mmc);
if (!ret) { if (!ret) {
writel(0, host->base + DAVINCI_MMCIM); writel(0, host->base + DAVINCI_MMCIM);
mmc_davinci_reset_ctrl(host, 1); mmc_davinci_reset_ctrl(host, 1);
mmc_host_disable(host->mmc);
clk_disable(host->clk); clk_disable(host->clk);
host->suspended = 1; host->suspended = 1;
} else { } else {
host->suspended = 0; host->suspended = 0;
mmc_host_disable(host->mmc);
} }
return ret; return ret;
...@@ -1444,7 +1489,6 @@ static int davinci_mmcsd_resume(struct device *dev) ...@@ -1444,7 +1489,6 @@ static int davinci_mmcsd_resume(struct device *dev)
return 0; return 0;
clk_enable(host->clk); clk_enable(host->clk);
mmc_host_enable(host->mmc);
mmc_davinci_reset_ctrl(host, 0); mmc_davinci_reset_ctrl(host, 0);
ret = mmc_resume_host(host->mmc); ret = mmc_resume_host(host->mmc);
......
/*
* 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");
/*
* 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");
...@@ -268,7 +268,7 @@ static void dw_mci_start_command(struct dw_mci *host, ...@@ -268,7 +268,7 @@ static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags) struct mmc_command *cmd, u32 cmd_flags)
{ {
host->cmd = cmd; host->cmd = cmd;
dev_vdbg(&host->pdev->dev, dev_vdbg(&host->dev,
"start command: ARGR=0x%08x CMDR=0x%08x\n", "start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags); cmd->arg, cmd_flags);
...@@ -295,15 +295,25 @@ static void dw_mci_stop_dma(struct dw_mci *host) ...@@ -295,15 +295,25 @@ static void dw_mci_stop_dma(struct dw_mci *host)
} }
} }
static int dw_mci_get_dma_dir(struct mmc_data *data)
{
if (data->flags & MMC_DATA_WRITE)
return DMA_TO_DEVICE;
else
return DMA_FROM_DEVICE;
}
#ifdef CONFIG_MMC_DW_IDMAC #ifdef CONFIG_MMC_DW_IDMAC
static void dw_mci_dma_cleanup(struct dw_mci *host) static void dw_mci_dma_cleanup(struct dw_mci *host)
{ {
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
if (data) if (data)
dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, if (!data->host_cookie)
((data->flags & MMC_DATA_WRITE) dma_unmap_sg(&host->dev,
? DMA_TO_DEVICE : DMA_FROM_DEVICE)); data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
} }
static void dw_mci_idmac_stop_dma(struct dw_mci *host) static void dw_mci_idmac_stop_dma(struct dw_mci *host)
...@@ -326,7 +336,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) ...@@ -326,7 +336,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
{ {
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
dev_vdbg(&host->pdev->dev, "DMA complete\n"); dev_vdbg(&host->dev, "DMA complete\n");
host->dma_ops->cleanup(host); host->dma_ops->cleanup(host);
...@@ -428,17 +438,15 @@ static struct dw_mci_dma_ops dw_mci_idmac_ops = { ...@@ -428,17 +438,15 @@ static struct dw_mci_dma_ops dw_mci_idmac_ops = {
}; };
#endif /* CONFIG_MMC_DW_IDMAC */ #endif /* CONFIG_MMC_DW_IDMAC */
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) static int dw_mci_pre_dma_transfer(struct dw_mci *host,
struct mmc_data *data,
bool next)
{ {
struct scatterlist *sg; struct scatterlist *sg;
unsigned int i, direction, sg_len; unsigned int i, sg_len;
u32 temp;
host->using_dma = 0;
/* If we don't have a channel, we can't do DMA */ if (!next && data->host_cookie)
if (!host->use_dma) return data->host_cookie;
return -ENODEV;
/* /*
* We don't do DMA on "complex" transfers, i.e. with * We don't do DMA on "complex" transfers, i.e. with
...@@ -447,6 +455,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) ...@@ -447,6 +455,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
*/ */
if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
return -EINVAL; return -EINVAL;
if (data->blksz & 3) if (data->blksz & 3)
return -EINVAL; return -EINVAL;
...@@ -455,17 +464,74 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) ...@@ -455,17 +464,74 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
return -EINVAL; return -EINVAL;
} }
host->using_dma = 1; sg_len = dma_map_sg(&host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
if (sg_len == 0)
return -EINVAL;
if (data->flags & MMC_DATA_READ) if (next)
direction = DMA_FROM_DEVICE; data->host_cookie = sg_len;
else
direction = DMA_TO_DEVICE; return sg_len;
}
static void dw_mci_pre_req(struct mmc_host *mmc,
struct mmc_request *mrq,
bool is_first_req)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, if (!slot->host->use_dma || !data)
direction); return;
dev_vdbg(&host->pdev->dev, if (data->host_cookie) {
data->host_cookie = 0;
return;
}
if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0)
data->host_cookie = 0;
}
static void dw_mci_post_req(struct mmc_host *mmc,
struct mmc_request *mrq,
int err)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
if (!slot->host->use_dma || !data)
return;
if (data->host_cookie)
dma_unmap_sg(&slot->host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
data->host_cookie = 0;
}
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
{
int sg_len;
u32 temp;
host->using_dma = 0;
/* If we don't have a channel, we can't do DMA */
if (!host->use_dma)
return -ENODEV;
sg_len = dw_mci_pre_dma_transfer(host, data, 0);
if (sg_len < 0)
return sg_len;
host->using_dma = 1;
dev_vdbg(&host->dev,
"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len); sg_len);
...@@ -579,8 +645,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) ...@@ -579,8 +645,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock */ /* enable clock */
mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE | mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE |
SDMMC_CLKEN_LOW_PWR); SDMMC_CLKEN_LOW_PWR) << slot->id));
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot,
...@@ -800,6 +866,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) ...@@ -800,6 +866,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
static const struct mmc_host_ops dw_mci_ops = { static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request, .request = dw_mci_request,
.pre_req = dw_mci_pre_req,
.post_req = dw_mci_post_req,
.set_ios = dw_mci_set_ios, .set_ios = dw_mci_set_ios,
.get_ro = dw_mci_get_ro, .get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd, .get_cd = dw_mci_get_cd,
...@@ -821,12 +889,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) ...@@ -821,12 +889,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
slot = list_entry(host->queue.next, slot = list_entry(host->queue.next,
struct dw_mci_slot, queue_node); struct dw_mci_slot, queue_node);
list_del(&slot->queue_node); list_del(&slot->queue_node);
dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n", dev_vdbg(&host->dev, "list not empty: %s is next\n",
mmc_hostname(slot->mmc)); mmc_hostname(slot->mmc));
host->state = STATE_SENDING_CMD; host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot); dw_mci_start_request(host, slot);
} else { } else {
dev_vdbg(&host->pdev->dev, "list empty\n"); dev_vdbg(&host->dev, "list empty\n");
host->state = STATE_IDLE; host->state = STATE_IDLE;
} }
...@@ -965,7 +1033,7 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -965,7 +1033,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
data->bytes_xfered = 0; data->bytes_xfered = 0;
data->error = -ETIMEDOUT; data->error = -ETIMEDOUT;
} else { } else {
dev_err(&host->pdev->dev, dev_err(&host->dev,
"data FIFO error " "data FIFO error "
"(status=%08x)\n", "(status=%08x)\n",
status); status);
...@@ -1682,7 +1750,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1682,7 +1750,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
struct mmc_host *mmc; struct mmc_host *mmc;
struct dw_mci_slot *slot; struct dw_mci_slot *slot;
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev); mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev);
if (!mmc) if (!mmc)
return -ENOMEM; return -ENOMEM;
...@@ -1720,13 +1788,11 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1720,13 +1788,11 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
#ifdef CONFIG_MMC_DW_IDMAC if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
mmc->max_segs = host->ring_size; mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
mmc->max_blk_size = 65536; else
mmc->max_blk_count = host->ring_size; mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
mmc->max_seg_size = 0x1000;
mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
#else
if (host->pdata->blk_settings) { if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_segs = host->pdata->blk_settings->max_segs;
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
...@@ -1735,13 +1801,20 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1735,13 +1801,20 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
} else { } else {
/* Useful defaults if platform data is unset. */ /* Useful defaults if platform data is unset. */
#ifdef CONFIG_MMC_DW_IDMAC
mmc->max_segs = host->ring_size;
mmc->max_blk_size = 65536;
mmc->max_blk_count = host->ring_size;
mmc->max_seg_size = 0x1000;
mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
#else
mmc->max_segs = 64; mmc->max_segs = 64;
mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
mmc->max_blk_count = 512; mmc->max_blk_count = 512;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size; mmc->max_seg_size = mmc->max_req_size;
}
#endif /* CONFIG_MMC_DW_IDMAC */ #endif /* CONFIG_MMC_DW_IDMAC */
}
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) { if (IS_ERR(host->vmmc)) {
...@@ -1789,10 +1862,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) ...@@ -1789,10 +1862,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
static void dw_mci_init_dma(struct dw_mci *host) static void dw_mci_init_dma(struct dw_mci *host)
{ {
/* Alloc memory for sg translation */ /* Alloc memory for sg translation */
host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE, host->sg_cpu = dma_alloc_coherent(&host->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL); &host->sg_dma, GFP_KERNEL);
if (!host->sg_cpu) { if (!host->sg_cpu) {
dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n", dev_err(&host->dev, "%s: could not alloc DMA memory\n",
__func__); __func__);
goto no_dma; goto no_dma;
} }
...@@ -1800,7 +1873,7 @@ static void dw_mci_init_dma(struct dw_mci *host) ...@@ -1800,7 +1873,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
/* Determine which DMA interface to use */ /* Determine which DMA interface to use */
#ifdef CONFIG_MMC_DW_IDMAC #ifdef CONFIG_MMC_DW_IDMAC
host->dma_ops = &dw_mci_idmac_ops; host->dma_ops = &dw_mci_idmac_ops;
dev_info(&host->pdev->dev, "Using internal DMA controller.\n"); dev_info(&host->dev, "Using internal DMA controller.\n");
#endif #endif
if (!host->dma_ops) if (!host->dma_ops)
...@@ -1808,12 +1881,12 @@ static void dw_mci_init_dma(struct dw_mci *host) ...@@ -1808,12 +1881,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
if (host->dma_ops->init) { if (host->dma_ops->init) {
if (host->dma_ops->init(host)) { if (host->dma_ops->init(host)) {
dev_err(&host->pdev->dev, "%s: Unable to initialize " dev_err(&host->dev, "%s: Unable to initialize "
"DMA Controller.\n", __func__); "DMA Controller.\n", __func__);
goto no_dma; goto no_dma;
} }
} else { } else {
dev_err(&host->pdev->dev, "DMA initialization not found.\n"); dev_err(&host->dev, "DMA initialization not found.\n");
goto no_dma; goto no_dma;
} }
...@@ -1821,7 +1894,7 @@ static void dw_mci_init_dma(struct dw_mci *host) ...@@ -1821,7 +1894,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
return; return;
no_dma: no_dma:
dev_info(&host->pdev->dev, "Using PIO mode.\n"); dev_info(&host->dev, "Using PIO mode.\n");
host->use_dma = 0; host->use_dma = 0;
return; return;
} }
...@@ -1847,61 +1920,37 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host) ...@@ -1847,61 +1920,37 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
return false; return false;
} }
static int dw_mci_probe(struct platform_device *pdev) int dw_mci_probe(struct dw_mci *host)
{ {
struct dw_mci *host; int width, i, ret = 0;
struct resource *regs;
struct dw_mci_board *pdata;
int irq, ret, i, width;
u32 fifo_size; u32 fifo_size;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->pdata || !host->pdata->init) {
if (!regs) dev_err(&host->dev,
return -ENXIO;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
if (!host)
return -ENOMEM;
host->pdev = pdev;
host->pdata = pdata = pdev->dev.platform_data;
if (!pdata || !pdata->init) {
dev_err(&pdev->dev,
"Platform data must supply init function\n"); "Platform data must supply init function\n");
ret = -ENODEV; return -ENODEV;
goto err_freehost;
} }
if (!pdata->select_slot && pdata->num_slots > 1) { if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
dev_err(&pdev->dev, dev_err(&host->dev,
"Platform data must supply select_slot function\n"); "Platform data must supply select_slot function\n");
ret = -ENODEV; return -ENODEV;
goto err_freehost;
} }
if (!pdata->bus_hz) { if (!host->pdata->bus_hz) {
dev_err(&pdev->dev, dev_err(&host->dev,
"Platform data must supply bus speed\n"); "Platform data must supply bus speed\n");
ret = -ENODEV; return -ENODEV;
goto err_freehost;
} }
host->bus_hz = pdata->bus_hz; host->bus_hz = host->pdata->bus_hz;
host->quirks = pdata->quirks; host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
INIT_LIST_HEAD(&host->queue); INIT_LIST_HEAD(&host->queue);
ret = -ENOMEM;
host->regs = ioremap(regs->start, resource_size(regs));
if (!host->regs)
goto err_freehost;
host->dma_ops = pdata->dma_ops; host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host); dw_mci_init_dma(host);
/* /*
...@@ -1931,7 +1980,7 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -1931,7 +1980,7 @@ static int dw_mci_probe(struct platform_device *pdev)
} }
/* Reset all blocks */ /* Reset all blocks */
if (!mci_wait_reset(&pdev->dev, host)) { if (!mci_wait_reset(&host->dev, host)) {
ret = -ENODEV; ret = -ENODEV;
goto err_dmaunmap; goto err_dmaunmap;
} }
...@@ -1974,13 +2023,10 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -1974,13 +2023,10 @@ static int dw_mci_probe(struct platform_device *pdev)
if (!dw_mci_card_workqueue) if (!dw_mci_card_workqueue)
goto err_dmaunmap; goto err_dmaunmap;
INIT_WORK(&host->card_work, dw_mci_work_routine_card); INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
if (ret) if (ret)
goto err_workqueue; goto err_workqueue;
platform_set_drvdata(pdev, host);
if (host->pdata->num_slots) if (host->pdata->num_slots)
host->num_slots = host->pdata->num_slots; host->num_slots = host->pdata->num_slots;
else else
...@@ -2000,7 +2046,7 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2000,7 +2046,7 @@ static int dw_mci_probe(struct platform_device *pdev)
* Need to check the version-id and set data-offset for DATA register. * Need to check the version-id and set data-offset for DATA register.
*/ */
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID)); host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
dev_info(&pdev->dev, "Version ID is %04x\n", host->verid); dev_info(&host->dev, "Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A) if (host->verid < DW_MMC_240A)
host->data_offset = DATA_OFFSET; host->data_offset = DATA_OFFSET;
...@@ -2017,12 +2063,12 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2017,12 +2063,12 @@ static int dw_mci_probe(struct platform_device *pdev)
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
dev_info(&pdev->dev, "DW MMC controller at irq %d, " dev_info(&host->dev, "DW MMC controller at irq %d, "
"%d bit host data width, " "%d bit host data width, "
"%u deep fifo\n", "%u deep fifo\n",
irq, width, fifo_size); host->irq, width, fifo_size);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n"); dev_info(&host->dev, "Internal DMAC interrupt fix enabled.\n");
return 0; return 0;
...@@ -2033,7 +2079,7 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2033,7 +2079,7 @@ static int dw_mci_probe(struct platform_device *pdev)
dw_mci_cleanup_slot(host->slot[i], i); dw_mci_cleanup_slot(host->slot[i], i);
i--; i--;
} }
free_irq(irq, host); free_irq(host->irq, host);
err_workqueue: err_workqueue:
destroy_workqueue(dw_mci_card_workqueue); destroy_workqueue(dw_mci_card_workqueue);
...@@ -2041,33 +2087,26 @@ static int dw_mci_probe(struct platform_device *pdev) ...@@ -2041,33 +2087,26 @@ static int dw_mci_probe(struct platform_device *pdev)
err_dmaunmap: err_dmaunmap:
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
dma_free_coherent(&host->pdev->dev, PAGE_SIZE, dma_free_coherent(&host->dev, PAGE_SIZE,
host->sg_cpu, host->sg_dma); host->sg_cpu, host->sg_dma);
iounmap(host->regs);
if (host->vmmc) { if (host->vmmc) {
regulator_disable(host->vmmc); regulator_disable(host->vmmc);
regulator_put(host->vmmc); regulator_put(host->vmmc);
} }
err_freehost:
kfree(host);
return ret; return ret;
} }
EXPORT_SYMBOL(dw_mci_probe);
static int __exit dw_mci_remove(struct platform_device *pdev) void dw_mci_remove(struct dw_mci *host)
{ {
struct dw_mci *host = platform_get_drvdata(pdev);
int i; int i;
mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
platform_set_drvdata(pdev, NULL);
for (i = 0; i < host->num_slots; i++) { for (i = 0; i < host->num_slots; i++) {
dev_dbg(&pdev->dev, "remove slot %d\n", i); dev_dbg(&host->dev, "remove slot %d\n", i);
if (host->slot[i]) if (host->slot[i])
dw_mci_cleanup_slot(host->slot[i], i); dw_mci_cleanup_slot(host->slot[i], i);
} }
...@@ -2076,9 +2115,9 @@ static int __exit dw_mci_remove(struct platform_device *pdev) ...@@ -2076,9 +2115,9 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
mci_writel(host, CLKENA, 0); mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0); mci_writel(host, CLKSRC, 0);
free_irq(platform_get_irq(pdev, 0), host); free_irq(host->irq, host);
destroy_workqueue(dw_mci_card_workqueue); destroy_workqueue(dw_mci_card_workqueue);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
...@@ -2088,20 +2127,18 @@ static int __exit dw_mci_remove(struct platform_device *pdev) ...@@ -2088,20 +2127,18 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
regulator_put(host->vmmc); regulator_put(host->vmmc);
} }
iounmap(host->regs);
kfree(host);
return 0;
} }
EXPORT_SYMBOL(dw_mci_remove);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/* /*
* TODO: we should probably disable the clock to the card in the suspend path. * TODO: we should probably disable the clock to the card in the suspend path.
*/ */
static int dw_mci_suspend(struct device *dev) int dw_mci_suspend(struct dw_mci *host)
{ {
int i, ret; int i, ret = 0;
struct dw_mci *host = dev_get_drvdata(dev);
for (i = 0; i < host->num_slots; i++) { for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i]; struct dw_mci_slot *slot = host->slot[i];
...@@ -2123,11 +2160,11 @@ static int dw_mci_suspend(struct device *dev) ...@@ -2123,11 +2160,11 @@ static int dw_mci_suspend(struct device *dev)
return 0; return 0;
} }
EXPORT_SYMBOL(dw_mci_suspend);
static int dw_mci_resume(struct device *dev) int dw_mci_resume(struct dw_mci *host)
{ {
int i, ret; int i, ret;
struct dw_mci *host = dev_get_drvdata(dev);
if (host->vmmc) if (host->vmmc)
regulator_enable(host->vmmc); regulator_enable(host->vmmc);
...@@ -2135,7 +2172,7 @@ static int dw_mci_resume(struct device *dev) ...@@ -2135,7 +2172,7 @@ static int dw_mci_resume(struct device *dev)
if (host->dma_ops->init) if (host->dma_ops->init)
host->dma_ops->init(host); host->dma_ops->init(host);
if (!mci_wait_reset(dev, host)) { if (!mci_wait_reset(&host->dev, host)) {
ret = -ENODEV; ret = -ENODEV;
return ret; return ret;
} }
...@@ -2157,32 +2194,19 @@ static int dw_mci_resume(struct device *dev) ...@@ -2157,32 +2194,19 @@ static int dw_mci_resume(struct device *dev)
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
return 0; return 0;
} }
#else EXPORT_SYMBOL(dw_mci_resume);
#define dw_mci_suspend NULL
#define dw_mci_resume NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume);
static struct platform_driver dw_mci_driver = {
.remove = __exit_p(dw_mci_remove),
.driver = {
.name = "dw_mmc",
.pm = &dw_mci_pmops,
},
};
static int __init dw_mci_init(void) static int __init dw_mci_init(void)
{ {
return platform_driver_probe(&dw_mci_driver, dw_mci_probe); printk(KERN_INFO "Synopsys Designware Multimedia Card Interface Driver");
return 0;
} }
static void __exit dw_mci_exit(void) static void __exit dw_mci_exit(void)
{ {
platform_driver_unregister(&dw_mci_driver);
} }
module_init(dw_mci_init); module_init(dw_mci_init);
......
...@@ -175,4 +175,11 @@ ...@@ -175,4 +175,11 @@
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
#endif #endif
extern int dw_mci_probe(struct dw_mci *host);
extern void dw_mci_remove(struct dw_mci *host);
#ifdef CONFIG_PM
extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host);
#endif
#endif /* _DW_MMC_H_ */ #endif /* _DW_MMC_H_ */
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/core.h> #include <linux/mmc/core.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
...@@ -106,17 +109,6 @@ ...@@ -106,17 +109,6 @@
#define SOFTRESET (1 << 1) #define SOFTRESET (1 << 1)
#define RESETDONE (1 << 0) #define RESETDONE (1 << 0)
/*
* FIXME: Most likely all the data using these _DEVID defines should come
* from the platform_data, or implemented in controller and slot specific
* functions.
*/
#define OMAP_MMC1_DEVID 0
#define OMAP_MMC2_DEVID 1
#define OMAP_MMC3_DEVID 2
#define OMAP_MMC4_DEVID 3
#define OMAP_MMC5_DEVID 4
#define MMC_AUTOSUSPEND_DELAY 100 #define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20 #define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MIN_CLOCK 400000 #define OMAP_MMC_MIN_CLOCK 400000
...@@ -164,7 +156,6 @@ struct omap_hsmmc_host { ...@@ -164,7 +156,6 @@ struct omap_hsmmc_host {
void __iomem *base; void __iomem *base;
resource_size_t mapbase; resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */ spinlock_t irq_lock; /* Prevent races with irq handler */
unsigned int id;
unsigned int dma_len; unsigned int dma_len;
unsigned int dma_sg_idx; unsigned int dma_sg_idx;
unsigned char bus_mode; unsigned char bus_mode;
...@@ -179,7 +170,6 @@ struct omap_hsmmc_host { ...@@ -179,7 +170,6 @@ struct omap_hsmmc_host {
int got_dbclk; int got_dbclk;
int response_busy; int response_busy;
int context_loss; int context_loss;
int dpm_state;
int vdd; int vdd;
int protect_card; int protect_card;
int reqs_blocked; int reqs_blocked;
...@@ -241,28 +231,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) ...@@ -241,28 +231,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
#ifdef CONFIG_REGULATOR #ifdef CONFIG_REGULATOR
static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
struct omap_hsmmc_host *host =
platform_get_drvdata(to_platform_device(dev));
int ret;
if (mmc_slot(host).before_set_reg)
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
if (power_on)
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
else
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
if (mmc_slot(host).after_set_reg)
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
return ret;
}
static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on,
int vdd) int vdd)
{ {
struct omap_hsmmc_host *host = struct omap_hsmmc_host *host =
...@@ -275,6 +244,13 @@ static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on, ...@@ -275,6 +244,13 @@ static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on,
*/ */
if (!host->vcc) if (!host->vcc)
return 0; return 0;
/*
* With DT, never turn OFF the regulator. This is because
* the pbias cell programming support is still missing when
* booting with Device tree
*/
if (of_have_populated_dt() && !vdd)
return 0;
if (mmc_slot(host).before_set_reg) if (mmc_slot(host).before_set_reg)
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
...@@ -318,106 +294,16 @@ static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on, ...@@ -318,106 +294,16 @@ static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on,
return ret; return ret;
} }
static int omap_hsmmc_4_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
return 0;
}
static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep,
int vdd, int cardsleep)
{
struct omap_hsmmc_host *host =
platform_get_drvdata(to_platform_device(dev));
int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
return regulator_set_mode(host->vcc, mode);
}
static int omap_hsmmc_235_set_sleep(struct device *dev, int slot, int sleep,
int vdd, int cardsleep)
{
struct omap_hsmmc_host *host =
platform_get_drvdata(to_platform_device(dev));
int err, mode;
/*
* If we don't see a Vcc regulator, assume it's a fixed
* voltage always-on regulator.
*/
if (!host->vcc)
return 0;
mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
if (!host->vcc_aux)
return regulator_set_mode(host->vcc, mode);
if (cardsleep) {
/* VCC can be turned off if card is asleep */
if (sleep)
err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
else
err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
} else
err = regulator_set_mode(host->vcc, mode);
if (err)
return err;
if (!mmc_slot(host).vcc_aux_disable_is_sleep)
return regulator_set_mode(host->vcc_aux, mode);
if (sleep)
return regulator_disable(host->vcc_aux);
else
return regulator_enable(host->vcc_aux);
}
static int omap_hsmmc_4_set_sleep(struct device *dev, int slot, int sleep,
int vdd, int cardsleep)
{
return 0;
}
static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
{ {
struct regulator *reg; struct regulator *reg;
int ret = 0;
int ocr_value = 0; int ocr_value = 0;
switch (host->id) { mmc_slot(host).set_power = omap_hsmmc_set_power;
case OMAP_MMC1_DEVID:
/* On-chip level shifting via PBIAS0/PBIAS1 */
mmc_slot(host).set_power = omap_hsmmc_1_set_power;
mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep;
break;
case OMAP_MMC2_DEVID:
case OMAP_MMC3_DEVID:
case OMAP_MMC5_DEVID:
/* Off-chip level shifting, or none */
mmc_slot(host).set_power = omap_hsmmc_235_set_power;
mmc_slot(host).set_sleep = omap_hsmmc_235_set_sleep;
break;
case OMAP_MMC4_DEVID:
mmc_slot(host).set_power = omap_hsmmc_4_set_power;
mmc_slot(host).set_sleep = omap_hsmmc_4_set_sleep;
default:
pr_err("MMC%d configuration not supported!\n", host->id);
return -EINVAL;
}
reg = regulator_get(host->dev, "vmmc"); reg = regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) { if (IS_ERR(reg)) {
dev_dbg(host->dev, "vmmc regulator missing\n"); dev_dbg(host->dev, "vmmc regulator missing\n");
/*
* HACK: until fixed.c regulator is usable,
* we don't require a main regulator
* for MMC2 or MMC3
*/
if (host->id == OMAP_MMC1_DEVID) {
ret = PTR_ERR(reg);
goto err;
}
} else { } else {
host->vcc = reg; host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg); ocr_value = mmc_regulator_get_ocrmask(reg);
...@@ -425,8 +311,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) ...@@ -425,8 +311,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
mmc_slot(host).ocr_mask = ocr_value; mmc_slot(host).ocr_mask = ocr_value;
} else { } else {
if (!(mmc_slot(host).ocr_mask & ocr_value)) { if (!(mmc_slot(host).ocr_mask & ocr_value)) {
pr_err("MMC%d ocrmask %x is not supported\n", dev_err(host->dev, "ocrmask %x is not supported\n",
host->id, mmc_slot(host).ocr_mask); mmc_slot(host).ocr_mask);
mmc_slot(host).ocr_mask = 0; mmc_slot(host).ocr_mask = 0;
return -EINVAL; return -EINVAL;
} }
...@@ -459,11 +345,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) ...@@ -459,11 +345,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
} }
return 0; return 0;
err:
mmc_slot(host).set_power = NULL;
mmc_slot(host).set_sleep = NULL;
return ret;
} }
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
...@@ -471,7 +352,6 @@ static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) ...@@ -471,7 +352,6 @@ static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
regulator_put(host->vcc); regulator_put(host->vcc);
regulator_put(host->vcc_aux); regulator_put(host->vcc_aux);
mmc_slot(host).set_power = NULL; mmc_slot(host).set_power = NULL;
mmc_slot(host).set_sleep = NULL;
} }
static inline int omap_hsmmc_have_reg(void) static inline int omap_hsmmc_have_reg(void)
...@@ -710,7 +590,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) ...@@ -710,7 +590,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, SYSCONFIG, OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
if (host->id == OMAP_MMC1_DEVID) { if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF && if (host->power_mode != MMC_POWER_OFF &&
(1 << ios->vdd) <= MMC_VDD_23_24) (1 << ios->vdd) <= MMC_VDD_23_24)
hctl = SDVS18; hctl = SDVS18;
...@@ -1261,14 +1141,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) ...@@ -1261,14 +1141,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
host->reqs_blocked = 0; host->reqs_blocked = 0;
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
if (host->protect_card) { if (host->protect_card) {
pr_info("%s: cover is closed, " dev_info(host->dev, "%s: cover is closed, "
"card is now accessible\n", "card is now accessible\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
host->protect_card = 0; host->protect_card = 0;
} }
} else { } else {
if (!host->protect_card) { if (!host->protect_card) {
pr_info("%s: cover is open, " dev_info(host->dev, "%s: cover is open, "
"card is now inaccessible\n", "card is now inaccessible\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
host->protect_card = 1; host->protect_card = 1;
...@@ -1405,7 +1285,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, ...@@ -1405,7 +1285,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
if (!next && data->host_cookie && if (!next && data->host_cookie &&
data->host_cookie != host->next_data.cookie) { data->host_cookie != host->next_data.cookie) {
pr_warning("[%s] invalid cookie: data->host_cookie %d" dev_warn(host->dev, "[%s] invalid cookie: data->host_cookie %d"
" host->next_data.cookie %d\n", " host->next_data.cookie %d\n",
__func__, data->host_cookie, host->next_data.cookie); __func__, data->host_cookie, host->next_data.cookie);
data->host_cookie = 0; data->host_cookie = 0;
...@@ -1663,7 +1543,13 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -1663,7 +1543,13 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* of external transceiver; but they all handle 1.8V. * of external transceiver; but they all handle 1.8V.
*/ */
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
(ios->vdd == DUAL_VOLT_OCR_BIT)) { (ios->vdd == DUAL_VOLT_OCR_BIT) &&
/*
* With pbias cell programming missing, this
* can't be allowed when booting with device
* tree.
*/
(!of_have_populated_dt())) {
/* /*
* The mmc_select_voltage fn of the core does * The mmc_select_voltage fn of the core does
* not seem to set the power_mode to * not seem to set the power_mode to
...@@ -1748,7 +1634,7 @@ static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) ...@@ -1748,7 +1634,7 @@ static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
return 0; return 0;
} }
static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy) static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
{ {
struct omap_hsmmc_host *host = mmc_priv(mmc); struct omap_hsmmc_host *host = mmc_priv(mmc);
...@@ -1782,15 +1668,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) ...@@ -1782,15 +1668,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
if (host->pdata->get_context_loss_count) if (host->pdata->get_context_loss_count)
context_loss = host->pdata->get_context_loss_count(host->dev); context_loss = host->pdata->get_context_loss_count(host->dev);
seq_printf(s, "mmc%d:\n" seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n",
" enabled:\t%d\n" mmc->index, host->context_loss, context_loss);
" dpm_state:\t%d\n"
" nesting_cnt:\t%d\n"
" ctx_loss:\t%d:%d\n"
"\nregs:\n",
mmc->index, mmc->enabled ? 1 : 0,
host->dpm_state, mmc->nesting_cnt,
host->context_loss, context_loss);
if (host->suspended) { if (host->suspended) {
seq_printf(s, "host suspended, can't read registers\n"); seq_printf(s, "host suspended, can't read registers\n");
...@@ -1847,6 +1726,65 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc) ...@@ -1847,6 +1726,65 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
#endif #endif
#ifdef CONFIG_OF
static u16 omap4_reg_offset = 0x100;
static const struct of_device_id omap_mmc_of_match[] = {
{
.compatible = "ti,omap2-hsmmc",
},
{
.compatible = "ti,omap3-hsmmc",
},
{
.compatible = "ti,omap4-hsmmc",
.data = &omap4_reg_offset,
},
{},
}
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
{
struct omap_mmc_platform_data *pdata;
struct device_node *np = dev->of_node;
u32 bus_width;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL; /* out of memory */
if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
/* This driver only supports 1 slot */
pdata->nr_slots = 1;
pdata->slots[0].switch_pin = of_get_named_gpio(np, "cd-gpios", 0);
pdata->slots[0].gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
if (of_find_property(np, "ti,non-removable", NULL)) {
pdata->slots[0].nonremovable = true;
pdata->slots[0].no_regulator_off_init = true;
}
of_property_read_u32(np, "ti,bus-width", &bus_width);
if (bus_width == 4)
pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA;
else if (bus_width == 8)
pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA;
if (of_find_property(np, "ti,needs-special-reset", NULL))
pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
return pdata;
}
#else
static inline struct omap_mmc_platform_data
*of_get_hsmmc_pdata(struct device *dev)
{
return NULL;
}
#endif
static int __init omap_hsmmc_probe(struct platform_device *pdev) static int __init omap_hsmmc_probe(struct platform_device *pdev)
{ {
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
...@@ -1854,6 +1792,16 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1854,6 +1792,16 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
struct omap_hsmmc_host *host = NULL; struct omap_hsmmc_host *host = NULL;
struct resource *res; struct resource *res;
int ret, irq; int ret, irq;
const struct of_device_id *match;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
pdata = of_get_hsmmc_pdata(&pdev->dev);
if (match->data) {
u16 *offsetp = match->data;
pdata->reg_offset = *offsetp;
}
}
if (pdata == NULL) { if (pdata == NULL) {
dev_err(&pdev->dev, "Platform Data is missing\n"); dev_err(&pdev->dev, "Platform Data is missing\n");
...@@ -1894,7 +1842,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1894,7 +1842,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->dev->dma_mask = &pdata->dma_mask; host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1; host->dma_ch = -1;
host->irq = irq; host->irq = irq;
host->id = pdev->id;
host->slot_id = 0; host->slot_id = 0;
host->mapbase = res->start; host->mapbase = res->start;
host->base = ioremap(host->mapbase, SZ_4K); host->base = ioremap(host->mapbase, SZ_4K);
...@@ -1913,6 +1860,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1913,6 +1860,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc_slot(host).no_off = 1; mmc_slot(host).no_off = 1;
mmc->f_min = OMAP_MMC_MIN_CLOCK; mmc->f_min = OMAP_MMC_MIN_CLOCK;
if (pdata->max_freq > 0)
mmc->f_max = pdata->max_freq;
else
mmc->f_max = OMAP_MMC_MAX_CLOCK; mmc->f_max = OMAP_MMC_MAX_CLOCK;
spin_lock_init(&host->irq_lock); spin_lock_init(&host->irq_lock);
...@@ -1926,7 +1877,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1926,7 +1877,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host); omap_hsmmc_context_save(host);
mmc->caps |= MMC_CAP_DISABLE;
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
...@@ -1977,32 +1927,19 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1977,32 +1927,19 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_conf_bus_power(host); omap_hsmmc_conf_bus_power(host);
/* Select DMA lines */ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
switch (host->id) { if (!res) {
case OMAP_MMC1_DEVID: dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
host->dma_line_tx = OMAP24XX_DMA_MMC1_TX; goto err_irq;
host->dma_line_rx = OMAP24XX_DMA_MMC1_RX; }
break; host->dma_line_tx = res->start;
case OMAP_MMC2_DEVID:
host->dma_line_tx = OMAP24XX_DMA_MMC2_TX; res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
host->dma_line_rx = OMAP24XX_DMA_MMC2_RX; if (!res) {
break; dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
case OMAP_MMC3_DEVID:
host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
break;
case OMAP_MMC4_DEVID:
host->dma_line_tx = OMAP44XX_DMA_MMC4_TX;
host->dma_line_rx = OMAP44XX_DMA_MMC4_RX;
break;
case OMAP_MMC5_DEVID:
host->dma_line_tx = OMAP44XX_DMA_MMC5_TX;
host->dma_line_rx = OMAP44XX_DMA_MMC5_RX;
break;
default:
dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
goto err_irq; goto err_irq;
} }
host->dma_line_rx = res->start;
/* Request IRQ for MMC operations */ /* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, 0, ret = request_irq(host->irq, omap_hsmmc_irq, 0,
...@@ -2083,6 +2020,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) ...@@ -2083,6 +2020,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
err_irq: err_irq:
pm_runtime_mark_last_busy(host->dev); pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev); pm_runtime_put_autosuspend(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk); clk_put(host->fclk);
if (host->got_dbclk) { if (host->got_dbclk) {
clk_disable(host->dbclk); clk_disable(host->dbclk);
...@@ -2269,6 +2207,7 @@ static struct platform_driver omap_hsmmc_driver = { ...@@ -2269,6 +2207,7 @@ static struct platform_driver omap_hsmmc_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &omap_hsmmc_dev_pm_ops, .pm = &omap_hsmmc_dev_pm_ops,
.of_match_table = of_match_ptr(omap_mmc_of_match),
}, },
}; };
......
/* /*
* Freescale eSDHC controller driver. * Freescale eSDHC controller driver.
* *
* Copyright (c) 2007, 2010 Freescale Semiconductor, Inc. * Copyright (c) 2007, 2010, 2012 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc. * Copyright (c) 2009 MontaVista Software, Inc.
* *
* Authors: Xiaobo Xie <X.Xie@freescale.com> * Authors: Xiaobo Xie <X.Xie@freescale.com>
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
*/ */
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
...@@ -114,6 +115,34 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) ...@@ -114,6 +115,34 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
return pltfm_host->clock / 256 / 16; return pltfm_host->clock / 256 / 16;
} }
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
{
/* Workaround to reduce the clock frequency for p1010 esdhc */
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
if (clock > 20000000)
clock -= 5000000;
if (clock > 40000000)
clock -= 5000000;
}
/* Set the clock */
esdhc_set_clock(host, clock);
}
#ifdef CONFIG_PM
static u32 esdhc_proctl;
static void esdhc_of_suspend(struct sdhci_host *host)
{
esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
}
static void esdhc_of_resume(struct sdhci_host *host)
{
esdhc_of_enable_dma(host);
sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
}
#endif
static struct sdhci_ops sdhci_esdhc_ops = { static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = sdhci_be32bs_readl, .read_l = sdhci_be32bs_readl,
.read_w = esdhc_readw, .read_w = esdhc_readw,
...@@ -121,10 +150,14 @@ static struct sdhci_ops sdhci_esdhc_ops = { ...@@ -121,10 +150,14 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.write_l = sdhci_be32bs_writel, .write_l = sdhci_be32bs_writel,
.write_w = esdhc_writew, .write_w = esdhc_writew,
.write_b = esdhc_writeb, .write_b = esdhc_writeb,
.set_clock = esdhc_set_clock, .set_clock = esdhc_of_set_clock,
.enable_dma = esdhc_of_enable_dma, .enable_dma = esdhc_of_enable_dma,
.get_max_clock = esdhc_of_get_max_clock, .get_max_clock = esdhc_of_get_max_clock,
.get_min_clock = esdhc_of_get_min_clock, .get_min_clock = esdhc_of_get_min_clock,
#ifdef CONFIG_PM
.platform_suspend = esdhc_of_suspend,
.platform_resume = esdhc_of_resume,
#endif
}; };
static struct sdhci_pltfm_data sdhci_esdhc_pdata = { static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
......
...@@ -28,6 +28,12 @@ ...@@ -28,6 +28,12 @@
#include "sdhci.h" #include "sdhci.h"
/*
* PCI device IDs
*/
#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
/* /*
* PCI registers * PCI registers
*/ */
...@@ -47,6 +53,7 @@ struct sdhci_pci_slot; ...@@ -47,6 +53,7 @@ struct sdhci_pci_slot;
struct sdhci_pci_fixes { struct sdhci_pci_fixes {
unsigned int quirks; unsigned int quirks;
unsigned int quirks2;
bool allow_runtime_pm; bool allow_runtime_pm;
int (*probe) (struct sdhci_pci_chip *); int (*probe) (struct sdhci_pci_chip *);
...@@ -73,6 +80,7 @@ struct sdhci_pci_chip { ...@@ -73,6 +80,7 @@ struct sdhci_pci_chip {
struct pci_dev *pdev; struct pci_dev *pdev;
unsigned int quirks; unsigned int quirks;
unsigned int quirks2;
bool allow_runtime_pm; bool allow_runtime_pm;
const struct sdhci_pci_fixes *fixes; const struct sdhci_pci_fixes *fixes;
...@@ -172,6 +180,12 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip) ...@@ -172,6 +180,12 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
return 0; return 0;
} }
static int pch_hc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
return 0;
}
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id) static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id)
...@@ -244,7 +258,8 @@ static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) ...@@ -244,7 +258,8 @@ static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
{ {
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC; slot->host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC |
MMC_CAP2_HC_ERASE_SZ;
return 0; return 0;
} }
...@@ -271,6 +286,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { ...@@ -271,6 +286,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.allow_runtime_pm = true, .allow_runtime_pm = true,
.probe_slot = mfd_sdio_probe_slot, .probe_slot = mfd_sdio_probe_slot,
}; };
...@@ -281,6 +297,11 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { ...@@ -281,6 +297,11 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
.probe_slot = mfd_emmc_probe_slot, .probe_slot = mfd_emmc_probe_slot,
}; };
static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
.quirks = SDHCI_QUIRK_BROKEN_ADMA,
.probe_slot = pch_hc_probe_slot,
};
/* O2Micro extra registers */ /* O2Micro extra registers */
#define O2_SD_LOCK_WP 0xD3 #define O2_SD_LOCK_WP 0xD3
#define O2_SD_MULTI_VCC3V 0xEE #define O2_SD_MULTI_VCC3V 0xEE
...@@ -816,6 +837,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { ...@@ -816,6 +837,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
}, },
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_PCH_SDIO0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_PCH_SDIO1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio,
},
{ {
.vendor = PCI_VENDOR_ID_O2, .vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_8120, .device = PCI_DEVICE_ID_O2_8120,
...@@ -1206,6 +1243,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( ...@@ -1206,6 +1243,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
host->hw_name = "PCI"; host->hw_name = "PCI";
host->ops = &sdhci_pci_ops; host->ops = &sdhci_pci_ops;
host->quirks = chip->quirks; host->quirks = chip->quirks;
host->quirks2 = chip->quirks2;
host->irq = pdev->irq; host->irq = pdev->irq;
...@@ -1365,6 +1403,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, ...@@ -1365,6 +1403,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
if (chip->fixes) { if (chip->fixes) {
chip->quirks = chip->fixes->quirks; chip->quirks = chip->fixes->quirks;
chip->quirks2 = chip->fixes->quirks2;
chip->allow_runtime_pm = chip->fixes->allow_runtime_pm; chip->allow_runtime_pm = chip->fixes->allow_runtime_pm;
} }
chip->num_slots = slots; chip->num_slots = slots;
...@@ -1379,6 +1418,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, ...@@ -1379,6 +1418,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
slots = chip->num_slots; /* Quirk may have changed this */ slots = chip->num_slots; /* Quirk may have changed this */
pci_enable_msi(pdev);
for (i = 0; i < slots; i++) { for (i = 0; i < slots; i++) {
slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i);
if (IS_ERR(slot)) { if (IS_ERR(slot)) {
...@@ -1397,6 +1438,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, ...@@ -1397,6 +1438,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
return 0; return 0;
free: free:
pci_disable_msi(pdev);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
kfree(chip); kfree(chip);
...@@ -1419,6 +1462,8 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) ...@@ -1419,6 +1462,8 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
for (i = 0; i < chip->num_slots; i++) for (i = 0; i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]); sdhci_pci_remove_slot(chip->slots[i]);
pci_disable_msi(pdev);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
kfree(chip); kfree(chip);
} }
......
...@@ -300,20 +300,15 @@ static int sdhci_resume(struct device *dev) ...@@ -300,20 +300,15 @@ static int sdhci_resume(struct device *dev)
return sdhci_resume_host(host); return sdhci_resume_host(host);
} }
const struct dev_pm_ops sdhci_pm_ops = {
.suspend = sdhci_suspend,
.resume = sdhci_resume,
};
#endif #endif
static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
static struct platform_driver sdhci_driver = { static struct platform_driver sdhci_driver = {
.driver = { .driver = {
.name = "sdhci", .name = "sdhci",
.owner = THIS_MODULE, .owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &sdhci_pm_ops, .pm = &sdhci_pm_ops,
#endif
}, },
.probe = sdhci_probe, .probe = sdhci_probe,
.remove = __devexit_p(sdhci_remove), .remove = __devexit_p(sdhci_remove),
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
...@@ -31,6 +32,19 @@ ...@@ -31,6 +32,19 @@
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
struct sdhci_tegra_soc_data {
struct sdhci_pltfm_data *pdata;
u32 nvquirks;
};
struct sdhci_tegra {
const struct tegra_sdhci_platform_data *plat;
const struct sdhci_tegra_soc_data *soc_data;
};
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
{ {
u32 val; u32 val;
...@@ -46,7 +60,12 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) ...@@ -46,7 +60,12 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
{ {
if (unlikely(reg == SDHCI_HOST_VERSION)) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
(reg == SDHCI_HOST_VERSION))) {
/* Erratum: Version register is invalid in HW. */ /* Erratum: Version register is invalid in HW. */
return SDHCI_SPEC_200; return SDHCI_SPEC_200;
} }
...@@ -56,6 +75,10 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) ...@@ -56,6 +75,10 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
/* Seems like we're getting spurious timeout and crc errors, so /* Seems like we're getting spurious timeout and crc errors, so
* disable signalling of them. In case of real errors software * disable signalling of them. In case of real errors software
* timers should take care of eventually detecting them. * timers should take care of eventually detecting them.
...@@ -65,7 +88,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) ...@@ -65,7 +88,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
writel(val, host->ioaddr + reg); writel(val, host->ioaddr + reg);
if (unlikely(reg == SDHCI_INT_ENABLE)) { if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
(reg == SDHCI_INT_ENABLE))) {
/* Erratum: Must enable block gap interrupt detection */ /* Erratum: Must enable block gap interrupt detection */
u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
if (val & SDHCI_INT_CARD_INT) if (val & SDHCI_INT_CARD_INT)
...@@ -76,10 +100,11 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) ...@@ -76,10 +100,11 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
} }
} }
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct tegra_sdhci_platform_data *plat = pltfm_host->priv; struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
if (!gpio_is_valid(plat->wp_gpio)) if (!gpio_is_valid(plat->wp_gpio))
return -1; return -1;
...@@ -98,7 +123,8 @@ static irqreturn_t carddetect_irq(int irq, void *data) ...@@ -98,7 +123,8 @@ static irqreturn_t carddetect_irq(int irq, void *data)
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct tegra_sdhci_platform_data *plat = pltfm_host->priv; struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
u32 ctrl; u32 ctrl;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
...@@ -124,7 +150,8 @@ static struct sdhci_ops tegra_sdhci_ops = { ...@@ -124,7 +150,8 @@ static struct sdhci_ops tegra_sdhci_ops = {
.platform_8bit_width = tegra_sdhci_8bit, .platform_8bit_width = tegra_sdhci_8bit,
}; };
static struct sdhci_pltfm_data sdhci_tegra_pdata = { #ifdef CONFIG_ARCH_TEGRA_2x_SOC
static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_HISPD_BIT |
...@@ -132,8 +159,35 @@ static struct sdhci_pltfm_data sdhci_tegra_pdata = { ...@@ -132,8 +159,35 @@ static struct sdhci_pltfm_data sdhci_tegra_pdata = {
.ops = &tegra_sdhci_ops, .ops = &tegra_sdhci_ops,
}; };
static struct sdhci_tegra_soc_data soc_data_tegra20 = {
.pdata = &sdhci_tegra20_pdata,
.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
NVQUIRK_ENABLE_BLOCK_GAP_DET,
};
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
.ops = &tegra_sdhci_ops,
};
static struct sdhci_tegra_soc_data soc_data_tegra30 = {
.pdata = &sdhci_tegra30_pdata,
};
#endif
static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = { static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
{ .compatible = "nvidia,tegra20-sdhci", }, #ifdef CONFIG_ARCH_TEGRA_3x_SOC
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
#endif
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
#endif
{} {}
}; };
MODULE_DEVICE_TABLE(of, sdhci_dt_ids); MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
...@@ -164,13 +218,22 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( ...@@ -164,13 +218,22 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
static int __devinit sdhci_tegra_probe(struct platform_device *pdev) static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
{ {
const struct of_device_id *match;
const struct sdhci_tegra_soc_data *soc_data;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
struct tegra_sdhci_platform_data *plat; struct tegra_sdhci_platform_data *plat;
struct sdhci_host *host; struct sdhci_tegra *tegra_host;
struct clk *clk; struct clk *clk;
int rc; int rc;
host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata); match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
if (match)
soc_data = match->data;
else
soc_data = &soc_data_tegra20;
host = sdhci_pltfm_init(pdev, soc_data->pdata);
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);
...@@ -187,7 +250,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) ...@@ -187,7 +250,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
goto err_no_plat; goto err_no_plat;
} }
pltfm_host->priv = plat; tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
if (!tegra_host) {
dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
rc = -ENOMEM;
goto err_no_plat;
}
tegra_host->plat = plat;
tegra_host->soc_data = soc_data;
pltfm_host->priv = tegra_host;
if (gpio_is_valid(plat->power_gpio)) { if (gpio_is_valid(plat->power_gpio)) {
rc = gpio_request(plat->power_gpio, "sdhci_power"); rc = gpio_request(plat->power_gpio, "sdhci_power");
...@@ -283,7 +356,8 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev) ...@@ -283,7 +356,8 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
{ {
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct tegra_sdhci_platform_data *plat = pltfm_host->priv; struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead); sdhci_remove_host(host, dead);
...@@ -326,5 +400,5 @@ static struct platform_driver sdhci_tegra_driver = { ...@@ -326,5 +400,5 @@ static struct platform_driver sdhci_tegra_driver = {
module_platform_driver(sdhci_tegra_driver); module_platform_driver(sdhci_tegra_driver);
MODULE_DESCRIPTION("SDHCI driver for Tegra"); MODULE_DESCRIPTION("SDHCI driver for Tegra");
MODULE_AUTHOR(" Google, Inc."); MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -2267,8 +2267,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -2267,8 +2267,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
{ {
irqreturn_t result; irqreturn_t result;
struct sdhci_host *host = dev_id; struct sdhci_host *host = dev_id;
u32 intmask; u32 intmask, unexpected = 0;
int cardint = 0; int cardint = 0, max_loops = 16;
spin_lock(&host->lock); spin_lock(&host->lock);
...@@ -2286,6 +2286,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -2286,6 +2286,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
goto out; goto out;
} }
again:
DBG("*** %s got interrupt: 0x%08x\n", DBG("*** %s got interrupt: 0x%08x\n",
mmc_hostname(host->mmc), intmask); mmc_hostname(host->mmc), intmask);
...@@ -2344,19 +2345,23 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -2344,19 +2345,23 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_CARD_INT; intmask &= ~SDHCI_INT_CARD_INT;
if (intmask) { if (intmask) {
pr_err("%s: Unexpected interrupt 0x%08x.\n", unexpected |= intmask;
mmc_hostname(host->mmc), intmask);
sdhci_dumpregs(host);
sdhci_writel(host, intmask, SDHCI_INT_STATUS); sdhci_writel(host, intmask, SDHCI_INT_STATUS);
} }
result = IRQ_HANDLED; result = IRQ_HANDLED;
mmiowb(); intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (intmask && --max_loops)
goto again;
out: out:
spin_unlock(&host->lock); spin_unlock(&host->lock);
if (unexpected) {
pr_err("%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), unexpected);
sdhci_dumpregs(host);
}
/* /*
* We have to delay this as it calls back into the driver. * We have to delay this as it calls back into the driver.
*/ */
...@@ -2379,6 +2384,9 @@ int sdhci_suspend_host(struct sdhci_host *host) ...@@ -2379,6 +2384,9 @@ int sdhci_suspend_host(struct sdhci_host *host)
int ret; int ret;
bool has_tuning_timer; bool has_tuning_timer;
if (host->ops->platform_suspend)
host->ops->platform_suspend(host);
sdhci_disable_card_detection(host); sdhci_disable_card_detection(host);
/* Disable tuning since we are suspending */ /* Disable tuning since we are suspending */
...@@ -2423,12 +2431,24 @@ int sdhci_resume_host(struct sdhci_host *host) ...@@ -2423,12 +2431,24 @@ int sdhci_resume_host(struct sdhci_host *host)
if (ret) if (ret)
return ret; return ret;
if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) &&
(host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) {
/* Card keeps power but host controller does not */
sdhci_init(host, 0);
host->pwr = 0;
host->clock = 0;
sdhci_do_set_ios(host, &host->mmc->ios);
} else {
sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
mmiowb(); mmiowb();
}
ret = mmc_resume_host(host->mmc); ret = mmc_resume_host(host->mmc);
sdhci_enable_card_detection(host); sdhci_enable_card_detection(host);
if (host->ops->platform_resume)
host->ops->platform_resume(host);
/* Set the re-tuning expiration flag */ /* Set the re-tuning expiration flag */
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
(host->tuning_mode == SDHCI_TUNING_MODE_1)) (host->tuning_mode == SDHCI_TUNING_MODE_1))
......
...@@ -275,6 +275,8 @@ struct sdhci_ops { ...@@ -275,6 +275,8 @@ struct sdhci_ops {
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host); void (*hw_reset)(struct sdhci_host *host);
void (*platform_suspend)(struct sdhci_host *host);
void (*platform_resume)(struct sdhci_host *host);
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
......
...@@ -746,7 +746,6 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, ...@@ -746,7 +746,6 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
case MMC_SET_WRITE_PROT: case MMC_SET_WRITE_PROT:
case MMC_CLR_WRITE_PROT: case MMC_CLR_WRITE_PROT:
case MMC_ERASE: case MMC_ERASE:
case MMC_GEN_CMD:
tmp |= CMD_SET_RBSY; tmp |= CMD_SET_RBSY;
break; break;
} }
...@@ -829,7 +828,6 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, ...@@ -829,7 +828,6 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
case MMC_SET_WRITE_PROT: case MMC_SET_WRITE_PROT:
case MMC_CLR_WRITE_PROT: case MMC_CLR_WRITE_PROT:
case MMC_ERASE: case MMC_ERASE:
case MMC_GEN_CMD:
mask = MASK_START_CMD | MASK_MRBSYE; mask = MASK_START_CMD | MASK_MRBSYE;
break; break;
default: default:
......
...@@ -90,6 +90,15 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr) ...@@ -90,6 +90,15 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
return 0; return 0;
} }
static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
{
mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100));
}
static const struct sh_mobile_sdhi_ops sdhi_ops = {
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
};
static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
{ {
struct sh_mobile_sdhi *priv; struct sh_mobile_sdhi *priv;
...@@ -109,6 +118,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -109,6 +118,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data = &priv->mmc_data; mmc_data = &priv->mmc_data;
p->pdata = mmc_data; p->pdata = mmc_data;
if (p->init) {
ret = p->init(pdev, &sdhi_ops);
if (ret)
goto einit;
}
snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
priv->clk = clk_get(&pdev->dev, clk_name); priv->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(priv->clk)) { if (IS_ERR(priv->clk)) {
...@@ -117,8 +132,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -117,8 +132,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
goto eclkget; goto eclkget;
} }
clk_enable(priv->clk);
mmc_data->hclk = clk_get_rate(priv->clk); mmc_data->hclk = clk_get_rate(priv->clk);
mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
mmc_data->get_cd = sh_mobile_sdhi_get_cd; mmc_data->get_cd = sh_mobile_sdhi_get_cd;
...@@ -129,6 +142,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -129,6 +142,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
mmc_data->ocr_mask = p->tmio_ocr_mask; mmc_data->ocr_mask = p->tmio_ocr_mask;
mmc_data->capabilities |= p->tmio_caps; mmc_data->capabilities |= p->tmio_caps;
mmc_data->cd_gpio = p->cd_gpio;
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
priv->param_tx.slave_id = p->dma_slave_tx; priv->param_tx.slave_id = p->dma_slave_tx;
...@@ -211,7 +225,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -211,7 +225,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long) mmc_hostname(host->mmc), (unsigned long)
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start), (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
mmc_data->hclk / 1000000); mmc_data->hclk / 1000000);
return ret; return ret;
...@@ -232,9 +246,11 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -232,9 +246,11 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
eirq_card_detect: eirq_card_detect:
tmio_mmc_host_remove(host); tmio_mmc_host_remove(host);
eprobe: eprobe:
clk_disable(priv->clk);
clk_put(priv->clk); clk_put(priv->clk);
eclkget: eclkget:
if (p->cleanup)
p->cleanup(pdev);
einit:
kfree(priv); kfree(priv);
return ret; return ret;
} }
...@@ -258,8 +274,11 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) ...@@ -258,8 +274,11 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
free_irq(irq, host); free_irq(irq, host);
} }
clk_disable(priv->clk);
clk_put(priv->clk); clk_put(priv->clk);
if (p->cleanup)
p->cleanup(pdev);
kfree(priv); kfree(priv);
return 0; return 0;
......
...@@ -47,16 +47,14 @@ struct tmio_mmc_host { ...@@ -47,16 +47,14 @@ struct tmio_mmc_host {
struct mmc_request *mrq; struct mmc_request *mrq;
struct mmc_data *data; struct mmc_data *data;
struct mmc_host *mmc; struct mmc_host *mmc;
unsigned int sdio_irq_enabled;
/* Controller power state */
bool power;
/* Callbacks for clock / power control */ /* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state); void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state);
int pm_error;
/* recognise system-wide suspend in runtime PM methods */
bool pm_global;
/* pio related stuff */ /* pio related stuff */
struct scatterlist *sg_ptr; struct scatterlist *sg_ptr;
struct scatterlist *sg_orig; struct scatterlist *sg_orig;
...@@ -86,6 +84,7 @@ struct tmio_mmc_host { ...@@ -86,6 +84,7 @@ struct tmio_mmc_host {
spinlock_t lock; /* protect host private data */ spinlock_t lock; /* protect host private data */
unsigned long last_req_ts; unsigned long last_req_ts;
struct mutex ios_lock; /* protect set_ios() context */ struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug;
}; };
int tmio_mmc_host_probe(struct tmio_mmc_host **host, int tmio_mmc_host_probe(struct tmio_mmc_host **host,
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/mfd/tmio.h> #include <linux/mfd/tmio.h>
#include <linux/mmc/cd-gpio.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/tmio.h> #include <linux/mmc/tmio.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -127,7 +128,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ...@@ -127,7 +128,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
if (enable) { if (enable) {
host->sdio_irq_enabled = 1;
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL & host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
~TMIO_SDIO_STAT_IOIRQ; ~TMIO_SDIO_STAT_IOIRQ;
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
...@@ -136,7 +136,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ...@@ -136,7 +136,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
host->sdio_irq_enabled = 0;
} }
} }
...@@ -304,6 +303,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command ...@@ -304,6 +303,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
{ {
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
int c = cmd->opcode; int c = cmd->opcode;
u32 irq_mask = TMIO_MASK_CMD;
/* Command 12 is handled by hardware */ /* Command 12 is handled by hardware */
if (cmd->opcode == 12 && !cmd->arg) { if (cmd->opcode == 12 && !cmd->arg) {
...@@ -339,7 +339,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command ...@@ -339,7 +339,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
c |= TRANSFER_READ; c |= TRANSFER_READ;
} }
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_CMD); if (!host->native_hotplug)
irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_mmc_irqs(host, irq_mask);
/* Fire off the command */ /* Fire off the command */
sd_ctrl_write32(host, CTL_ARG_REG, cmd->arg); sd_ctrl_write32(host, CTL_ARG_REG, cmd->arg);
...@@ -758,7 +760,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -758,7 +760,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{ {
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
struct tmio_mmc_data *pdata = host->pdata; struct device *dev = &host->pdev->dev;
unsigned long flags; unsigned long flags;
mutex_lock(&host->ios_lock); mutex_lock(&host->ios_lock);
...@@ -766,13 +768,13 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -766,13 +768,13 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
if (host->mrq) { if (host->mrq) {
if (IS_ERR(host->mrq)) { if (IS_ERR(host->mrq)) {
dev_dbg(&host->pdev->dev, dev_dbg(dev,
"%s.%d: concurrent .set_ios(), clk %u, mode %u\n", "%s.%d: concurrent .set_ios(), clk %u, mode %u\n",
current->comm, task_pid_nr(current), current->comm, task_pid_nr(current),
ios->clock, ios->power_mode); ios->clock, ios->power_mode);
host->mrq = ERR_PTR(-EINTR); host->mrq = ERR_PTR(-EINTR);
} else { } else {
dev_dbg(&host->pdev->dev, dev_dbg(dev,
"%s.%d: CMD%u active since %lu, now %lu!\n", "%s.%d: CMD%u active since %lu, now %lu!\n",
current->comm, task_pid_nr(current), current->comm, task_pid_nr(current),
host->mrq->cmd->opcode, host->last_req_ts, jiffies); host->mrq->cmd->opcode, host->last_req_ts, jiffies);
...@@ -788,13 +790,15 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -788,13 +790,15 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
/* /*
* pdata->power == false only if COLD_CD is available, otherwise only * host->power toggles between false and true in both cases - either
* in short time intervals during probing or resuming * or not the controller can be runtime-suspended during inactivity.
* But if the controller has to be kept on, the runtime-pm usage_count
* is kept positive, so no suspending actually takes place.
*/ */
if (ios->power_mode == MMC_POWER_ON && ios->clock) { if (ios->power_mode == MMC_POWER_ON && ios->clock) {
if (!pdata->power) { if (!host->power) {
pm_runtime_get_sync(&host->pdev->dev); pm_runtime_get_sync(dev);
pdata->power = true; host->power = true;
} }
tmio_mmc_set_clock(host, ios->clock); tmio_mmc_set_clock(host, ios->clock);
/* power up SD bus */ /* power up SD bus */
...@@ -805,9 +809,9 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -805,9 +809,9 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} else if (ios->power_mode != MMC_POWER_UP) { } else if (ios->power_mode != MMC_POWER_UP) {
if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) if (host->set_pwr && ios->power_mode == MMC_POWER_OFF)
host->set_pwr(host->pdev, 0); host->set_pwr(host->pdev, 0);
if (pdata->power) { if (host->power) {
pdata->power = false; host->power = false;
pm_runtime_put(&host->pdev->dev); pm_runtime_put(dev);
} }
tmio_mmc_clk_stop(host); tmio_mmc_clk_stop(host);
} }
...@@ -913,7 +917,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -913,7 +917,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
else else
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
pdata->power = false; _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
mmc->caps & MMC_CAP_NONREMOVABLE);
_host->power = false;
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev); ret = pm_runtime_resume(&pdev->dev);
if (ret < 0) if (ret < 0)
...@@ -926,14 +934,13 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -926,14 +934,13 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
* 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL * 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL
* 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE * 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE
* *
* While we increment the rtpm counter for all scenarios when the mmc * While we increment the runtime PM counter for all scenarios when
* core activates us by calling an appropriate set_ios(), we must * the mmc core activates us by calling an appropriate set_ios(), we
* must additionally ensure that in case 2) the tmio mmc hardware stays
* additionally ensure that in case 2) the tmio mmc hardware stays * additionally ensure that in case 2) the tmio mmc hardware stays
* powered on during runtime for the card detection to work. * powered on during runtime for the card detection to work.
*/ */
if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD if (_host->native_hotplug)
|| mmc->caps & MMC_CAP_NEEDS_POLL
|| mmc->caps & MMC_CAP_NONREMOVABLE))
pm_runtime_get_noresume(&pdev->dev); pm_runtime_get_noresume(&pdev->dev);
tmio_mmc_clk_stop(_host); tmio_mmc_clk_stop(_host);
...@@ -963,9 +970,19 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -963,9 +970,19 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
irq_mask |= TMIO_MASK_READOP; irq_mask |= TMIO_MASK_READOP;
if (!_host->chan_tx) if (!_host->chan_tx)
irq_mask |= TMIO_MASK_WRITEOP; irq_mask |= TMIO_MASK_WRITEOP;
if (!_host->native_hotplug)
irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_mmc_irqs(_host, irq_mask); tmio_mmc_enable_mmc_irqs(_host, irq_mask);
if (pdata->flags & TMIO_MMC_USE_GPIO_CD) {
ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio);
if (ret < 0) {
tmio_mmc_host_remove(_host);
return ret;
}
}
*host = _host; *host = _host;
return 0; return 0;
...@@ -983,22 +1000,22 @@ EXPORT_SYMBOL(tmio_mmc_host_probe); ...@@ -983,22 +1000,22 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
void tmio_mmc_host_remove(struct tmio_mmc_host *host) void tmio_mmc_host_remove(struct tmio_mmc_host *host)
{ {
struct platform_device *pdev = host->pdev; struct platform_device *pdev = host->pdev;
struct tmio_mmc_data *pdata = host->pdata;
struct mmc_host *mmc = host->mmc;
if (pdata->flags & TMIO_MMC_USE_GPIO_CD)
/* /*
* We don't have to manipulate pdata->power here: if there is a card in * This means we can miss a card-eject, but this is anyway
* the slot, the runtime PM is active and our .runtime_resume() will not * possible, because of delayed processing of hotplug events.
* be run. If there is no card in the slot and the platform can suspend
* the controller, the runtime PM is suspended and pdata->power == false,
* so, our .runtime_resume() will not try to detect a card in the slot.
*/ */
if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD mmc_cd_gpio_free(mmc);
|| host->mmc->caps & MMC_CAP_NEEDS_POLL
|| host->mmc->caps & MMC_CAP_NONREMOVABLE) if (!host->native_hotplug)
pm_runtime_get_sync(&pdev->dev); pm_runtime_get_sync(&pdev->dev);
dev_pm_qos_hide_latency_limit(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev);
mmc_remove_host(host->mmc); mmc_remove_host(mmc);
cancel_work_sync(&host->done); cancel_work_sync(&host->done);
cancel_delayed_work_sync(&host->delayed_reset_work); cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host); tmio_mmc_release_dma(host);
...@@ -1007,7 +1024,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) ...@@ -1007,7 +1024,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
iounmap(host->ctl); iounmap(host->ctl);
mmc_free_host(host->mmc); mmc_free_host(mmc);
} }
EXPORT_SYMBOL(tmio_mmc_host_remove); EXPORT_SYMBOL(tmio_mmc_host_remove);
...@@ -1021,8 +1038,6 @@ int tmio_mmc_host_suspend(struct device *dev) ...@@ -1021,8 +1038,6 @@ int tmio_mmc_host_suspend(struct device *dev)
if (!ret) if (!ret)
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
host->pm_error = pm_runtime_put_sync(dev);
return ret; return ret;
} }
EXPORT_SYMBOL(tmio_mmc_host_suspend); EXPORT_SYMBOL(tmio_mmc_host_suspend);
...@@ -1032,20 +1047,10 @@ int tmio_mmc_host_resume(struct device *dev) ...@@ -1032,20 +1047,10 @@ int tmio_mmc_host_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
/* The MMC core will perform the complete set up */
host->pdata->power = false;
host->pm_global = true;
if (!host->pm_error)
pm_runtime_get_sync(dev);
if (host->pm_global) {
/* Runtime PM resume callback didn't run */
tmio_mmc_reset(host); tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true); tmio_mmc_enable_dma(host, true);
host->pm_global = false;
}
/* The MMC core will perform the complete set up */
return mmc_resume_host(mmc); return mmc_resume_host(mmc);
} }
EXPORT_SYMBOL(tmio_mmc_host_resume); EXPORT_SYMBOL(tmio_mmc_host_resume);
...@@ -1062,19 +1067,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev) ...@@ -1062,19 +1067,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
{ {
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
struct tmio_mmc_data *pdata = host->pdata;
tmio_mmc_reset(host); tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true); tmio_mmc_enable_dma(host, true);
if (pdata->power) {
/* Only entered after a card-insert interrupt */
if (!mmc->card)
tmio_mmc_set_ios(mmc, &mmc->ios);
mmc_detect_change(mmc, msecs_to_jiffies(100));
}
host->pm_global = false;
return 0; return 0;
} }
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume); EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
......
#ifndef MFD_TMIO_H #ifndef MFD_TMIO_H
#define MFD_TMIO_H #define MFD_TMIO_H
#include <linux/device.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
...@@ -64,8 +66,8 @@ ...@@ -64,8 +66,8 @@
#define TMIO_MMC_SDIO_IRQ (1 << 2) #define TMIO_MMC_SDIO_IRQ (1 << 2)
/* /*
* Some platforms can detect card insertion events with controller powered * Some platforms can detect card insertion events with controller powered
* down, in which case they have to call tmio_mmc_cd_wakeup() to power up the * down, using a GPIO IRQ, in which case they have to fill in cd_irq, cd_gpio,
* controller and report the event to the driver. * and cd_flags fields of struct tmio_mmc_data.
*/ */
#define TMIO_MMC_HAS_COLD_CD (1 << 3) #define TMIO_MMC_HAS_COLD_CD (1 << 3)
/* /*
...@@ -73,6 +75,12 @@ ...@@ -73,6 +75,12 @@
* idle before writing to some registers. * idle before writing to some registers.
*/ */
#define TMIO_MMC_HAS_IDLE_WAIT (1 << 4) #define TMIO_MMC_HAS_IDLE_WAIT (1 << 4)
/*
* A GPIO is used for card hotplug detection. We need an extra flag for this,
* because 0 is a valid GPIO number too, and requiring users to specify
* cd_gpio < 0 to disable GPIO hotplug would break backwards compatibility.
*/
#define TMIO_MMC_USE_GPIO_CD (1 << 5)
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
...@@ -97,19 +105,23 @@ struct tmio_mmc_data { ...@@ -97,19 +105,23 @@ struct tmio_mmc_data {
u32 ocr_mask; /* available voltages */ u32 ocr_mask; /* available voltages */
struct tmio_mmc_dma *dma; struct tmio_mmc_dma *dma;
struct device *dev; struct device *dev;
bool power; unsigned int cd_gpio;
void (*set_pwr)(struct platform_device *host, int state); void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state);
int (*get_cd)(struct platform_device *host); int (*get_cd)(struct platform_device *host);
int (*write16_hook)(struct tmio_mmc_host *host, int addr); int (*write16_hook)(struct tmio_mmc_host *host, int addr);
}; };
/*
* This function is deprecated and will be removed soon. Please, convert your
* platform to use drivers/mmc/core/cd-gpio.c
*/
#include <linux/mmc/host.h>
static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata) static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
{ {
if (pdata && !pdata->power) { if (pdata)
pdata->power = true; mmc_detect_change(dev_get_drvdata(pdata->dev),
pm_runtime_get(pdata->dev); msecs_to_jiffies(100));
}
} }
/* /*
......
...@@ -72,6 +72,8 @@ struct mmc_ext_csd { ...@@ -72,6 +72,8 @@ struct mmc_ext_csd {
bool hpi_en; /* HPI enablebit */ bool hpi_en; /* HPI enablebit */
bool hpi; /* HPI support bit */ bool hpi; /* HPI support bit */
unsigned int hpi_cmd; /* cmd used as HPI */ unsigned int hpi_cmd; /* cmd used as HPI */
unsigned int data_sector_size; /* 512 bytes or 4KB */
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */ unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable; bool boot_ro_lockable;
u8 raw_partition_support; /* 160 */ u8 raw_partition_support; /* 160 */
......
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
#define MMC_CD_GPIO_H #define MMC_CD_GPIO_H
struct mmc_host; struct mmc_host;
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio);
unsigned int irq, unsigned long flags);
void mmc_cd_gpio_free(struct mmc_host *host); void mmc_cd_gpio_free(struct mmc_host *host);
#endif #endif
...@@ -175,7 +175,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); ...@@ -175,7 +175,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host); extern void mmc_release_host(struct mmc_host *host);
extern void mmc_do_release_host(struct mmc_host *host);
extern int mmc_try_claim_host(struct mmc_host *host); extern int mmc_try_claim_host(struct mmc_host *host);
extern int mmc_flush_cache(struct mmc_card *); extern int mmc_flush_cache(struct mmc_card *);
......
...@@ -76,7 +76,7 @@ struct mmc_data; ...@@ -76,7 +76,7 @@ struct mmc_data;
* @num_slots: Number of slots available. * @num_slots: Number of slots available.
* @verid: Denote Version ID. * @verid: Denote Version ID.
* @data_offset: Set the offset of DATA register according to VERID. * @data_offset: Set the offset of DATA register according to VERID.
* @pdev: Platform device associated with the MMC controller. * @dev: Device associated with the MMC controller.
* @pdata: Platform data associated with the MMC controller. * @pdata: Platform data associated with the MMC controller.
* @slot: Slots sharing this MMC controller. * @slot: Slots sharing this MMC controller.
* @fifo_depth: depth of FIFO. * @fifo_depth: depth of FIFO.
...@@ -87,6 +87,8 @@ struct mmc_data; ...@@ -87,6 +87,8 @@ struct mmc_data;
* @push_data: Pointer to FIFO push function. * @push_data: Pointer to FIFO push function.
* @pull_data: Pointer to FIFO pull function. * @pull_data: Pointer to FIFO pull function.
* @quirks: Set of quirks that apply to specific versions of the IP. * @quirks: Set of quirks that apply to specific versions of the IP.
* @irq_flags: The flags to be passed to request_irq.
* @irq: The irq value to be passed to request_irq.
* *
* Locking * Locking
* ======= * =======
...@@ -153,7 +155,7 @@ struct dw_mci { ...@@ -153,7 +155,7 @@ struct dw_mci {
u32 fifoth_val; u32 fifoth_val;
u16 verid; u16 verid;
u16 data_offset; u16 data_offset;
struct platform_device *pdev; struct device dev;
struct dw_mci_board *pdata; struct dw_mci_board *pdata;
struct dw_mci_slot *slot[MAX_MCI_SLOTS]; struct dw_mci_slot *slot[MAX_MCI_SLOTS];
...@@ -174,6 +176,8 @@ struct dw_mci { ...@@ -174,6 +176,8 @@ struct dw_mci {
u32 quirks; u32 quirks;
struct regulator *vmmc; /* Power regulator */ struct regulator *vmmc; /* Power regulator */
unsigned long irq_flags; /* IRQ flags */
unsigned int irq;
}; };
/* DMA ops for Internal/External DMAC interface */ /* DMA ops for Internal/External DMAC interface */
......
...@@ -81,34 +81,11 @@ struct mmc_ios { ...@@ -81,34 +81,11 @@ struct mmc_ios {
struct mmc_host_ops { struct mmc_host_ops {
/* /*
* Hosts that support power saving can use the 'enable' and 'disable' * 'enable' is called when the host is claimed and 'disable' is called
* methods to exit and enter power saving states. 'enable' is called * when the host is released. 'enable' and 'disable' are deprecated.
* when the host is claimed and 'disable' is called (or scheduled with
* a delay) when the host is released. The 'disable' is scheduled if
* the disable delay set by 'mmc_set_disable_delay()' is non-zero,
* otherwise 'disable' is called immediately. 'disable' may be
* scheduled repeatedly, to permit ever greater power saving at the
* expense of ever greater latency to re-enable. Rescheduling is
* determined by the return value of the 'disable' method. A positive
* value gives the delay in milliseconds.
*
* In the case where a host function (like set_ios) may be called
* with or without the host claimed, enabling and disabling can be
* done directly and will nest correctly. Call 'mmc_host_enable()' and
* 'mmc_host_lazy_disable()' for this purpose, but note that these
* functions must be paired.
*
* Alternatively, 'mmc_host_enable()' may be paired with
* 'mmc_host_disable()' which calls 'disable' immediately. In this
* case the 'disable' method will be called with 'lazy' set to 0.
* This is mainly useful for error paths.
*
* Because lazy disable may be called from a work queue, the 'disable'
* method must claim the host when 'lazy' != 0, which will work
* correctly because recursion is detected and handled.
*/ */
int (*enable)(struct mmc_host *host); int (*enable)(struct mmc_host *host);
int (*disable)(struct mmc_host *host, int lazy); int (*disable)(struct mmc_host *host);
/* /*
* It is optional for the host to implement pre_req and post_req in * It is optional for the host to implement pre_req and post_req in
* order to support double buffering of requests (prepare one * order to support double buffering of requests (prepare one
...@@ -219,7 +196,7 @@ struct mmc_host { ...@@ -219,7 +196,7 @@ struct mmc_host {
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ #define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ #define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ #define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
...@@ -259,6 +236,8 @@ struct mmc_host { ...@@ -259,6 +236,8 @@ struct mmc_host {
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
MMC_CAP2_HS200_1_2V_SDR) MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ #define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
mmc_pm_flag_t pm_caps; /* supported pm features */ mmc_pm_flag_t pm_caps; /* supported pm features */
unsigned int power_notify_type; unsigned int power_notify_type;
...@@ -301,13 +280,7 @@ struct mmc_host { ...@@ -301,13 +280,7 @@ struct mmc_host {
unsigned int removed:1; /* host is being removed */ unsigned int removed:1; /* host is being removed */
#endif #endif
/* Only used with MMC_CAP_DISABLE */
int enabled; /* host is enabled */
int rescan_disable; /* disable card detection */ int rescan_disable; /* disable card detection */
int nesting_cnt; /* "enable" nesting count */
int en_dis_recurs; /* detect recursion */
unsigned int disable_delay; /* disable delay in msecs */
struct delayed_work disable; /* disabling work */
struct mmc_card *card; /* device attached to this host */ struct mmc_card *card; /* device attached to this host */
...@@ -407,17 +380,8 @@ int mmc_card_awake(struct mmc_host *host); ...@@ -407,17 +380,8 @@ int mmc_card_awake(struct mmc_host *host);
int mmc_card_sleep(struct mmc_host *host); int mmc_card_sleep(struct mmc_host *host);
int mmc_card_can_sleep(struct mmc_host *host); int mmc_card_can_sleep(struct mmc_host *host);
int mmc_host_enable(struct mmc_host *host);
int mmc_host_disable(struct mmc_host *host);
int mmc_host_lazy_disable(struct mmc_host *host);
int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *); int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
static inline void mmc_set_disable_delay(struct mmc_host *host,
unsigned int disable_delay)
{
host->disable_delay = disable_delay;
}
/* Module parameter */ /* Module parameter */
extern bool mmc_assume_removable; extern bool mmc_assume_removable;
......
...@@ -274,6 +274,7 @@ struct _mmc_csd { ...@@ -274,6 +274,7 @@ struct _mmc_csd {
#define EXT_CSD_FLUSH_CACHE 32 /* W */ #define EXT_CSD_FLUSH_CACHE 32 /* W */
#define EXT_CSD_CACHE_CTRL 33 /* R/W */ #define EXT_CSD_CACHE_CTRL 33 /* R/W */
#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ #define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
...@@ -315,6 +316,8 @@ struct _mmc_csd { ...@@ -315,6 +316,8 @@ struct _mmc_csd {
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ #define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
#define EXT_CSD_HPI_FEATURES 503 /* RO */ #define EXT_CSD_HPI_FEATURES 503 /* RO */
/* /*
......
...@@ -90,6 +90,8 @@ struct sdhci_host { ...@@ -90,6 +90,8 @@ struct sdhci_host {
unsigned int quirks2; /* More deviations from spec. */ unsigned int quirks2; /* More deviations from spec. */
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
int irq; /* Device IRQ */ int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */ void __iomem *ioaddr; /* Mapped address */
......
...@@ -77,18 +77,15 @@ struct sh_mmcif_plat_data { ...@@ -77,18 +77,15 @@ struct sh_mmcif_plat_data {
/* CE_CLK_CTRL */ /* CE_CLK_CTRL */
#define CLK_ENABLE (1 << 24) /* 1: output mmc clock */ #define CLK_ENABLE (1 << 24) /* 1: output mmc clock */
#define CLK_CLEAR ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) #define CLK_CLEAR (0xf << 16)
#define CLK_SUP_PCLK ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) #define CLK_SUP_PCLK (0xf << 16)
#define CLKDIV_4 (1<<16) /* mmc clock frequency. #define CLKDIV_4 (1 << 16) /* mmc clock frequency.
* n: bus clock/(2^(n+1)) */ * n: bus clock/(2^(n+1)) */
#define CLKDIV_256 (7<<16) /* mmc clock frequency. (see above) */ #define CLKDIV_256 (7 << 16) /* mmc clock frequency. (see above) */
#define SRSPTO_256 ((1 << 13) | (0 << 12)) /* resp timeout */ #define SRSPTO_256 (2 << 12) /* resp timeout */
#define SRBSYTO_29 ((1 << 11) | (1 << 10) | \ #define SRBSYTO_29 (0xf << 8) /* resp busy timeout */
(1 << 9) | (1 << 8)) /* resp busy timeout */ #define SRWDTO_29 (0xf << 4) /* read/write timeout */
#define SRWDTO_29 ((1 << 7) | (1 << 6) | \ #define SCCSTO_29 (0xf << 0) /* ccs timeout */
(1 << 5) | (1 << 4)) /* read/write timeout */
#define SCCSTO_29 ((1 << 3) | (1 << 2) | \
(1 << 1) | (1 << 0)) /* ccs timeout */
/* CE_VERSION */ /* CE_VERSION */
#define SOFT_RST_ON (1 << 31) #define SOFT_RST_ON (1 << 31)
......
...@@ -10,15 +10,29 @@ struct tmio_mmc_data; ...@@ -10,15 +10,29 @@ struct tmio_mmc_data;
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard" #define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio" #define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
/**
* struct sh_mobile_sdhi_ops - SDHI driver callbacks
* @cd_wakeup: trigger a card-detection run
*/
struct sh_mobile_sdhi_ops {
void (*cd_wakeup)(const struct platform_device *pdev);
};
struct sh_mobile_sdhi_info { struct sh_mobile_sdhi_info {
int dma_slave_tx; int dma_slave_tx;
int dma_slave_rx; int dma_slave_rx;
unsigned long tmio_flags; unsigned long tmio_flags;
unsigned long tmio_caps; unsigned long tmio_caps;
u32 tmio_ocr_mask; /* available MMC voltages */ u32 tmio_ocr_mask; /* available MMC voltages */
unsigned int cd_gpio;
struct tmio_mmc_data *pdata; struct tmio_mmc_data *pdata;
void (*set_pwr)(struct platform_device *pdev, int state); void (*set_pwr)(struct platform_device *pdev, int state);
int (*get_cd)(struct platform_device *pdev); int (*get_cd)(struct platform_device *pdev);
/* callbacks for board specific setup code */
int (*init)(struct platform_device *pdev,
const struct sh_mobile_sdhi_ops *ops);
void (*cleanup)(struct platform_device *pdev);
}; };
#endif /* LINUX_MMC_SH_MOBILE_SDHI_H */ #endif /* LINUX_MMC_SH_MOBILE_SDHI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment