Commit 38720df4 authored by Juha Yrjola's avatar Juha Yrjola Committed by Tony Lindgren

Add OMAP MMC driver

Adds OMAP MMC driver.
Signed-off-by: default avatarJuha Yrjola <juha.yrjola@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 0b0dc113
......@@ -29,6 +29,23 @@ config MMC_BLOCK
mount the filesystem. Almost everyone wishing MMC support
should say Y or M here.
config MMC_BLOCK_BROKEN_RFD
boolean "Write work-around for incompatible cards"
depends on MMC_BLOCK
default n
help
Say y here if your MMC card fails write operations. Some cards
lie about being ready to receive data while they actually are not.
config MMC_BULKTRANSFER
bool "Multi-block writes (EXPERIMENTAL)"
depends on MMC_BLOCK != n && EXPERIMENTAL
default n
help
By default all writes are done one sector at a time. Enable
this option to transfer as large blocks as the host supports.
The transfer speed is in most cases doubled.
config MMC_ARMMMCI
tristate "ARM AMBA Multimedia Card Interface support"
depends on ARM_AMBA && MMC
......@@ -49,6 +66,36 @@ config MMC_PXA
If unsure, say N.
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP && MMC
select TPS65010 if MACH_OMAP_H2
help
This selects the TI OMAP Multimedia card Interface.
If you have an OMAP board with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
config MMC_OMAP16XX_BLOCK1
boolean "First MMC block on OMAP16XX"
depends on ARCH_OMAP16XX && MMC_OMAP
default y if MACH_OMAP_H2 || MACH_OMAP_H3
help
This enables the first of two MMC blocks on OMAP1610 multimedia
processor. You need to enable the correct block to activate your
MMC slot.
config MMC_OMAP16XX_BLOCK2
boolean "Second MMC block on OMAP16XX"
depends on ARCH_OMAP16XX && MMC_OMAP
default n if MACH_OMAP_H2 || MACH_OMAP_H3
default y
help
This enables the second of two MMC blocks on OMAP1610 multimedia
processor. You need to enable the correct block to activate your
MMC slot.
config MMC_WBSD
tristate "Winbond W83L51xD SD/MMC Card Interface support"
depends on MMC && ISA && ISA_DMA_API
......
......@@ -18,5 +18,6 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_OMAP) += omap.o
mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
......@@ -474,13 +474,13 @@ static void mmc_power_up(struct mmc_host *host)
int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
host->ios.clock = host->f_min;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.power_mode = MMC_POWER_UP;
host->ops->set_ios(host, &host->ios);
mmc_delay(1);
host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
host->ops->set_ios(host, &host->ios);
......@@ -512,7 +512,7 @@ static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
break;
mmc_delay(1);
err = MMC_ERR_TIMEOUT;
mmc_delay(10);
......
......@@ -54,6 +54,7 @@ struct mmc_blk_data {
unsigned int usage;
unsigned int block_bits;
unsigned int suspended;
};
static DECLARE_MUTEX(open_lock);
......@@ -158,6 +159,19 @@ static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req)
stat = BLKPREP_KILL;
}
if (md->suspended) {
blk_plug_device(md->queue.queue);
stat = BLKPREP_DEFER;
}
/*
* Check for excessive requests.
*/
if (req->sector + req->nr_sectors > get_capacity(req->rq_disk)) {
printk("bad request size\n");
stat = BLKPREP_KILL;
}
return stat;
}
......@@ -166,9 +180,25 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
int ret;
#ifdef CONFIG_MMC_BULKTRANSFER
int failsafe;
#endif
if (mmc_card_claim_host(card))
goto cmd_err;
#ifdef CONFIG_MMC_BULKTRANSFER
/*
* We first try transfering multiple blocks. If this fails
* we fall back to single block transfers.
*
* This gives us good performance when all is well and the
* possibility to determine which sector fails when all
* is not well.
*/
failsafe = 0;
#endif
do {
struct mmc_blk_request brq;
......@@ -188,14 +218,32 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.stop.arg = 0;
brq.stop.flags = MMC_RSP_R1B;
#ifdef CONFIG_MMC_BULKTRANSFER
/*
* A multi-block transfer failed. Falling back to single
* blocks.
*/
if (failsafe)
brq.data.blocks = 1;
#else
/*
* Writes are done one sector at a time.
*/
if (rq_data_dir(req) != READ)
brq.data.blocks = 1;
#endif
ret = 1;
if (rq_data_dir(req) == READ) {
brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
brq.data.flags |= MMC_DATA_READ;
} else {
brq.cmd.opcode = MMC_WRITE_BLOCK;
brq.cmd.opcode = brq.data.blocks > 1 ? MMC_WRITE_MULTIPLE_BLOCK :
MMC_WRITE_BLOCK;
brq.cmd.flags = MMC_RSP_R1B;
brq.data.flags |= MMC_DATA_WRITE;
brq.data.blocks = 1;
}
brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL;
......@@ -206,19 +254,19 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
if (brq.cmd.error) {
printk(KERN_ERR "%s: error %d sending read/write command\n",
req->rq_disk->disk_name, brq.cmd.error);
goto cmd_err;
goto cmd_fail;
}
if (brq.data.error) {
printk(KERN_ERR "%s: error %d transferring data\n",
req->rq_disk->disk_name, brq.data.error);
goto cmd_err;
goto cmd_fail;
}
if (brq.stop.error) {
printk(KERN_ERR "%s: error %d sending stop command\n",
req->rq_disk->disk_name, brq.stop.error);
goto cmd_err;
goto cmd_fail;
}
do {
......@@ -231,8 +279,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
if (err) {
printk(KERN_ERR "%s: error %d requesting status\n",
req->rq_disk->disk_name, err);
goto cmd_err;
goto cmd_fail;
}
#ifdef CONFIG_MMC_BLOCK_BROKEN_RFD
/* Work-around for broken cards setting READY_FOR_DATA
* when not actually ready.
*/
if (R1_CURRENT_STATE(cmd.resp[0]) == 7)
cmd.resp[0] &= ~R1_READY_FOR_DATA;
#endif
} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
#if 0
......@@ -257,6 +312,27 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
end_that_request_last(req);
}
spin_unlock_irq(&md->lock);
#ifdef CONFIG_MMC_BULKTRANSFER
/*
* Go back to bulk mode if in failsafe mode.
*/
failsafe = 0;
#endif
continue;
cmd_fail:
#ifdef CONFIG_MMC_BULKTRANSFER
if (failsafe)
goto cmd_err;
else
failsafe = 1;
#else
goto cmd_err;
#endif
} while (ret);
mmc_card_release_host(card);
......
This diff is collapsed.
#ifndef DRIVERS_MEDIA_MMC_OMAP_H
#define DRIVERS_MEDIA_MMC_OMAP_H
#define OMAP_MMC1_BASE 0xfffb7800
#define OMAP_MMC2_BASE 0xfffb7c00
#define OMAP_MMC_REG_CMD 0x00
#define OMAP_MMC_REG_ARGL 0x04
#define OMAP_MMC_REG_ARGH 0x08
#define OMAP_MMC_REG_CON 0x0c
#define OMAP_MMC_REG_STAT 0x10
#define OMAP_MMC_REG_IE 0x14
#define OMAP_MMC_REG_CTO 0x18
#define OMAP_MMC_REG_DTO 0x1c
#define OMAP_MMC_REG_DATA 0x20
#define OMAP_MMC_REG_BLEN 0x24
#define OMAP_MMC_REG_NBLK 0x28
#define OMAP_MMC_REG_BUF 0x2c
#define OMAP_MMC_REG_SDIO 0x34
#define OMAP_MMC_REG_REV 0x3c
#define OMAP_MMC_REG_RSP0 0x40
#define OMAP_MMC_REG_RSP1 0x44
#define OMAP_MMC_REG_RSP2 0x48
#define OMAP_MMC_REG_RSP3 0x4c
#define OMAP_MMC_REG_RSP4 0x50
#define OMAP_MMC_REG_RSP5 0x54
#define OMAP_MMC_REG_RSP6 0x58
#define OMAP_MMC_REG_RSP7 0x5c
#define OMAP_MMC_REG_IOSR 0x60
#define OMAP_MMC_REG_SYSC 0x64
#define OMAP_MMC_REG_SYSS 0x68
#define OMAP_MMC_STAT_CARD_ERR (1 << 14)
#define OMAP_MMC_STAT_CARD_IRQ (1 << 13)
#define OMAP_MMC_STAT_OCR_BUSY (1 << 12)
#define OMAP_MMC_STAT_A_EMPTY (1 << 11)
#define OMAP_MMC_STAT_A_FULL (1 << 10)
#define OMAP_MMC_STAT_CMD_CRC (1 << 8)
#define OMAP_MMC_STAT_CMD_TOUT (1 << 7)
#define OMAP_MMC_STAT_DATA_CRC (1 << 6)
#define OMAP_MMC_STAT_DATA_TOUT (1 << 5)
#define OMAP_MMC_STAT_END_BUSY (1 << 4)
#define OMAP_MMC_STAT_END_OF_DATA (1 << 3)
#define OMAP_MMC_STAT_CARD_BUSY (1 << 2)
#define OMAP_MMC_STAT_END_OF_CMD (1 << 0)
#define OMAP_MMC_READ(base, reg) __raw_readw((base) + OMAP_MMC_REG_##reg)
#define OMAP_MMC_WRITE(base, reg, val) __raw_writew((val), (base) + OMAP_MMC_REG_##reg)
/*
* Command types
*/
#define OMAP_MMC_CMDTYPE_BC 0
#define OMAP_MMC_CMDTYPE_BCR 1
#define OMAP_MMC_CMDTYPE_AC 2
#define OMAP_MMC_CMDTYPE_ADTC 3
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment