Commit 92bf3d09 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mmc-merge-for-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC changes from Chris Ball
 - at91-mci: This driver will be replaced by atmel-mci in 3.7.
 - atmel-mci: Add support for old at91-mci hardware.
 - dw_mmc: Allow multiple controllers; this previously caused
   corruption.
 - imxmmc: Remove this driver, replaced by mxcmmc.
 - mmci: Add device tree support.
 - omap: Allow multiple controllers.
 - omap_hsmmc: Auto CMD12, DDR support.
 - tegra: Support SD 3.0 spec.

Fix up the usual trivial conflicts in feature-removal-schedule.txt

* tag 'mmc-merge-for-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (38 commits)
  mmc: at91-mci: this driver is now deprecated
  mmc: omap_hsmmc: pass IRQF_ONESHOT to request_threaded_irq
  mmc: block: Allow disabling 512B sector size emulation
  mmc: atmel-mci: add debug logs
  mmc: atmel-mci: add support for version lower than v2xx
  mmc: atmel-mci: change the state machine for compatibility with old IP
  mmc: atmel-mci: the r/w proof capability lack was not well managed
  mmc: dw_mmc: Fixed sdio interrupt mask bit setting bug
  mmc: omap: convert to module_platform_driver
  mmc: omap: make it behave well as a module
  mmc: omap: convert to per instance workqueue
  mmc: core: Remove dead code
  mmc: card: Avoid null pointer dereference
  mmc: core: Prevent eMMC VCC supply to be cut from late init
  mmc: dw_mmc: make multiple instances of dw_mci_card_workqueue
  mmc: queue: remove redundant memsets
  mmc: queue: rename mmc_request function
  mmc: core: skip card initialization if power class selection fails
  mmc: core: fix the signaling 1.8V for HS200
  mmc: core: fix the decision of HS200/DDR card-type
  ...
parents 603d6637 0caaa953
* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1
The ARM PrimeCell MMCI PL180 and PL181 provides and interface for
reading and writing to MultiMedia and SD cards alike.
Required properties:
- compatible : contains "arm,pl18x", "arm,primecell".
- reg : contains pl18x registers and length.
- interrupts : contains the device IRQ(s).
- arm,primecell-periphid : contains the PrimeCell Peripheral ID.
Optional properties:
- wp-gpios : contains any write protect (ro) gpios
- cd-gpios : contains any card detection gpios
- cd-inverted : indicates whether the cd gpio is inverted
- max-frequency : contains the maximum operating frequency
- bus-width : number of data lines, can be <1>, <4>, or <8>
- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable
- mmc-cap-sd-highspeed : indicates whether SD is high speed capable
...@@ -595,3 +595,14 @@ Why: KVM tracepoints provide mostly equivalent information in a much more ...@@ -595,3 +595,14 @@ Why: KVM tracepoints provide mostly equivalent information in a much more
flexible fashion. flexible fashion.
---------------------------- ----------------------------
What: at91-mci driver ("CONFIG_MMC_AT91")
When: 3.7
Why: There are two mci drivers: at91-mci and atmel-mci. The PDC support
was added to atmel-mci as a first step to support more chips.
Then at91-mci was kept only for old IP versions (on at91rm9200 and
at91sam9261). The support of these IP versions has just been added
to atmel-mci, so atmel-mci can be used for all chips.
Who: Ludovic Desroches <ludovic.desroches@atmel.com>
----------------------------
...@@ -384,7 +384,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, ...@@ -384,7 +384,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
md = mmc_blk_get(bdev->bd_disk); md = mmc_blk_get(bdev->bd_disk);
if (!md) { if (!md) {
err = -EINVAL; err = -EINVAL;
goto cmd_done; goto cmd_err;
} }
card = md->queue.card; card = md->queue.card;
...@@ -483,6 +483,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, ...@@ -483,6 +483,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
cmd_done: cmd_done:
mmc_blk_put(md); mmc_blk_put(md);
cmd_err:
kfree(idata->buf); kfree(idata->buf);
kfree(idata); kfree(idata);
return err; return err;
...@@ -1283,7 +1284,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) ...@@ -1283,7 +1284,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
int ret = 1, disable_multi = 0, retry = 0, type; int ret = 1, disable_multi = 0, retry = 0, type;
enum mmc_blk_status status; enum mmc_blk_status status;
struct mmc_queue_req *mq_rq; struct mmc_queue_req *mq_rq;
struct request *req; struct request *req = rqc;
struct mmc_async_req *areq; struct mmc_async_req *areq;
if (!rqc && !mq->mqrq_prev->req) if (!rqc && !mq->mqrq_prev->req)
...@@ -1291,6 +1292,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) ...@@ -1291,6 +1292,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
do { do {
if (rqc) { if (rqc) {
/*
* When 4KB native sector is enabled, only 8 blocks
* multiple read or write is allowed
*/
if ((brq->data.blocks & 0x07) &&
(card->ext_csd.data_sector_size == 4096)) {
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
req->rq_disk->disk_name);
goto cmd_abort;
}
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq); mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
areq = &mq->mqrq_cur->mmc_active; areq = &mq->mqrq_cur->mmc_active;
} else } else
...@@ -1538,7 +1549,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, ...@@ -1538,7 +1549,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
"mmcblk%d%s", md->name_idx, subname ? subname : ""); "mmcblk%d%s", md->name_idx, subname ? subname : "");
blk_queue_logical_block_size(md->queue.queue, 512); if (mmc_card_mmc(card))
blk_queue_logical_block_size(md->queue.queue,
card->ext_csd.data_sector_size);
else
blk_queue_logical_block_size(md->queue.queue, 512);
set_capacity(md->disk, size); set_capacity(md->disk, size);
if (mmc_host_cmd23(card->host)) { if (mmc_host_cmd23(card->host)) {
......
...@@ -96,7 +96,7 @@ static int mmc_queue_thread(void *d) ...@@ -96,7 +96,7 @@ static int mmc_queue_thread(void *d)
* on any queue on this host, and attempt to issue it. This may * on any queue on this host, and attempt to issue it. This may
* not be the queue we were asked to process. * not be the queue we were asked to process.
*/ */
static void mmc_request(struct request_queue *q) static void mmc_request_fn(struct request_queue *q)
{ {
struct mmc_queue *mq = q->queuedata; struct mmc_queue *mq = q->queuedata;
struct request *req; struct request *req;
...@@ -171,12 +171,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, ...@@ -171,12 +171,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
limit = *mmc_dev(host)->dma_mask; limit = *mmc_dev(host)->dma_mask;
mq->card = card; mq->card = card;
mq->queue = blk_init_queue(mmc_request, lock); mq->queue = blk_init_queue(mmc_request_fn, lock);
if (!mq->queue) if (!mq->queue)
return -ENOMEM; return -ENOMEM;
memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
mq->mqrq_cur = mqrq_cur; mq->mqrq_cur = mqrq_cur;
mq->mqrq_prev = mqrq_prev; mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq; mq->queue->queuedata = mq;
......
...@@ -122,6 +122,7 @@ static int mmc_bus_remove(struct device *dev) ...@@ -122,6 +122,7 @@ static int mmc_bus_remove(struct device *dev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int mmc_bus_suspend(struct device *dev) static int mmc_bus_suspend(struct device *dev)
{ {
struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_driver *drv = to_mmc_driver(dev->driver);
...@@ -143,6 +144,7 @@ static int mmc_bus_resume(struct device *dev) ...@@ -143,6 +144,7 @@ static int mmc_bus_resume(struct device *dev)
ret = drv->resume(card); ret = drv->resume(card);
return ret; return ret;
} }
#endif
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
......
...@@ -73,6 +73,9 @@ void mmc_cd_gpio_free(struct mmc_host *host) ...@@ -73,6 +73,9 @@ void mmc_cd_gpio_free(struct mmc_host *host)
{ {
struct mmc_cd_gpio *cd = host->hotplug.handler_priv; struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
if (!cd)
return;
free_irq(host->hotplug.irq, host); free_irq(host->hotplug.irq, host);
gpio_free(cd->gpio); gpio_free(cd->gpio);
kfree(cd); kfree(cd);
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "sdio_ops.h" #include "sdio_ops.h"
static struct workqueue_struct *workqueue; static struct workqueue_struct *workqueue;
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
/* /*
* Enabling software CRCs on the data blocks can be a significant (30%) * Enabling software CRCs on the data blocks can be a significant (30%)
...@@ -1157,6 +1158,9 @@ static void mmc_power_up(struct mmc_host *host) ...@@ -1157,6 +1158,9 @@ static void mmc_power_up(struct mmc_host *host)
{ {
int bit; int bit;
if (host->ios.power_mode == MMC_POWER_ON)
return;
mmc_host_clk_hold(host); mmc_host_clk_hold(host);
/* If ocr is set, we use it */ /* If ocr is set, we use it */
...@@ -1199,6 +1203,10 @@ static void mmc_power_up(struct mmc_host *host) ...@@ -1199,6 +1203,10 @@ static void mmc_power_up(struct mmc_host *host)
void mmc_power_off(struct mmc_host *host) void mmc_power_off(struct mmc_host *host)
{ {
int err = 0; int err = 0;
if (host->ios.power_mode == MMC_POWER_OFF)
return;
mmc_host_clk_hold(host); mmc_host_clk_hold(host);
host->ios.clock = 0; host->ios.clock = 0;
...@@ -2005,7 +2013,6 @@ EXPORT_SYMBOL(mmc_detect_card_removed); ...@@ -2005,7 +2013,6 @@ EXPORT_SYMBOL(mmc_detect_card_removed);
void mmc_rescan(struct work_struct *work) void mmc_rescan(struct work_struct *work)
{ {
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
struct mmc_host *host = struct mmc_host *host =
container_of(work, struct mmc_host, detect.work); container_of(work, struct mmc_host, detect.work);
int i; int i;
...@@ -2044,8 +2051,12 @@ void mmc_rescan(struct work_struct *work) ...@@ -2044,8 +2051,12 @@ void mmc_rescan(struct work_struct *work)
*/ */
mmc_bus_put(host); mmc_bus_put(host);
if (host->ops->get_cd && host->ops->get_cd(host) == 0) if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);
goto out; goto out;
}
mmc_claim_host(host); mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) { for (i = 0; i < ARRAY_SIZE(freqs); i++) {
...@@ -2063,7 +2074,8 @@ void mmc_rescan(struct work_struct *work) ...@@ -2063,7 +2074,8 @@ void mmc_rescan(struct work_struct *work)
void mmc_start_host(struct mmc_host *host) void mmc_start_host(struct mmc_host *host)
{ {
mmc_power_off(host); host->f_init = max(freqs[0], host->f_min);
mmc_power_up(host);
mmc_detect_change(host, 0); mmc_detect_change(host, 0);
} }
......
...@@ -235,6 +235,36 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) ...@@ -235,6 +235,36 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
return err; return err;
} }
static void mmc_select_card_type(struct mmc_card *card)
{
struct mmc_host *host = card->host;
u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK;
unsigned int caps = host->caps, caps2 = host->caps2;
unsigned int hs_max_dtr = 0;
if (card_type & EXT_CSD_CARD_TYPE_26)
hs_max_dtr = MMC_HIGH_26_MAX_DTR;
if (caps & MMC_CAP_MMC_HIGHSPEED &&
card_type & EXT_CSD_CARD_TYPE_52)
hs_max_dtr = MMC_HIGH_52_MAX_DTR;
if ((caps & MMC_CAP_1_8V_DDR &&
card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) ||
(caps & MMC_CAP_1_2V_DDR &&
card_type & EXT_CSD_CARD_TYPE_DDR_1_2V))
hs_max_dtr = MMC_HIGH_DDR_MAX_DTR;
if ((caps2 & MMC_CAP2_HS200_1_8V_SDR &&
card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) ||
(caps2 & MMC_CAP2_HS200_1_2V_SDR &&
card_type & EXT_CSD_CARD_TYPE_SDR_1_2V))
hs_max_dtr = MMC_HS200_MAX_DTR;
card->ext_csd.hs_max_dtr = hs_max_dtr;
card->ext_csd.card_type = card_type;
}
/* /*
* Decode extended CSD. * Decode extended CSD.
*/ */
...@@ -284,56 +314,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -284,56 +314,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
mmc_card_set_blockaddr(card); mmc_card_set_blockaddr(card);
} }
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { mmc_select_card_type(card);
case EXT_CSD_CARD_TYPE_SDR_ALL:
case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
card->ext_csd.hs_max_dtr = 200000000;
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
break;
case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
card->ext_csd.hs_max_dtr = 200000000;
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
break;
case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
card->ext_csd.hs_max_dtr = 200000000;
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
break;
case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_52;
break;
case EXT_CSD_CARD_TYPE_DDR_1_2V | EXT_CSD_CARD_TYPE_52 |
EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_2V;
break;
case EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_52 |
EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_8V;
break;
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
break;
case EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 26000000;
break;
default:
/* MMC v4 spec says this cannot happen */
pr_warning("%s: card is mmc v4 but doesn't "
"support any high-speed modes.\n",
mmc_hostname(card->host));
}
card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT]; card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT];
card->ext_csd.raw_erase_timeout_mult = card->ext_csd.raw_erase_timeout_mult =
...@@ -533,6 +516,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -533,6 +516,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
} else { } else {
card->ext_csd.data_tag_unit_size = 0; card->ext_csd.data_tag_unit_size = 0;
} }
} else {
card->ext_csd.data_sector_size = 512;
} }
out: out:
...@@ -556,14 +541,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) ...@@ -556,14 +541,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
err = mmc_get_ext_csd(card, &bw_ext_csd); err = mmc_get_ext_csd(card, &bw_ext_csd);
if (err || bw_ext_csd == NULL) { if (err || bw_ext_csd == NULL) {
if (bus_width != MMC_BUS_WIDTH_1) err = -EINVAL;
err = -EINVAL;
goto out; goto out;
} }
if (bus_width == MMC_BUS_WIDTH_1)
goto out;
/* only compare read only fields */ /* only compare read only fields */
err = !((card->ext_csd.raw_partition_support == err = !((card->ext_csd.raw_partition_support ==
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
...@@ -736,6 +717,10 @@ static int mmc_select_powerclass(struct mmc_card *card, ...@@ -736,6 +717,10 @@ static int mmc_select_powerclass(struct mmc_card *card,
card->ext_csd.generic_cmd6_time); card->ext_csd.generic_cmd6_time);
} }
if (err)
pr_err("%s: power class selection for ext_csd_bus_width %d"
" failed\n", mmc_hostname(card->host), bus_width);
return err; return err;
} }
...@@ -745,7 +730,7 @@ static int mmc_select_powerclass(struct mmc_card *card, ...@@ -745,7 +730,7 @@ static int mmc_select_powerclass(struct mmc_card *card,
*/ */
static int mmc_select_hs200(struct mmc_card *card) static int mmc_select_hs200(struct mmc_card *card)
{ {
int idx, err = 0; int idx, err = -EINVAL;
struct mmc_host *host; struct mmc_host *host;
static unsigned ext_csd_bits[] = { static unsigned ext_csd_bits[] = {
EXT_CSD_BUS_WIDTH_4, EXT_CSD_BUS_WIDTH_4,
...@@ -761,10 +746,12 @@ static int mmc_select_hs200(struct mmc_card *card) ...@@ -761,10 +746,12 @@ static int mmc_select_hs200(struct mmc_card *card)
host = card->host; host = card->host;
if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
host->caps2 & MMC_CAP2_HS200_1_2V_SDR) host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0)) err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0);
err = mmc_set_signal_voltage(host,
MMC_SIGNAL_VOLTAGE_180, 0); if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V &&
host->caps2 & MMC_CAP2_HS200_1_8V_SDR)
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 0);
/* If fails try again during next card power cycle */ /* If fails try again during next card power cycle */
if (err) if (err)
...@@ -1117,9 +1104,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1117,9 +1104,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
err = mmc_select_powerclass(card, ext_csd_bits, ext_csd); err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
if (err) if (err)
pr_warning("%s: power class selection to bus width %d" goto err;
" failed\n", mmc_hostname(card->host),
1 << bus_width);
} }
/* /*
...@@ -1151,10 +1136,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1151,10 +1136,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_select_powerclass(card, ext_csd_bits[idx][0], err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
ext_csd); ext_csd);
if (err) if (err)
pr_warning("%s: power class selection to " goto err;
"bus width %d failed\n",
mmc_hostname(card->host),
1 << bus_width);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH,
...@@ -1182,10 +1164,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1182,10 +1164,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_select_powerclass(card, ext_csd_bits[idx][1], err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
ext_csd); ext_csd);
if (err) if (err)
pr_warning("%s: power class selection to " goto err;
"bus width %d ddr %d failed\n",
mmc_hostname(card->host),
1 << bus_width, ddr);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH,
......
...@@ -947,7 +947,7 @@ static int mmc_sdio_resume(struct mmc_host *host) ...@@ -947,7 +947,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
} }
if (!err && host->sdio_irqs) if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host); wake_up_process(host->sdio_irq_thread);
mmc_release_host(host); mmc_release_host(host);
/* /*
......
...@@ -28,18 +28,20 @@ ...@@ -28,18 +28,20 @@
#include "sdio_ops.h" #include "sdio_ops.h"
static int process_sdio_pending_irqs(struct mmc_card *card) static int process_sdio_pending_irqs(struct mmc_host *host)
{ {
struct mmc_card *card = host->card;
int i, ret, count; int i, ret, count;
unsigned char pending; unsigned char pending;
struct sdio_func *func; struct sdio_func *func;
/* /*
* Optimization, if there is only 1 function interrupt registered * Optimization, if there is only 1 function interrupt registered
* call irq handler directly * and we know an IRQ was signaled then call irq handler directly.
* Otherwise do the full probe.
*/ */
func = card->sdio_single_irq; func = card->sdio_single_irq;
if (func) { if (func && host->sdio_irq_pending) {
func->irq_handler(func); func->irq_handler(func);
return 1; return 1;
} }
...@@ -116,7 +118,8 @@ static int sdio_irq_thread(void *_host) ...@@ -116,7 +118,8 @@ static int sdio_irq_thread(void *_host)
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret) if (ret)
break; break;
ret = process_sdio_pending_irqs(host->card); ret = process_sdio_pending_irqs(host);
host->sdio_irq_pending = false;
mmc_release_host(host); mmc_release_host(host);
/* /*
......
...@@ -278,10 +278,13 @@ choice ...@@ -278,10 +278,13 @@ choice
Choose which driver to use for the Atmel MCI Silicon Choose which driver to use for the Atmel MCI Silicon
config MMC_AT91 config MMC_AT91
tristate "AT91 SD/MMC Card Interface support" tristate "AT91 SD/MMC Card Interface support (DEPRECATED)"
depends on ARCH_AT91 depends on ARCH_AT91
help help
This selects the AT91 MCI controller. This selects the AT91 MCI controller. This driver will
be removed soon (for more information have a look to
Documentation/feature-removal-schedule.txt). Please use
MMC_ATMEL_MCI.
If unsure, say N. If unsure, say N.
...@@ -307,16 +310,6 @@ config MMC_ATMELMCI_DMA ...@@ -307,16 +310,6 @@ config MMC_ATMELMCI_DMA
If unsure, say N. If unsure, say N.
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
depends on ARCH_MX1
help
This selects the Motorola i.MX Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_MSM config MMC_MSM
tristate "Qualcomm SDCC Controller Support" tristate "Qualcomm SDCC Controller Support"
depends on MMC && ARCH_MSM depends on MMC && ARCH_MSM
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o
......
...@@ -45,19 +45,19 @@ ...@@ -45,19 +45,19 @@
#define ATMCI_DMA_THRESHOLD 16 #define ATMCI_DMA_THRESHOLD 16
enum { enum {
EVENT_CMD_COMPLETE = 0, EVENT_CMD_RDY = 0,
EVENT_XFER_COMPLETE, EVENT_XFER_COMPLETE,
EVENT_DATA_COMPLETE, EVENT_NOTBUSY,
EVENT_DATA_ERROR, EVENT_DATA_ERROR,
}; };
enum atmel_mci_state { enum atmel_mci_state {
STATE_IDLE = 0, STATE_IDLE = 0,
STATE_SENDING_CMD, STATE_SENDING_CMD,
STATE_SENDING_DATA, STATE_DATA_XFER,
STATE_DATA_BUSY, STATE_WAITING_NOTBUSY,
STATE_SENDING_STOP, STATE_SENDING_STOP,
STATE_DATA_ERROR, STATE_END_REQUEST,
}; };
enum atmci_xfer_dir { enum atmci_xfer_dir {
...@@ -78,6 +78,9 @@ struct atmel_mci_caps { ...@@ -78,6 +78,9 @@ struct atmel_mci_caps {
bool has_highspeed; bool has_highspeed;
bool has_rwproof; bool has_rwproof;
bool has_odd_clk_div; bool has_odd_clk_div;
bool has_bad_data_ordering;
bool need_reset_after_xfer;
bool need_blksz_mul_4;
}; };
struct atmel_mci_dma { struct atmel_mci_dma {
...@@ -91,6 +94,11 @@ struct atmel_mci_dma { ...@@ -91,6 +94,11 @@ struct atmel_mci_dma {
* @regs: Pointer to MMIO registers. * @regs: Pointer to MMIO registers.
* @sg: Scatterlist entry currently being processed by PIO or PDC code. * @sg: Scatterlist entry currently being processed by PIO or PDC code.
* @pio_offset: Offset into the current scatterlist entry. * @pio_offset: Offset into the current scatterlist entry.
* @buffer: Buffer used if we don't have the r/w proof capability. We
* don't have the time to switch pdc buffers so we have to use only
* one buffer for the full transaction.
* @buf_size: size of the buffer.
* @phys_buf_addr: buffer address needed for pdc.
* @cur_slot: The slot which is currently using the controller. * @cur_slot: The slot which is currently using the controller.
* @mrq: The request currently being processed on @cur_slot, * @mrq: The request currently being processed on @cur_slot,
* or NULL if the controller is idle. * or NULL if the controller is idle.
...@@ -116,6 +124,7 @@ struct atmel_mci_dma { ...@@ -116,6 +124,7 @@ struct atmel_mci_dma {
* @queue: List of slots waiting for access to the controller. * @queue: List of slots waiting for access to the controller.
* @need_clock_update: Update the clock rate before the next request. * @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request. * @need_reset: Reset controller before next request.
* @timer: Timer to balance the data timeout error flag which cannot rise.
* @mode_reg: Value of the MR register. * @mode_reg: Value of the MR register.
* @cfg_reg: Value of the CFG register. * @cfg_reg: Value of the CFG register.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
...@@ -166,6 +175,9 @@ struct atmel_mci { ...@@ -166,6 +175,9 @@ struct atmel_mci {
struct scatterlist *sg; struct scatterlist *sg;
unsigned int pio_offset; unsigned int pio_offset;
unsigned int *buffer;
unsigned int buf_size;
dma_addr_t buf_phys_addr;
struct atmel_mci_slot *cur_slot; struct atmel_mci_slot *cur_slot;
struct mmc_request *mrq; struct mmc_request *mrq;
...@@ -189,6 +201,7 @@ struct atmel_mci { ...@@ -189,6 +201,7 @@ struct atmel_mci {
bool need_clock_update; bool need_clock_update;
bool need_reset; bool need_reset;
struct timer_list timer;
u32 mode_reg; u32 mode_reg;
u32 cfg_reg; u32 cfg_reg;
unsigned long bus_hz; unsigned long bus_hz;
...@@ -480,6 +493,32 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot) ...@@ -480,6 +493,32 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n"); dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
} }
static inline unsigned int atmci_get_version(struct atmel_mci *host)
{
return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
}
static void atmci_timeout_timer(unsigned long data)
{
struct atmel_mci *host;
host = (struct atmel_mci *)data;
dev_dbg(&host->pdev->dev, "software timeout\n");
if (host->mrq->cmd->data) {
host->mrq->cmd->data->error = -ETIMEDOUT;
host->data = NULL;
} else {
host->mrq->cmd->error = -ETIMEDOUT;
host->cmd = NULL;
}
host->need_reset = 1;
host->state = STATE_END_REQUEST;
smp_wmb();
tasklet_schedule(&host->tasklet);
}
static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host, static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
unsigned int ns) unsigned int ns)
{ {
...@@ -591,6 +630,7 @@ static void atmci_send_command(struct atmel_mci *host, ...@@ -591,6 +630,7 @@ static void atmci_send_command(struct atmel_mci *host,
static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data) static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
{ {
dev_dbg(&host->pdev->dev, "send stop command\n");
atmci_send_command(host, data->stop, host->stop_cmdr); atmci_send_command(host, data->stop, host->stop_cmdr);
atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY); atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
} }
...@@ -603,6 +643,7 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host, ...@@ -603,6 +643,7 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host,
enum atmci_xfer_dir dir, enum atmci_pdc_buf buf_nb) enum atmci_xfer_dir dir, enum atmci_pdc_buf buf_nb)
{ {
u32 pointer_reg, counter_reg; u32 pointer_reg, counter_reg;
unsigned int buf_size;
if (dir == XFER_RECEIVE) { if (dir == XFER_RECEIVE) {
pointer_reg = ATMEL_PDC_RPR; pointer_reg = ATMEL_PDC_RPR;
...@@ -617,8 +658,15 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host, ...@@ -617,8 +658,15 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host,
counter_reg += ATMEL_PDC_SCND_BUF_OFF; counter_reg += ATMEL_PDC_SCND_BUF_OFF;
} }
atmci_writel(host, pointer_reg, sg_dma_address(host->sg)); if (!host->caps.has_rwproof) {
if (host->data_size <= sg_dma_len(host->sg)) { buf_size = host->buf_size;
atmci_writel(host, pointer_reg, host->buf_phys_addr);
} else {
buf_size = sg_dma_len(host->sg);
atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
}
if (host->data_size <= buf_size) {
if (host->data_size & 0x3) { if (host->data_size & 0x3) {
/* If size is different from modulo 4, transfer bytes */ /* If size is different from modulo 4, transfer bytes */
atmci_writel(host, counter_reg, host->data_size); atmci_writel(host, counter_reg, host->data_size);
...@@ -670,7 +718,20 @@ static void atmci_pdc_cleanup(struct atmel_mci *host) ...@@ -670,7 +718,20 @@ static void atmci_pdc_cleanup(struct atmel_mci *host)
*/ */
static void atmci_pdc_complete(struct atmel_mci *host) static void atmci_pdc_complete(struct atmel_mci *host)
{ {
int transfer_size = host->data->blocks * host->data->blksz;
int i;
atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
if ((!host->caps.has_rwproof)
&& (host->data->flags & MMC_DATA_READ)) {
if (host->caps.has_bad_data_ordering)
for (i = 0; i < transfer_size; i++)
host->buffer[i] = swab32(host->buffer[i]);
sg_copy_from_buffer(host->data->sg, host->data->sg_len,
host->buffer, transfer_size);
}
atmci_pdc_cleanup(host); atmci_pdc_cleanup(host);
/* /*
...@@ -678,9 +739,10 @@ static void atmci_pdc_complete(struct atmel_mci *host) ...@@ -678,9 +739,10 @@ static void atmci_pdc_complete(struct atmel_mci *host)
* to send the stop command or waiting for NBUSY in this case. * to send the stop command or waiting for NBUSY in this case.
*/ */
if (host->data) { if (host->data) {
dev_dbg(&host->pdev->dev,
"(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE); atmci_set_pending(host, EVENT_XFER_COMPLETE);
tasklet_schedule(&host->tasklet); tasklet_schedule(&host->tasklet);
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
} }
} }
...@@ -716,6 +778,8 @@ static void atmci_dma_complete(void *arg) ...@@ -716,6 +778,8 @@ static void atmci_dma_complete(void *arg)
* to send the stop command or waiting for NBUSY in this case. * to send the stop command or waiting for NBUSY in this case.
*/ */
if (data) { if (data) {
dev_dbg(&host->pdev->dev,
"(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE); atmci_set_pending(host, EVENT_XFER_COMPLETE);
tasklet_schedule(&host->tasklet); tasklet_schedule(&host->tasklet);
...@@ -791,6 +855,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data) ...@@ -791,6 +855,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
u32 iflags, tmp; u32 iflags, tmp;
unsigned int sg_len; unsigned int sg_len;
enum dma_data_direction dir; enum dma_data_direction dir;
int i;
data->error = -EINPROGRESS; data->error = -EINPROGRESS;
...@@ -806,7 +871,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data) ...@@ -806,7 +871,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
iflags |= ATMCI_ENDRX | ATMCI_RXBUFF; iflags |= ATMCI_ENDRX | ATMCI_RXBUFF;
} else { } else {
dir = DMA_TO_DEVICE; dir = DMA_TO_DEVICE;
iflags |= ATMCI_ENDTX | ATMCI_TXBUFE; iflags |= ATMCI_ENDTX | ATMCI_TXBUFE | ATMCI_BLKE;
} }
/* Set BLKLEN */ /* Set BLKLEN */
...@@ -818,6 +883,16 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data) ...@@ -818,6 +883,16 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
/* Configure PDC */ /* Configure PDC */
host->data_size = data->blocks * data->blksz; host->data_size = data->blocks * data->blksz;
sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir); sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir);
if ((!host->caps.has_rwproof)
&& (host->data->flags & MMC_DATA_WRITE)) {
sg_copy_to_buffer(host->data->sg, host->data->sg_len,
host->buffer, host->data_size);
if (host->caps.has_bad_data_ordering)
for (i = 0; i < host->data_size; i++)
host->buffer[i] = swab32(host->buffer[i]);
}
if (host->data_size) if (host->data_size)
atmci_pdc_set_both_buf(host, atmci_pdc_set_both_buf(host,
((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT)); ((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT));
...@@ -931,6 +1006,8 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) ...@@ -931,6 +1006,8 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
static void atmci_stop_transfer(struct atmel_mci *host) static void atmci_stop_transfer(struct atmel_mci *host)
{ {
dev_dbg(&host->pdev->dev,
"(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE); atmci_set_pending(host, EVENT_XFER_COMPLETE);
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY); atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
} }
...@@ -940,8 +1017,7 @@ static void atmci_stop_transfer(struct atmel_mci *host) ...@@ -940,8 +1017,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
*/ */
static void atmci_stop_transfer_pdc(struct atmel_mci *host) static void atmci_stop_transfer_pdc(struct atmel_mci *host)
{ {
atmci_set_pending(host, EVENT_XFER_COMPLETE); atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
} }
static void atmci_stop_transfer_dma(struct atmel_mci *host) static void atmci_stop_transfer_dma(struct atmel_mci *host)
...@@ -953,6 +1029,8 @@ static void atmci_stop_transfer_dma(struct atmel_mci *host) ...@@ -953,6 +1029,8 @@ static void atmci_stop_transfer_dma(struct atmel_mci *host)
atmci_dma_cleanup(host); atmci_dma_cleanup(host);
} else { } else {
/* Data transfer was stopped by the interrupt handler */ /* Data transfer was stopped by the interrupt handler */
dev_dbg(&host->pdev->dev,
"(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE); atmci_set_pending(host, EVENT_XFER_COMPLETE);
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY); atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
} }
...@@ -977,9 +1055,12 @@ static void atmci_start_request(struct atmel_mci *host, ...@@ -977,9 +1055,12 @@ static void atmci_start_request(struct atmel_mci *host,
host->pending_events = 0; host->pending_events = 0;
host->completed_events = 0; host->completed_events = 0;
host->cmd_status = 0;
host->data_status = 0; host->data_status = 0;
if (host->need_reset) { dev_dbg(&host->pdev->dev, "start request: cmd %u\n", mrq->cmd->opcode);
if (host->need_reset || host->caps.need_reset_after_xfer) {
iflags = atmci_readl(host, ATMCI_IMR); iflags = atmci_readl(host, ATMCI_IMR);
iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB); iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB);
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
...@@ -994,7 +1075,7 @@ static void atmci_start_request(struct atmel_mci *host, ...@@ -994,7 +1075,7 @@ static void atmci_start_request(struct atmel_mci *host,
iflags = atmci_readl(host, ATMCI_IMR); iflags = atmci_readl(host, ATMCI_IMR);
if (iflags & ~(ATMCI_SDIOIRQA | ATMCI_SDIOIRQB)) if (iflags & ~(ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n", dev_dbg(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
iflags); iflags);
if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) { if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
...@@ -1043,6 +1124,8 @@ static void atmci_start_request(struct atmel_mci *host, ...@@ -1043,6 +1124,8 @@ static void atmci_start_request(struct atmel_mci *host,
* prepared yet.) * prepared yet.)
*/ */
atmci_writel(host, ATMCI_IER, iflags); atmci_writel(host, ATMCI_IER, iflags);
mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
} }
static void atmci_queue_request(struct atmel_mci *host, static void atmci_queue_request(struct atmel_mci *host,
...@@ -1057,6 +1140,7 @@ static void atmci_queue_request(struct atmel_mci *host, ...@@ -1057,6 +1140,7 @@ static void atmci_queue_request(struct atmel_mci *host,
host->state = STATE_SENDING_CMD; host->state = STATE_SENDING_CMD;
atmci_start_request(host, slot); atmci_start_request(host, slot);
} else { } else {
dev_dbg(&host->pdev->dev, "queue request\n");
list_add_tail(&slot->queue_node, &host->queue); list_add_tail(&slot->queue_node, &host->queue);
} }
spin_unlock_bh(&host->lock); spin_unlock_bh(&host->lock);
...@@ -1069,6 +1153,7 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -1069,6 +1153,7 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_data *data; struct mmc_data *data;
WARN_ON(slot->mrq); WARN_ON(slot->mrq);
dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
/* /*
* We may "know" the card is gone even though there's still an * We may "know" the card is gone even though there's still an
...@@ -1308,6 +1393,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) ...@@ -1308,6 +1393,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
host->state = STATE_IDLE; host->state = STATE_IDLE;
} }
del_timer(&host->timer);
spin_unlock(&host->lock); spin_unlock(&host->lock);
mmc_request_done(prev_mmc, mrq); mmc_request_done(prev_mmc, mrq);
spin_lock(&host->lock); spin_lock(&host->lock);
...@@ -1330,21 +1417,13 @@ static void atmci_command_complete(struct atmel_mci *host, ...@@ -1330,21 +1417,13 @@ static void atmci_command_complete(struct atmel_mci *host,
cmd->error = -EILSEQ; cmd->error = -EILSEQ;
else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE)) else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
cmd->error = -EIO; cmd->error = -EIO;
else else if (host->mrq->data && (host->mrq->data->blksz & 3)) {
cmd->error = 0; if (host->caps.need_blksz_mul_4) {
cmd->error = -EINVAL;
if (cmd->error) { host->need_reset = 1;
dev_dbg(&host->pdev->dev,
"command error: status=0x%08x\n", status);
if (cmd->data) {
host->stop_transfer(host);
host->data = NULL;
atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY
| ATMCI_TXRDY | ATMCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS);
} }
} } else
cmd->error = 0;
} }
static void atmci_detect_change(unsigned long data) static void atmci_detect_change(unsigned long data)
...@@ -1407,23 +1486,21 @@ static void atmci_detect_change(unsigned long data) ...@@ -1407,23 +1486,21 @@ static void atmci_detect_change(unsigned long data)
break; break;
case STATE_SENDING_CMD: case STATE_SENDING_CMD:
mrq->cmd->error = -ENOMEDIUM; mrq->cmd->error = -ENOMEDIUM;
if (!mrq->data) if (mrq->data)
break; host->stop_transfer(host);
/* fall through */ break;
case STATE_SENDING_DATA: case STATE_DATA_XFER:
mrq->data->error = -ENOMEDIUM; mrq->data->error = -ENOMEDIUM;
host->stop_transfer(host); host->stop_transfer(host);
break; break;
case STATE_DATA_BUSY: case STATE_WAITING_NOTBUSY:
case STATE_DATA_ERROR: mrq->data->error = -ENOMEDIUM;
if (mrq->data->error == -EINPROGRESS) break;
mrq->data->error = -ENOMEDIUM;
if (!mrq->stop)
break;
/* fall through */
case STATE_SENDING_STOP: case STATE_SENDING_STOP:
mrq->stop->error = -ENOMEDIUM; mrq->stop->error = -ENOMEDIUM;
break; break;
case STATE_END_REQUEST:
break;
} }
atmci_request_end(host, mrq); atmci_request_end(host, mrq);
...@@ -1451,7 +1528,6 @@ static void atmci_tasklet_func(unsigned long priv) ...@@ -1451,7 +1528,6 @@ static void atmci_tasklet_func(unsigned long priv)
struct atmel_mci *host = (struct atmel_mci *)priv; struct atmel_mci *host = (struct atmel_mci *)priv;
struct mmc_request *mrq = host->mrq; struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data; struct mmc_data *data = host->data;
struct mmc_command *cmd = host->cmd;
enum atmel_mci_state state = host->state; enum atmel_mci_state state = host->state;
enum atmel_mci_state prev_state; enum atmel_mci_state prev_state;
u32 status; u32 status;
...@@ -1467,107 +1543,186 @@ static void atmci_tasklet_func(unsigned long priv) ...@@ -1467,107 +1543,186 @@ static void atmci_tasklet_func(unsigned long priv)
do { do {
prev_state = state; prev_state = state;
dev_dbg(&host->pdev->dev, "FSM: state=%d\n", state);
switch (state) { switch (state) {
case STATE_IDLE: case STATE_IDLE:
break; break;
case STATE_SENDING_CMD: case STATE_SENDING_CMD:
/*
* Command has been sent, we are waiting for command
* ready. Then we have three next states possible:
* END_REQUEST by default, WAITING_NOTBUSY if it's a
* command needing it or DATA_XFER if there is data.
*/
dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
if (!atmci_test_and_clear_pending(host, if (!atmci_test_and_clear_pending(host,
EVENT_CMD_COMPLETE)) EVENT_CMD_RDY))
break; break;
dev_dbg(&host->pdev->dev, "set completed cmd ready\n");
host->cmd = NULL; host->cmd = NULL;
atmci_set_completed(host, EVENT_CMD_COMPLETE); atmci_set_completed(host, EVENT_CMD_RDY);
atmci_command_complete(host, mrq->cmd); atmci_command_complete(host, mrq->cmd);
if (!mrq->data || cmd->error) { if (mrq->data) {
atmci_request_end(host, host->mrq); dev_dbg(&host->pdev->dev,
goto unlock; "command with data transfer");
} /*
* If there is a command error don't start
* data transfer.
*/
if (mrq->cmd->error) {
host->stop_transfer(host);
host->data = NULL;
atmci_writel(host, ATMCI_IDR,
ATMCI_TXRDY | ATMCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS);
state = STATE_END_REQUEST;
} else
state = STATE_DATA_XFER;
} else if ((!mrq->data) && (mrq->cmd->flags & MMC_RSP_BUSY)) {
dev_dbg(&host->pdev->dev,
"command response need waiting notbusy");
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY;
} else
state = STATE_END_REQUEST;
prev_state = state = STATE_SENDING_DATA; break;
/* fall through */
case STATE_SENDING_DATA: case STATE_DATA_XFER:
if (atmci_test_and_clear_pending(host, if (atmci_test_and_clear_pending(host,
EVENT_DATA_ERROR)) { EVENT_DATA_ERROR)) {
host->stop_transfer(host); dev_dbg(&host->pdev->dev, "set completed data error\n");
if (data->stop) atmci_set_completed(host, EVENT_DATA_ERROR);
atmci_send_stop_cmd(host, data); state = STATE_END_REQUEST;
state = STATE_DATA_ERROR;
break; break;
} }
/*
* A data transfer is in progress. The event expected
* to move to the next state depends of data transfer
* type (PDC or DMA). Once transfer done we can move
* to the next step which is WAITING_NOTBUSY in write
* case and directly SENDING_STOP in read case.
*/
dev_dbg(&host->pdev->dev, "FSM: xfer complete?\n");
if (!atmci_test_and_clear_pending(host, if (!atmci_test_and_clear_pending(host,
EVENT_XFER_COMPLETE)) EVENT_XFER_COMPLETE))
break; break;
dev_dbg(&host->pdev->dev,
"(%s) set completed xfer complete\n",
__func__);
atmci_set_completed(host, EVENT_XFER_COMPLETE); atmci_set_completed(host, EVENT_XFER_COMPLETE);
prev_state = state = STATE_DATA_BUSY;
/* fall through */
case STATE_DATA_BUSY: if (host->data->flags & MMC_DATA_WRITE) {
if (!atmci_test_and_clear_pending(host, atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
EVENT_DATA_COMPLETE)) state = STATE_WAITING_NOTBUSY;
break; } else if (host->mrq->stop) {
atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
host->data = NULL; atmci_send_stop_cmd(host, data);
atmci_set_completed(host, EVENT_DATA_COMPLETE); state = STATE_SENDING_STOP;
status = host->data_status;
if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
if (status & ATMCI_DTOE) {
dev_dbg(&host->pdev->dev,
"data timeout error\n");
data->error = -ETIMEDOUT;
} else if (status & ATMCI_DCRCE) {
dev_dbg(&host->pdev->dev,
"data CRC error\n");
data->error = -EILSEQ;
} else {
dev_dbg(&host->pdev->dev,
"data FIFO error (status=%08x)\n",
status);
data->error = -EIO;
}
} else { } else {
host->data = NULL;
data->bytes_xfered = data->blocks * data->blksz; data->bytes_xfered = data->blocks * data->blksz;
data->error = 0; data->error = 0;
atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS); state = STATE_END_REQUEST;
} }
break;
if (!data->stop) { case STATE_WAITING_NOTBUSY:
atmci_request_end(host, host->mrq); /*
goto unlock; * We can be in the state for two reasons: a command
} * requiring waiting not busy signal (stop command
* included) or a write operation. In the latest case,
* we need to send a stop command.
*/
dev_dbg(&host->pdev->dev, "FSM: not busy?\n");
if (!atmci_test_and_clear_pending(host,
EVENT_NOTBUSY))
break;
prev_state = state = STATE_SENDING_STOP; dev_dbg(&host->pdev->dev, "set completed not busy\n");
if (!data->error) atmci_set_completed(host, EVENT_NOTBUSY);
atmci_send_stop_cmd(host, data);
/* fall through */ if (host->data) {
/*
* For some commands such as CMD53, even if
* there is data transfer, there is no stop
* command to send.
*/
if (host->mrq->stop) {
atmci_writel(host, ATMCI_IER,
ATMCI_CMDRDY);
atmci_send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
} else {
host->data = NULL;
data->bytes_xfered = data->blocks
* data->blksz;
data->error = 0;
state = STATE_END_REQUEST;
}
} else
state = STATE_END_REQUEST;
break;
case STATE_SENDING_STOP: case STATE_SENDING_STOP:
/*
* In this state, it is important to set host->data to
* NULL (which is tested in the waiting notbusy state)
* in order to go to the end request state instead of
* sending stop again.
*/
dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
if (!atmci_test_and_clear_pending(host, if (!atmci_test_and_clear_pending(host,
EVENT_CMD_COMPLETE)) EVENT_CMD_RDY))
break; break;
dev_dbg(&host->pdev->dev, "FSM: cmd ready\n");
host->cmd = NULL; host->cmd = NULL;
host->data = NULL;
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
atmci_command_complete(host, mrq->stop); atmci_command_complete(host, mrq->stop);
atmci_request_end(host, host->mrq); if (mrq->stop->error) {
goto unlock; host->stop_transfer(host);
atmci_writel(host, ATMCI_IDR,
ATMCI_TXRDY | ATMCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS);
state = STATE_END_REQUEST;
} else {
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY;
}
break;
case STATE_DATA_ERROR: case STATE_END_REQUEST:
if (!atmci_test_and_clear_pending(host, atmci_writel(host, ATMCI_IDR, ATMCI_TXRDY | ATMCI_RXRDY
EVENT_XFER_COMPLETE)) | ATMCI_DATA_ERROR_FLAGS);
break; status = host->data_status;
if (unlikely(status)) {
host->stop_transfer(host);
host->data = NULL;
if (status & ATMCI_DTOE) {
data->error = -ETIMEDOUT;
} else if (status & ATMCI_DCRCE) {
data->error = -EILSEQ;
} else {
data->error = -EIO;
}
}
state = STATE_DATA_BUSY; atmci_request_end(host, host->mrq);
state = STATE_IDLE;
break; break;
} }
} while (state != prev_state); } while (state != prev_state);
host->state = state; host->state = state;
unlock:
spin_unlock(&host->lock); spin_unlock(&host->lock);
} }
...@@ -1620,9 +1775,6 @@ static void atmci_read_data_pio(struct atmel_mci *host) ...@@ -1620,9 +1775,6 @@ static void atmci_read_data_pio(struct atmel_mci *host)
| ATMCI_DATA_ERROR_FLAGS)); | ATMCI_DATA_ERROR_FLAGS));
host->data_status = status; host->data_status = status;
data->bytes_xfered += nbytes; data->bytes_xfered += nbytes;
smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
return; return;
} }
} while (status & ATMCI_RXRDY); } while (status & ATMCI_RXRDY);
...@@ -1691,9 +1843,6 @@ static void atmci_write_data_pio(struct atmel_mci *host) ...@@ -1691,9 +1843,6 @@ static void atmci_write_data_pio(struct atmel_mci *host)
| ATMCI_DATA_ERROR_FLAGS)); | ATMCI_DATA_ERROR_FLAGS));
host->data_status = status; host->data_status = status;
data->bytes_xfered += nbytes; data->bytes_xfered += nbytes;
smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
return; return;
} }
} while (status & ATMCI_TXRDY); } while (status & ATMCI_TXRDY);
...@@ -1711,16 +1860,6 @@ static void atmci_write_data_pio(struct atmel_mci *host) ...@@ -1711,16 +1860,6 @@ static void atmci_write_data_pio(struct atmel_mci *host)
atmci_set_pending(host, EVENT_XFER_COMPLETE); atmci_set_pending(host, EVENT_XFER_COMPLETE);
} }
static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
{
atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
host->cmd_status = status;
smp_wmb();
atmci_set_pending(host, EVENT_CMD_COMPLETE);
tasklet_schedule(&host->tasklet);
}
static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status) static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
{ {
int i; int i;
...@@ -1748,17 +1887,21 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) ...@@ -1748,17 +1887,21 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
break; break;
if (pending & ATMCI_DATA_ERROR_FLAGS) { if (pending & ATMCI_DATA_ERROR_FLAGS) {
dev_dbg(&host->pdev->dev, "IRQ: data error\n");
atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS
| ATMCI_RXRDY | ATMCI_TXRDY); | ATMCI_RXRDY | ATMCI_TXRDY
pending &= atmci_readl(host, ATMCI_IMR); | ATMCI_ENDRX | ATMCI_ENDTX
| ATMCI_RXBUFF | ATMCI_TXBUFE);
host->data_status = status; host->data_status = status;
dev_dbg(&host->pdev->dev, "set pending data error\n");
smp_wmb(); smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR); atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet); tasklet_schedule(&host->tasklet);
} }
if (pending & ATMCI_TXBUFE) { if (pending & ATMCI_TXBUFE) {
dev_dbg(&host->pdev->dev, "IRQ: tx buffer empty\n");
atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE); atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE);
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX); atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
/* /*
...@@ -1774,6 +1917,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) ...@@ -1774,6 +1917,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
atmci_pdc_complete(host); atmci_pdc_complete(host);
} }
} else if (pending & ATMCI_ENDTX) { } else if (pending & ATMCI_ENDTX) {
dev_dbg(&host->pdev->dev, "IRQ: end of tx buffer\n");
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX); atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
if (host->data_size) { if (host->data_size) {
...@@ -1784,6 +1928,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) ...@@ -1784,6 +1928,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
} }
if (pending & ATMCI_RXBUFF) { if (pending & ATMCI_RXBUFF) {
dev_dbg(&host->pdev->dev, "IRQ: rx buffer full\n");
atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF); atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF);
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX); atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
/* /*
...@@ -1799,6 +1944,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) ...@@ -1799,6 +1944,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
atmci_pdc_complete(host); atmci_pdc_complete(host);
} }
} else if (pending & ATMCI_ENDRX) { } else if (pending & ATMCI_ENDRX) {
dev_dbg(&host->pdev->dev, "IRQ: end of rx buffer\n");
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX); atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
if (host->data_size) { if (host->data_size) {
...@@ -1808,23 +1954,44 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) ...@@ -1808,23 +1954,44 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
} }
} }
/*
* First mci IPs, so mainly the ones having pdc, have some
* issues with the notbusy signal. You can't get it after
* data transmission if you have not sent a stop command.
* The appropriate workaround is to use the BLKE signal.
*/
if (pending & ATMCI_BLKE) {
dev_dbg(&host->pdev->dev, "IRQ: blke\n");
atmci_writel(host, ATMCI_IDR, ATMCI_BLKE);
smp_wmb();
dev_dbg(&host->pdev->dev, "set pending notbusy\n");
atmci_set_pending(host, EVENT_NOTBUSY);
tasklet_schedule(&host->tasklet);
}
if (pending & ATMCI_NOTBUSY) { if (pending & ATMCI_NOTBUSY) {
atmci_writel(host, ATMCI_IDR, dev_dbg(&host->pdev->dev, "IRQ: not_busy\n");
ATMCI_DATA_ERROR_FLAGS | ATMCI_NOTBUSY); atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY);
if (!host->data_status)
host->data_status = status;
smp_wmb(); smp_wmb();
atmci_set_pending(host, EVENT_DATA_COMPLETE); dev_dbg(&host->pdev->dev, "set pending notbusy\n");
atmci_set_pending(host, EVENT_NOTBUSY);
tasklet_schedule(&host->tasklet); tasklet_schedule(&host->tasklet);
} }
if (pending & ATMCI_RXRDY) if (pending & ATMCI_RXRDY)
atmci_read_data_pio(host); atmci_read_data_pio(host);
if (pending & ATMCI_TXRDY) if (pending & ATMCI_TXRDY)
atmci_write_data_pio(host); atmci_write_data_pio(host);
if (pending & ATMCI_CMDRDY) if (pending & ATMCI_CMDRDY) {
atmci_cmd_interrupt(host, status); dev_dbg(&host->pdev->dev, "IRQ: cmd ready\n");
atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
host->cmd_status = status;
smp_wmb();
dev_dbg(&host->pdev->dev, "set pending cmd rdy\n");
atmci_set_pending(host, EVENT_CMD_RDY);
tasklet_schedule(&host->tasklet);
}
if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB)) if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
atmci_sdio_interrupt(host, status); atmci_sdio_interrupt(host, status);
...@@ -1877,13 +2044,26 @@ static int __init atmci_init_slot(struct atmel_mci *host, ...@@ -1877,13 +2044,26 @@ static int __init atmci_init_slot(struct atmel_mci *host,
mmc->caps |= MMC_CAP_SDIO_IRQ; mmc->caps |= MMC_CAP_SDIO_IRQ;
if (host->caps.has_highspeed) if (host->caps.has_highspeed)
mmc->caps |= MMC_CAP_SD_HIGHSPEED; mmc->caps |= MMC_CAP_SD_HIGHSPEED;
if (slot_data->bus_width >= 4) /*
* Without the read/write proof capability, it is strongly suggested to
* use only one bit for data to prevent fifo underruns and overruns
* which will corrupt data.
*/
if ((slot_data->bus_width >= 4) && host->caps.has_rwproof)
mmc->caps |= MMC_CAP_4_BIT_DATA; mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->max_segs = 64; if (atmci_get_version(host) < 0x200) {
mmc->max_req_size = 32768 * 512; mmc->max_segs = 256;
mmc->max_blk_size = 32768; mmc->max_blk_size = 4095;
mmc->max_blk_count = 512; mmc->max_blk_count = 256;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_blk_size * mmc->max_segs;
} else {
mmc->max_segs = 64;
mmc->max_req_size = 32768 * 512;
mmc->max_blk_size = 32768;
mmc->max_blk_count = 512;
}
/* Assume card is present initially */ /* Assume card is present initially */
set_bit(ATMCI_CARD_PRESENT, &slot->flags); set_bit(ATMCI_CARD_PRESENT, &slot->flags);
...@@ -2007,11 +2187,6 @@ static bool atmci_configure_dma(struct atmel_mci *host) ...@@ -2007,11 +2187,6 @@ static bool atmci_configure_dma(struct atmel_mci *host)
} }
} }
static inline unsigned int atmci_get_version(struct atmel_mci *host)
{
return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
}
/* /*
* HSMCI (High Speed MCI) module is not fully compatible with MCI module. * HSMCI (High Speed MCI) module is not fully compatible with MCI module.
* HSMCI provides DMA support and a new config register but no more supports * HSMCI provides DMA support and a new config register but no more supports
...@@ -2032,6 +2207,9 @@ static void __init atmci_get_cap(struct atmel_mci *host) ...@@ -2032,6 +2207,9 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_highspeed = 0; host->caps.has_highspeed = 0;
host->caps.has_rwproof = 0; host->caps.has_rwproof = 0;
host->caps.has_odd_clk_div = 0; host->caps.has_odd_clk_div = 0;
host->caps.has_bad_data_ordering = 1;
host->caps.need_reset_after_xfer = 1;
host->caps.need_blksz_mul_4 = 1;
/* keep only major version number */ /* keep only major version number */
switch (version & 0xf00) { switch (version & 0xf00) {
...@@ -2051,7 +2229,11 @@ static void __init atmci_get_cap(struct atmel_mci *host) ...@@ -2051,7 +2229,11 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_highspeed = 1; host->caps.has_highspeed = 1;
case 0x200: case 0x200:
host->caps.has_rwproof = 1; host->caps.has_rwproof = 1;
host->caps.need_blksz_mul_4 = 0;
case 0x100: case 0x100:
host->caps.has_bad_data_ordering = 0;
host->caps.need_reset_after_xfer = 0;
case 0x0:
break; break;
default: default:
host->caps.has_pdc = 0; host->caps.has_pdc = 0;
...@@ -2138,14 +2320,20 @@ static int __init atmci_probe(struct platform_device *pdev) ...@@ -2138,14 +2320,20 @@ static int __init atmci_probe(struct platform_device *pdev)
if (pdata->slot[0].bus_width) { if (pdata->slot[0].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[0], ret = atmci_init_slot(host, &pdata->slot[0],
0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA); 0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
if (!ret) if (!ret) {
nr_slots++; nr_slots++;
host->buf_size = host->slot[0]->mmc->max_req_size;
}
} }
if (pdata->slot[1].bus_width) { if (pdata->slot[1].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[1], ret = atmci_init_slot(host, &pdata->slot[1],
1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB); 1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
if (!ret) if (!ret) {
nr_slots++; nr_slots++;
if (host->slot[1]->mmc->max_req_size > host->buf_size)
host->buf_size =
host->slot[1]->mmc->max_req_size;
}
} }
if (!nr_slots) { if (!nr_slots) {
...@@ -2153,6 +2341,19 @@ static int __init atmci_probe(struct platform_device *pdev) ...@@ -2153,6 +2341,19 @@ static int __init atmci_probe(struct platform_device *pdev)
goto err_init_slot; goto err_init_slot;
} }
if (!host->caps.has_rwproof) {
host->buffer = dma_alloc_coherent(&pdev->dev, host->buf_size,
&host->buf_phys_addr,
GFP_KERNEL);
if (!host->buffer) {
ret = -ENOMEM;
dev_err(&pdev->dev, "buffer allocation failed\n");
goto err_init_slot;
}
}
setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
dev_info(&pdev->dev, dev_info(&pdev->dev,
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n", "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
host->mapbase, irq, nr_slots); host->mapbase, irq, nr_slots);
...@@ -2179,6 +2380,10 @@ static int __exit atmci_remove(struct platform_device *pdev) ...@@ -2179,6 +2380,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
if (host->buffer)
dma_free_coherent(&pdev->dev, host->buf_size,
host->buffer, host->buf_phys_addr);
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (host->slot[i]) if (host->slot[i])
atmci_cleanup_slot(host->slot[i], i); atmci_cleanup_slot(host->slot[i], i);
......
...@@ -1533,4 +1533,5 @@ module_exit(davinci_mmcsd_exit); ...@@ -1533,4 +1533,5 @@ module_exit(davinci_mmcsd_exit);
MODULE_AUTHOR("Texas Instruments India"); MODULE_AUTHOR("Texas Instruments India");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller"); MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller");
MODULE_ALIAS("platform:davinci_mmc");
...@@ -100,8 +100,6 @@ struct dw_mci_slot { ...@@ -100,8 +100,6 @@ struct dw_mci_slot {
int last_detect_state; int last_detect_state;
}; };
static struct workqueue_struct *dw_mci_card_workqueue;
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v) static int dw_mci_req_show(struct seq_file *s, void *v)
{ {
...@@ -859,10 +857,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) ...@@ -859,10 +857,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
int_mask = mci_readl(host, INTMASK); int_mask = mci_readl(host, INTMASK);
if (enb) { if (enb) {
mci_writel(host, INTMASK, mci_writel(host, INTMASK,
(int_mask | (1 << SDMMC_INT_SDIO(slot->id)))); (int_mask | SDMMC_INT_SDIO(slot->id)));
} else { } else {
mci_writel(host, INTMASK, mci_writel(host, INTMASK,
(int_mask & ~(1 << SDMMC_INT_SDIO(slot->id)))); (int_mask & ~SDMMC_INT_SDIO(slot->id)));
} }
} }
...@@ -1605,7 +1603,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) ...@@ -1605,7 +1603,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_CD) { if (pending & SDMMC_INT_CD) {
mci_writel(host, RINTSTS, SDMMC_INT_CD); mci_writel(host, RINTSTS, SDMMC_INT_CD);
queue_work(dw_mci_card_workqueue, &host->card_work); queue_work(host->card_workqueue, &host->card_work);
} }
/* Handle SDIO Interrupts */ /* Handle SDIO Interrupts */
...@@ -1844,7 +1842,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1844,7 +1842,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
* Card may have been plugged in prior to boot so we * Card may have been plugged in prior to boot so we
* need to run the detect tasklet * need to run the detect tasklet
*/ */
queue_work(dw_mci_card_workqueue, &host->card_work); queue_work(host->card_workqueue, &host->card_work);
return 0; return 0;
} }
...@@ -2021,9 +2019,9 @@ int dw_mci_probe(struct dw_mci *host) ...@@ -2021,9 +2019,9 @@ int dw_mci_probe(struct dw_mci *host)
mci_writel(host, CLKSRC, 0); mci_writel(host, CLKSRC, 0);
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
dw_mci_card_workqueue = alloc_workqueue("dw-mci-card", host->card_workqueue = alloc_workqueue("dw-mci-card",
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1); WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
if (!dw_mci_card_workqueue) if (!host->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(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
...@@ -2085,7 +2083,7 @@ int dw_mci_probe(struct dw_mci *host) ...@@ -2085,7 +2083,7 @@ int dw_mci_probe(struct dw_mci *host)
free_irq(host->irq, host); free_irq(host->irq, host);
err_workqueue: err_workqueue:
destroy_workqueue(dw_mci_card_workqueue); destroy_workqueue(host->card_workqueue);
err_dmaunmap: err_dmaunmap:
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
...@@ -2119,7 +2117,7 @@ void dw_mci_remove(struct dw_mci *host) ...@@ -2119,7 +2117,7 @@ void dw_mci_remove(struct dw_mci *host)
mci_writel(host, CLKSRC, 0); mci_writel(host, CLKSRC, 0);
free_irq(host->irq, host); free_irq(host->irq, host);
destroy_workqueue(dw_mci_card_workqueue); destroy_workqueue(host->card_workqueue);
dma_free_coherent(&host->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)
......
/*
* linux/drivers/mmc/host/imxmmc.c - Motorola i.MX MMCI driver
*
* Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha@saschahauer.de>
* Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
*
* derived from pxamci.c by Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <mach/mmc.h>
#include <mach/imx-dma.h>
#include "imxmmc.h"
#define DRIVER_NAME "imx-mmc"
#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
struct imxmci_host {
struct mmc_host *mmc;
spinlock_t lock;
struct resource *res;
void __iomem *base;
int irq;
imx_dmach_t dma;
volatile unsigned int imask;
unsigned int power_mode;
unsigned int present;
struct imxmmc_platform_data *pdata;
struct mmc_request *req;
struct mmc_command *cmd;
struct mmc_data *data;
struct timer_list timer;
struct tasklet_struct tasklet;
unsigned int status_reg;
unsigned long pending_events;
/* Next two fields are there for CPU driven transfers to overcome SDHC deficiencies */
u16 *data_ptr;
unsigned int data_cnt;
atomic_t stuck_timeout;
unsigned int dma_nents;
unsigned int dma_size;
unsigned int dma_dir;
int dma_allocated;
unsigned char actual_bus_width;
int prev_cmd_code;
struct clk *clk;
};
#define IMXMCI_PEND_IRQ_b 0
#define IMXMCI_PEND_DMA_END_b 1
#define IMXMCI_PEND_DMA_ERR_b 2
#define IMXMCI_PEND_WAIT_RESP_b 3
#define IMXMCI_PEND_DMA_DATA_b 4
#define IMXMCI_PEND_CPU_DATA_b 5
#define IMXMCI_PEND_CARD_XCHG_b 6
#define IMXMCI_PEND_SET_INIT_b 7
#define IMXMCI_PEND_STARTED_b 8
#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b)
#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b)
#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b)
#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b)
#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b)
#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b)
#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b)
#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b)
#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b)
static void imxmci_stop_clock(struct imxmci_host *host)
{
int i = 0;
u16 reg;
reg = readw(host->base + MMC_REG_STR_STP_CLK);
writew(reg & ~STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
while (i < 0x1000) {
if (!(i & 0x7f)) {
reg = readw(host->base + MMC_REG_STR_STP_CLK);
writew(reg | STR_STP_CLK_STOP_CLK,
host->base + MMC_REG_STR_STP_CLK);
}
reg = readw(host->base + MMC_REG_STATUS);
if (!(reg & STATUS_CARD_BUS_CLK_RUN)) {
/* Check twice before cut */
reg = readw(host->base + MMC_REG_STATUS);
if (!(reg & STATUS_CARD_BUS_CLK_RUN))
return;
}
i++;
}
dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
}
static int imxmci_start_clock(struct imxmci_host *host)
{
unsigned int trials = 0;
unsigned int delay_limit = 128;
unsigned long flags;
u16 reg;
reg = readw(host->base + MMC_REG_STR_STP_CLK);
writew(reg & ~STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
/*
* Command start of the clock, this usually succeeds in less
* then 6 delay loops, but during card detection (low clockrate)
* it takes up to 5000 delay loops and sometimes fails for the first time
*/
reg = readw(host->base + MMC_REG_STR_STP_CLK);
writew(reg | STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
do {
unsigned int delay = delay_limit;
while (delay--) {
reg = readw(host->base + MMC_REG_STATUS);
if (reg & STATUS_CARD_BUS_CLK_RUN) {
/* Check twice before cut */
reg = readw(host->base + MMC_REG_STATUS);
if (reg & STATUS_CARD_BUS_CLK_RUN)
return 0;
}
if (test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
return 0;
}
local_irq_save(flags);
/*
* Ensure, that request is not doubled under all possible circumstances.
* It is possible, that cock running state is missed, because some other
* IRQ or schedule delays this function execution and the clocks has
* been already stopped by other means (response processing, SDHC HW)
*/
if (!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) {
reg = readw(host->base + MMC_REG_STR_STP_CLK);
writew(reg | STR_STP_CLK_START_CLK,
host->base + MMC_REG_STR_STP_CLK);
}
local_irq_restore(flags);
} while (++trials < 256);
dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
return -1;
}
static void imxmci_softreset(struct imxmci_host *host)
{
int i;
/* reset sequence */
writew(0x08, host->base + MMC_REG_STR_STP_CLK);
writew(0x0D, host->base + MMC_REG_STR_STP_CLK);
for (i = 0; i < 8; i++)
writew(0x05, host->base + MMC_REG_STR_STP_CLK);
writew(0xff, host->base + MMC_REG_RES_TO);
writew(512, host->base + MMC_REG_BLK_LEN);
writew(1, host->base + MMC_REG_NOB);
}
static int imxmci_busy_wait_for_status(struct imxmci_host *host,
unsigned int *pstat, unsigned int stat_mask,
int timeout, const char *where)
{
int loops = 0;
while (!(*pstat & stat_mask)) {
loops += 2;
if (loops >= timeout) {
dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
where, *pstat, stat_mask);
return -1;
}
udelay(2);
*pstat |= readw(host->base + MMC_REG_STATUS);
}
if (!loops)
return 0;
/* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
if (!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock >= 8000000))
dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
loops, where, *pstat, stat_mask);
return loops;
}
static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
{
unsigned int nob = data->blocks;
unsigned int blksz = data->blksz;
unsigned int datasz = nob * blksz;
int i;
if (data->flags & MMC_DATA_STREAM)
nob = 0xffff;
host->data = data;
data->bytes_xfered = 0;
writew(nob, host->base + MMC_REG_NOB);
writew(blksz, host->base + MMC_REG_BLK_LEN);
/*
* DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
* We are in big troubles for non-512 byte transfers according to note in the paragraph
* 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least.
* The situation is even more complex in reality. The SDHC in not able to handle wll
* partial FIFO fills and reads. The length has to be rounded up to burst size multiple.
* This is required for SCR read at least.
*/
if (datasz < 512) {
host->dma_size = datasz;
if (data->flags & MMC_DATA_READ) {
host->dma_dir = DMA_FROM_DEVICE;
/* Hack to enable read SCR */
writew(1, host->base + MMC_REG_NOB);
writew(512, host->base + MMC_REG_BLK_LEN);
} else {
host->dma_dir = DMA_TO_DEVICE;
}
/* Convert back to virtual address */
host->data_ptr = (u16 *)sg_virt(data->sg);
host->data_cnt = 0;
clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
return;
}
if (data->flags & MMC_DATA_READ) {
host->dma_dir = DMA_FROM_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
host->res->start + MMC_REG_BUFFER_ACCESS,
DMA_MODE_READ);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
} else {
host->dma_dir = DMA_TO_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
host->res->start + MMC_REG_BUFFER_ACCESS,
DMA_MODE_WRITE);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
}
#if 1 /* This code is there only for consistency checking and can be disabled in future */
host->dma_size = 0;
for (i = 0; i < host->dma_nents; i++)
host->dma_size += data->sg[i].length;
if (datasz > host->dma_size) {
dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
datasz, host->dma_size);
}
#endif
host->dma_size = datasz;
wmb();
set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
/* start DMA engine for read, write is delayed after initial response */
if (host->dma_dir == DMA_FROM_DEVICE)
imx_dma_enable(host->dma);
}
static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
{
unsigned long flags;
u32 imask;
WARN_ON(host->cmd != NULL);
host->cmd = cmd;
/* Ensure, that clock are stopped else command programming and start fails */
imxmci_stop_clock(host);
if (cmd->flags & MMC_RSP_BUSY)
cmdat |= CMD_DAT_CONT_BUSY;
switch (mmc_resp_type(cmd)) {
case MMC_RSP_R1: /* short CRC, OPCODE */
case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
break;
case MMC_RSP_R2: /* long 136 bit + CRC */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
break;
case MMC_RSP_R3: /* short */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
break;
default:
break;
}
if (test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events))
cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
if (host->actual_bus_width == MMC_BUS_WIDTH_4)
cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
writew(cmd->opcode, host->base + MMC_REG_CMD);
writew(cmd->arg >> 16, host->base + MMC_REG_ARGH);
writew(cmd->arg & 0xffff, host->base + MMC_REG_ARGL);
writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
atomic_set(&host->stuck_timeout, 0);
set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
imask = IMXMCI_INT_MASK_DEFAULT;
imask &= ~INT_MASK_END_CMD_RES;
if (cmdat & CMD_DAT_CONT_DATA_ENABLE) {
/* imask &= ~INT_MASK_BUF_READY; */
imask &= ~INT_MASK_DATA_TRAN;
if (cmdat & CMD_DAT_CONT_WRITE)
imask &= ~INT_MASK_WRITE_OP_DONE;
if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
imask &= ~INT_MASK_BUF_READY;
}
spin_lock_irqsave(&host->lock, flags);
host->imask = imask;
writew(host->imask, host->base + MMC_REG_INT_MASK);
spin_unlock_irqrestore(&host->lock, flags);
dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
cmd->opcode, cmd->opcode, imask);
imxmci_start_clock(host);
}
static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
host->imask = IMXMCI_INT_MASK_DEFAULT;
writew(host->imask, host->base + MMC_REG_INT_MASK);
spin_unlock_irqrestore(&host->lock, flags);
if (req && req->cmd)
host->prev_cmd_code = req->cmd->opcode;
host->req = NULL;
host->cmd = NULL;
host->data = NULL;
mmc_request_done(host->mmc, req);
}
static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
{
struct mmc_data *data = host->data;
int data_error;
if (test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
imx_dma_disable(host->dma);
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
host->dma_dir);
}
if (stat & STATUS_ERR_MASK) {
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", stat);
if (stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
data->error = -EILSEQ;
else if (stat & STATUS_TIME_OUT_READ)
data->error = -ETIMEDOUT;
else
data->error = -EIO;
} else {
data->bytes_xfered = host->dma_size;
}
data_error = data->error;
host->data = NULL;
return data_error;
}
static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
{
struct mmc_command *cmd = host->cmd;
int i;
u32 a, b, c;
struct mmc_data *data = host->data;
if (!cmd)
return 0;
host->cmd = NULL;
if (stat & STATUS_TIME_OUT_RESP) {
dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
cmd->error = -ETIMEDOUT;
} else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
cmd->error = -EILSEQ;
}
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
for (i = 0; i < 4; i++) {
a = readw(host->base + MMC_REG_RES_FIFO);
b = readw(host->base + MMC_REG_RES_FIFO);
cmd->resp[i] = a << 16 | b;
}
} else {
a = readw(host->base + MMC_REG_RES_FIFO);
b = readw(host->base + MMC_REG_RES_FIFO);
c = readw(host->base + MMC_REG_RES_FIFO);
cmd->resp[0] = a << 24 | b << 8 | c >> 8;
}
}
dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) {
if (host->req->data->flags & MMC_DATA_WRITE) {
/* Wait for FIFO to be empty before starting DMA write */
stat = readw(host->base + MMC_REG_STATUS);
if (imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FE,
40, "imxmci_cmd_done DMA WR") < 0) {
cmd->error = -EIO;
imxmci_finish_data(host, stat);
if (host->req)
imxmci_finish_request(host, host->req);
dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
stat);
return 0;
}
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
imx_dma_enable(host->dma);
}
} else {
struct mmc_request *req;
imxmci_stop_clock(host);
req = host->req;
if (data)
imxmci_finish_data(host, stat);
if (req)
imxmci_finish_request(host, req);
else
dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
}
return 1;
}
static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
{
struct mmc_data *data = host->data;
int data_error;
if (!data)
return 0;
data_error = imxmci_finish_data(host, stat);
if (host->req->stop) {
imxmci_stop_clock(host);
imxmci_start_cmd(host, host->req->stop, 0);
} else {
struct mmc_request *req;
req = host->req;
if (req)
imxmci_finish_request(host, req);
else
dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
}
return 1;
}
static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
{
int i;
int burst_len;
int trans_done = 0;
unsigned int stat = *pstat;
if (host->actual_bus_width != MMC_BUS_WIDTH_4)
burst_len = 16;
else
burst_len = 64;
/* This is unfortunately required */
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n",
stat);
udelay(20); /* required for clocks < 8MHz*/
if (host->dma_dir == DMA_FROM_DEVICE) {
imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
STATUS_TIME_OUT_READ,
50, "imxmci_cpu_driven_data read");
while ((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
!(stat & STATUS_TIME_OUT_READ) &&
(host->data_cnt < 512)) {
udelay(20); /* required for clocks < 8MHz*/
for (i = burst_len; i >= 2 ; i -= 2) {
u16 data;
data = readw(host->base + MMC_REG_BUFFER_ACCESS);
udelay(10); /* required for clocks < 8MHz*/
if (host->data_cnt+2 <= host->dma_size) {
*(host->data_ptr++) = data;
} else {
if (host->data_cnt < host->dma_size)
*(u8 *)(host->data_ptr) = data;
}
host->data_cnt += 2;
}
stat = readw(host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",
host->data_cnt, burst_len, stat);
}
if ((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
trans_done = 1;
if (host->dma_size & 0x1ff)
stat &= ~STATUS_CRC_READ_ERR;
if (stat & STATUS_TIME_OUT_READ) {
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n",
stat);
trans_done = -1;
}
} else {
imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FE,
20, "imxmci_cpu_driven_data write");
while ((stat & STATUS_APPL_BUFF_FE) &&
(host->data_cnt < host->dma_size)) {
if (burst_len >= host->dma_size - host->data_cnt) {
burst_len = host->dma_size - host->data_cnt;
host->data_cnt = host->dma_size;
trans_done = 1;
} else {
host->data_cnt += burst_len;
}
for (i = burst_len; i > 0 ; i -= 2)
writew(*(host->data_ptr++), host->base + MMC_REG_BUFFER_ACCESS);
stat = readw(host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
burst_len, stat);
}
}
*pstat = stat;
return trans_done;
}
static void imxmci_dma_irq(int dma, void *devid)
{
struct imxmci_host *host = devid;
u32 stat = readw(host->base + MMC_REG_STATUS);
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
static irqreturn_t imxmci_irq(int irq, void *devid)
{
struct imxmci_host *host = devid;
u32 stat = readw(host->base + MMC_REG_STATUS);
int handled = 1;
writew(host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT,
host->base + MMC_REG_INT_MASK);
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
tasklet_schedule(&host->tasklet);
return IRQ_RETVAL(handled);
}
static void imxmci_tasklet_fnc(unsigned long data)
{
struct imxmci_host *host = (struct imxmci_host *)data;
u32 stat;
unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */
int timeout = 0;
if (atomic_read(&host->stuck_timeout) > 4) {
char *what;
timeout = 1;
stat = readw(host->base + MMC_REG_STATUS);
host->status_reg = stat;
if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
what = "RESP+DMA";
else
what = "RESP";
else
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
what = "DATA";
else
what = "DMA";
else
what = "???";
dev_err(mmc_dev(host->mmc),
"%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
what, stat,
readw(host->base + MMC_REG_INT_MASK));
dev_err(mmc_dev(host->mmc),
"CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
readw(host->base + MMC_REG_CMD_DAT_CONT),
readw(host->base + MMC_REG_BLK_LEN),
readw(host->base + MMC_REG_NOB),
CCR(host->dma));
dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",
host->cmd ? host->cmd->opcode : 0,
host->prev_cmd_code,
1 << host->actual_bus_width, host->dma_size);
}
if (!host->present || timeout)
host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
if (test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
stat = readw(host->base + MMC_REG_STATUS);
/*
* This is not required in theory, but there is chance to miss some flag
* which clears automatically by mask write, FreeScale original code keeps
* stat from IRQ time so do I
*/
stat |= host->status_reg;
if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
stat &= ~STATUS_CRC_READ_ERR;
if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
imxmci_busy_wait_for_status(host, &stat,
STATUS_END_CMD_RESP | STATUS_ERR_MASK,
20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
}
if (stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
if (host->data && (stat & STATUS_ERR_MASK))
imxmci_data_done(host, stat);
}
if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
stat |= readw(host->base + MMC_REG_STATUS);
if (imxmci_cpu_driven_data(host, &stat)) {
if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
&host->pending_events);
imxmci_data_done(host, stat);
}
}
}
if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
!test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
stat = readw(host->base + MMC_REG_STATUS);
/* Same as above */
stat |= host->status_reg;
if (host->dma_dir == DMA_TO_DEVICE)
data_dir_mask = STATUS_WRITE_OP_DONE;
else
data_dir_mask = STATUS_DATA_TRANS_DONE;
if (stat & data_dir_mask) {
clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
imxmci_data_done(host, stat);
}
}
if (test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
if (host->cmd)
imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
if (host->data)
imxmci_data_done(host, STATUS_TIME_OUT_READ |
STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
if (host->req)
imxmci_finish_request(host, host->req);
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
}
}
static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
{
struct imxmci_host *host = mmc_priv(mmc);
unsigned int cmdat;
WARN_ON(host->req != NULL);
host->req = req;
cmdat = 0;
if (req->data) {
imxmci_setup_data(host, req->data);
cmdat |= CMD_DAT_CONT_DATA_ENABLE;
if (req->data->flags & MMC_DATA_WRITE)
cmdat |= CMD_DAT_CONT_WRITE;
if (req->data->flags & MMC_DATA_STREAM)
cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
}
imxmci_start_cmd(host, req->cmd, cmdat);
}
#define CLK_RATE 19200000
static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct imxmci_host *host = mmc_priv(mmc);
int prescaler;
if (ios->bus_width == MMC_BUS_WIDTH_4) {
host->actual_bus_width = MMC_BUS_WIDTH_4;
imx_gpio_mode(PB11_PF_SD_DAT3);
BLR(host->dma) = 0; /* burst 64 byte read/write */
} else {
host->actual_bus_width = MMC_BUS_WIDTH_1;
imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
BLR(host->dma) = 16; /* burst 16 byte read/write */
}
if (host->power_mode != ios->power_mode) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
break;
case MMC_POWER_UP:
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
break;
case MMC_POWER_ON:
break;
}
host->power_mode = ios->power_mode;
}
if (ios->clock) {
unsigned int clk;
u16 reg;
/* The prescaler is 5 for PERCLK2 equal to 96MHz
* then 96MHz / 5 = 19.2 MHz
*/
clk = clk_get_rate(host->clk);
prescaler = (clk + (CLK_RATE * 7) / 8) / CLK_RATE;
switch (prescaler) {
case 0:
case 1: prescaler = 0;
break;
case 2: prescaler = 1;
break;
case 3: prescaler = 2;
break;
case 4: prescaler = 4;
break;
default:
case 5: prescaler = 5;
break;
}
dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
clk, prescaler);
for (clk = 0; clk < 8; clk++) {
int x;
x = CLK_RATE / (1 << clk);
if (x <= ios->clock)
break;
}
/* enable controller */
reg = readw(host->base + MMC_REG_STR_STP_CLK);
writew(reg | STR_STP_CLK_ENABLE,
host->base + MMC_REG_STR_STP_CLK);
imxmci_stop_clock(host);
writew((prescaler << 3) | clk, host->base + MMC_REG_CLK_RATE);
/*
* Under my understanding, clock should not be started there, because it would
* initiate SDHC sequencer and send last or random command into card
*/
/* imxmci_start_clock(host); */
dev_dbg(mmc_dev(host->mmc),
"MMC_CLK_RATE: 0x%08x\n",
readw(host->base + MMC_REG_CLK_RATE));
} else {
imxmci_stop_clock(host);
}
}
static int imxmci_get_ro(struct mmc_host *mmc)
{
struct imxmci_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_ro)
return !!host->pdata->get_ro(mmc_dev(mmc));
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}
static const struct mmc_host_ops imxmci_ops = {
.request = imxmci_request,
.set_ios = imxmci_set_ios,
.get_ro = imxmci_get_ro,
};
static void imxmci_check_status(unsigned long data)
{
struct imxmci_host *host = (struct imxmci_host *)data;
if (host->pdata && host->pdata->card_present &&
host->pdata->card_present(mmc_dev(host->mmc)) != host->present) {
host->present ^= 1;
dev_info(mmc_dev(host->mmc), "card %s\n",
host->present ? "inserted" : "removed");
set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
atomic_inc(&host->stuck_timeout);
if (atomic_read(&host->stuck_timeout) > 4)
tasklet_schedule(&host->tasklet);
} else {
atomic_set(&host->stuck_timeout, 0);
}
mod_timer(&host->timer, jiffies + (HZ>>1));
}
static int __init imxmci_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct imxmci_host *host = NULL;
struct resource *r;
int ret = 0, irq;
u16 rev_no;
pr_info("i.MX mmc driver\n");
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!r || irq < 0)
return -ENXIO;
r = request_mem_region(r->start, resource_size(r), pdev->name);
if (!r)
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto out;
}
mmc->ops = &imxmci_ops;
mmc->f_min = 150000;
mmc->f_max = CLK_RATE/2;
mmc->ocr_avail = MMC_VDD_32_33;
mmc->caps = MMC_CAP_4_BIT_DATA;
/* MMC core transfer sizes tunable parameters */
mmc->max_segs = 64;
mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */
mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */
mmc->max_blk_size = 2048;
mmc->max_blk_count = 65535;
host = mmc_priv(mmc);
host->base = ioremap(r->start, resource_size(r));
if (!host->base) {
ret = -ENOMEM;
goto out;
}
host->mmc = mmc;
host->dma_allocated = 0;
host->pdata = pdev->dev.platform_data;
if (!host->pdata)
dev_warn(&pdev->dev, "No platform data provided!\n");
spin_lock_init(&host->lock);
host->res = r;
host->irq = irq;
host->clk = clk_get(&pdev->dev, "perclk2");
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
goto out;
}
clk_enable(host->clk);
imx_gpio_mode(PB8_PF_SD_DAT0);
imx_gpio_mode(PB9_PF_SD_DAT1);
imx_gpio_mode(PB10_PF_SD_DAT2);
/* Configured as GPIO with pull-up to ensure right MCC card mode */
/* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */
imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
/* imx_gpio_mode(PB11_PF_SD_DAT3); */
imx_gpio_mode(PB12_PF_SD_CLK);
imx_gpio_mode(PB13_PF_SD_CMD);
imxmci_softreset(host);
rev_no = readw(host->base + MMC_REG_REV_NO);
if (rev_no != 0x390) {
dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
readw(host->base + MMC_REG_REV_NO));
goto out;
}
/* recommended in data sheet */
writew(0x2db4, host->base + MMC_REG_READ_TO);
host->imask = IMXMCI_INT_MASK_DEFAULT;
writew(host->imask, host->base + MMC_REG_INT_MASK);
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
if(host->dma < 0) {
dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");
ret = -EBUSY;
goto out;
}
host->dma_allocated = 1;
imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
RSSR(host->dma) = DMA_REQ_SDHC;
tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
host->status_reg=0;
host->pending_events=0;
ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host);
if (ret)
goto out;
if (host->pdata && host->pdata->card_present)
host->present = host->pdata->card_present(mmc_dev(mmc));
else /* if there is no way to detect assume that card is present */
host->present = 1;
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
host->timer.function = imxmci_check_status;
add_timer(&host->timer);
mod_timer(&host->timer, jiffies + (HZ >> 1));
platform_set_drvdata(pdev, mmc);
mmc_add_host(mmc);
return 0;
out:
if (host) {
if (host->dma_allocated) {
imx_dma_free(host->dma);
host->dma_allocated = 0;
}
if (host->clk) {
clk_disable(host->clk);
clk_put(host->clk);
}
if (host->base)
iounmap(host->base);
}
if (mmc)
mmc_free_host(mmc);
release_mem_region(r->start, resource_size(r));
return ret;
}
static int __exit imxmci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (mmc) {
struct imxmci_host *host = mmc_priv(mmc);
tasklet_disable(&host->tasklet);
del_timer_sync(&host->timer);
mmc_remove_host(mmc);
free_irq(host->irq, host);
iounmap(host->base);
if (host->dma_allocated) {
imx_dma_free(host->dma);
host->dma_allocated = 0;
}
tasklet_kill(&host->tasklet);
clk_disable(host->clk);
clk_put(host->clk);
release_mem_region(host->res->start, resource_size(host->res));
mmc_free_host(mmc);
}
return 0;
}
#ifdef CONFIG_PM
static int imxmci_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc);
return ret;
}
static int imxmci_resume(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
struct imxmci_host *host;
int ret = 0;
if (mmc) {
host = mmc_priv(mmc);
if (host)
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
ret = mmc_resume_host(mmc);
}
return ret;
}
#else
#define imxmci_suspend NULL
#define imxmci_resume NULL
#endif /* CONFIG_PM */
static struct platform_driver imxmci_driver = {
.remove = __exit_p(imxmci_remove),
.suspend = imxmci_suspend,
.resume = imxmci_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int __init imxmci_init(void)
{
return platform_driver_probe(&imxmci_driver, imxmci_probe);
}
static void __exit imxmci_exit(void)
{
platform_driver_unregister(&imxmci_driver);
}
module_init(imxmci_init);
module_exit(imxmci_exit);
MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:imx-mmc");
#define MMC_REG_STR_STP_CLK 0x00
#define MMC_REG_STATUS 0x04
#define MMC_REG_CLK_RATE 0x08
#define MMC_REG_CMD_DAT_CONT 0x0C
#define MMC_REG_RES_TO 0x10
#define MMC_REG_READ_TO 0x14
#define MMC_REG_BLK_LEN 0x18
#define MMC_REG_NOB 0x1C
#define MMC_REG_REV_NO 0x20
#define MMC_REG_INT_MASK 0x24
#define MMC_REG_CMD 0x28
#define MMC_REG_ARGH 0x2C
#define MMC_REG_ARGL 0x30
#define MMC_REG_RES_FIFO 0x34
#define MMC_REG_BUFFER_ACCESS 0x38
#define STR_STP_CLK_IPG_CLK_GATE_DIS (1<<15)
#define STR_STP_CLK_IPG_PERCLK_GATE_DIS (1<<14)
#define STR_STP_CLK_ENDIAN (1<<5)
#define STR_STP_CLK_RESET (1<<3)
#define STR_STP_CLK_ENABLE (1<<2)
#define STR_STP_CLK_START_CLK (1<<1)
#define STR_STP_CLK_STOP_CLK (1<<0)
#define STATUS_CARD_PRESENCE (1<<15)
#define STATUS_SDIO_INT_ACTIVE (1<<14)
#define STATUS_END_CMD_RESP (1<<13)
#define STATUS_WRITE_OP_DONE (1<<12)
#define STATUS_DATA_TRANS_DONE (1<<11)
#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10)
#define STATUS_CARD_BUS_CLK_RUN (1<<8)
#define STATUS_APPL_BUFF_FF (1<<7)
#define STATUS_APPL_BUFF_FE (1<<6)
#define STATUS_RESP_CRC_ERR (1<<5)
#define STATUS_CRC_READ_ERR (1<<3)
#define STATUS_CRC_WRITE_ERR (1<<2)
#define STATUS_TIME_OUT_RESP (1<<1)
#define STATUS_TIME_OUT_READ (1<<0)
#define STATUS_ERR_MASK 0x2f
#define CLK_RATE_PRESCALER(x) ((x) & 0x7)
#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3)
#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12)
#define CMD_DAT_CONT_STOP_READWAIT (1<<11)
#define CMD_DAT_CONT_START_READWAIT (1<<10)
#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8)
#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8)
#define CMD_DAT_CONT_INIT (1<<7)
#define CMD_DAT_CONT_BUSY (1<<6)
#define CMD_DAT_CONT_STREAM_BLOCK (1<<5)
#define CMD_DAT_CONT_WRITE (1<<4)
#define CMD_DAT_CONT_DATA_ENABLE (1<<3)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
#define INT_MASK_AUTO_CARD_DETECT (1<<6)
#define INT_MASK_DAT0_EN (1<<5)
#define INT_MASK_SDIO (1<<4)
#define INT_MASK_BUF_READY (1<<3)
#define INT_MASK_END_CMD_RES (1<<2)
#define INT_MASK_WRITE_OP_DONE (1<<1)
#define INT_MASK_DATA_TRAN (1<<0)
#define INT_ALL (0x7f)
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/highmem.h> #include <linux/highmem.h>
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
...@@ -1207,21 +1209,76 @@ static const struct mmc_host_ops mmci_ops = { ...@@ -1207,21 +1209,76 @@ static const struct mmc_host_ops mmci_ops = {
.get_cd = mmci_get_cd, .get_cd = mmci_get_cd,
}; };
#ifdef CONFIG_OF
static void mmci_dt_populate_generic_pdata(struct device_node *np,
struct mmci_platform_data *pdata)
{
int bus_width = 0;
pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
if (!pdata->gpio_wp)
pdata->gpio_wp = -1;
pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
if (!pdata->gpio_cd)
pdata->gpio_cd = -1;
if (of_get_property(np, "cd-inverted", NULL))
pdata->cd_invert = true;
else
pdata->cd_invert = false;
of_property_read_u32(np, "max-frequency", &pdata->f_max);
if (!pdata->f_max)
pr_warn("%s has no 'max-frequency' property\n", np->full_name);
if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
pdata->capabilities |= MMC_CAP_MMC_HIGHSPEED;
if (of_get_property(np, "mmc-cap-sd-highspeed", NULL))
pdata->capabilities |= MMC_CAP_SD_HIGHSPEED;
of_property_read_u32(np, "bus-width", &bus_width);
switch (bus_width) {
case 0 :
/* No bus-width supplied. */
break;
case 4 :
pdata->capabilities |= MMC_CAP_4_BIT_DATA;
break;
case 8 :
pdata->capabilities |= MMC_CAP_8_BIT_DATA;
break;
default :
pr_warn("%s: Unsupported bus width\n", np->full_name);
}
}
#else
static void mmci_dt_populate_generic_pdata(struct device_node *np,
struct mmci_platform_data *pdata)
{
return;
}
#endif
static int __devinit mmci_probe(struct amba_device *dev, static int __devinit mmci_probe(struct amba_device *dev,
const struct amba_id *id) const struct amba_id *id)
{ {
struct mmci_platform_data *plat = dev->dev.platform_data; struct mmci_platform_data *plat = dev->dev.platform_data;
struct device_node *np = dev->dev.of_node;
struct variant_data *variant = id->data; struct variant_data *variant = id->data;
struct mmci_host *host; struct mmci_host *host;
struct mmc_host *mmc; struct mmc_host *mmc;
int ret; int ret;
/* must have platform data */ /* Must have platform data or Device Tree. */
if (!plat) { if (!plat && !np) {
ret = -EINVAL; dev_err(&dev->dev, "No plat data or DT found\n");
goto out; return -EINVAL;
} }
if (np)
mmci_dt_populate_generic_pdata(np, plat);
ret = amba_request_regions(dev, DRIVER_NAME); ret = amba_request_regions(dev, DRIVER_NAME);
if (ret) if (ret)
goto out; goto out;
......
...@@ -169,11 +169,11 @@ struct mmc_omap_host { ...@@ -169,11 +169,11 @@ struct mmc_omap_host {
struct timer_list clk_timer; struct timer_list clk_timer;
spinlock_t clk_lock; /* for changing enabled state */ spinlock_t clk_lock; /* for changing enabled state */
unsigned int fclk_enabled:1; unsigned int fclk_enabled:1;
struct workqueue_struct *mmc_omap_wq;
struct omap_mmc_platform_data *pdata; struct omap_mmc_platform_data *pdata;
}; };
static struct workqueue_struct *mmc_omap_wq;
static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot) static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
{ {
...@@ -291,7 +291,7 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) ...@@ -291,7 +291,7 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
host->next_slot = new_slot; host->next_slot = new_slot;
host->mmc = new_slot->mmc; host->mmc = new_slot->mmc;
spin_unlock_irqrestore(&host->slot_lock, flags); spin_unlock_irqrestore(&host->slot_lock, flags);
queue_work(mmc_omap_wq, &host->slot_release_work); queue_work(host->mmc_omap_wq, &host->slot_release_work);
return; return;
} }
...@@ -459,7 +459,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) ...@@ -459,7 +459,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
} }
host->stop_data = data; host->stop_data = data;
queue_work(mmc_omap_wq, &host->send_stop_work); queue_work(host->mmc_omap_wq, &host->send_stop_work);
} }
static void static void
...@@ -639,7 +639,7 @@ mmc_omap_cmd_timer(unsigned long data) ...@@ -639,7 +639,7 @@ mmc_omap_cmd_timer(unsigned long data)
OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, IE, 0);
disable_irq(host->irq); disable_irq(host->irq);
host->abort = 1; host->abort = 1;
queue_work(mmc_omap_wq, &host->cmd_abort_work); queue_work(host->mmc_omap_wq, &host->cmd_abort_work);
} }
spin_unlock_irqrestore(&host->slot_lock, flags); spin_unlock_irqrestore(&host->slot_lock, flags);
} }
...@@ -828,7 +828,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -828,7 +828,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
host->abort = 1; host->abort = 1;
OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, IE, 0);
disable_irq_nosync(host->irq); disable_irq_nosync(host->irq);
queue_work(mmc_omap_wq, &host->cmd_abort_work); queue_work(host->mmc_omap_wq, &host->cmd_abort_work);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1389,13 +1389,13 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) ...@@ -1389,13 +1389,13 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
tasklet_kill(&slot->cover_tasklet); tasklet_kill(&slot->cover_tasklet);
del_timer_sync(&slot->cover_timer); del_timer_sync(&slot->cover_timer);
flush_workqueue(mmc_omap_wq); flush_workqueue(slot->host->mmc_omap_wq);
mmc_remove_host(mmc); mmc_remove_host(mmc);
mmc_free_host(mmc); mmc_free_host(mmc);
} }
static int __init mmc_omap_probe(struct platform_device *pdev) static int __devinit mmc_omap_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;
struct mmc_omap_host *host = NULL; struct mmc_omap_host *host = NULL;
...@@ -1497,6 +1497,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev) ...@@ -1497,6 +1497,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->reg_shift = (cpu_is_omap7xx() ? 1 : 2); host->reg_shift = (cpu_is_omap7xx() ? 1 : 2);
host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
if (!host->mmc_omap_wq)
goto err_plat_cleanup;
return 0; return 0;
err_plat_cleanup: err_plat_cleanup:
...@@ -1518,7 +1522,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev) ...@@ -1518,7 +1522,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
return ret; return ret;
} }
static int mmc_omap_remove(struct platform_device *pdev) static int __devexit mmc_omap_remove(struct platform_device *pdev)
{ {
struct mmc_omap_host *host = platform_get_drvdata(pdev); struct mmc_omap_host *host = platform_get_drvdata(pdev);
int i; int i;
...@@ -1542,6 +1546,7 @@ static int mmc_omap_remove(struct platform_device *pdev) ...@@ -1542,6 +1546,7 @@ static int mmc_omap_remove(struct platform_device *pdev)
iounmap(host->virt_base); iounmap(host->virt_base);
release_mem_region(pdev->resource[0].start, release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1); pdev->resource[0].end - pdev->resource[0].start + 1);
destroy_workqueue(host->mmc_omap_wq);
kfree(host); kfree(host);
...@@ -1599,7 +1604,8 @@ static int mmc_omap_resume(struct platform_device *pdev) ...@@ -1599,7 +1604,8 @@ static int mmc_omap_resume(struct platform_device *pdev)
#endif #endif
static struct platform_driver mmc_omap_driver = { static struct platform_driver mmc_omap_driver = {
.remove = mmc_omap_remove, .probe = mmc_omap_probe,
.remove = __devexit_p(mmc_omap_remove),
.suspend = mmc_omap_suspend, .suspend = mmc_omap_suspend,
.resume = mmc_omap_resume, .resume = mmc_omap_resume,
.driver = { .driver = {
...@@ -1608,29 +1614,7 @@ static struct platform_driver mmc_omap_driver = { ...@@ -1608,29 +1614,7 @@ static struct platform_driver mmc_omap_driver = {
}, },
}; };
static int __init mmc_omap_init(void) module_platform_driver(mmc_omap_driver);
{
int ret;
mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
if (!mmc_omap_wq)
return -ENOMEM;
ret = platform_driver_probe(&mmc_omap_driver, mmc_omap_probe);
if (ret)
destroy_workqueue(mmc_omap_wq);
return ret;
}
static void __exit mmc_omap_exit(void)
{
platform_driver_unregister(&mmc_omap_driver);
destroy_workqueue(mmc_omap_wq);
}
module_init(mmc_omap_init);
module_exit(mmc_omap_exit);
MODULE_DESCRIPTION("OMAP Multimedia Card driver"); MODULE_DESCRIPTION("OMAP Multimedia Card driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_ALIAS("platform:" DRIVER_NAME);
......
...@@ -85,12 +85,14 @@ ...@@ -85,12 +85,14 @@
#define BRR_ENABLE (1 << 5) #define BRR_ENABLE (1 << 5)
#define DTO_ENABLE (1 << 20) #define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1) #define INIT_STREAM (1 << 1)
#define ACEN_ACMD12 (1 << 2)
#define DP_SELECT (1 << 21) #define DP_SELECT (1 << 21)
#define DDIR (1 << 4) #define DDIR (1 << 4)
#define DMA_EN 0x1 #define DMA_EN 0x1
#define MSBS (1 << 5) #define MSBS (1 << 5)
#define BCE (1 << 1) #define BCE (1 << 1)
#define FOUR_BIT (1 << 1) #define FOUR_BIT (1 << 1)
#define DDR (1 << 19)
#define DW8 (1 << 5) #define DW8 (1 << 5)
#define CC 0x1 #define CC 0x1
#define TC 0x02 #define TC 0x02
...@@ -115,6 +117,7 @@ ...@@ -115,6 +117,7 @@
#define OMAP_MMC_MAX_CLOCK 52000000 #define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc" #define DRIVER_NAME "omap_hsmmc"
#define AUTO_CMD12 (1 << 0) /* Auto CMD12 support */
/* /*
* One controller can have multiple slots, like on some omap boards using * One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known * omap.c controller driver. Luckily this is not currently done on any known
...@@ -167,7 +170,6 @@ struct omap_hsmmc_host { ...@@ -167,7 +170,6 @@ struct omap_hsmmc_host {
int use_dma, dma_ch; int use_dma, dma_ch;
int dma_line_tx, dma_line_rx; int dma_line_tx, dma_line_rx;
int slot_id; int slot_id;
int got_dbclk;
int response_busy; int response_busy;
int context_loss; int context_loss;
int vdd; int vdd;
...@@ -175,6 +177,7 @@ struct omap_hsmmc_host { ...@@ -175,6 +177,7 @@ struct omap_hsmmc_host {
int reqs_blocked; int reqs_blocked;
int use_reg; int use_reg;
int req_in_progress; int req_in_progress;
unsigned int flags;
struct omap_hsmmc_next next_data; struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata; struct omap_mmc_platform_data *pdata;
...@@ -520,6 +523,10 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host) ...@@ -520,6 +523,10 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
u32 con; u32 con;
con = OMAP_HSMMC_READ(host->base, CON); con = OMAP_HSMMC_READ(host->base, CON);
if (ios->timing == MMC_TIMING_UHS_DDR50)
con |= DDR; /* configure in DDR mode */
else
con &= ~DDR;
switch (ios->bus_width) { switch (ios->bus_width) {
case MMC_BUS_WIDTH_8: case MMC_BUS_WIDTH_8:
OMAP_HSMMC_WRITE(host->base, CON, con | DW8); OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
...@@ -766,6 +773,8 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, ...@@ -766,6 +773,8 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdtype = 0x3; cmdtype = 0x3;
cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
if ((host->flags & AUTO_CMD12) && mmc_op_multi(cmd->opcode))
cmdreg |= ACEN_ACMD12;
if (data) { if (data) {
cmdreg |= DP_SELECT | MSBS | BCE; cmdreg |= DP_SELECT | MSBS | BCE;
...@@ -796,11 +805,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data) ...@@ -796,11 +805,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq) static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
{ {
int dma_ch; int dma_ch;
unsigned long flags;
spin_lock(&host->irq_lock); spin_lock_irqsave(&host->irq_lock, flags);
host->req_in_progress = 0; host->req_in_progress = 0;
dma_ch = host->dma_ch; dma_ch = host->dma_ch;
spin_unlock(&host->irq_lock); spin_unlock_irqrestore(&host->irq_lock, flags);
omap_hsmmc_disable_irq(host); omap_hsmmc_disable_irq(host);
/* Do not complete the request if DMA is still in progress */ /* Do not complete the request if DMA is still in progress */
...@@ -837,11 +847,14 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) ...@@ -837,11 +847,14 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
else else
data->bytes_xfered = 0; data->bytes_xfered = 0;
if (!data->stop) { if (data->stop && ((!(host->flags & AUTO_CMD12)) || data->error)) {
omap_hsmmc_start_command(host, data->stop, NULL);
} else {
if (data->stop)
data->stop->resp[0] = OMAP_HSMMC_READ(host->base,
RSP76);
omap_hsmmc_request_done(host, data->mrq); omap_hsmmc_request_done(host, data->mrq);
return;
} }
omap_hsmmc_start_command(host, data->stop, NULL);
} }
/* /*
...@@ -874,13 +887,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) ...@@ -874,13 +887,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
{ {
int dma_ch; int dma_ch;
unsigned long flags;
host->data->error = errno; host->data->error = errno;
spin_lock(&host->irq_lock); spin_lock_irqsave(&host->irq_lock, flags);
dma_ch = host->dma_ch; dma_ch = host->dma_ch;
host->dma_ch = -1; host->dma_ch = -1;
spin_unlock(&host->irq_lock); spin_unlock_irqrestore(&host->irq_lock, flags);
if (host->use_dma && dma_ch != -1) { if (host->use_dma && dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
...@@ -1082,7 +1096,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) ...@@ -1082,7 +1096,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
/* Disable the clocks */ /* Disable the clocks */
pm_runtime_put_sync(host->dev); pm_runtime_put_sync(host->dev);
if (host->got_dbclk) if (host->dbclk)
clk_disable(host->dbclk); clk_disable(host->dbclk);
/* Turn the power off */ /* Turn the power off */
...@@ -1093,7 +1107,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) ...@@ -1093,7 +1107,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
vdd); vdd);
pm_runtime_get_sync(host->dev); pm_runtime_get_sync(host->dev);
if (host->got_dbclk) if (host->dbclk)
clk_enable(host->dbclk); clk_enable(host->dbclk);
if (ret != 0) if (ret != 0)
...@@ -1234,6 +1248,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) ...@@ -1234,6 +1248,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
struct omap_hsmmc_host *host = cb_data; struct omap_hsmmc_host *host = cb_data;
struct mmc_data *data; struct mmc_data *data;
int dma_ch, req_in_progress; int dma_ch, req_in_progress;
unsigned long flags;
if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
...@@ -1241,9 +1256,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) ...@@ -1241,9 +1256,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
return; return;
} }
spin_lock(&host->irq_lock); spin_lock_irqsave(&host->irq_lock, flags);
if (host->dma_ch < 0) { if (host->dma_ch < 0) {
spin_unlock(&host->irq_lock); spin_unlock_irqrestore(&host->irq_lock, flags);
return; return;
} }
...@@ -1253,7 +1268,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) ...@@ -1253,7 +1268,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
/* Fire up the next transfer. */ /* Fire up the next transfer. */
omap_hsmmc_config_dma_params(host, data, omap_hsmmc_config_dma_params(host, data,
data->sg + host->dma_sg_idx); data->sg + host->dma_sg_idx);
spin_unlock(&host->irq_lock); spin_unlock_irqrestore(&host->irq_lock, flags);
return; return;
} }
...@@ -1264,7 +1279,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) ...@@ -1264,7 +1279,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
req_in_progress = host->req_in_progress; req_in_progress = host->req_in_progress;
dma_ch = host->dma_ch; dma_ch = host->dma_ch;
host->dma_ch = -1; host->dma_ch = -1;
spin_unlock(&host->irq_lock); spin_unlock_irqrestore(&host->irq_lock, flags);
omap_free_dma(dma_ch); omap_free_dma(dma_ch);
...@@ -1844,6 +1859,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1844,6 +1859,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
host->mapbase = res->start + pdata->reg_offset; host->mapbase = res->start + pdata->reg_offset;
host->base = ioremap(host->mapbase, SZ_4K); host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF; host->power_mode = MMC_POWER_OFF;
host->flags = AUTO_CMD12;
host->next_data.cookie = 1; host->next_data.cookie = 1;
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
...@@ -1885,21 +1901,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1885,21 +1901,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host); omap_hsmmc_context_save(host);
if (cpu_is_omap2430()) { host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); /*
/* * MMC can still work without debounce clock.
* MMC can still work without debounce clock. */
*/ if (IS_ERR(host->dbclk)) {
if (IS_ERR(host->dbclk)) dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
dev_warn(mmc_dev(host->mmc), host->dbclk = NULL;
"Failed to get debounce clock\n"); } else if (clk_enable(host->dbclk) != 0) {
else dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
host->got_dbclk = 1; clk_put(host->dbclk);
host->dbclk = NULL;
if (host->got_dbclk)
if (clk_enable(host->dbclk) != 0)
dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
" clk failed\n");
} }
/* Since we do only SG emulation, we can have as many segs /* Since we do only SG emulation, we can have as many segs
...@@ -1969,7 +1981,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ...@@ -1969,7 +1981,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = request_threaded_irq(mmc_slot(host).card_detect_irq, ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
NULL, NULL,
omap_hsmmc_detect, omap_hsmmc_detect,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
mmc_hostname(mmc), host); mmc_hostname(mmc), host);
if (ret) { if (ret) {
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),
...@@ -2019,7 +2031,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ...@@ -2019,7 +2031,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
pm_runtime_put_sync(host->dev); pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev); pm_runtime_disable(host->dev);
clk_put(host->fclk); clk_put(host->fclk);
if (host->got_dbclk) { if (host->dbclk) {
clk_disable(host->dbclk); clk_disable(host->dbclk);
clk_put(host->dbclk); clk_put(host->dbclk);
} }
...@@ -2030,7 +2042,9 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ...@@ -2030,7 +2042,9 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
err_alloc: err_alloc:
omap_hsmmc_gpio_free(pdata); omap_hsmmc_gpio_free(pdata);
err: err:
release_mem_region(res->start, resource_size(res)); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
return ret; return ret;
} }
...@@ -2052,7 +2066,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev) ...@@ -2052,7 +2066,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
pm_runtime_put_sync(host->dev); pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev); pm_runtime_disable(host->dev);
clk_put(host->fclk); clk_put(host->fclk);
if (host->got_dbclk) { if (host->dbclk) {
clk_disable(host->dbclk); clk_disable(host->dbclk);
clk_put(host->dbclk); clk_put(host->dbclk);
} }
...@@ -2110,7 +2124,7 @@ static int omap_hsmmc_suspend(struct device *dev) ...@@ -2110,7 +2124,7 @@ static int omap_hsmmc_suspend(struct device *dev)
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
} }
if (host->got_dbclk) if (host->dbclk)
clk_disable(host->dbclk); clk_disable(host->dbclk);
err: err:
pm_runtime_put_sync(host->dev); pm_runtime_put_sync(host->dev);
...@@ -2131,7 +2145,7 @@ static int omap_hsmmc_resume(struct device *dev) ...@@ -2131,7 +2145,7 @@ static int omap_hsmmc_resume(struct device *dev)
pm_runtime_get_sync(host->dev); pm_runtime_get_sync(host->dev);
if (host->got_dbclk) if (host->dbclk)
clk_enable(host->dbclk); clk_enable(host->dbclk);
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
......
...@@ -75,8 +75,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -75,8 +75,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
struct spear_sdhci *sdhci; struct spear_sdhci *sdhci;
int ret; int ret;
BUG_ON(pdev == NULL);
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) { if (!iomem) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -84,18 +82,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -84,18 +82,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
goto err; goto err;
} }
if (!request_mem_region(iomem->start, resource_size(iomem), if (!devm_request_mem_region(&pdev->dev, iomem->start,
"spear-sdhci")) { resource_size(iomem), "spear-sdhci")) {
ret = -EBUSY; ret = -EBUSY;
dev_dbg(&pdev->dev, "cannot request region\n"); dev_dbg(&pdev->dev, "cannot request region\n");
goto err; goto err;
} }
sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL); sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
if (!sdhci) { if (!sdhci) {
ret = -ENOMEM; ret = -ENOMEM;
dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
goto err_kzalloc; goto err;
} }
/* clk enable */ /* clk enable */
...@@ -103,13 +101,13 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -103,13 +101,13 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (IS_ERR(sdhci->clk)) { if (IS_ERR(sdhci->clk)) {
ret = PTR_ERR(sdhci->clk); ret = PTR_ERR(sdhci->clk);
dev_dbg(&pdev->dev, "Error getting clock\n"); dev_dbg(&pdev->dev, "Error getting clock\n");
goto err_clk_get; goto err;
} }
ret = clk_enable(sdhci->clk); ret = clk_enable(sdhci->clk);
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n"); dev_dbg(&pdev->dev, "Error enabling clock\n");
goto err_clk_enb; goto put_clk;
} }
/* overwrite platform_data */ /* overwrite platform_data */
...@@ -124,7 +122,7 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -124,7 +122,7 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (IS_ERR(host)) { if (IS_ERR(host)) {
ret = PTR_ERR(host); ret = PTR_ERR(host);
dev_dbg(&pdev->dev, "error allocating host\n"); dev_dbg(&pdev->dev, "error allocating host\n");
goto err_alloc_host; goto disable_clk;
} }
host->hw_name = "sdhci"; host->hw_name = "sdhci";
...@@ -132,17 +130,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -132,17 +130,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
host->quirks = SDHCI_QUIRK_BROKEN_ADMA; host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
host->ioaddr = ioremap(iomem->start, resource_size(iomem)); host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
resource_size(iomem));
if (!host->ioaddr) { if (!host->ioaddr) {
ret = -ENOMEM; ret = -ENOMEM;
dev_dbg(&pdev->dev, "failed to remap registers\n"); dev_dbg(&pdev->dev, "failed to remap registers\n");
goto err_ioremap; goto free_host;
} }
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "error adding host\n"); dev_dbg(&pdev->dev, "error adding host\n");
goto err_add_host; goto free_host;
} }
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
...@@ -161,11 +160,12 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -161,11 +160,12 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (sdhci->data->card_power_gpio >= 0) { if (sdhci->data->card_power_gpio >= 0) {
int val = 0; int val = 0;
ret = gpio_request(sdhci->data->card_power_gpio, "sdhci"); ret = devm_gpio_request(&pdev->dev,
sdhci->data->card_power_gpio, "sdhci");
if (ret < 0) { if (ret < 0) {
dev_dbg(&pdev->dev, "gpio request fail: %d\n", dev_dbg(&pdev->dev, "gpio request fail: %d\n",
sdhci->data->card_power_gpio); sdhci->data->card_power_gpio);
goto err_pgpio_request; goto set_drvdata;
} }
if (sdhci->data->power_always_enb) if (sdhci->data->power_always_enb)
...@@ -177,60 +177,48 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -177,60 +177,48 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
sdhci->data->card_power_gpio); sdhci->data->card_power_gpio);
goto err_pgpio_direction; goto set_drvdata;
} }
} }
if (sdhci->data->card_int_gpio >= 0) { if (sdhci->data->card_int_gpio >= 0) {
ret = gpio_request(sdhci->data->card_int_gpio, "sdhci"); ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
"sdhci");
if (ret < 0) { if (ret < 0) {
dev_dbg(&pdev->dev, "gpio request fail: %d\n", dev_dbg(&pdev->dev, "gpio request fail: %d\n",
sdhci->data->card_int_gpio); sdhci->data->card_int_gpio);
goto err_igpio_request; goto set_drvdata;
} }
ret = gpio_direction_input(sdhci->data->card_int_gpio); ret = gpio_direction_input(sdhci->data->card_int_gpio);
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
sdhci->data->card_int_gpio); sdhci->data->card_int_gpio);
goto err_igpio_direction; goto set_drvdata;
} }
ret = request_irq(gpio_to_irq(sdhci->data->card_int_gpio), ret = devm_request_irq(&pdev->dev,
gpio_to_irq(sdhci->data->card_int_gpio),
sdhci_gpio_irq, IRQF_TRIGGER_LOW, sdhci_gpio_irq, IRQF_TRIGGER_LOW,
mmc_hostname(host->mmc), pdev); mmc_hostname(host->mmc), pdev);
if (ret) { if (ret) {
dev_dbg(&pdev->dev, "gpio request irq fail: %d\n", dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
sdhci->data->card_int_gpio); sdhci->data->card_int_gpio);
goto err_igpio_request_irq; goto set_drvdata;
} }
} }
return 0; return 0;
err_igpio_request_irq: set_drvdata:
err_igpio_direction:
if (sdhci->data->card_int_gpio >= 0)
gpio_free(sdhci->data->card_int_gpio);
err_igpio_request:
err_pgpio_direction:
if (sdhci->data->card_power_gpio >= 0)
gpio_free(sdhci->data->card_power_gpio);
err_pgpio_request:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
sdhci_remove_host(host, 1); sdhci_remove_host(host, 1);
err_add_host: free_host:
iounmap(host->ioaddr);
err_ioremap:
sdhci_free_host(host); sdhci_free_host(host);
err_alloc_host: disable_clk:
clk_disable(sdhci->clk); clk_disable(sdhci->clk);
err_clk_enb: put_clk:
clk_put(sdhci->clk); clk_put(sdhci->clk);
err_clk_get:
kfree(sdhci);
err_kzalloc:
release_mem_region(iomem->start, resource_size(iomem));
err: err:
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
return ret; return ret;
...@@ -239,35 +227,19 @@ static int __devinit sdhci_probe(struct platform_device *pdev) ...@@ -239,35 +227,19 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
static int __devexit sdhci_remove(struct platform_device *pdev) static int __devexit sdhci_remove(struct platform_device *pdev)
{ {
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
int dead; int dead = 0;
u32 scratch; u32 scratch;
if (sdhci->data) {
if (sdhci->data->card_int_gpio >= 0) {
free_irq(gpio_to_irq(sdhci->data->card_int_gpio), pdev);
gpio_free(sdhci->data->card_int_gpio);
}
if (sdhci->data->card_power_gpio >= 0)
gpio_free(sdhci->data->card_power_gpio);
}
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
dead = 0;
scratch = readl(host->ioaddr + SDHCI_INT_STATUS); scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1) if (scratch == (u32)-1)
dead = 1; dead = 1;
sdhci_remove_host(host, dead); sdhci_remove_host(host, dead);
iounmap(host->ioaddr);
sdhci_free_host(host); sdhci_free_host(host);
clk_disable(sdhci->clk); clk_disable(sdhci->clk);
clk_put(sdhci->clk); clk_put(sdhci->clk);
kfree(sdhci);
if (iomem)
release_mem_region(iomem->start, resource_size(iomem));
return 0; return 0;
} }
......
...@@ -32,8 +32,13 @@ ...@@ -32,8 +32,13 @@
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
/* Tegra SDHOST controller vendor register definitions */
#define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
struct sdhci_tegra_soc_data { struct sdhci_tegra_soc_data {
struct sdhci_pltfm_data *pdata; struct sdhci_pltfm_data *pdata;
...@@ -120,6 +125,25 @@ static irqreturn_t carddetect_irq(int irq, void *data) ...@@ -120,6 +125,25 @@ static irqreturn_t carddetect_irq(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
}; };
static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
{
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 (!(mask & SDHCI_RESET_ALL))
return;
/* Erratum: Enable SDHCI spec v3.00 support */
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) {
u32 misc_ctrl;
misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
}
}
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);
...@@ -148,6 +172,7 @@ static struct sdhci_ops tegra_sdhci_ops = { ...@@ -148,6 +172,7 @@ static struct sdhci_ops tegra_sdhci_ops = {
.read_w = tegra_sdhci_readw, .read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel, .write_l = tegra_sdhci_writel,
.platform_8bit_width = tegra_sdhci_8bit, .platform_8bit_width = tegra_sdhci_8bit,
.platform_reset_exit = tegra_sdhci_reset_exit,
}; };
#ifdef CONFIG_ARCH_TEGRA_2x_SOC #ifdef CONFIG_ARCH_TEGRA_2x_SOC
...@@ -178,6 +203,7 @@ static struct sdhci_pltfm_data sdhci_tegra30_pdata = { ...@@ -178,6 +203,7 @@ static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
static struct sdhci_tegra_soc_data soc_data_tegra30 = { static struct sdhci_tegra_soc_data soc_data_tegra30 = {
.pdata = &sdhci_tegra30_pdata, .pdata = &sdhci_tegra30_pdata,
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
}; };
#endif #endif
......
...@@ -680,8 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) ...@@ -680,8 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
} }
if (count >= 0xF) { if (count >= 0xF) {
pr_warning("%s: Too large timeout requested for CMD%d!\n", pr_warning("%s: Too large timeout 0x%x requested for CMD%d!\n",
mmc_hostname(host->mmc), cmd->opcode); mmc_hostname(host->mmc), count, cmd->opcode);
count = 0xE; count = 0xE;
} }
......
...@@ -58,6 +58,10 @@ struct mmc_ext_csd { ...@@ -58,6 +58,10 @@ struct mmc_ext_csd {
unsigned int generic_cmd6_time; /* Units: 10ms */ unsigned int generic_cmd6_time; /* Units: 10ms */
unsigned int power_off_longtime; /* Units: ms */ unsigned int power_off_longtime; /* Units: ms */
unsigned int hs_max_dtr; unsigned int hs_max_dtr;
#define MMC_HIGH_26_MAX_DTR 26000000
#define MMC_HIGH_52_MAX_DTR 52000000
#define MMC_HIGH_DDR_MAX_DTR 52000000
#define MMC_HS200_MAX_DTR 200000000
unsigned int sectors; unsigned int sectors;
unsigned int card_type; unsigned int card_type;
unsigned int hc_erase_size; /* In sectors */ unsigned int hc_erase_size; /* In sectors */
......
...@@ -125,6 +125,7 @@ struct dw_mci { ...@@ -125,6 +125,7 @@ struct dw_mci {
struct mmc_request *mrq; struct mmc_request *mrq;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_data *data; struct mmc_data *data;
struct workqueue_struct *card_workqueue;
/* DMA interface members*/ /* DMA interface members*/
int use_dma; int use_dma;
......
...@@ -297,6 +297,7 @@ struct mmc_host { ...@@ -297,6 +297,7 @@ struct mmc_host {
unsigned int sdio_irqs; unsigned int sdio_irqs;
struct task_struct *sdio_irq_thread; struct task_struct *sdio_irq_thread;
bool sdio_irq_pending;
atomic_t sdio_irq_thread_abort; atomic_t sdio_irq_thread_abort;
mmc_pm_flag_t pm_flags; /* requested pm features */ mmc_pm_flag_t pm_flags; /* requested pm features */
...@@ -352,6 +353,7 @@ extern int mmc_cache_ctrl(struct mmc_host *, u8); ...@@ -352,6 +353,7 @@ extern int mmc_cache_ctrl(struct mmc_host *, u8);
static inline void mmc_signal_sdio_irq(struct mmc_host *host) static inline void mmc_signal_sdio_irq(struct mmc_host *host)
{ {
host->ops->enable_sdio_irq(host, 0); host->ops->enable_sdio_irq(host, 0);
host->sdio_irq_pending = true;
wake_up_process(host->sdio_irq_thread); wake_up_process(host->sdio_irq_thread);
} }
......
...@@ -354,66 +354,6 @@ struct _mmc_csd { ...@@ -354,66 +354,6 @@ struct _mmc_csd {
#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */ #define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */
/* SDR mode @1.2V I/O */ /* SDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V | \
EXT_CSD_CARD_TYPE_SDR_1_2V)
#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200 | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL (EXT_CSD_CARD_TYPE_SDR_1_2V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL (EXT_CSD_CARD_TYPE_SDR_1_8V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_2V | \
EXT_CSD_CARD_TYPE_DDR_1_8V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_8V | \
EXT_CSD_CARD_TYPE_DDR_1_8V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_2V | \
EXT_CSD_CARD_TYPE_DDR_1_2V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_8V | \
EXT_CSD_CARD_TYPE_DDR_1_2V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_2V | \
EXT_CSD_CARD_TYPE_DDR_52 | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_8V | \
EXT_CSD_CARD_TYPE_DDR_52 | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_200 | \
EXT_CSD_CARD_TYPE_DDR_1_8V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_200 | \
EXT_CSD_CARD_TYPE_DDR_1_2V | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 (EXT_CSD_CARD_TYPE_SDR_200 | \
EXT_CSD_CARD_TYPE_DDR_52 | \
EXT_CSD_CARD_TYPE_52 | \
EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
......
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