MMC highlights for 3.5:
Drivers: - 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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPvtlQAAoJEHNBYZ7TNxYMxr8P/0oKBszCCpvYQVH9F7WxWdcs haXcfvQBshKLTw8GxnJhNOuoMpDO565pLCxtL1NigFzA/nnvDrsGu03Rjy8vmeGT vtuda+T+OVOmHwGx868fHMMtp3OeV0cyE2I9WQe0R1M0IW5YFpOCS3zzuaXofMlA dYK6KJC0RlZnc/Usn4esQhXCBS3dH80ynMfORjNtHl2wDnuvp+k2DD2kB2SFmw0H raieXeqcfGTqK9UYWqYYDvFM1D1FZcaokyYIs4Ut8WQtnKWSCWyMEqy5JjC1xbIr YZs+08uIUEcGBnYAuuB6XDcmWfPInqTiStQ6lX6iO6msY8DNh9n2wjt5V30X9GWx fVT8a27qB7gCf7D/ACRbGq+sgRjCL/2de4UcuH/wZyFu585lohinUqpZM6ODz7wA 7AyfPYixpegbYLrppESgzdWzDUFY5HFyhogI9JUemtI1etIvy/tH1n6+l0388Qmy vYYV91U3ommxw4XgyFI2psJrl5y+XveEdv4q0NuqD5nkVrTDQ3cmp/vXXMfIo7fe YliEQuOwV1DAUsvA+FgNKCTYDtHveKIacHVJtQqJcelUOn6Klxs0xgHvsAXgux8U ZDLmAPrrg7O+YmDGsHC0lU8joaXQmtfauqVGm+JSSoQv4iSNI+SqwGaSTlzRURd5 12htXRTx0TTINOAs7vLH =rjPa -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
92bf3d0941
|
@ -0,0 +1,19 @@
|
|||
* 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
|
|||
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,
|
|||
md = mmc_blk_get(bdev->bd_disk);
|
||||
if (!md) {
|
||||
err = -EINVAL;
|
||||
goto cmd_done;
|
||||
goto cmd_err;
|
||||
}
|
||||
|
||||
card = md->queue.card;
|
||||
|
@ -483,6 +483,7 @@ cmd_rel_host:
|
|||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
cmd_err:
|
||||
kfree(idata->buf);
|
||||
kfree(idata);
|
||||
return err;
|
||||
|
@ -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;
|
||||
enum mmc_blk_status status;
|
||||
struct mmc_queue_req *mq_rq;
|
||||
struct request *req;
|
||||
struct request *req = rqc;
|
||||
struct mmc_async_req *areq;
|
||||
|
||||
if (!rqc && !mq->mqrq_prev->req)
|
||||
|
@ -1291,6 +1292,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
|
||||
do {
|
||||
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);
|
||||
areq = &mq->mqrq_cur->mmc_active;
|
||||
} else
|
||||
|
@ -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),
|
||||
"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);
|
||||
|
||||
if (mmc_host_cmd23(card->host)) {
|
||||
|
|
|
@ -96,7 +96,7 @@ static int mmc_queue_thread(void *d)
|
|||
* on any queue on this host, and attempt to issue it. This may
|
||||
* 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 request *req;
|
||||
|
@ -171,12 +171,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
|
|||
limit = *mmc_dev(host)->dma_mask;
|
||||
|
||||
mq->card = card;
|
||||
mq->queue = blk_init_queue(mmc_request, lock);
|
||||
mq->queue = blk_init_queue(mmc_request_fn, lock);
|
||||
if (!mq->queue)
|
||||
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_prev = mqrq_prev;
|
||||
mq->queue->queuedata = mq;
|
||||
|
|
|
@ -122,6 +122,7 @@ static int mmc_bus_remove(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mmc_bus_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
|
@ -143,6 +144,7 @@ static int mmc_bus_resume(struct device *dev)
|
|||
ret = drv->resume(card);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ void mmc_cd_gpio_free(struct mmc_host *host)
|
|||
{
|
||||
struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
|
||||
|
||||
if (!cd)
|
||||
return;
|
||||
|
||||
free_irq(host->hotplug.irq, host);
|
||||
gpio_free(cd->gpio);
|
||||
kfree(cd);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "sdio_ops.h"
|
||||
|
||||
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%)
|
||||
|
@ -1157,6 +1158,9 @@ static void mmc_power_up(struct mmc_host *host)
|
|||
{
|
||||
int bit;
|
||||
|
||||
if (host->ios.power_mode == MMC_POWER_ON)
|
||||
return;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
/* If ocr is set, we use it */
|
||||
|
@ -1199,6 +1203,10 @@ static void mmc_power_up(struct mmc_host *host)
|
|||
void mmc_power_off(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (host->ios.power_mode == MMC_POWER_OFF)
|
||||
return;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
host->ios.clock = 0;
|
||||
|
@ -2005,7 +2013,6 @@ EXPORT_SYMBOL(mmc_detect_card_removed);
|
|||
|
||||
void mmc_rescan(struct work_struct *work)
|
||||
{
|
||||
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
struct mmc_host *host =
|
||||
container_of(work, struct mmc_host, detect.work);
|
||||
int i;
|
||||
|
@ -2044,8 +2051,12 @@ void mmc_rescan(struct work_struct *work)
|
|||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
mmc_claim_host(host);
|
||||
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
|
||||
|
@ -2063,7 +2074,8 @@ void mmc_rescan(struct work_struct *work)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -235,6 +235,36 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
|||
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.
|
||||
*/
|
||||
|
@ -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)
|
||||
mmc_card_set_blockaddr(card);
|
||||
}
|
||||
|
||||
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
||||
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
|
||||
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));
|
||||
}
|
||||
mmc_select_card_type(card);
|
||||
|
||||
card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT];
|
||||
card->ext_csd.raw_erase_timeout_mult =
|
||||
|
@ -533,6 +516,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
} else {
|
||||
card->ext_csd.data_tag_unit_size = 0;
|
||||
}
|
||||
} else {
|
||||
card->ext_csd.data_sector_size = 512;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -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);
|
||||
|
||||
if (err || bw_ext_csd == NULL) {
|
||||
if (bus_width != MMC_BUS_WIDTH_1)
|
||||
err = -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bus_width == MMC_BUS_WIDTH_1)
|
||||
goto out;
|
||||
|
||||
/* only compare read only fields */
|
||||
err = !((card->ext_csd.raw_partition_support ==
|
||||
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
|
||||
|
@ -736,6 +717,10 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -745,7 +730,7 @@ static int mmc_select_powerclass(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;
|
||||
static unsigned ext_csd_bits[] = {
|
||||
EXT_CSD_BUS_WIDTH_4,
|
||||
|
@ -761,10 +746,12 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||
host = card->host;
|
||||
|
||||
if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
|
||||
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_180, 0);
|
||||
host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 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 (err)
|
||||
|
@ -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;
|
||||
err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to bus width %d"
|
||||
" failed\n", mmc_hostname(card->host),
|
||||
1 << bus_width);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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],
|
||||
ext_csd);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to "
|
||||
"bus width %d failed\n",
|
||||
mmc_hostname(card->host),
|
||||
1 << bus_width);
|
||||
goto err;
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
|
@ -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],
|
||||
ext_csd);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to "
|
||||
"bus width %d ddr %d failed\n",
|
||||
mmc_hostname(card->host),
|
||||
1 << bus_width, ddr);
|
||||
goto err;
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
|
|
|
@ -947,7 +947,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
}
|
||||
|
||||
if (!err && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
wake_up_process(host->sdio_irq_thread);
|
||||
mmc_release_host(host);
|
||||
|
||||
/*
|
||||
|
|
|
@ -28,18 +28,20 @@
|
|||
|
||||
#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;
|
||||
unsigned char pending;
|
||||
struct sdio_func *func;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
if (func) {
|
||||
if (func && host->sdio_irq_pending) {
|
||||
func->irq_handler(func);
|
||||
return 1;
|
||||
}
|
||||
|
@ -116,7 +118,8 @@ static int sdio_irq_thread(void *_host)
|
|||
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
|
||||
if (ret)
|
||||
break;
|
||||
ret = process_sdio_pending_irqs(host->card);
|
||||
ret = process_sdio_pending_irqs(host);
|
||||
host->sdio_irq_pending = false;
|
||||
mmc_release_host(host);
|
||||
|
||||
/*
|
||||
|
|
|
@ -278,10 +278,13 @@ choice
|
|||
Choose which driver to use for the Atmel MCI Silicon
|
||||
|
||||
config MMC_AT91
|
||||
tristate "AT91 SD/MMC Card Interface support"
|
||||
tristate "AT91 SD/MMC Card Interface support (DEPRECATED)"
|
||||
depends on ARCH_AT91
|
||||
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.
|
||||
|
||||
|
@ -307,16 +310,6 @@ config MMC_ATMELMCI_DMA
|
|||
|
||||
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
|
||||
tristate "Qualcomm SDCC Controller Support"
|
||||
depends on MMC && ARCH_MSM
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
|
||||
obj-$(CONFIG_MMC_PXA) += pxamci.o
|
||||
obj-$(CONFIG_MMC_IMX) += imxmmc.o
|
||||
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
|
||||
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
|
||||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
|
|
|
@ -45,19 +45,19 @@
|
|||
#define ATMCI_DMA_THRESHOLD 16
|
||||
|
||||
enum {
|
||||
EVENT_CMD_COMPLETE = 0,
|
||||
EVENT_CMD_RDY = 0,
|
||||
EVENT_XFER_COMPLETE,
|
||||
EVENT_DATA_COMPLETE,
|
||||
EVENT_NOTBUSY,
|
||||
EVENT_DATA_ERROR,
|
||||
};
|
||||
|
||||
enum atmel_mci_state {
|
||||
STATE_IDLE = 0,
|
||||
STATE_SENDING_CMD,
|
||||
STATE_SENDING_DATA,
|
||||
STATE_DATA_BUSY,
|
||||
STATE_DATA_XFER,
|
||||
STATE_WAITING_NOTBUSY,
|
||||
STATE_SENDING_STOP,
|
||||
STATE_DATA_ERROR,
|
||||
STATE_END_REQUEST,
|
||||
};
|
||||
|
||||
enum atmci_xfer_dir {
|
||||
|
@ -78,6 +78,9 @@ struct atmel_mci_caps {
|
|||
bool has_highspeed;
|
||||
bool has_rwproof;
|
||||
bool has_odd_clk_div;
|
||||
bool has_bad_data_ordering;
|
||||
bool need_reset_after_xfer;
|
||||
bool need_blksz_mul_4;
|
||||
};
|
||||
|
||||
struct atmel_mci_dma {
|
||||
|
@ -91,6 +94,11 @@ struct atmel_mci_dma {
|
|||
* @regs: Pointer to MMIO registers.
|
||||
* @sg: Scatterlist entry currently being processed by PIO or PDC code.
|
||||
* @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.
|
||||
* @mrq: The request currently being processed on @cur_slot,
|
||||
* or NULL if the controller is idle.
|
||||
|
@ -116,6 +124,7 @@ struct atmel_mci_dma {
|
|||
* @queue: List of slots waiting for access to the controller.
|
||||
* @need_clock_update: Update the clock rate before the 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.
|
||||
* @cfg_reg: Value of the CFG register.
|
||||
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
|
||||
|
@ -166,6 +175,9 @@ struct atmel_mci {
|
|||
|
||||
struct scatterlist *sg;
|
||||
unsigned int pio_offset;
|
||||
unsigned int *buffer;
|
||||
unsigned int buf_size;
|
||||
dma_addr_t buf_phys_addr;
|
||||
|
||||
struct atmel_mci_slot *cur_slot;
|
||||
struct mmc_request *mrq;
|
||||
|
@ -189,6 +201,7 @@ struct atmel_mci {
|
|||
|
||||
bool need_clock_update;
|
||||
bool need_reset;
|
||||
struct timer_list timer;
|
||||
u32 mode_reg;
|
||||
u32 cfg_reg;
|
||||
unsigned long bus_hz;
|
||||
|
@ -480,6 +493,32 @@ err:
|
|||
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,
|
||||
unsigned int ns)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
dev_dbg(&host->pdev->dev, "send stop command\n");
|
||||
atmci_send_command(host, data->stop, host->stop_cmdr);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
u32 pointer_reg, counter_reg;
|
||||
unsigned int buf_size;
|
||||
|
||||
if (dir == XFER_RECEIVE) {
|
||||
pointer_reg = ATMEL_PDC_RPR;
|
||||
|
@ -617,8 +658,15 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host,
|
|||
counter_reg += ATMEL_PDC_SCND_BUF_OFF;
|
||||
}
|
||||
|
||||
atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
|
||||
if (host->data_size <= sg_dma_len(host->sg)) {
|
||||
if (!host->caps.has_rwproof) {
|
||||
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 size is different from modulo 4, transfer bytes */
|
||||
atmci_writel(host, counter_reg, host->data_size);
|
||||
|
@ -670,7 +718,20 @@ static void atmci_pdc_cleanup(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);
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
|
@ -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.
|
||||
*/
|
||||
if (host->data) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -716,6 +778,8 @@ static void atmci_dma_complete(void *arg)
|
|||
* to send the stop command or waiting for NBUSY in this case.
|
||||
*/
|
||||
if (data) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
|
||||
|
@ -791,6 +855,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
|||
u32 iflags, tmp;
|
||||
unsigned int sg_len;
|
||||
enum dma_data_direction dir;
|
||||
int i;
|
||||
|
||||
data->error = -EINPROGRESS;
|
||||
|
||||
|
@ -806,7 +871,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
|||
iflags |= ATMCI_ENDRX | ATMCI_RXBUFF;
|
||||
} else {
|
||||
dir = DMA_TO_DEVICE;
|
||||
iflags |= ATMCI_ENDTX | ATMCI_TXBUFE;
|
||||
iflags |= ATMCI_ENDTX | ATMCI_TXBUFE | ATMCI_BLKE;
|
||||
}
|
||||
|
||||
/* Set BLKLEN */
|
||||
|
@ -818,6 +883,16 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
|||
/* Configure PDC */
|
||||
host->data_size = data->blocks * data->blksz;
|
||||
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)
|
||||
atmci_pdc_set_both_buf(host,
|
||||
((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT));
|
||||
|
@ -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)
|
||||
{
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
}
|
||||
|
@ -940,8 +1017,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
|
|||
*/
|
||||
static void atmci_stop_transfer_pdc(struct atmel_mci *host)
|
||||
{
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
/* 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_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
}
|
||||
|
@ -977,9 +1055,12 @@ static void atmci_start_request(struct atmel_mci *host,
|
|||
|
||||
host->pending_events = 0;
|
||||
host->completed_events = 0;
|
||||
host->cmd_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_SDIOIRQA | ATMCI_SDIOIRQB);
|
||||
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
||||
|
@ -994,7 +1075,7 @@ static void atmci_start_request(struct atmel_mci *host,
|
|||
|
||||
iflags = atmci_readl(host, ATMCI_IMR);
|
||||
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);
|
||||
|
||||
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,
|
|||
* prepared yet.)
|
||||
*/
|
||||
atmci_writel(host, ATMCI_IER, iflags);
|
||||
|
||||
mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
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;
|
||||
atmci_start_request(host, slot);
|
||||
} else {
|
||||
dev_dbg(&host->pdev->dev, "queue request\n");
|
||||
list_add_tail(&slot->queue_node, &host->queue);
|
||||
}
|
||||
spin_unlock_bh(&host->lock);
|
||||
|
@ -1069,6 +1153,7 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
struct mmc_data *data;
|
||||
|
||||
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
|
||||
|
@ -1308,6 +1393,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
|||
host->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
del_timer(&host->timer);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
mmc_request_done(prev_mmc, mrq);
|
||||
spin_lock(&host->lock);
|
||||
|
@ -1330,21 +1417,13 @@ static void atmci_command_complete(struct atmel_mci *host,
|
|||
cmd->error = -EILSEQ;
|
||||
else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
|
||||
cmd->error = -EIO;
|
||||
else
|
||||
cmd->error = 0;
|
||||
|
||||
if (cmd->error) {
|
||||
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 if (host->mrq->data && (host->mrq->data->blksz & 3)) {
|
||||
if (host->caps.need_blksz_mul_4) {
|
||||
cmd->error = -EINVAL;
|
||||
host->need_reset = 1;
|
||||
}
|
||||
}
|
||||
} else
|
||||
cmd->error = 0;
|
||||
}
|
||||
|
||||
static void atmci_detect_change(unsigned long data)
|
||||
|
@ -1407,23 +1486,21 @@ static void atmci_detect_change(unsigned long data)
|
|||
break;
|
||||
case STATE_SENDING_CMD:
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
if (!mrq->data)
|
||||
break;
|
||||
/* fall through */
|
||||
case STATE_SENDING_DATA:
|
||||
if (mrq->data)
|
||||
host->stop_transfer(host);
|
||||
break;
|
||||
case STATE_DATA_XFER:
|
||||
mrq->data->error = -ENOMEDIUM;
|
||||
host->stop_transfer(host);
|
||||
break;
|
||||
case STATE_DATA_BUSY:
|
||||
case STATE_DATA_ERROR:
|
||||
if (mrq->data->error == -EINPROGRESS)
|
||||
mrq->data->error = -ENOMEDIUM;
|
||||
if (!mrq->stop)
|
||||
break;
|
||||
/* fall through */
|
||||
case STATE_WAITING_NOTBUSY:
|
||||
mrq->data->error = -ENOMEDIUM;
|
||||
break;
|
||||
case STATE_SENDING_STOP:
|
||||
mrq->stop->error = -ENOMEDIUM;
|
||||
break;
|
||||
case STATE_END_REQUEST:
|
||||
break;
|
||||
}
|
||||
|
||||
atmci_request_end(host, mrq);
|
||||
|
@ -1451,7 +1528,6 @@ static void atmci_tasklet_func(unsigned long priv)
|
|||
struct atmel_mci *host = (struct atmel_mci *)priv;
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mmc_data *data = host->data;
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
enum atmel_mci_state state = host->state;
|
||||
enum atmel_mci_state prev_state;
|
||||
u32 status;
|
||||
|
@ -1467,107 +1543,186 @@ static void atmci_tasklet_func(unsigned long priv)
|
|||
|
||||
do {
|
||||
prev_state = state;
|
||||
dev_dbg(&host->pdev->dev, "FSM: state=%d\n", state);
|
||||
|
||||
switch (state) {
|
||||
case STATE_IDLE:
|
||||
break;
|
||||
|
||||
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,
|
||||
EVENT_CMD_COMPLETE))
|
||||
EVENT_CMD_RDY))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev, "set completed cmd ready\n");
|
||||
host->cmd = NULL;
|
||||
atmci_set_completed(host, EVENT_CMD_COMPLETE);
|
||||
atmci_set_completed(host, EVENT_CMD_RDY);
|
||||
atmci_command_complete(host, mrq->cmd);
|
||||
if (!mrq->data || cmd->error) {
|
||||
atmci_request_end(host, host->mrq);
|
||||
goto unlock;
|
||||
}
|
||||
if (mrq->data) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"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;
|
||||
/* fall through */
|
||||
break;
|
||||
|
||||
case STATE_SENDING_DATA:
|
||||
case STATE_DATA_XFER:
|
||||
if (atmci_test_and_clear_pending(host,
|
||||
EVENT_DATA_ERROR)) {
|
||||
host->stop_transfer(host);
|
||||
if (data->stop)
|
||||
atmci_send_stop_cmd(host, data);
|
||||
state = STATE_DATA_ERROR;
|
||||
dev_dbg(&host->pdev->dev, "set completed data error\n");
|
||||
atmci_set_completed(host, EVENT_DATA_ERROR);
|
||||
state = STATE_END_REQUEST;
|
||||
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,
|
||||
EVENT_XFER_COMPLETE))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set completed xfer complete\n",
|
||||
__func__);
|
||||
atmci_set_completed(host, EVENT_XFER_COMPLETE);
|
||||
prev_state = state = STATE_DATA_BUSY;
|
||||
/* fall through */
|
||||
|
||||
case STATE_DATA_BUSY:
|
||||
if (!atmci_test_and_clear_pending(host,
|
||||
EVENT_DATA_COMPLETE))
|
||||
break;
|
||||
|
||||
host->data = NULL;
|
||||
atmci_set_completed(host, EVENT_DATA_COMPLETE);
|
||||
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;
|
||||
}
|
||||
if (host->data->flags & MMC_DATA_WRITE) {
|
||||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
state = STATE_WAITING_NOTBUSY;
|
||||
} else 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;
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS);
|
||||
state = STATE_END_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
if (!data->stop) {
|
||||
atmci_request_end(host, host->mrq);
|
||||
goto unlock;
|
||||
}
|
||||
case STATE_WAITING_NOTBUSY:
|
||||
/*
|
||||
* 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;
|
||||
if (!data->error)
|
||||
atmci_send_stop_cmd(host, data);
|
||||
/* fall through */
|
||||
dev_dbg(&host->pdev->dev, "set completed not busy\n");
|
||||
atmci_set_completed(host, EVENT_NOTBUSY);
|
||||
|
||||
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:
|
||||
/*
|
||||
* 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,
|
||||
EVENT_CMD_COMPLETE))
|
||||
EVENT_CMD_RDY))
|
||||
break;
|
||||
|
||||
dev_dbg(&host->pdev->dev, "FSM: cmd ready\n");
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
data->bytes_xfered = data->blocks * data->blksz;
|
||||
data->error = 0;
|
||||
atmci_command_complete(host, mrq->stop);
|
||||
if (mrq->stop->error) {
|
||||
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_END_REQUEST:
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_TXRDY | ATMCI_RXRDY
|
||||
| ATMCI_DATA_ERROR_FLAGS);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
atmci_request_end(host, host->mrq);
|
||||
goto unlock;
|
||||
|
||||
case STATE_DATA_ERROR:
|
||||
if (!atmci_test_and_clear_pending(host,
|
||||
EVENT_XFER_COMPLETE))
|
||||
break;
|
||||
|
||||
state = STATE_DATA_BUSY;
|
||||
state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
} while (state != prev_state);
|
||||
|
||||
host->state = state;
|
||||
|
||||
unlock:
|
||||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
|
@ -1620,9 +1775,6 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
|||
| ATMCI_DATA_ERROR_FLAGS));
|
||||
host->data_status = status;
|
||||
data->bytes_xfered += nbytes;
|
||||
smp_wmb();
|
||||
atmci_set_pending(host, EVENT_DATA_ERROR);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
return;
|
||||
}
|
||||
} while (status & ATMCI_RXRDY);
|
||||
|
@ -1691,9 +1843,6 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
|||
| ATMCI_DATA_ERROR_FLAGS));
|
||||
host->data_status = status;
|
||||
data->bytes_xfered += nbytes;
|
||||
smp_wmb();
|
||||
atmci_set_pending(host, EVENT_DATA_ERROR);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
return;
|
||||
}
|
||||
} while (status & ATMCI_TXRDY);
|
||||
|
@ -1711,16 +1860,6 @@ done:
|
|||
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)
|
||||
{
|
||||
int i;
|
||||
|
@ -1748,17 +1887,21 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
|||
break;
|
||||
|
||||
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_RXRDY | ATMCI_TXRDY);
|
||||
pending &= atmci_readl(host, ATMCI_IMR);
|
||||
| ATMCI_RXRDY | ATMCI_TXRDY
|
||||
| ATMCI_ENDRX | ATMCI_ENDTX
|
||||
| ATMCI_RXBUFF | ATMCI_TXBUFE);
|
||||
|
||||
host->data_status = status;
|
||||
dev_dbg(&host->pdev->dev, "set pending data error\n");
|
||||
smp_wmb();
|
||||
atmci_set_pending(host, EVENT_DATA_ERROR);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
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_ENDTX);
|
||||
/*
|
||||
|
@ -1774,6 +1917,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
|||
atmci_pdc_complete(host);
|
||||
}
|
||||
} else if (pending & ATMCI_ENDTX) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: end of tx buffer\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
|
||||
|
||||
if (host->data_size) {
|
||||
|
@ -1784,6 +1928,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
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_ENDRX);
|
||||
/*
|
||||
|
@ -1799,6 +1944,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
|||
atmci_pdc_complete(host);
|
||||
}
|
||||
} else if (pending & ATMCI_ENDRX) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: end of rx buffer\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
|
||||
|
||||
if (host->data_size) {
|
||||
|
@ -1808,23 +1954,44 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (pending & ATMCI_NOTBUSY) {
|
||||
atmci_writel(host, ATMCI_IDR,
|
||||
ATMCI_DATA_ERROR_FLAGS | ATMCI_NOTBUSY);
|
||||
if (!host->data_status)
|
||||
host->data_status = status;
|
||||
/*
|
||||
* 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();
|
||||
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);
|
||||
}
|
||||
|
||||
if (pending & ATMCI_NOTBUSY) {
|
||||
dev_dbg(&host->pdev->dev, "IRQ: not_busy\n");
|
||||
atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY);
|
||||
smp_wmb();
|
||||
dev_dbg(&host->pdev->dev, "set pending notbusy\n");
|
||||
atmci_set_pending(host, EVENT_NOTBUSY);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
if (pending & ATMCI_RXRDY)
|
||||
atmci_read_data_pio(host);
|
||||
if (pending & ATMCI_TXRDY)
|
||||
atmci_write_data_pio(host);
|
||||
|
||||
if (pending & ATMCI_CMDRDY)
|
||||
atmci_cmd_interrupt(host, status);
|
||||
if (pending & ATMCI_CMDRDY) {
|
||||
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))
|
||||
atmci_sdio_interrupt(host, status);
|
||||
|
@ -1877,13 +2044,26 @@ static int __init atmci_init_slot(struct atmel_mci *host,
|
|||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||
if (host->caps.has_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->max_segs = 64;
|
||||
mmc->max_req_size = 32768 * 512;
|
||||
mmc->max_blk_size = 32768;
|
||||
mmc->max_blk_count = 512;
|
||||
if (atmci_get_version(host) < 0x200) {
|
||||
mmc->max_segs = 256;
|
||||
mmc->max_blk_size = 4095;
|
||||
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 */
|
||||
set_bit(ATMCI_CARD_PRESENT, &slot->flags);
|
||||
|
@ -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 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)
|
|||
host->caps.has_highspeed = 0;
|
||||
host->caps.has_rwproof = 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 */
|
||||
switch (version & 0xf00) {
|
||||
|
@ -2051,7 +2229,11 @@ static void __init atmci_get_cap(struct atmel_mci *host)
|
|||
host->caps.has_highspeed = 1;
|
||||
case 0x200:
|
||||
host->caps.has_rwproof = 1;
|
||||
host->caps.need_blksz_mul_4 = 0;
|
||||
case 0x100:
|
||||
host->caps.has_bad_data_ordering = 0;
|
||||
host->caps.need_reset_after_xfer = 0;
|
||||
case 0x0:
|
||||
break;
|
||||
default:
|
||||
host->caps.has_pdc = 0;
|
||||
|
@ -2138,14 +2320,20 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|||
if (pdata->slot[0].bus_width) {
|
||||
ret = atmci_init_slot(host, &pdata->slot[0],
|
||||
0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
nr_slots++;
|
||||
host->buf_size = host->slot[0]->mmc->max_req_size;
|
||||
}
|
||||
}
|
||||
if (pdata->slot[1].bus_width) {
|
||||
ret = atmci_init_slot(host, &pdata->slot[1],
|
||||
1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
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) {
|
||||
|
@ -2153,6 +2341,19 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|||
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,
|
||||
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
|
||||
host->mapbase, irq, nr_slots);
|
||||
|
@ -2179,6 +2380,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
|||
|
||||
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++) {
|
||||
if (host->slot[i])
|
||||
atmci_cleanup_slot(host->slot[i], i);
|
||||
|
|
|
@ -1533,4 +1533,5 @@ module_exit(davinci_mmcsd_exit);
|
|||
MODULE_AUTHOR("Texas Instruments India");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller");
|
||||
MODULE_ALIAS("platform:davinci_mmc");
|
||||
|
||||
|
|
|
@ -100,8 +100,6 @@ struct dw_mci_slot {
|
|||
int last_detect_state;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *dw_mci_card_workqueue;
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
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)
|
|||
int_mask = mci_readl(host, INTMASK);
|
||||
if (enb) {
|
||||
mci_writel(host, INTMASK,
|
||||
(int_mask | (1 << SDMMC_INT_SDIO(slot->id))));
|
||||
(int_mask | SDMMC_INT_SDIO(slot->id)));
|
||||
} else {
|
||||
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)
|
|||
|
||||
if (pending & 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 */
|
||||
|
@ -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
|
||||
* 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;
|
||||
}
|
||||
|
@ -2021,9 +2019,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
mci_writel(host, CLKSRC, 0);
|
||||
|
||||
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);
|
||||
if (!dw_mci_card_workqueue)
|
||||
if (!host->card_workqueue)
|
||||
goto err_dmaunmap;
|
||||
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
|
||||
ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
|
||||
|
@ -2085,7 +2083,7 @@ err_init_slot:
|
|||
free_irq(host->irq, host);
|
||||
|
||||
err_workqueue:
|
||||
destroy_workqueue(dw_mci_card_workqueue);
|
||||
destroy_workqueue(host->card_workqueue);
|
||||
|
||||
err_dmaunmap:
|
||||
if (host->use_dma && host->dma_ops->exit)
|
||||
|
@ -2119,7 +2117,7 @@ void dw_mci_remove(struct dw_mci *host)
|
|||
mci_writel(host, CLKSRC, 0);
|
||||
|
||||
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);
|
||||
|
||||
if (host->use_dma && host->dma_ops->exit)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,64 +0,0 @@
|
|||
#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 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
@ -1207,21 +1209,76 @@ static const struct mmc_host_ops mmci_ops = {
|
|||
.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,
|
||||
const struct amba_id *id)
|
||||
{
|
||||
struct mmci_platform_data *plat = dev->dev.platform_data;
|
||||
struct device_node *np = dev->dev.of_node;
|
||||
struct variant_data *variant = id->data;
|
||||
struct mmci_host *host;
|
||||
struct mmc_host *mmc;
|
||||
int ret;
|
||||
|
||||
/* must have platform data */
|
||||
if (!plat) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
/* Must have platform data or Device Tree. */
|
||||
if (!plat && !np) {
|
||||
dev_err(&dev->dev, "No plat data or DT found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (np)
|
||||
mmci_dt_populate_generic_pdata(np, plat);
|
||||
|
||||
ret = amba_request_regions(dev, DRIVER_NAME);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
|
@ -169,11 +169,11 @@ struct mmc_omap_host {
|
|||
struct timer_list clk_timer;
|
||||
spinlock_t clk_lock; /* for changing enabled state */
|
||||
unsigned int fclk_enabled:1;
|
||||
struct workqueue_struct *mmc_omap_wq;
|
||||
|
||||
struct omap_mmc_platform_data *pdata;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *mmc_omap_wq;
|
||||
|
||||
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)
|
|||
host->next_slot = new_slot;
|
||||
host->mmc = new_slot->mmc;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -459,7 +459,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_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
|
||||
|
@ -639,7 +639,7 @@ mmc_omap_cmd_timer(unsigned long data)
|
|||
OMAP_MMC_WRITE(host, IE, 0);
|
||||
disable_irq(host->irq);
|
||||
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);
|
||||
}
|
||||
|
@ -828,7 +828,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
|
|||
host->abort = 1;
|
||||
OMAP_MMC_WRITE(host, IE, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1389,13 +1389,13 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
|
|||
|
||||
tasklet_kill(&slot->cover_tasklet);
|
||||
del_timer_sync(&slot->cover_timer);
|
||||
flush_workqueue(mmc_omap_wq);
|
||||
flush_workqueue(slot->host->mmc_omap_wq);
|
||||
|
||||
mmc_remove_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 mmc_omap_host *host = NULL;
|
||||
|
@ -1497,6 +1497,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
|
|||
|
||||
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;
|
||||
|
||||
err_plat_cleanup:
|
||||
|
@ -1518,7 +1522,7 @@ err_free_mem_region:
|
|||
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);
|
||||
int i;
|
||||
|
@ -1542,6 +1546,7 @@ static int mmc_omap_remove(struct platform_device *pdev)
|
|||
iounmap(host->virt_base);
|
||||
release_mem_region(pdev->resource[0].start,
|
||||
pdev->resource[0].end - pdev->resource[0].start + 1);
|
||||
destroy_workqueue(host->mmc_omap_wq);
|
||||
|
||||
kfree(host);
|
||||
|
||||
|
@ -1599,7 +1604,8 @@ static int mmc_omap_resume(struct platform_device *pdev)
|
|||
#endif
|
||||
|
||||
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,
|
||||
.resume = mmc_omap_resume,
|
||||
.driver = {
|
||||
|
@ -1608,29 +1614,7 @@ static struct platform_driver mmc_omap_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init mmc_omap_init(void)
|
||||
{
|
||||
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_platform_driver(mmc_omap_driver);
|
||||
MODULE_DESCRIPTION("OMAP Multimedia Card driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
|
|
@ -85,12 +85,14 @@
|
|||
#define BRR_ENABLE (1 << 5)
|
||||
#define DTO_ENABLE (1 << 20)
|
||||
#define INIT_STREAM (1 << 1)
|
||||
#define ACEN_ACMD12 (1 << 2)
|
||||
#define DP_SELECT (1 << 21)
|
||||
#define DDIR (1 << 4)
|
||||
#define DMA_EN 0x1
|
||||
#define MSBS (1 << 5)
|
||||
#define BCE (1 << 1)
|
||||
#define FOUR_BIT (1 << 1)
|
||||
#define DDR (1 << 19)
|
||||
#define DW8 (1 << 5)
|
||||
#define CC 0x1
|
||||
#define TC 0x02
|
||||
|
@ -115,6 +117,7 @@
|
|||
#define OMAP_MMC_MAX_CLOCK 52000000
|
||||
#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
|
||||
* omap.c controller driver. Luckily this is not currently done on any known
|
||||
|
@ -167,7 +170,6 @@ struct omap_hsmmc_host {
|
|||
int use_dma, dma_ch;
|
||||
int dma_line_tx, dma_line_rx;
|
||||
int slot_id;
|
||||
int got_dbclk;
|
||||
int response_busy;
|
||||
int context_loss;
|
||||
int vdd;
|
||||
|
@ -175,6 +177,7 @@ struct omap_hsmmc_host {
|
|||
int reqs_blocked;
|
||||
int use_reg;
|
||||
int req_in_progress;
|
||||
unsigned int flags;
|
||||
struct omap_hsmmc_next next_data;
|
||||
|
||||
struct omap_mmc_platform_data *pdata;
|
||||
|
@ -520,6 +523,10 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
|
|||
u32 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) {
|
||||
case MMC_BUS_WIDTH_8:
|
||||
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,
|
|||
cmdtype = 0x3;
|
||||
|
||||
cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
|
||||
if ((host->flags & AUTO_CMD12) && mmc_op_multi(cmd->opcode))
|
||||
cmdreg |= ACEN_ACMD12;
|
||||
|
||||
if (data) {
|
||||
cmdreg |= DP_SELECT | MSBS | BCE;
|
||||
|
@ -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)
|
||||
{
|
||||
int dma_ch;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&host->irq_lock);
|
||||
spin_lock_irqsave(&host->irq_lock, flags);
|
||||
host->req_in_progress = 0;
|
||||
dma_ch = host->dma_ch;
|
||||
spin_unlock(&host->irq_lock);
|
||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||
|
||||
omap_hsmmc_disable_irq(host);
|
||||
/* 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)
|
|||
else
|
||||
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);
|
||||
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)
|
|||
static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
|
||||
{
|
||||
int dma_ch;
|
||||
unsigned long flags;
|
||||
|
||||
host->data->error = errno;
|
||||
|
||||
spin_lock(&host->irq_lock);
|
||||
spin_lock_irqsave(&host->irq_lock, flags);
|
||||
dma_ch = host->dma_ch;
|
||||
host->dma_ch = -1;
|
||||
spin_unlock(&host->irq_lock);
|
||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||
|
||||
if (host->use_dma && dma_ch != -1) {
|
||||
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)
|
|||
|
||||
/* Disable the clocks */
|
||||
pm_runtime_put_sync(host->dev);
|
||||
if (host->got_dbclk)
|
||||
if (host->dbclk)
|
||||
clk_disable(host->dbclk);
|
||||
|
||||
/* Turn the power off */
|
||||
|
@ -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,
|
||||
vdd);
|
||||
pm_runtime_get_sync(host->dev);
|
||||
if (host->got_dbclk)
|
||||
if (host->dbclk)
|
||||
clk_enable(host->dbclk);
|
||||
|
||||
if (ret != 0)
|
||||
|
@ -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 mmc_data *data;
|
||||
int dma_ch, req_in_progress;
|
||||
unsigned long flags;
|
||||
|
||||
if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
|
||||
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)
|
|||
return;
|
||||
}
|
||||
|
||||
spin_lock(&host->irq_lock);
|
||||
spin_lock_irqsave(&host->irq_lock, flags);
|
||||
if (host->dma_ch < 0) {
|
||||
spin_unlock(&host->irq_lock);
|
||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1268,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|||
/* Fire up the next transfer. */
|
||||
omap_hsmmc_config_dma_params(host, data,
|
||||
data->sg + host->dma_sg_idx);
|
||||
spin_unlock(&host->irq_lock);
|
||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
dma_ch = host->dma_ch;
|
||||
host->dma_ch = -1;
|
||||
spin_unlock(&host->irq_lock);
|
||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||
|
||||
omap_free_dma(dma_ch);
|
||||
|
||||
|
@ -1844,6 +1859,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
host->mapbase = res->start + pdata->reg_offset;
|
||||
host->base = ioremap(host->mapbase, SZ_4K);
|
||||
host->power_mode = MMC_POWER_OFF;
|
||||
host->flags = AUTO_CMD12;
|
||||
host->next_data.cookie = 1;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
@ -1885,21 +1901,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
omap_hsmmc_context_save(host);
|
||||
|
||||
if (cpu_is_omap2430()) {
|
||||
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
|
||||
/*
|
||||
* MMC can still work without debounce clock.
|
||||
*/
|
||||
if (IS_ERR(host->dbclk))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"Failed to get debounce clock\n");
|
||||
else
|
||||
host->got_dbclk = 1;
|
||||
|
||||
if (host->got_dbclk)
|
||||
if (clk_enable(host->dbclk) != 0)
|
||||
dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
|
||||
" clk failed\n");
|
||||
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
|
||||
/*
|
||||
* MMC can still work without debounce clock.
|
||||
*/
|
||||
if (IS_ERR(host->dbclk)) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
|
||||
host->dbclk = NULL;
|
||||
} else if (clk_enable(host->dbclk) != 0) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
|
||||
clk_put(host->dbclk);
|
||||
host->dbclk = NULL;
|
||||
}
|
||||
|
||||
/* 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)
|
|||
ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
|
||||
NULL,
|
||||
omap_hsmmc_detect,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
|
@ -2019,7 +2031,7 @@ err_irq:
|
|||
pm_runtime_put_sync(host->dev);
|
||||
pm_runtime_disable(host->dev);
|
||||
clk_put(host->fclk);
|
||||
if (host->got_dbclk) {
|
||||
if (host->dbclk) {
|
||||
clk_disable(host->dbclk);
|
||||
clk_put(host->dbclk);
|
||||
}
|
||||
|
@ -2030,7 +2042,9 @@ err1:
|
|||
err_alloc:
|
||||
omap_hsmmc_gpio_free(pdata);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2052,7 +2066,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
|
|||
pm_runtime_put_sync(host->dev);
|
||||
pm_runtime_disable(host->dev);
|
||||
clk_put(host->fclk);
|
||||
if (host->got_dbclk) {
|
||||
if (host->dbclk) {
|
||||
clk_disable(host->dbclk);
|
||||
clk_put(host->dbclk);
|
||||
}
|
||||
|
@ -2110,7 +2124,7 @@ static int omap_hsmmc_suspend(struct device *dev)
|
|||
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
|
||||
}
|
||||
|
||||
if (host->got_dbclk)
|
||||
if (host->dbclk)
|
||||
clk_disable(host->dbclk);
|
||||
err:
|
||||
pm_runtime_put_sync(host->dev);
|
||||
|
@ -2131,7 +2145,7 @@ static int omap_hsmmc_resume(struct device *dev)
|
|||
|
||||
pm_runtime_get_sync(host->dev);
|
||||
|
||||
if (host->got_dbclk)
|
||||
if (host->dbclk)
|
||||
clk_enable(host->dbclk);
|
||||
|
||||
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
|
||||
|
|
|
@ -75,8 +75,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
struct spear_sdhci *sdhci;
|
||||
int ret;
|
||||
|
||||
BUG_ON(pdev == NULL);
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iomem) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -84,18 +82,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (!request_mem_region(iomem->start, resource_size(iomem),
|
||||
"spear-sdhci")) {
|
||||
if (!devm_request_mem_region(&pdev->dev, iomem->start,
|
||||
resource_size(iomem), "spear-sdhci")) {
|
||||
ret = -EBUSY;
|
||||
dev_dbg(&pdev->dev, "cannot request region\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL);
|
||||
sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
|
||||
if (!sdhci) {
|
||||
ret = -ENOMEM;
|
||||
dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
|
||||
goto err_kzalloc;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* clk enable */
|
||||
|
@ -103,13 +101,13 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(sdhci->clk)) {
|
||||
ret = PTR_ERR(sdhci->clk);
|
||||
dev_dbg(&pdev->dev, "Error getting clock\n");
|
||||
goto err_clk_get;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = clk_enable(sdhci->clk);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "Error enabling clock\n");
|
||||
goto err_clk_enb;
|
||||
goto put_clk;
|
||||
}
|
||||
|
||||
/* overwrite platform_data */
|
||||
|
@ -124,7 +122,7 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
dev_dbg(&pdev->dev, "error allocating host\n");
|
||||
goto err_alloc_host;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
host->hw_name = "sdhci";
|
||||
|
@ -132,17 +130,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
host->irq = platform_get_irq(pdev, 0);
|
||||
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) {
|
||||
ret = -ENOMEM;
|
||||
dev_dbg(&pdev->dev, "failed to remap registers\n");
|
||||
goto err_ioremap;
|
||||
goto free_host;
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "error adding host\n");
|
||||
goto err_add_host;
|
||||
goto free_host;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
@ -161,11 +160,12 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
if (sdhci->data->card_power_gpio >= 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) {
|
||||
dev_dbg(&pdev->dev, "gpio request fail: %d\n",
|
||||
sdhci->data->card_power_gpio);
|
||||
goto err_pgpio_request;
|
||||
goto set_drvdata;
|
||||
}
|
||||
|
||||
if (sdhci->data->power_always_enb)
|
||||
|
@ -177,60 +177,48 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
|
||||
sdhci->data->card_power_gpio);
|
||||
goto err_pgpio_direction;
|
||||
goto set_drvdata;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
dev_dbg(&pdev->dev, "gpio request fail: %d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
goto err_igpio_request;
|
||||
goto set_drvdata;
|
||||
}
|
||||
|
||||
ret = gpio_direction_input(sdhci->data->card_int_gpio);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
|
||||
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,
|
||||
mmc_hostname(host->mmc), pdev);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
|
||||
sdhci->data->card_int_gpio);
|
||||
goto err_igpio_request_irq;
|
||||
goto set_drvdata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_igpio_request_irq:
|
||||
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:
|
||||
set_drvdata:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sdhci_remove_host(host, 1);
|
||||
err_add_host:
|
||||
iounmap(host->ioaddr);
|
||||
err_ioremap:
|
||||
free_host:
|
||||
sdhci_free_host(host);
|
||||
err_alloc_host:
|
||||
disable_clk:
|
||||
clk_disable(sdhci->clk);
|
||||
err_clk_enb:
|
||||
put_clk:
|
||||
clk_put(sdhci->clk);
|
||||
err_clk_get:
|
||||
kfree(sdhci);
|
||||
err_kzalloc:
|
||||
release_mem_region(iomem->start, resource_size(iomem));
|
||||
err:
|
||||
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
|
||||
return ret;
|
||||
|
@ -239,35 +227,19 @@ err:
|
|||
static int __devexit sdhci_remove(struct platform_device *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);
|
||||
int dead;
|
||||
int dead = 0;
|
||||
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);
|
||||
dead = 0;
|
||||
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
|
||||
if (scratch == (u32)-1)
|
||||
dead = 1;
|
||||
|
||||
sdhci_remove_host(host, dead);
|
||||
iounmap(host->ioaddr);
|
||||
sdhci_free_host(host);
|
||||
clk_disable(sdhci->clk);
|
||||
clk_put(sdhci->clk);
|
||||
kfree(sdhci);
|
||||
if (iomem)
|
||||
release_mem_region(iomem->start, resource_size(iomem));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -32,8 +32,13 @@
|
|||
|
||||
#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_ENABLE_BLOCK_GAP_DET BIT(1)
|
||||
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
struct sdhci_pltfm_data *pdata;
|
||||
|
@ -120,6 +125,25 @@ static irqreturn_t carddetect_irq(int irq, void *data)
|
|||
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)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -148,6 +172,7 @@ static struct sdhci_ops tegra_sdhci_ops = {
|
|||
.read_w = tegra_sdhci_readw,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.platform_8bit_width = tegra_sdhci_8bit,
|
||||
.platform_reset_exit = tegra_sdhci_reset_exit,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
|
@ -178,6 +203,7 @@ static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
|
|||
|
||||
static struct sdhci_tegra_soc_data soc_data_tegra30 = {
|
||||
.pdata = &sdhci_tegra30_pdata,
|
||||
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -680,8 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
}
|
||||
|
||||
if (count >= 0xF) {
|
||||
pr_warning("%s: Too large timeout requested for CMD%d!\n",
|
||||
mmc_hostname(host->mmc), cmd->opcode);
|
||||
pr_warning("%s: Too large timeout 0x%x requested for CMD%d!\n",
|
||||
mmc_hostname(host->mmc), count, cmd->opcode);
|
||||
count = 0xE;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,10 @@ struct mmc_ext_csd {
|
|||
unsigned int generic_cmd6_time; /* Units: 10ms */
|
||||
unsigned int power_off_longtime; /* Units: ms */
|
||||
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 card_type;
|
||||
unsigned int hc_erase_size; /* In sectors */
|
||||
|
|
|
@ -125,6 +125,7 @@ struct dw_mci {
|
|||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
struct workqueue_struct *card_workqueue;
|
||||
|
||||
/* DMA interface members*/
|
||||
int use_dma;
|
||||
|
|
|
@ -297,6 +297,7 @@ struct mmc_host {
|
|||
|
||||
unsigned int sdio_irqs;
|
||||
struct task_struct *sdio_irq_thread;
|
||||
bool sdio_irq_pending;
|
||||
atomic_t sdio_irq_thread_abort;
|
||||
|
||||
mmc_pm_flag_t pm_flags; /* requested pm features */
|
||||
|
@ -352,6 +353,7 @@ extern int mmc_cache_ctrl(struct mmc_host *, u8);
|
|||
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
||||
{
|
||||
host->ops->enable_sdio_irq(host, 0);
|
||||
host->sdio_irq_pending = true;
|
||||
wake_up_process(host->sdio_irq_thread);
|
||||
}
|
||||
|
||||
|
|
|
@ -354,66 +354,6 @@ struct _mmc_csd {
|
|||
#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */
|
||||
/* 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_4 1 /* Card is in 4 bit mode */
|
||||
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
|
||||
|
|
Loading…
Reference in New Issue