2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Block driver for media (i.e., flash cards)
|
|
|
|
*
|
|
|
|
* Copyright 2002 Hewlett-Packard Company
|
2008-06-29 18:19:47 +08:00
|
|
|
* Copyright 2005-2008 Pierre Ossman
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Use consistent with the GNU GPL is permitted,
|
|
|
|
* provided that this copyright notice is
|
|
|
|
* preserved in its entirety in all copies and derived works.
|
|
|
|
*
|
|
|
|
* HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
|
|
|
|
* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
|
|
|
|
* FITNESS FOR ANY PARTICULAR PURPOSE.
|
|
|
|
*
|
|
|
|
* Many thanks to Alessandro Rubini and Jonathan Corbet!
|
|
|
|
*
|
|
|
|
* Author: Andrew Christian
|
|
|
|
* 28 May 2002
|
|
|
|
*/
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/fs.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/hdreg.h>
|
|
|
|
#include <linux/kdev_t.h>
|
|
|
|
#include <linux/blkdev.h>
|
2006-01-13 02:43:35 +08:00
|
|
|
#include <linux/mutex.h>
|
2006-10-06 15:44:03 +08:00
|
|
|
#include <linux/scatterlist.h>
|
2008-09-06 16:57:57 +08:00
|
|
|
#include <linux/string_helpers.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/mmc/card.h>
|
2006-06-18 20:34:37 +08:00
|
|
|
#include <linux/mmc/host.h>
|
2006-12-25 05:46:55 +08:00
|
|
|
#include <linux/mmc/mmc.h>
|
|
|
|
#include <linux/mmc/sd.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2006-12-24 03:03:02 +08:00
|
|
|
#include "queue.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-02-23 20:38:41 +08:00
|
|
|
MODULE_ALIAS("mmc:block");
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* max 8 partitions per card
|
|
|
|
*/
|
|
|
|
#define MMC_SHIFT 3
|
2007-11-22 01:45:12 +08:00
|
|
|
#define MMC_NUM_MINORS (256 >> MMC_SHIFT)
|
|
|
|
|
2010-06-02 20:28:52 +08:00
|
|
|
static DEFINE_MUTEX(block_mutex);
|
2008-06-06 07:10:21 +08:00
|
|
|
static DECLARE_BITMAP(dev_use, MMC_NUM_MINORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* There is one mmc_blk_data per slot.
|
|
|
|
*/
|
|
|
|
struct mmc_blk_data {
|
|
|
|
spinlock_t lock;
|
|
|
|
struct gendisk *disk;
|
|
|
|
struct mmc_queue queue;
|
|
|
|
|
|
|
|
unsigned int usage;
|
2006-01-04 06:38:44 +08:00
|
|
|
unsigned int read_only;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2006-01-13 02:43:35 +08:00
|
|
|
static DEFINE_MUTEX(open_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md;
|
|
|
|
|
2006-01-13 02:43:35 +08:00
|
|
|
mutex_lock(&open_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
md = disk->private_data;
|
|
|
|
if (md && md->usage == 0)
|
|
|
|
md = NULL;
|
|
|
|
if (md)
|
|
|
|
md->usage++;
|
2006-01-13 02:43:35 +08:00
|
|
|
mutex_unlock(&open_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mmc_blk_put(struct mmc_blk_data *md)
|
|
|
|
{
|
2006-01-13 02:43:35 +08:00
|
|
|
mutex_lock(&open_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
md->usage--;
|
|
|
|
if (md->usage == 0) {
|
2010-01-09 06:42:58 +08:00
|
|
|
int devmaj = MAJOR(disk_devt(md->disk));
|
2008-09-03 15:01:48 +08:00
|
|
|
int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT;
|
2010-01-09 06:42:58 +08:00
|
|
|
|
|
|
|
if (!devmaj)
|
|
|
|
devidx = md->disk->first_minor >> MMC_SHIFT;
|
|
|
|
|
2010-01-09 06:43:00 +08:00
|
|
|
blk_cleanup_queue(md->queue.queue);
|
|
|
|
|
2007-11-22 01:45:12 +08:00
|
|
|
__clear_bit(devidx, dev_use);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
put_disk(md->disk);
|
|
|
|
kfree(md);
|
|
|
|
}
|
2006-01-13 02:43:35 +08:00
|
|
|
mutex_unlock(&open_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-03-02 23:33:30 +08:00
|
|
|
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-03-02 23:33:30 +08:00
|
|
|
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret = -ENXIO;
|
|
|
|
|
2010-06-02 20:28:52 +08:00
|
|
|
mutex_lock(&block_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (md) {
|
|
|
|
if (md->usage == 2)
|
2008-03-02 23:33:30 +08:00
|
|
|
check_disk_change(bdev);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = 0;
|
2005-09-07 06:18:52 +08:00
|
|
|
|
2008-03-02 23:33:30 +08:00
|
|
|
if ((mode & FMODE_WRITE) && md->read_only) {
|
2008-09-06 05:00:24 +08:00
|
|
|
mmc_blk_put(md);
|
2005-09-07 06:18:52 +08:00
|
|
|
ret = -EROFS;
|
2008-09-06 05:00:24 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-06-02 20:28:52 +08:00
|
|
|
mutex_unlock(&block_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-02 23:33:30 +08:00
|
|
|
static int mmc_blk_release(struct gendisk *disk, fmode_t mode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-03-02 23:33:30 +08:00
|
|
|
struct mmc_blk_data *md = disk->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-02 20:28:52 +08:00
|
|
|
mutex_lock(&block_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
mmc_blk_put(md);
|
2010-06-02 20:28:52 +08:00
|
|
|
mutex_unlock(&block_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-01-08 17:02:50 +08:00
|
|
|
mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-01-08 17:02:50 +08:00
|
|
|
geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16);
|
|
|
|
geo->heads = 4;
|
|
|
|
geo->sectors = 16;
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-09-22 08:01:13 +08:00
|
|
|
static const struct block_device_operations mmc_bdops = {
|
2008-03-02 23:33:30 +08:00
|
|
|
.open = mmc_blk_open,
|
|
|
|
.release = mmc_blk_release,
|
2006-01-08 17:02:50 +08:00
|
|
|
.getgeo = mmc_blk_getgeo,
|
2005-04-17 06:20:36 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mmc_blk_request {
|
|
|
|
struct mmc_request mrq;
|
|
|
|
struct mmc_command cmd;
|
|
|
|
struct mmc_command stop;
|
|
|
|
struct mmc_data data;
|
|
|
|
};
|
|
|
|
|
2006-10-06 15:44:03 +08:00
|
|
|
static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
int err;
|
2009-06-09 06:33:57 +08:00
|
|
|
u32 result;
|
|
|
|
__be32 *blocks;
|
2006-10-06 15:44:03 +08:00
|
|
|
|
|
|
|
struct mmc_request mrq;
|
|
|
|
struct mmc_command cmd;
|
|
|
|
struct mmc_data data;
|
|
|
|
unsigned int timeout_us;
|
|
|
|
|
|
|
|
struct scatterlist sg;
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
|
|
|
|
cmd.opcode = MMC_APP_CMD;
|
|
|
|
cmd.arg = card->rca << 16;
|
2007-08-09 00:10:23 +08:00
|
|
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
2006-10-06 15:44:03 +08:00
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
2007-08-09 00:10:23 +08:00
|
|
|
if (err)
|
|
|
|
return (u32)-1;
|
|
|
|
if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
|
2006-10-06 15:44:03 +08:00
|
|
|
return (u32)-1;
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
|
|
|
|
cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
|
|
|
|
cmd.arg = 0;
|
2007-08-09 00:10:23 +08:00
|
|
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
|
2006-10-06 15:44:03 +08:00
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct mmc_data));
|
|
|
|
|
|
|
|
data.timeout_ns = card->csd.tacc_ns * 100;
|
|
|
|
data.timeout_clks = card->csd.tacc_clks * 100;
|
|
|
|
|
|
|
|
timeout_us = data.timeout_ns / 1000;
|
|
|
|
timeout_us += data.timeout_clks * 1000 /
|
|
|
|
(card->host->ios.clock / 1000);
|
|
|
|
|
|
|
|
if (timeout_us > 100000) {
|
|
|
|
data.timeout_ns = 100000000;
|
|
|
|
data.timeout_clks = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.blksz = 4;
|
|
|
|
data.blocks = 1;
|
|
|
|
data.flags = MMC_DATA_READ;
|
|
|
|
data.sg = &sg;
|
|
|
|
data.sg_len = 1;
|
|
|
|
|
|
|
|
memset(&mrq, 0, sizeof(struct mmc_request));
|
|
|
|
|
|
|
|
mrq.cmd = &cmd;
|
|
|
|
mrq.data = &data;
|
|
|
|
|
2009-06-09 06:33:57 +08:00
|
|
|
blocks = kmalloc(4, GFP_KERNEL);
|
|
|
|
if (!blocks)
|
|
|
|
return (u32)-1;
|
|
|
|
|
|
|
|
sg_init_one(&sg, blocks, 4);
|
2006-10-06 15:44:03 +08:00
|
|
|
|
|
|
|
mmc_wait_for_req(card->host, &mrq);
|
|
|
|
|
2009-06-09 06:33:57 +08:00
|
|
|
result = ntohl(*blocks);
|
|
|
|
kfree(blocks);
|
|
|
|
|
2007-07-23 04:18:46 +08:00
|
|
|
if (cmd.error || data.error)
|
2009-06-09 06:33:57 +08:00
|
|
|
result = (u32)-1;
|
2006-10-06 15:44:03 +08:00
|
|
|
|
2009-06-09 06:33:57 +08:00
|
|
|
return result;
|
2006-10-06 15:44:03 +08:00
|
|
|
}
|
|
|
|
|
2008-10-16 17:55:25 +08:00
|
|
|
static u32 get_card_status(struct mmc_card *card, struct request *req)
|
|
|
|
{
|
|
|
|
struct mmc_command cmd;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
cmd.opcode = MMC_SEND_STATUS;
|
|
|
|
if (!mmc_host_is_spi(card->host))
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
|
|
|
if (err)
|
|
|
|
printk(KERN_ERR "%s: error %d sending status comand",
|
|
|
|
req->rq_disk->disk_name, err);
|
|
|
|
return cmd.resp[0];
|
|
|
|
}
|
|
|
|
|
2010-08-12 05:17:47 +08:00
|
|
|
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md = mq->data;
|
|
|
|
struct mmc_card *card = md->queue.card;
|
|
|
|
unsigned int from, nr, arg;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
mmc_claim_host(card->host);
|
|
|
|
|
|
|
|
if (!mmc_can_erase(card)) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
from = blk_rq_pos(req);
|
|
|
|
nr = blk_rq_sectors(req);
|
|
|
|
|
|
|
|
if (mmc_can_trim(card))
|
|
|
|
arg = MMC_TRIM_ARG;
|
|
|
|
else
|
|
|
|
arg = MMC_ERASE_ARG;
|
|
|
|
|
|
|
|
err = mmc_erase(card, from, nr, arg);
|
|
|
|
out:
|
|
|
|
spin_lock_irq(&md->lock);
|
|
|
|
__blk_end_request(req, err, blk_rq_bytes(req));
|
|
|
|
spin_unlock_irq(&md->lock);
|
|
|
|
|
|
|
|
mmc_release_host(card->host);
|
|
|
|
|
|
|
|
return err ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
2010-08-12 05:17:50 +08:00
|
|
|
static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
|
|
|
struct request *req)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md = mq->data;
|
|
|
|
struct mmc_card *card = md->queue.card;
|
|
|
|
unsigned int from, nr, arg;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
mmc_claim_host(card->host);
|
|
|
|
|
|
|
|
if (!mmc_can_secure_erase_trim(card)) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
from = blk_rq_pos(req);
|
|
|
|
nr = blk_rq_sectors(req);
|
|
|
|
|
|
|
|
if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
|
|
|
|
arg = MMC_SECURE_TRIM1_ARG;
|
|
|
|
else
|
|
|
|
arg = MMC_SECURE_ERASE_ARG;
|
|
|
|
|
|
|
|
err = mmc_erase(card, from, nr, arg);
|
|
|
|
if (!err && arg == MMC_SECURE_TRIM1_ARG)
|
|
|
|
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
|
|
|
|
out:
|
|
|
|
spin_lock_irq(&md->lock);
|
|
|
|
__blk_end_request(req, err, blk_rq_bytes(req));
|
|
|
|
spin_unlock_irq(&md->lock);
|
|
|
|
|
|
|
|
mmc_release_host(card->host);
|
|
|
|
|
|
|
|
return err ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
2010-08-12 05:17:47 +08:00
|
|
|
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct mmc_blk_data *md = mq->data;
|
|
|
|
struct mmc_card *card = md->queue.card;
|
2006-10-04 17:15:41 +08:00
|
|
|
struct mmc_blk_request brq;
|
2009-01-01 01:21:17 +08:00
|
|
|
int ret = 1, disable_multi = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-01-04 02:47:29 +08:00
|
|
|
mmc_claim_host(card->host);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
do {
|
|
|
|
struct mmc_command cmd;
|
2008-10-16 17:55:25 +08:00
|
|
|
u32 readcmd, writecmd, status = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
memset(&brq, 0, sizeof(struct mmc_blk_request));
|
|
|
|
brq.mrq.cmd = &brq.cmd;
|
|
|
|
brq.mrq.data = &brq.data;
|
|
|
|
|
2009-05-07 21:24:39 +08:00
|
|
|
brq.cmd.arg = blk_rq_pos(req);
|
2007-01-04 22:57:32 +08:00
|
|
|
if (!mmc_card_blockaddr(card))
|
|
|
|
brq.cmd.arg <<= 9;
|
2007-08-09 00:10:23 +08:00
|
|
|
brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
|
2008-08-31 20:10:08 +08:00
|
|
|
brq.data.blksz = 512;
|
2005-04-17 06:20:36 +08:00
|
|
|
brq.stop.opcode = MMC_STOP_TRANSMISSION;
|
|
|
|
brq.stop.arg = 0;
|
2007-08-09 00:10:23 +08:00
|
|
|
brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
2009-05-07 21:24:39 +08:00
|
|
|
brq.data.blocks = blk_rq_sectors(req);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-10 23:52:57 +08:00
|
|
|
/*
|
|
|
|
* The block layer doesn't support all sector count
|
|
|
|
* restrictions, so we need to be prepared for too big
|
|
|
|
* requests.
|
|
|
|
*/
|
|
|
|
if (brq.data.blocks > card->host->max_blk_count)
|
|
|
|
brq.data.blocks = card->host->max_blk_count;
|
|
|
|
|
2009-01-01 01:21:17 +08:00
|
|
|
/*
|
|
|
|
* After a read error, we redo the request one sector at a time
|
|
|
|
* in order to accurately determine which sectors can be read
|
|
|
|
* successfully.
|
|
|
|
*/
|
|
|
|
if (disable_multi && brq.data.blocks > 1)
|
|
|
|
brq.data.blocks = 1;
|
|
|
|
|
2006-01-10 05:12:17 +08:00
|
|
|
if (brq.data.blocks > 1) {
|
2007-08-09 00:10:23 +08:00
|
|
|
/* SPI multiblock writes terminate using a special
|
|
|
|
* token, not a STOP_TRANSMISSION request.
|
|
|
|
*/
|
|
|
|
if (!mmc_host_is_spi(card->host)
|
|
|
|
|| rq_data_dir(req) == READ)
|
|
|
|
brq.mrq.stop = &brq.stop;
|
2006-08-30 22:14:56 +08:00
|
|
|
readcmd = MMC_READ_MULTIPLE_BLOCK;
|
|
|
|
writecmd = MMC_WRITE_MULTIPLE_BLOCK;
|
2006-01-10 05:12:17 +08:00
|
|
|
} else {
|
|
|
|
brq.mrq.stop = NULL;
|
2006-08-30 22:14:56 +08:00
|
|
|
readcmd = MMC_READ_SINGLE_BLOCK;
|
|
|
|
writecmd = MMC_WRITE_BLOCK;
|
|
|
|
}
|
|
|
|
if (rq_data_dir(req) == READ) {
|
|
|
|
brq.cmd.opcode = readcmd;
|
|
|
|
brq.data.flags |= MMC_DATA_READ;
|
|
|
|
} else {
|
|
|
|
brq.cmd.opcode = writecmd;
|
|
|
|
brq.data.flags |= MMC_DATA_WRITE;
|
2006-01-10 05:12:17 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-07-25 01:16:54 +08:00
|
|
|
mmc_set_data_timeout(&brq.data, card);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
brq.data.sg = mq->sg;
|
2007-05-12 06:26:16 +08:00
|
|
|
brq.data.sg_len = mmc_queue_map_sg(mq);
|
|
|
|
|
2009-01-01 01:21:17 +08:00
|
|
|
/*
|
|
|
|
* Adjust the sg list so it is the same size as the
|
|
|
|
* request.
|
|
|
|
*/
|
2009-05-07 21:24:39 +08:00
|
|
|
if (brq.data.blocks != blk_rq_sectors(req)) {
|
2009-01-01 01:21:17 +08:00
|
|
|
int i, data_size = brq.data.blocks << 9;
|
|
|
|
struct scatterlist *sg;
|
|
|
|
|
|
|
|
for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
|
|
|
|
data_size -= sg->length;
|
|
|
|
if (data_size <= 0) {
|
|
|
|
sg->length += data_size;
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
brq.data.sg_len = i;
|
|
|
|
}
|
|
|
|
|
2007-05-12 06:26:16 +08:00
|
|
|
mmc_queue_bounce_pre(mq);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
mmc_wait_for_req(card->host, &brq.mrq);
|
2007-05-12 06:26:16 +08:00
|
|
|
|
|
|
|
mmc_queue_bounce_post(mq);
|
|
|
|
|
2008-06-29 18:19:47 +08:00
|
|
|
/*
|
|
|
|
* Check for errors here, but don't jump to cmd_err
|
|
|
|
* until later as we need to wait for the card to leave
|
|
|
|
* programming mode even when things go wrong.
|
|
|
|
*/
|
2009-01-01 01:21:17 +08:00
|
|
|
if (brq.cmd.error || brq.data.error || brq.stop.error) {
|
|
|
|
if (brq.data.blocks > 1 && rq_data_dir(req) == READ) {
|
|
|
|
/* Redo read one sector at a time */
|
|
|
|
printk(KERN_WARNING "%s: retrying using single "
|
|
|
|
"block read\n", req->rq_disk->disk_name);
|
|
|
|
disable_multi = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2008-10-16 17:55:25 +08:00
|
|
|
status = get_card_status(card, req);
|
2009-01-01 01:21:17 +08:00
|
|
|
}
|
2008-10-16 17:55:25 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (brq.cmd.error) {
|
2008-10-16 17:55:25 +08:00
|
|
|
printk(KERN_ERR "%s: error %d sending read/write "
|
|
|
|
"command, response %#x, card status %#x\n",
|
|
|
|
req->rq_disk->disk_name, brq.cmd.error,
|
|
|
|
brq.cmd.resp[0], status);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (brq.data.error) {
|
2008-10-16 17:55:25 +08:00
|
|
|
if (brq.data.error == -ETIMEDOUT && brq.mrq.stop)
|
|
|
|
/* 'Stop' response contains card status */
|
|
|
|
status = brq.mrq.stop->resp[0];
|
|
|
|
printk(KERN_ERR "%s: error %d transferring data,"
|
|
|
|
" sector %u, nr %u, card status %#x\n",
|
|
|
|
req->rq_disk->disk_name, brq.data.error,
|
2009-05-07 21:24:39 +08:00
|
|
|
(unsigned)blk_rq_pos(req),
|
|
|
|
(unsigned)blk_rq_sectors(req), status);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (brq.stop.error) {
|
2008-10-16 17:55:25 +08:00
|
|
|
printk(KERN_ERR "%s: error %d sending stop command, "
|
|
|
|
"response %#x, card status %#x\n",
|
|
|
|
req->rq_disk->disk_name, brq.stop.error,
|
|
|
|
brq.stop.resp[0], status);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-08-09 00:10:23 +08:00
|
|
|
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
|
2006-09-24 17:46:43 +08:00
|
|
|
do {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SEND_STATUS;
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
err = mmc_wait_for_cmd(card->host, &cmd, 5);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: error %d requesting status\n",
|
|
|
|
req->rq_disk->disk_name, err);
|
|
|
|
goto cmd_err;
|
|
|
|
}
|
2007-11-03 01:21:13 +08:00
|
|
|
/*
|
|
|
|
* Some cards mishandle the status bits,
|
|
|
|
* so make sure to check both the busy
|
|
|
|
* indication and the card state.
|
|
|
|
*/
|
|
|
|
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
|
|
|
|
(R1_CURRENT_STATE(cmd.resp[0]) == 7));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#if 0
|
2006-09-24 17:46:43 +08:00
|
|
|
if (cmd.resp[0] & ~0x00000900)
|
|
|
|
printk(KERN_ERR "%s: status = %08x\n",
|
|
|
|
req->rq_disk->disk_name, cmd.resp[0]);
|
|
|
|
if (mmc_decode_status(cmd.resp))
|
|
|
|
goto cmd_err;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2006-09-24 17:46:43 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-01 01:21:17 +08:00
|
|
|
if (brq.cmd.error || brq.stop.error || brq.data.error) {
|
|
|
|
if (rq_data_dir(req) == READ) {
|
|
|
|
/*
|
|
|
|
* After an error, we redo I/O one sector at a
|
|
|
|
* time, so we only reach here after trying to
|
|
|
|
* read a single sector.
|
|
|
|
*/
|
|
|
|
spin_lock_irq(&md->lock);
|
|
|
|
ret = __blk_end_request(req, -EIO, brq.data.blksz);
|
|
|
|
spin_unlock_irq(&md->lock);
|
|
|
|
continue;
|
|
|
|
}
|
2008-06-29 18:19:47 +08:00
|
|
|
goto cmd_err;
|
2009-01-01 01:21:17 +08:00
|
|
|
}
|
2008-06-29 18:19:47 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* A block was successfully transferred.
|
|
|
|
*/
|
|
|
|
spin_lock_irq(&md->lock);
|
2007-12-12 06:48:29 +08:00
|
|
|
ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irq(&md->lock);
|
|
|
|
} while (ret);
|
|
|
|
|
2007-01-04 02:47:29 +08:00
|
|
|
mmc_release_host(card->host);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
cmd_err:
|
2006-10-06 15:44:03 +08:00
|
|
|
/*
|
|
|
|
* If this is an SD card and we're writing, we can first
|
|
|
|
* mark the known good sectors as ok.
|
|
|
|
*
|
|
|
|
* If the card is not SD, we can still ok written sectors
|
2008-07-06 07:10:27 +08:00
|
|
|
* as reported by the controller (which might be less than
|
|
|
|
* the real number of written sectors, but never more).
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2009-01-01 01:21:17 +08:00
|
|
|
if (mmc_card_sd(card)) {
|
|
|
|
u32 blocks;
|
2008-07-06 07:10:27 +08:00
|
|
|
|
2009-01-01 01:21:17 +08:00
|
|
|
blocks = mmc_sd_num_wr_blocks(card);
|
|
|
|
if (blocks != (u32)-1) {
|
2006-10-06 15:44:03 +08:00
|
|
|
spin_lock_irq(&md->lock);
|
2009-01-01 01:21:17 +08:00
|
|
|
ret = __blk_end_request(req, 0, blocks << 9);
|
2006-10-06 15:44:03 +08:00
|
|
|
spin_unlock_irq(&md->lock);
|
|
|
|
}
|
2009-01-01 01:21:17 +08:00
|
|
|
} else {
|
|
|
|
spin_lock_irq(&md->lock);
|
|
|
|
ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
|
|
|
|
spin_unlock_irq(&md->lock);
|
2006-10-04 17:15:41 +08:00
|
|
|
}
|
|
|
|
|
2007-01-04 02:47:29 +08:00
|
|
|
mmc_release_host(card->host);
|
2006-10-06 15:44:03 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_irq(&md->lock);
|
2007-12-12 06:48:29 +08:00
|
|
|
while (ret)
|
|
|
|
ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irq(&md->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-08-12 05:17:47 +08:00
|
|
|
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|
|
|
{
|
2010-08-12 05:17:50 +08:00
|
|
|
if (req->cmd_flags & REQ_DISCARD) {
|
|
|
|
if (req->cmd_flags & REQ_SECURE)
|
|
|
|
return mmc_blk_issue_secdiscard_rq(mq, req);
|
|
|
|
else
|
|
|
|
return mmc_blk_issue_discard_rq(mq, req);
|
|
|
|
} else {
|
2010-08-12 05:17:47 +08:00
|
|
|
return mmc_blk_issue_rw_rq(mq, req);
|
2010-08-12 05:17:50 +08:00
|
|
|
}
|
2010-08-12 05:17:47 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-04 06:38:44 +08:00
|
|
|
static inline int mmc_blk_readonly(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
return mmc_card_readonly(card) ||
|
|
|
|
!(card->csd.cmdclass & CCC_BLOCK_WRITE);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md;
|
|
|
|
int devidx, ret;
|
|
|
|
|
|
|
|
devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
|
|
|
|
if (devidx >= MMC_NUM_MINORS)
|
|
|
|
return ERR_PTR(-ENOSPC);
|
|
|
|
__set_bit(devidx, dev_use);
|
|
|
|
|
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 16:49:03 +08:00
|
|
|
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
|
2006-01-04 06:38:44 +08:00
|
|
|
if (!md) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
2006-01-04 06:38:44 +08:00
|
|
|
/*
|
|
|
|
* Set the read-only status based on the supported commands
|
|
|
|
* and the write protect switch.
|
|
|
|
*/
|
|
|
|
md->read_only = mmc_blk_readonly(card);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-04 06:38:44 +08:00
|
|
|
md->disk = alloc_disk(1 << MMC_SHIFT);
|
|
|
|
if (md->disk == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_kfree;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-04 06:38:44 +08:00
|
|
|
spin_lock_init(&md->lock);
|
|
|
|
md->usage = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-04 06:38:44 +08:00
|
|
|
ret = mmc_init_queue(&md->queue, card, &md->lock);
|
|
|
|
if (ret)
|
|
|
|
goto err_putdisk;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-04 06:38:44 +08:00
|
|
|
md->queue.issue_fn = mmc_blk_issue_rq;
|
|
|
|
md->queue.data = md;
|
2005-12-23 07:21:38 +08:00
|
|
|
|
2007-05-14 23:27:29 +08:00
|
|
|
md->disk->major = MMC_BLOCK_MAJOR;
|
2006-01-04 06:38:44 +08:00
|
|
|
md->disk->first_minor = devidx << MMC_SHIFT;
|
|
|
|
md->disk->fops = &mmc_bdops;
|
|
|
|
md->disk->private_data = md;
|
|
|
|
md->disk->queue = md->queue.queue;
|
|
|
|
md->disk->driverfs_dev = &card->dev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As discussed on lkml, GENHD_FL_REMOVABLE should:
|
|
|
|
*
|
|
|
|
* - be set for removable media with permanent block devices
|
|
|
|
* - be unset for removable block devices with permanent media
|
|
|
|
*
|
|
|
|
* Since MMC block devices clearly fall under the second
|
|
|
|
* case, we do not set GENHD_FL_REMOVABLE. Userspace
|
|
|
|
* should use the block device creation/destruction hotplug
|
|
|
|
* messages to tell when the card is present.
|
|
|
|
*/
|
|
|
|
|
2010-06-17 22:58:46 +08:00
|
|
|
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
|
|
|
|
"mmcblk%d", devidx);
|
2006-01-04 06:38:44 +08:00
|
|
|
|
2009-05-23 05:17:49 +08:00
|
|
|
blk_queue_logical_block_size(md->queue.queue, 512);
|
2006-01-04 06:38:44 +08:00
|
|
|
|
2007-02-18 05:15:27 +08:00
|
|
|
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
|
|
|
|
/*
|
|
|
|
* The EXT_CSD sector count is in number or 512 byte
|
|
|
|
* sectors.
|
|
|
|
*/
|
|
|
|
set_capacity(md->disk, card->ext_csd.sectors);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The CSD capacity field is in units of read_blkbits.
|
|
|
|
* set_capacity takes units of 512 bytes.
|
|
|
|
*/
|
|
|
|
set_capacity(md->disk,
|
|
|
|
card->csd.capacity << (card->csd.read_blkbits - 9));
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return md;
|
2006-01-04 06:38:44 +08:00
|
|
|
|
|
|
|
err_putdisk:
|
|
|
|
put_disk(md->disk);
|
|
|
|
err_kfree:
|
|
|
|
kfree(md);
|
|
|
|
out:
|
|
|
|
return ERR_PTR(ret);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2007-01-04 02:47:29 +08:00
|
|
|
mmc_claim_host(card->host);
|
2010-08-24 18:20:26 +08:00
|
|
|
err = mmc_set_blocklen(card, 512);
|
2007-01-04 02:47:29 +08:00
|
|
|
mmc_release_host(card->host);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (err) {
|
2010-08-24 18:20:26 +08:00
|
|
|
printk(KERN_ERR "%s: unable to set block size to 512: %d\n",
|
|
|
|
md->disk->disk_name, err);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mmc_blk_probe(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md;
|
|
|
|
int err;
|
|
|
|
|
2008-09-06 16:57:57 +08:00
|
|
|
char cap_str[10];
|
|
|
|
|
2005-05-21 17:27:02 +08:00
|
|
|
/*
|
|
|
|
* Check that the card supports the command class(es) we need.
|
|
|
|
*/
|
|
|
|
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
md = mmc_blk_alloc(card);
|
|
|
|
if (IS_ERR(md))
|
|
|
|
return PTR_ERR(md);
|
|
|
|
|
|
|
|
err = mmc_blk_set_blksize(md, card);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2009-02-05 15:31:57 +08:00
|
|
|
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
|
2008-09-06 16:57:57 +08:00
|
|
|
cap_str, sizeof(cap_str));
|
|
|
|
printk(KERN_INFO "%s: %s %s %s %s\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
2008-09-06 16:57:57 +08:00
|
|
|
cap_str, md->read_only ? "(ro)" : "");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
mmc_set_drvdata(card, md);
|
|
|
|
add_disk(md->disk);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
2010-01-09 06:42:59 +08:00
|
|
|
mmc_cleanup_queue(&md->queue);
|
2005-04-17 06:20:36 +08:00
|
|
|
mmc_blk_put(md);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mmc_blk_remove(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
|
|
|
|
|
|
|
if (md) {
|
2006-11-15 05:08:16 +08:00
|
|
|
/* Stop new requests from getting into the queue */
|
2005-04-17 06:20:36 +08:00
|
|
|
del_gendisk(md->disk);
|
|
|
|
|
2006-11-15 05:08:16 +08:00
|
|
|
/* Then flush out any already in there */
|
|
|
|
mmc_cleanup_queue(&md->queue);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
mmc_blk_put(md);
|
|
|
|
}
|
|
|
|
mmc_set_drvdata(card, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
|
|
|
|
|
|
|
if (md) {
|
|
|
|
mmc_queue_suspend(&md->queue);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mmc_blk_resume(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
|
|
|
|
|
|
|
if (md) {
|
|
|
|
mmc_blk_set_blksize(md, card);
|
|
|
|
mmc_queue_resume(&md->queue);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define mmc_blk_suspend NULL
|
|
|
|
#define mmc_blk_resume NULL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static struct mmc_driver mmc_driver = {
|
|
|
|
.drv = {
|
|
|
|
.name = "mmcblk",
|
|
|
|
},
|
|
|
|
.probe = mmc_blk_probe,
|
|
|
|
.remove = mmc_blk_remove,
|
|
|
|
.suspend = mmc_blk_suspend,
|
|
|
|
.resume = mmc_blk_resume,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init mmc_blk_init(void)
|
|
|
|
{
|
2008-09-13 18:02:07 +08:00
|
|
|
int res;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-14 23:27:29 +08:00
|
|
|
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
|
|
|
if (res)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
|
2008-09-13 18:02:07 +08:00
|
|
|
res = mmc_register_driver(&mmc_driver);
|
|
|
|
if (res)
|
|
|
|
goto out2;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-09-13 18:02:07 +08:00
|
|
|
return 0;
|
|
|
|
out2:
|
|
|
|
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit mmc_blk_exit(void)
|
|
|
|
{
|
|
|
|
mmc_unregister_driver(&mmc_driver);
|
2007-05-14 23:27:29 +08:00
|
|
|
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(mmc_blk_init);
|
|
|
|
module_exit(mmc_blk_exit);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
|
|
|
|
|