Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (53 commits)
  mmc: dw_mmc: support mmc power control with regulator
  mmc: dw_mmc: fix suspend/resume operation
  mmc: dw_mmc: add quirks for unreliable card detect, and capabilities
  mmc: tmio: fix address in kunmap_atomic() calls
  mmc: core: reset card voltage after power off
  mmc: core: export function mmc_do_release_host()
  mmc: sdio: remember new card RCA when redetecting card
  mmc: dw_mmc: Remove set-but-unused variable.
  mmc: sdhci-esdhc-imx: add card detect on custom GPIO for mx25/35
  mmc: sdhci-esdhc: broken card detection is not a default quirk
  mmc: sdhci-esdhc-imx: add write protect on custom GPIO on mx25/35
  mmc: msm_sdcc: remove needless cache flush after dma_unmap_sg()
  mmc: sh_mmcif: support aggressive clock gating
  mmc: check if mmc cards < 2GB do sector addressing
  mmc: core: comment on why sdio_reset is done at init time
  mmc: dw_mmc: support DDR mode
  mmc: via-sdmmc: Remove set-but-unused variable.
  mmc: cb710: Return err value in cb710_wait_while_busy()
  mmc: sdhci-pci: Remove set-but-unused variable.
  mmc: mxs-mmc: add mmc host driver for i.MX23/28
  ...
This commit is contained in:
Linus Torvalds
2011-03-18 22:32:40 -07:00
36 changed files with 1812 additions and 260 deletions
@@ -0,0 +1,21 @@
What: /sys/devices/.../mmc_host/mmcX/mmcX:XXXX/enhanced_area_offset
Date: January 2011
Contact: Chuanxiao Dong <chuanxiao.dong@intel.com>
Description:
Enhanced area is a new feature defined in eMMC4.4 standard.
eMMC4.4 or later card can support such feature. This kind of
area can help to improve the card performance. If the feature
is enabled, this attribute will indicate the start address of
enhanced data area. If not, this attribute will be -EINVAL.
Unit Byte. Format decimal.
What: /sys/devices/.../mmc_host/mmcX/mmcX:XXXX/enhanced_area_size
Date: January 2011
Contact: Chuanxiao Dong <chuanxiao.dong@intel.com>
Description:
Enhanced area is a new feature defined in eMMC4.4 standard.
eMMC4.4 or later card can support such feature. This kind of
area can help to improve the card performance. If the feature
is enabled, this attribute will indicate the size of enhanced
data area. If not, this attribute will be -EINVAL.
Unit KByte. Format decimal.
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
*
* 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.
*/
#ifndef __MACH_MXS_MMC_H__
#define __MACH_MXS_MMC_H__
struct mxs_mmc_platform_data {
int wp_gpio; /* write protect pin */
unsigned int flags;
#define SLOTF_4_BIT_CAPABLE (1 << 0)
#define SLOTF_8_BIT_CAPABLE (1 << 1)
};
#endif /* __MACH_MXS_MMC_H__ */
+11 -1
View File
@@ -10,7 +10,17 @@
#ifndef __ASM_ARCH_IMX_ESDHC_H
#define __ASM_ARCH_IMX_ESDHC_H
/**
* struct esdhc_platform_data - optional platform data for esdhc on i.MX
*
* strongly recommended for i.MX25/35, not needed for other variants
*
* @wp_gpio: gpio for write_protect (-EINVAL if unused)
* @cd_gpio: gpio for card_detect interrupt (-EINVAL if unused)
*/
struct esdhc_platform_data {
unsigned int wp_gpio; /* write protect pin */
unsigned int wp_gpio;
unsigned int cd_gpio;
};
#endif /* __ASM_ARCH_IMX_ESDHC_H */
+1 -2
View File
@@ -58,12 +58,11 @@ config SDIO_UART
config MMC_TEST
tristate "MMC host test driver"
default n
help
Development driver that performs a series of reads and writes
to a memory card in order to expose certain well known bugs
in host controllers. The tests are executed by writing to the
"test" file in sysfs under each card. Note that whatever is
"test" file in debugfs under each card. Note that whatever is
on your card will be overwritten by these tests.
This driver is only of interest to those developing or
+1
View File
@@ -621,6 +621,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
md->disk->private_data = md;
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = &card->dev;
set_disk_ro(md->disk, md->read_only);
/*
* As discussed on lkml, GENHD_FL_REMOVABLE should:
+245 -26
View File
@@ -88,6 +88,7 @@ struct mmc_test_area {
* @sectors: amount of sectors to check in one group
* @ts: time values of transfer
* @rate: calculated transfer rate
* @iops: I/O operations per second (times 100)
*/
struct mmc_test_transfer_result {
struct list_head link;
@@ -95,6 +96,7 @@ struct mmc_test_transfer_result {
unsigned int sectors;
struct timespec ts;
unsigned int rate;
unsigned int iops;
};
/**
@@ -226,9 +228,10 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
if (!busy && mmc_test_busy(&cmd)) {
busy = 1;
printk(KERN_INFO "%s: Warning: Host did not "
"wait for busy state to end.\n",
mmc_hostname(test->card->host));
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
printk(KERN_INFO "%s: Warning: Host did not "
"wait for busy state to end.\n",
mmc_hostname(test->card->host));
}
} while (mmc_test_busy(&cmd));
@@ -494,7 +497,7 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts)
*/
static void mmc_test_save_transfer_result(struct mmc_test_card *test,
unsigned int count, unsigned int sectors, struct timespec ts,
unsigned int rate)
unsigned int rate, unsigned int iops)
{
struct mmc_test_transfer_result *tr;
@@ -509,6 +512,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test,
tr->sectors = sectors;
tr->ts = ts;
tr->rate = rate;
tr->iops = iops;
list_add_tail(&tr->link, &test->gr->tr_lst);
}
@@ -519,20 +523,22 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test,
static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
struct timespec *ts1, struct timespec *ts2)
{
unsigned int rate, sectors = bytes >> 9;
unsigned int rate, iops, sectors = bytes >> 9;
struct timespec ts;
ts = timespec_sub(*ts2, *ts1);
rate = mmc_test_rate(bytes, &ts);
iops = mmc_test_rate(100, &ts); /* I/O ops per sec x 100 */
printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
"seconds (%u kB/s, %u KiB/s)\n",
"seconds (%u kB/s, %u KiB/s, %u.%02u IOPS)\n",
mmc_hostname(test->card->host), sectors, sectors >> 1,
(sectors & 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
(unsigned long)ts.tv_nsec, rate / 1000, rate / 1024);
(unsigned long)ts.tv_nsec, rate / 1000, rate / 1024,
iops / 100, iops % 100);
mmc_test_save_transfer_result(test, 1, sectors, ts, rate);
mmc_test_save_transfer_result(test, 1, sectors, ts, rate, iops);
}
/*
@@ -542,22 +548,24 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
unsigned int count, struct timespec *ts1,
struct timespec *ts2)
{
unsigned int rate, sectors = bytes >> 9;
unsigned int rate, iops, sectors = bytes >> 9;
uint64_t tot = bytes * count;
struct timespec ts;
ts = timespec_sub(*ts2, *ts1);
rate = mmc_test_rate(tot, &ts);
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
"%lu.%09lu seconds (%u kB/s, %u KiB/s)\n",
"%lu.%09lu seconds (%u kB/s, %u KiB/s, "
"%u.%02u IOPS)\n",
mmc_hostname(test->card->host), count, sectors, count,
sectors >> 1, (sectors & 1 ? ".5" : ""),
(unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec,
rate / 1000, rate / 1024);
rate / 1000, rate / 1024, iops / 100, iops % 100);
mmc_test_save_transfer_result(test, count, sectors, ts, rate);
mmc_test_save_transfer_result(test, count, sectors, ts, rate, iops);
}
/*
@@ -1425,28 +1433,29 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test)
}
/*
* Initialize an area for testing large transfers. The size of the area is the
* preferred erase size which is a good size for optimal transfer speed. Note
* that is typically 4MiB for modern cards. The test area is set to the middle
* of the card because cards may have different charateristics at the front
* (for FAT file system optimization). Optionally, the area is erased (if the
* card supports it) which may improve write performance. Optionally, the area
* is filled with data for subsequent read tests.
* Initialize an area for testing large transfers. The test area is set to the
* middle of the card because cards may have different charateristics at the
* front (for FAT file system optimization). Optionally, the area is erased
* (if the card supports it) which may improve write performance. Optionally,
* the area is filled with data for subsequent read tests.
*/
static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
{
struct mmc_test_area *t = &test->area;
unsigned long min_sz = 64 * 1024;
unsigned long min_sz = 64 * 1024, sz;
int ret;
ret = mmc_test_set_blksize(test, 512);
if (ret)
return ret;
if (test->card->pref_erase > TEST_AREA_MAX_SIZE >> 9)
t->max_sz = TEST_AREA_MAX_SIZE;
else
t->max_sz = (unsigned long)test->card->pref_erase << 9;
/* Make the test area size about 4MiB */
sz = (unsigned long)test->card->pref_erase << 9;
t->max_sz = sz;
while (t->max_sz < 4 * 1024 * 1024)
t->max_sz += sz;
while (t->max_sz > TEST_AREA_MAX_SIZE && t->max_sz > sz)
t->max_sz -= sz;
t->max_segs = test->card->host->max_segs;
t->max_seg_sz = test->card->host->max_seg_size;
@@ -1766,6 +1775,188 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
return 0;
}
static unsigned int rnd_next = 1;
static unsigned int mmc_test_rnd_num(unsigned int rnd_cnt)
{
uint64_t r;
rnd_next = rnd_next * 1103515245 + 12345;
r = (rnd_next >> 16) & 0x7fff;
return (r * rnd_cnt) >> 15;
}
static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print,
unsigned long sz)
{
unsigned int dev_addr, cnt, rnd_addr, range1, range2, last_ea = 0, ea;
unsigned int ssz;
struct timespec ts1, ts2, ts;
int ret;
ssz = sz >> 9;
rnd_addr = mmc_test_capacity(test->card) / 4;
range1 = rnd_addr / test->card->pref_erase;
range2 = range1 / ssz;
getnstimeofday(&ts1);
for (cnt = 0; cnt < UINT_MAX; cnt++) {
getnstimeofday(&ts2);
ts = timespec_sub(ts2, ts1);
if (ts.tv_sec >= 10)
break;
ea = mmc_test_rnd_num(range1);
if (ea == last_ea)
ea -= 1;
last_ea = ea;
dev_addr = rnd_addr + test->card->pref_erase * ea +
ssz * mmc_test_rnd_num(range2);
ret = mmc_test_area_io(test, sz, dev_addr, write, 0, 0);
if (ret)
return ret;
}
if (print)
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
return 0;
}
static int mmc_test_random_perf(struct mmc_test_card *test, int write)
{
unsigned int next;
unsigned long sz;
int ret;
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
/*
* When writing, try to get more consistent results by running
* the test twice with exactly the same I/O but outputting the
* results only for the 2nd run.
*/
if (write) {
next = rnd_next;
ret = mmc_test_rnd_perf(test, write, 0, sz);
if (ret)
return ret;
rnd_next = next;
}
ret = mmc_test_rnd_perf(test, write, 1, sz);
if (ret)
return ret;
}
sz = test->area.max_tfr;
if (write) {
next = rnd_next;
ret = mmc_test_rnd_perf(test, write, 0, sz);
if (ret)
return ret;
rnd_next = next;
}
return mmc_test_rnd_perf(test, write, 1, sz);
}
/*
* Random read performance by transfer size.
*/
static int mmc_test_random_read_perf(struct mmc_test_card *test)
{
return mmc_test_random_perf(test, 0);
}
/*
* Random write performance by transfer size.
*/
static int mmc_test_random_write_perf(struct mmc_test_card *test)
{
return mmc_test_random_perf(test, 1);
}
static int mmc_test_seq_perf(struct mmc_test_card *test, int write,
unsigned int tot_sz, int max_scatter)
{
unsigned int dev_addr, i, cnt, sz, ssz;
struct timespec ts1, ts2, ts;
int ret;
sz = test->area.max_tfr;
/*
* In the case of a maximally scattered transfer, the maximum transfer
* size is further limited by using PAGE_SIZE segments.
*/
if (max_scatter) {
struct mmc_test_area *t = &test->area;
unsigned long max_tfr;
if (t->max_seg_sz >= PAGE_SIZE)
max_tfr = t->max_segs * PAGE_SIZE;
else
max_tfr = t->max_segs * t->max_seg_sz;
if (sz > max_tfr)
sz = max_tfr;
}
ssz = sz >> 9;
dev_addr = mmc_test_capacity(test->card) / 4;
if (tot_sz > dev_addr << 9)
tot_sz = dev_addr << 9;
cnt = tot_sz / sz;
dev_addr &= 0xffff0000; /* Round to 64MiB boundary */
getnstimeofday(&ts1);
for (i = 0; i < cnt; i++) {
ret = mmc_test_area_io(test, sz, dev_addr, write,
max_scatter, 0);
if (ret)
return ret;
dev_addr += ssz;
}
getnstimeofday(&ts2);
ts = timespec_sub(ts2, ts1);
mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
return 0;
}
static int mmc_test_large_seq_perf(struct mmc_test_card *test, int write)
{
int ret, i;
for (i = 0; i < 10; i++) {
ret = mmc_test_seq_perf(test, write, 10 * 1024 * 1024, 1);
if (ret)
return ret;
}
for (i = 0; i < 5; i++) {
ret = mmc_test_seq_perf(test, write, 100 * 1024 * 1024, 1);
if (ret)
return ret;
}
for (i = 0; i < 3; i++) {
ret = mmc_test_seq_perf(test, write, 1000 * 1024 * 1024, 1);
if (ret)
return ret;
}
return ret;
}
/*
* Large sequential read performance.
*/
static int mmc_test_large_seq_read_perf(struct mmc_test_card *test)
{
return mmc_test_large_seq_perf(test, 0);
}
/*
* Large sequential write performance.
*/
static int mmc_test_large_seq_write_perf(struct mmc_test_card *test)
{
return mmc_test_large_seq_perf(test, 1);
}
static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (no data verification)",
@@ -2005,6 +2196,34 @@ static const struct mmc_test_case mmc_test_cases[] = {
.cleanup = mmc_test_area_cleanup,
},
{
.name = "Random read performance by transfer size",
.prepare = mmc_test_area_prepare,
.run = mmc_test_random_read_perf,
.cleanup = mmc_test_area_cleanup,
},
{
.name = "Random write performance by transfer size",
.prepare = mmc_test_area_prepare,
.run = mmc_test_random_write_perf,
.cleanup = mmc_test_area_cleanup,
},
{
.name = "Large sequential read into scattered pages",
.prepare = mmc_test_area_prepare,
.run = mmc_test_large_seq_read_perf,
.cleanup = mmc_test_area_cleanup,
},
{
.name = "Large sequential write from scattered pages",
.prepare = mmc_test_area_prepare,
.run = mmc_test_large_seq_write_perf,
.cleanup = mmc_test_area_cleanup,
},
};
static DEFINE_MUTEX(mmc_test_lock);
@@ -2148,11 +2367,11 @@ static int mtf_test_show(struct seq_file *sf, void *data)
seq_printf(sf, "Test %d: %d\n", gr->testcase + 1, gr->result);
list_for_each_entry(tr, &gr->tr_lst, link) {
seq_printf(sf, "%u %d %lu.%09lu %u\n",
seq_printf(sf, "%u %d %lu.%09lu %u %u.%02u\n",
tr->count, tr->sectors,
(unsigned long)tr->ts.tv_sec,
(unsigned long)tr->ts.tv_nsec,
tr->rate);
tr->rate, tr->iops / 100, tr->iops % 100);
}
}
+2 -1
View File
@@ -6,6 +6,7 @@ obj-$(CONFIG_MMC) += mmc_core.o
mmc_core-y := core.o bus.o host.o \
mmc.o mmc_ops.o sd.o sd_ops.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o
sdio_cis.o sdio_io.o sdio_irq.o \
quirks.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
+23 -3
View File
@@ -167,8 +167,6 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
WARN_ON(!host->claimed);
led_trigger_event(host->led, LED_FULL);
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
@@ -194,6 +192,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
}
}
mmc_host_clk_ungate(host);
led_trigger_event(host->led, LED_FULL);
host->ops->request(host, mrq);
}
@@ -528,7 +527,14 @@ int mmc_try_claim_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_try_claim_host);
static void mmc_do_release_host(struct mmc_host *host)
/**
* mmc_do_release_host - release a claimed host
* @host: mmc host to release
*
* If you successfully claimed a host, this function will
* release it again.
*/
void mmc_do_release_host(struct mmc_host *host)
{
unsigned long flags;
@@ -543,6 +549,7 @@ static void mmc_do_release_host(struct mmc_host *host)
wake_up(&host->wq);
}
}
EXPORT_SYMBOL(mmc_do_release_host);
void mmc_host_deeper_disable(struct work_struct *work)
{
@@ -1002,6 +1009,13 @@ static void mmc_power_off(struct mmc_host *host)
{
host->ios.clock = 0;
host->ios.vdd = 0;
/*
* Reset ocr mask to be the highest possible voltage supported for
* this mmc host. This value will be used at next power up.
*/
host->ocr = 1 << (fls(host->ocr_avail) - 1);
if (!mmc_host_is_spi(host)) {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
@@ -1495,6 +1509,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
mmc_hostname(host), __func__, host->f_init);
#endif
mmc_power_up(host);
/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
*/
sdio_reset(host);
mmc_go_idle(host);
+2
View File
@@ -61,6 +61,8 @@ int mmc_attach_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host);
int mmc_attach_sdio(struct mmc_host *host);
void mmc_fixup_device(struct mmc_card *card);
/* Module parameters */
extern int use_spi_crc;
+1 -4
View File
@@ -160,10 +160,7 @@ static bool mmc_host_may_gate_card(struct mmc_card *card)
* gate the clock, because there is somebody out there that may still
* be using it.
*/
if (mmc_card_sdio(card))
return false;
return true;
return !(card->quirks & MMC_QUIRK_BROKEN_CLK_GATING);
}
/**
+85 -1
View File
@@ -302,6 +302,44 @@ static int mmc_read_ext_csd(struct mmc_card *card)
}
if (card->ext_csd.rev >= 4) {
/*
* Enhanced area feature support -- check whether the eMMC
* card has the Enhanced area enabled. If so, export enhanced
* area offset and size to user by adding sysfs interface.
*/
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
u8 hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
u8 hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
card->ext_csd.enhanced_area_en = 1;
/*
* calculate the enhanced data area offset, in bytes
*/
card->ext_csd.enhanced_area_offset =
(ext_csd[139] << 24) + (ext_csd[138] << 16) +
(ext_csd[137] << 8) + ext_csd[136];
if (mmc_card_blockaddr(card))
card->ext_csd.enhanced_area_offset <<= 9;
/*
* calculate the enhanced data area size, in kilobytes
*/
card->ext_csd.enhanced_area_size =
(ext_csd[142] << 16) + (ext_csd[141] << 8) +
ext_csd[140];
card->ext_csd.enhanced_area_size *=
(size_t)(hc_erase_grp_sz * hc_wp_grp_sz);
card->ext_csd.enhanced_area_size <<= 9;
} else {
/*
* If the enhanced area is not enabled, disable these
* device attributes.
*/
card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL;
}
card->ext_csd.sec_trim_mult =
ext_csd[EXT_CSD_SEC_TRIM_MULT];
card->ext_csd.sec_erase_mult =
@@ -336,6 +374,9 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
card->ext_csd.enhanced_area_offset);
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
@@ -349,6 +390,8 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
NULL,
};
@@ -378,6 +421,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
int err, ddr = 0;
u32 cid[4];
unsigned int max_dtr;
u32 rocr;
BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -391,7 +435,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
mmc_go_idle(host);
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
if (err)
goto err;
@@ -479,10 +523,50 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_read_ext_csd(card);
if (err)
goto free_card;
/* If doing byte addressing, check if required to do sector
* addressing. Handle the case of <2GB cards needing sector
* addressing. See section 8.1 JEDEC Standard JED84-A441;
* ocr register has bit 30 set for sector addressing.
*/
if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30)))
mmc_card_set_blockaddr(card);
/* Erase size depends on CSD and Extended CSD */
mmc_set_erase_size(card);
}
/*
* If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF
* bit. This bit will be lost everytime after a reset or power off.
*/
if (card->ext_csd.enhanced_area_en) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1);
if (err && err != -EBADMSG)
goto free_card;
if (err) {
err = 0;
/*
* Just disable enhanced area off & sz
* will try to enable ERASE_GROUP_DEF
* during next time reinit
*/
card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL;
} else {
card->ext_csd.erase_group_def = 1;
/*
* enable ERASE_GRP_DEF successfully.
* This will affect the erase size, so
* here need to reset erase size
*/
mmc_set_erase_size(card);
}
}
/*
* Activate high speed (if supported)
*/
+84
View File
@@ -0,0 +1,84 @@
/*
* This file contains work-arounds for many known sdio hardware
* bugs.
*
* Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
* Inspired from pci fixup code:
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mmc/card.h>
#include <linux/mod_devicetable.h>
/*
* The world is not perfect and supplies us with broken mmc/sdio devices.
* For at least a part of these bugs we need a work-around
*/
struct mmc_fixup {
u16 vendor, device; /* You can use SDIO_ANY_ID here of course */
void (*vendor_fixup)(struct mmc_card *card, int data);
int data;
};
/*
* This hook just adds a quirk unconditionnally
*/
static void __maybe_unused add_quirk(struct mmc_card *card, int data)
{
card->quirks |= data;
}
/*
* This hook just removes a quirk unconditionnally
*/
static void __maybe_unused remove_quirk(struct mmc_card *card, int data)
{
card->quirks &= ~data;
}
/*
* This hook just adds a quirk for all sdio devices
*/
static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
{
if (mmc_card_sdio(card))
card->quirks |= data;
}
#ifndef SDIO_VENDOR_ID_TI
#define SDIO_VENDOR_ID_TI 0x0097
#endif
#ifndef SDIO_DEVICE_ID_TI_WL1271
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif
static const struct mmc_fixup mmc_fixup_methods[] = {
/* by default sdio devices are considered CLK_GATING broken */
/* good cards will be whitelisted as they are tested */
{ SDIO_ANY_ID, SDIO_ANY_ID,
add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING },
{ SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING },
{ 0 }
};
void mmc_fixup_device(struct mmc_card *card)
{
const struct mmc_fixup *f;
for (f = mmc_fixup_methods; f->vendor_fixup; f++) {
if ((f->vendor == card->cis.vendor
|| f->vendor == (u16) SDIO_ANY_ID) &&
(f->device == card->cis.device
|| f->device == (u16) SDIO_ANY_ID)) {
dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup);
f->vendor_fixup(card, f->data);
}
}
}
EXPORT_SYMBOL(mmc_fixup_device);
+1
View File
@@ -21,6 +21,7 @@
#include "core.h"
#include "bus.h"
#include "mmc_ops.h"
#include "sd.h"
#include "sd_ops.h"
static const unsigned int tran_exp[] = {
+9
View File
@@ -395,6 +395,14 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto remove;
/*
* Update oldcard with the new RCA received from the SDIO
* device -- we're doing this so that it's updated in the
* "card" struct when oldcard overwrites that later.
*/
if (oldcard)
oldcard->rca = card->rca;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
@@ -458,6 +466,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
card = oldcard;
}
mmc_fixup_device(card);
if (card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_setup_card(host, card, oldcard != NULL);
+10 -1
View File
@@ -311,7 +311,7 @@ config MMC_MSM
config MMC_MXC
tristate "Freescale i.MX2/3 Multimedia Card Interface support"
depends on ARCH_MXC
depends on MACH_MX21 || MACH_MX27 || ARCH_MX31
help
This selects the Freescale i.MX2/3 Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
@@ -319,6 +319,15 @@ config MMC_MXC
If unsure, say N.
config MMC_MXS
tristate "Freescale MXS Multimedia Card Interface support"
depends on ARCH_MXS && MXS_DMA
help
This selects the Freescale SSP MMC controller found on MXS based
platforms like mx23/28.
If unsure, say N.
config MMC_TIFM_SD
tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)"
depends on EXPERIMENTAL && PCI
+1
View File
@@ -6,6 +6,7 @@ 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
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-pxa.o
+10 -9
View File
@@ -578,7 +578,8 @@ static void atmci_dma_cleanup(struct atmel_mci *host)
struct mmc_data *data = host->data;
if (data)
dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
dma_unmap_sg(host->dma.chan->device->dev,
data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE)
? DMA_TO_DEVICE : DMA_FROM_DEVICE));
}
@@ -588,7 +589,7 @@ static void atmci_stop_dma(struct atmel_mci *host)
struct dma_chan *chan = host->data_chan;
if (chan) {
chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
dmaengine_terminate_all(chan);
atmci_dma_cleanup(host);
} else {
/* Data transfer was stopped by the interrupt handler */
@@ -684,11 +685,11 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
else
direction = DMA_TO_DEVICE;
sglen = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, direction);
if (sglen != data->sg_len)
goto unmap_exit;
sglen = dma_map_sg(chan->device->dev, data->sg,
data->sg_len, direction);
desc = chan->device->device_prep_slave_sg(chan,
data->sg, data->sg_len, direction,
data->sg, sglen, direction,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
goto unmap_exit;
@@ -699,7 +700,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
return 0;
unmap_exit:
dma_unmap_sg(&host->pdev->dev, data->sg, sglen, direction);
dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, direction);
return -ENOMEM;
}
@@ -709,8 +710,8 @@ static void atmci_submit_data(struct atmel_mci *host)
struct dma_async_tx_descriptor *desc = host->dma.data_desc;
if (chan) {
desc->tx_submit(desc);
chan->device->device_issue_pending(chan);
dmaengine_submit(desc);
dma_async_issue_pending(chan);
}
}
+1 -1
View File
@@ -205,7 +205,7 @@ static int cb710_wait_while_busy(struct cb710_slot *slot, uint8_t mask)
"WAIT12: waited %d loops, mask %02X, entry val %08X, exit val %08X\n",
limit, mask, e, x);
#endif
return 0;
return err;
}
static void cb710_mmc_set_transfer_size(struct cb710_slot *slot,
+73 -10
View File
@@ -32,6 +32,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
#include "dw_mmc.h"
@@ -562,7 +563,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock */
mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE |
SDMMC_CLKEN_LOW_PWR);
/* inform CIU */
mci_send_cmd(slot,
@@ -661,6 +663,7 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
u32 regs;
/* set default 1 bit mode */
slot->ctype = SDMMC_CTYPE_1BIT;
@@ -672,6 +675,16 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_BUS_WIDTH_4:
slot->ctype = SDMMC_CTYPE_4BIT;
break;
case MMC_BUS_WIDTH_8:
slot->ctype = SDMMC_CTYPE_8BIT;
break;
}
/* DDR mode set */
if (ios->ddr) {
regs = mci_readl(slot->host, UHS_REG);
regs |= (0x1 << slot->id) << 16;
mci_writel(slot->host, UHS_REG, regs);
}
if (ios->clock) {
@@ -717,7 +730,9 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
struct dw_mci_board *brd = slot->host->pdata;
/* Use platform get_cd function, else try onboard card detect */
if (brd->get_cd)
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
present = 1;
else if (brd->get_cd)
present = !brd->get_cd(slot->id);
else
present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
@@ -1019,13 +1034,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
struct mmc_data *data = host->data;
int shift = host->data_shift;
u32 status;
unsigned int nbytes = 0, len, old_len, count = 0;
unsigned int nbytes = 0, len;
do {
len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
if (count == 0)
old_len = len;
if (offset + len <= sg->length) {
host->pull_data(host, (void *)(buf + offset), len);
@@ -1070,7 +1082,6 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
tasklet_schedule(&host->tasklet);
return;
}
count++;
} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
host->pio_offset = offset;
@@ -1395,7 +1406,11 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->setpower)
host->pdata->setpower(id, 0);
mmc->caps = 0;
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
else
mmc->caps = 0;
if (host->pdata->get_bus_wd)
if (host->pdata->get_bus_wd(slot->id) >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
@@ -1426,6 +1441,13 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
}
#endif /* CONFIG_MMC_DW_IDMAC */
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL;
} else
regulator_enable(host->vmmc);
if (dw_mci_get_cd(mmc))
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
else
@@ -1441,6 +1463,12 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
/* Card initially undetected */
slot->last_detect_state = 0;
/*
* Card may have been plugged in prior to boot so we
* need to run the detect tasklet
*/
tasklet_schedule(&host->card_tasklet);
return 0;
}
@@ -1619,8 +1647,9 @@ static int dw_mci_probe(struct platform_device *pdev)
*/
fifo_size = mci_readl(host, FIFOTH);
fifo_size = (fifo_size >> 16) & 0x7ff;
mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
((fifo_size/2) << 0)));
host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
((fifo_size/2) << 0));
mci_writel(host, FIFOTH, host->fifoth_val);
/* disable clock to CIU */
mci_writel(host, CLKENA, 0);
@@ -1683,6 +1712,12 @@ err_dmaunmap:
host->sg_cpu, host->sg_dma);
iounmap(host->regs);
if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc);
}
err_freehost:
kfree(host);
return ret;
@@ -1714,6 +1749,11 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc);
}
iounmap(host->regs);
kfree(host);
@@ -1729,6 +1769,9 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
int i, ret;
struct dw_mci *host = platform_get_drvdata(pdev);
if (host->vmmc)
regulator_enable(host->vmmc);
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (!slot)
@@ -1744,6 +1787,9 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
}
}
if (host->vmmc)
regulator_disable(host->vmmc);
return 0;
}
@@ -1752,6 +1798,23 @@ static int dw_mci_resume(struct platform_device *pdev)
int i, ret;
struct dw_mci *host = platform_get_drvdata(pdev);
if (host->dma_ops->init)
host->dma_ops->init(host);
if (!mci_wait_reset(&pdev->dev, host)) {
ret = -ENODEV;
return ret;
}
/* Restore the old value at FIFOTH register */
mci_writel(host, FIFOTH, host->fifoth_val);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (!slot)
+1 -1
View File
@@ -43,6 +43,7 @@
#define SDMMC_USRID 0x068
#define SDMMC_VERID 0x06c
#define SDMMC_HCON 0x070
#define SDMMC_UHS_REG 0x074
#define SDMMC_BMOD 0x080
#define SDMMC_PLDMND 0x084
#define SDMMC_DBADDR 0x088
@@ -51,7 +52,6 @@
#define SDMMC_DSCADDR 0x094
#define SDMMC_BUFADDR 0x098
#define SDMMC_DATA 0x100
#define SDMMC_DATA_ADR 0x100
/* shift bit field */
#define _SBF(f, v) ((v) << (f))

Some files were not shown because too many files have changed in this diff Show More