You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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.
|
||||
@@ -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__ */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user