Commit 4a74a5e5 authored by Kevin Hilman's avatar Kevin Hilman

ARM: DaVinci: forward port MMC driver

This is a forward port of the MMC driver, originally written by TI,
from the MV 2.6.10 kernel.

This currently will not build.  It needs an update from using the
obsoleted 'req' field of struct mmc_data.
Signed-off-by: default avatarKevin Hilman <khilman@mvista.com>
parent e5f81fa4
......@@ -71,6 +71,14 @@ config MMC_OMAP
If unsure, say N.
config MMC_DAVINCI
tristate "TI DAVINCI Multimedia Card Interface support"
depends on MMC
help
This selects the TI DAVINCI Multimedia card Interface.
If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N.
config MMC_WBSD
tristate "Winbond W83L51xD SD/MMC Card Interface support"
depends on MMC && ISA_DMA_API
......
......@@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
......
/*
* linux/drivers/mmc/davinci.c
*
* TI DaVinci MMC controller file
*
* Copyright (C) 2006 Texas Instruments.
*
* ----------------------------------------------------------------------------
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
Modifications:
ver. 1.0: Oct 2005, Purushotam Kumar Initial version
ver 1.1: Nov 2005, Purushotam Kumar Solved bugs
ver 1.2: Jan 2066, Purushotam Kumar Added card remove insert support
-
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/blkdev.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/arch/irqs.h>
#include <asm/arch/hardware.h>
#include "davinci_mmc.h"
#include <asm/arch/edma.h>
/* FIXME: old defines from old mmc.h */
/* #define MMC_RSP_NONE (0 << 0) */
/* #define MMC_RSP_SHORT (1 << 0) */
/* #define MMC_RSP_LONG (2 << 0) */
/* #define MMC_RSP_MASK (3 << 0) */
/* #define MMC_RSP_CRC (1 << 3) /\* expect valid crc *\/ */
/* #define MMC_RSP_BUSY (1 << 4) /\* card may send busy *\/ */
#define MMC_RSP_SHORT MMC_RSP_PRESENT
#define MMC_RSP_LONG MMC_RSP_136
#define MMC_RSP_MASK (MMC_RSP_PRESENT | MMC_RSP_136)
extern void davinci_clean_channel(int ch_no);
/* MMCSD Init clock in Hz in opendain mode */
#define MMCSD_INIT_CLOCK 200000
#define DRIVER_NAME "mmc0"
#define MMCINT_INTERRUPT IRQ_MMCINT
#define MMCSD_REGS_BASE_ADDR DAVINCI_MMC_SD_BASE
#define TCINTEN (0x1<<20)
/* This macro could not be defined to 0 (ZERO) or -ve value.
* This value is multiplied to "HZ"
* while requesting for timer interrupt every time for probing card.
*/
#define MULTIPILER_TO_HZ 1
struct device mmc_dev;
struct clk *mmc_clkp = NULL;
mmcsd_config_def mmcsd_cfg = {
/* read write thresholds (in bytes) can be any power of 2 from 2 to 64 */
32,
/* To use the DMA or not-- 1- Use DMA, 0-Interrupt mode */
1
};
volatile mmcsd_regs_base *mmcsd_regs;
static unsigned int mmc_input_clk = 0;
/* Used to identify whether card being used currently by linux core or not */
static unsigned int is_card_busy = 0;
/* used to identify whether card probe(detection) is currently in progress */
static unsigned int is_card_detect_progress = 0;
/* used to identify whether core is icurrently initilizing the card or not */
static unsigned int is_init_progress = 0;
/* used to identify whether core request has been queue up or
* not because request has come when card detection/probe was in progress
*/
static unsigned int is_req_queued_up = 0;
/* data struture to queue one request */
static struct mmc_host *que_mmc_host = NULL;
/* data structure to queue one request */
static struct mmc_request *que_mmc_request = NULL;
/* tells whether card is initizlzed or not */
static unsigned int is_card_initialized = 0;
static unsigned int new_card_state = 0; /* tells current state of card */
static DEFINE_SPINLOCK(mmc_lock);
static void mmc_davinci_start_command(struct mmc_davinci_host *host,
struct mmc_command *cmd)
{
u32 cmd_reg = 0;
u32 resp_type = 0;
u32 cmd_type = 0;
int byte_cnt = 0, i = 0;
unsigned long flags;
dev_dbg(&mmc_dev, "\nMMCSD : CMD%d, argument 0x%08x",
cmd->opcode, cmd->arg);
if (cmd->flags & MMC_RSP_SHORT)
dev_dbg(&mmc_dev, ", 32-bit response");
if (cmd->flags & MMC_RSP_LONG)
dev_dbg(&mmc_dev, ", 128-bit response");
if (cmd->flags & MMC_RSP_CRC)
dev_dbg(&mmc_dev, ", CRC");
if (cmd->flags & MMC_RSP_BUSY)
dev_dbg(&mmc_dev, ", busy notification");
else
dev_dbg(&mmc_dev, ", No busy notification");
dev_dbg(&mmc_dev, "\n");
host->cmd = cmd;
/* Protocol layer does not provide response type,
* but our hardware needs to know exact type, not just size!
*/
switch (cmd->flags & MMC_RSP_MASK) {
case MMC_RSP_NONE:
/* resp 0 */
break;
case MMC_RSP_SHORT:
/* resp 1, resp 1b */
/* OR resp 3!! (assume this if bus is set opendrain) */
if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) {
resp_type = 3;
if (cmd->opcode == 3)
resp_type = 1;
} else {
resp_type = 1;
}
break;
case MMC_RSP_LONG:
/* resp 2 */
resp_type = 2;
break;
}
/* Protocol layer does not provide command type, but our hardware
* needs it!
* any data transfer means adtc type (but that information is not
* in command structure, so we flagged it into host struct.)
* However, telling bc, bcr and ac apart based on response is
* not foolproof:
* CMD0 = bc = resp0 CMD15 = ac = resp0
* CMD2 = bcr = resp2 CMD10 = ac = resp2
*
* Resolve to best guess with some exception testing:
* resp0 -> bc, except CMD15 = ac
* rest are ac, except if opendrain
*/
if (host->data_dir) {
cmd_type = DAVINCI_MMC_CMDTYPE_ADTC;
} else if (resp_type == 0 && cmd->opcode != 15) {
cmd_type = DAVINCI_MMC_CMDTYPE_BC;
} else if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) {
cmd_type = DAVINCI_MMC_CMDTYPE_BCR;
} else {
cmd_type = DAVINCI_MMC_CMDTYPE_AC;
}
/* Set command Busy or not */
if (cmd->flags & MMC_RSP_BUSY) {
/*
* Linux core sending BUSY which is not defined for cmd 24
* as per mmc standard
*/
if (cmd->opcode != 24) {
cmd_reg = cmd_reg | (1 << 8);
}
}
/* Set command index */
cmd_reg |= cmd->opcode;
/* Setting initialize clock */
if (cmd->opcode == 0) {
cmd_reg = cmd_reg | (1 << 14);
}
/* Set for generating DMA Xfer event */
if ((host->use_dma == 1) && (host->data != NULL)
&& ((cmd->opcode == 18) || (cmd->opcode == 25)
|| (cmd->opcode == 24) || (cmd->opcode == 17))) {
cmd_reg = cmd_reg | (1 << 16);
}
/* Setting whether command involves data transfer or not */
if (cmd_type == DAVINCI_MMC_CMDTYPE_ADTC) {
cmd_reg = cmd_reg | (1 << 13);
}
/* Setting whether stream or block transfer */
if (cmd->flags & MMC_DATA_STREAM) {
cmd_reg = cmd_reg | (1 << 12);
}
/* Setting whether data read or write */
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
cmd_reg = cmd_reg | (1 << 11);
}
/* Setting response type */
cmd_reg = cmd_reg | (resp_type << 9);
if (host->bus_mode == MMC_BUSMODE_PUSHPULL) {
cmd_reg = cmd_reg | (1 << 7);
}
/* set Command timeout */
mmcsd_regs->mmc_tor = 0xFFFF;
/* Enable interrupt */
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
if (host->use_dma != 1) {
mmcsd_regs->mmc_im = (MMCSD_EVENT_EOFCMD |
MMCSD_EVENT_WRITE |
MMCSD_EVENT_ERROR_CMDCRC |
MMCSD_EVENT_ERROR_DATACRC |
MMCSD_EVENT_ERROR_CMDTIMEOUT |
MMCSD_EVENT_ERROR_DATATIMEOUT |
MMCSD_EVENT_BLOCK_XFERRED);
} else {
mmcsd_regs->mmc_im = (MMCSD_EVENT_EOFCMD |
MMCSD_EVENT_ERROR_CMDCRC |
MMCSD_EVENT_ERROR_DATACRC |
MMCSD_EVENT_ERROR_CMDTIMEOUT |
MMCSD_EVENT_ERROR_DATATIMEOUT |
MMCSD_EVENT_BLOCK_XFERRED);
}
} else if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
if (host->use_dma != 1) {
mmcsd_regs->mmc_im = (MMCSD_EVENT_EOFCMD |
MMCSD_EVENT_READ |
MMCSD_EVENT_ERROR_CMDCRC |
MMCSD_EVENT_ERROR_DATACRC |
MMCSD_EVENT_ERROR_CMDTIMEOUT |
MMCSD_EVENT_ERROR_DATATIMEOUT |
MMCSD_EVENT_BLOCK_XFERRED);
} else {
mmcsd_regs->mmc_im = (MMCSD_EVENT_EOFCMD |
MMCSD_EVENT_ERROR_CMDCRC |
MMCSD_EVENT_ERROR_DATACRC |
MMCSD_EVENT_ERROR_CMDTIMEOUT |
MMCSD_EVENT_ERROR_DATATIMEOUT |
MMCSD_EVENT_BLOCK_XFERRED);
}
} else {
mmcsd_regs->mmc_im = (MMCSD_EVENT_EOFCMD |
MMCSD_EVENT_ERROR_CMDCRC |
MMCSD_EVENT_ERROR_DATACRC |
MMCSD_EVENT_ERROR_CMDTIMEOUT |
MMCSD_EVENT_ERROR_DATATIMEOUT);
}
/*
* It is required by controoler b4 WRITE command that
* FIFO should be populated with 32 bytes
*/
if ((host->data_dir == DAVINCI_MMC_DATADIR_WRITE) &&
(cmd_type == DAVINCI_MMC_CMDTYPE_ADTC) && (host->use_dma != 1)) {
byte_cnt = mmcsd_cfg.rw_threshold;
host->bytes_left -= mmcsd_cfg.rw_threshold;
for (i = 0; i < (byte_cnt / 4); i++) {
mmcsd_regs->mmc_dxr = *host->buffer;
host->buffer++;
}
}
if (cmd->opcode == 7) {
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 1;
is_card_initialized = 1;
host->old_card_state = new_card_state;
is_init_progress = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
}
if (cmd->opcode == 1) {
spin_lock_irqsave(&mmc_lock, flags);
is_init_progress = 1;
spin_unlock_irqrestore(&mmc_lock, flags);
}
host->is_core_command = 1;
mmcsd_regs->mmc_arghl = cmd->arg;
mmcsd_regs->mmc_cmd = cmd_reg;
}
static void mmc_davinci_dma_cb(int lch, u16 ch_status, void *data)
{
int sync_dev = 0;
struct mmc_davinci_host *host = (struct mmc_davinci_host *)data;
if (DMA_COMPLETE == ch_status) {
if (host->cmd == NULL && host->data == NULL) {
if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
sync_dev = DAVINCI_DMA_MMCTXEVT;
} else {
sync_dev = DAVINCI_DMA_MMCRXEVT;
}
dev_dbg(&mmc_dev,
"Interrupt from DMA when no request has been made\n");
davinci_stop_dma(sync_dev);
return;
}
if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
sync_dev = DAVINCI_DMA_MMCTXEVT; /* Write */
} else {
sync_dev = DAVINCI_DMA_MMCRXEVT; /* Read */
}
davinci_stop_dma(sync_dev);
} else {
/* Handing of Event missed interreupt from DMA */
dev_dbg(&mmc_dev,
"Event miss interrupt has been generated by DMA\n");
if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
sync_dev = DAVINCI_DMA_MMCTXEVT; /* Write */
} else {
sync_dev = DAVINCI_DMA_MMCRXEVT; /* Read */
}
davinci_clean_channel(sync_dev);
}
}
static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
struct mmc_request *req)
{
const char *dev_name;
int sync_dev, r, edma_ch = 0, tcc = 0;
unsigned char i, j;
unsigned short acnt, bcnt, ccnt;
unsigned int src_port, dst_port, temp_ccnt;
enum address_mode mode_src, mode_dst;
enum fifo_width fifo_width_src, fifo_width_dst;
unsigned short src_bidx, dst_bidx;
unsigned short src_cidx, dst_cidx;
unsigned short bcntrld;
enum sync_dimension sync_mode;
edmacc_paramentry_regs temp;
enum dma_event_q queue_no = EVENTQ_0;
int edma_chan_num;
unsigned int num_eight_words = (req->data->blocks * 512) / 32;
static unsigned int option_read = 0;
static unsigned int option_write = 0;
static unsigned char dma_read_req = 1;
static unsigned char dma_write_req = 1;
#define MAX_C_CNT 64000
if ((req->data->flags & MMC_DATA_WRITE)) {
sync_dev = DAVINCI_DMA_MMCTXEVT; /* Write */
dev_name = "MMC_WRITE";
if (dma_write_req) {
r = davinci_request_dma(sync_dev, dev_name,
mmc_davinci_dma_cb, host,
&edma_ch, &tcc, queue_no);
if (r != 0) {
dev_dbg(&mmc_dev,
"MMC: davinci_request_dma() failed with %d\n",
r);
return r;
}
dma_write_req = 0;
}
} else {
sync_dev = DAVINCI_DMA_MMCRXEVT; /* Read */
dev_name = "MMC_READ";
if (dma_read_req) {
r = davinci_request_dma(sync_dev, dev_name,
mmc_davinci_dma_cb, host,
&edma_ch, &tcc, queue_no);
if (r != 0) {
dev_dbg(&mmc_dev,
"MMC: davinci_request_dma() failed with %d\n",
r);
return r;
}
dma_read_req = 0;
}
}
if ((req->data->flags & MMC_DATA_WRITE)) {
/* AB Sync Transfer */
/* Acnt =32, Bcnt= , Cnt=1 */
sync_dev = DAVINCI_DMA_MMCTXEVT; /* Write */
acnt = 4;
bcnt = 8;
if (num_eight_words > MAX_C_CNT) {
temp_ccnt = MAX_C_CNT;
ccnt = temp_ccnt;
} else {
ccnt = num_eight_words;
temp_ccnt = ccnt;
}
src_port = (unsigned int)virt_to_phys(req->data->req->buffer);
mode_src = INCR;
fifo_width_src = W8BIT; /* It's not cared as modeDsr is INCR */
src_bidx = 4;
src_cidx = 32;
dst_port = MMCSD_REGS_BASE_ADDR + 0x2C;
mode_dst = INCR;
fifo_width_dst = W8BIT; /* It's not cared as modeDsr is INCR */
dst_bidx = 0;
dst_cidx = 0;
bcntrld = 8;
sync_mode = ABSYNC;
} else {
sync_dev = DAVINCI_DMA_MMCRXEVT; /* Read */
acnt = 4;
bcnt = 8;
if (num_eight_words > MAX_C_CNT) {
temp_ccnt = MAX_C_CNT;
ccnt = temp_ccnt;
} else {
ccnt = num_eight_words;
temp_ccnt = ccnt;
}
src_port = MMCSD_REGS_BASE_ADDR + 0x28;
mode_src = INCR;
fifo_width_src = W8BIT;
src_bidx = 0;
src_cidx = 0;
dst_port = (unsigned int)virt_to_phys(req->data->req->buffer);
mode_dst = INCR;
fifo_width_dst = W8BIT; /* It's not cared as modeDsr is INCR */
dst_bidx = 4;
dst_cidx = 32;
bcntrld = 8;
sync_mode = ABSYNC;
}
davinci_set_dma_src_params(sync_dev, src_port, mode_src,
fifo_width_src);
davinci_set_dma_dest_params(sync_dev, dst_port, mode_dst,
fifo_width_dst);
davinci_set_dma_src_index(sync_dev, src_bidx, src_cidx);
davinci_set_dma_dest_index(sync_dev, dst_bidx, dst_cidx);
davinci_set_dma_transfer_params(sync_dev, acnt, bcnt, ccnt, bcntrld,
sync_mode);
host->edma_ch_details.cnt_chanel = 0;
davinci_get_dma_params(sync_dev, &temp);
if (sync_dev == DAVINCI_DMA_MMCTXEVT) {
if (option_write == 0) {
option_write = temp.opt;
} else {
temp.opt = option_write;
davinci_set_dma_params(sync_dev, &temp);
}
}
if (sync_dev == DAVINCI_DMA_MMCRXEVT) {
if (option_read == 0) {
option_read = temp.opt;
} else {
temp.opt = option_read;
davinci_set_dma_params(sync_dev, &temp);
}
}
if (num_eight_words > MAX_C_CNT) { /* Linking will be performed */
davinci_get_dma_params(sync_dev, &temp);
temp.opt &= ~TCINTEN;
davinci_set_dma_params(sync_dev, &temp);
for (i = 0; i < EDMA_MAX_LOGICAL_CHA_ALLOWED; i++) {
if (i != 0) {
j = i - 1;
davinci_get_dma_params(host->edma_ch_details.
chanel_num[j], &temp);
temp.opt &= ~TCINTEN;
davinci_set_dma_params(host->edma_ch_details.
chanel_num[j], &temp);
}
host->edma_ch_details.cnt_chanel++;
davinci_request_dma(DAVINCI_EDMA_PARAM_ANY, "LINK",
NULL, NULL, &edma_chan_num,
&sync_dev, queue_no);
host->edma_ch_details.chanel_num[i] = edma_chan_num;
ccnt = temp.ccnt & 0x0000FFFF;
if (sync_dev == DAVINCI_DMA_MMCTXEVT) {
temp.src = temp.src + (acnt * bcnt * ccnt);
} else {
temp.dst = temp.dst + (acnt * bcnt * ccnt);
}
temp.opt |= TCINTEN;
if ((num_eight_words - temp_ccnt) > MAX_C_CNT) {
temp.ccnt =
(temp.ccnt & 0xFFFF0000) | MAX_C_CNT;
ccnt = temp.ccnt & 0x0000FFFF;
temp_ccnt = temp_ccnt + ccnt;
} else {
temp.ccnt =
(temp.
ccnt & 0xFFFF0000) | (num_eight_words -
temp_ccnt);
ccnt = temp.ccnt & 0x0000FFFF;
temp_ccnt = temp_ccnt + ccnt;
}
davinci_set_dma_params(edma_chan_num, &temp);
if (i != 0) {
j = i - 1;
davinci_dma_link_lch(host->edma_ch_details.
chanel_num[j],
edma_chan_num);
}
if (temp_ccnt == num_eight_words) {
break;
}
}
davinci_dma_link_lch(sync_dev,
host->edma_ch_details.chanel_num[0]);
}
davinci_start_dma(sync_dev);
return 0;
}
static void mmc_davinci_prepare_data(struct mmc_davinci_host *host,
struct mmc_request *req)
{
int timeout;
host->data = req->data;
if (req->data == NULL) {
host->data_dir = DAVINCI_MMC_DATADIR_NONE;
mmcsd_regs->mmc_blen = 0;
mmcsd_regs->mmc_nblk = 0;
return;
}
dev_dbg(&mmc_dev,
"MMCSD : Data xfer (%s %s), "
"DTO %d cycles + %d ns, %d blocks of %d bytes\r\n",
(req->data->flags & MMC_DATA_STREAM) ? "stream" : "block",
(req->data->flags & MMC_DATA_WRITE) ? "write" : "read",
req->data->timeout_clks, req->data->timeout_ns,
req->data->blocks, 1 << req->data->blksz_bits);
/* Convert ns to clock cycles by assuming 20MHz frequency
* 1 cycle at 20MHz = 500 ns
*/
timeout = req->data->timeout_clks + req->data->timeout_ns / 500;
if (timeout > 0xffff) {
timeout = 0xffff;
}
mmcsd_regs->mmc_tod = timeout;
mmcsd_regs->mmc_nblk = req->data->blocks;
mmcsd_regs->mmc_blen = (1 << req->data->blksz_bits);
host->data_dir = (req->data->flags & MMC_DATA_WRITE) ?
DAVINCI_MMC_DATADIR_WRITE : DAVINCI_MMC_DATADIR_READ;
/* Configure the FIFO */
switch (host->data_dir) {
case DAVINCI_MMC_DATADIR_WRITE:
mmcsd_regs->mmc_fifo_ctl = mmcsd_regs->mmc_fifo_ctl | 0x1;
mmcsd_regs->mmc_fifo_ctl = 0x0;
mmcsd_regs->mmc_fifo_ctl = mmcsd_regs->mmc_fifo_ctl | (1 << 1);
mmcsd_regs->mmc_fifo_ctl = mmcsd_regs->mmc_fifo_ctl | (1 << 2);
break;
case DAVINCI_MMC_DATADIR_READ:
mmcsd_regs->mmc_fifo_ctl = mmcsd_regs->mmc_fifo_ctl | 0x1;
mmcsd_regs->mmc_fifo_ctl = 0x0;
mmcsd_regs->mmc_fifo_ctl = mmcsd_regs->mmc_fifo_ctl | (1 << 2);
break;
default:
break;
}
if ((host->use_dma == 1)
&& (mmc_davinci_start_dma_transfer(host, req) == 0)) {
host->buffer = NULL;
host->bytes_left = 0;
} else {
/* Revert to CPU Copy */
host->buffer = (u32 *) (req->data->req->buffer);
host->bytes_left =
req->data->blocks * (1 << req->data->blksz_bits);
host->use_dma = 0;
}
}
static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req)
{
struct mmc_davinci_host *host = mmc_priv(mmc);
unsigned long flags;
if (!is_card_detect_progress) {
spin_lock_irqsave(&mmc_lock, flags);
is_card_busy = 1;
spin_unlock_irqrestore(&mmc_lock, flags);
mmc_davinci_prepare_data(host, req);
mmc_davinci_start_command(host, req->cmd);
} else {
/* Queu up the request as card dectection is being excuted */
que_mmc_host = mmc;
que_mmc_request = req;
spin_lock_irqsave(&mmc_lock, flags);
is_req_queued_up = 1;
spin_unlock_irqrestore(&mmc_lock, flags);
}
}
static unsigned int calculate_freq_for_card(unsigned int mmc_req_freq)
{
unsigned int mmc_freq = 0, cpu_arm_clk = 0, mmc_push_pull = 0;
cpu_arm_clk = mmc_input_clk;
if (cpu_arm_clk > (2 * mmc_req_freq)) {
mmc_push_pull =
((unsigned int)cpu_arm_clk / (2 * mmc_req_freq)) - 1;
} else {
mmc_push_pull = 0;
}
mmc_freq = (unsigned int)cpu_arm_clk / (2 * (mmc_push_pull + 1));
if (mmc_freq > mmc_req_freq) {
mmc_push_pull = mmc_push_pull + 1;
}
return mmc_push_pull;
}
static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
unsigned short status;
unsigned int open_drain_freq = 0, cpu_arm_clk = 0;
unsigned int mmc_push_pull_freq = 0;
struct mmc_davinci_host *host = mmc_priv(mmc);
cpu_arm_clk = mmc_input_clk;
dev_dbg(&mmc_dev, "clock %dHz busmode %d powermode %d Vdd %d.%02d\r\n",
ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd / 100, ios->vdd % 100);
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
open_drain_freq =
((unsigned int)cpu_arm_clk / (2 * MMCSD_INIT_CLOCK)) - 1;
mmcsd_regs->mmc_clk =
(mmcsd_regs->mmc_clk & ~(0xFF)) | open_drain_freq;
} else {
mmc_push_pull_freq = calculate_freq_for_card(ios->clock);
mmcsd_regs->mmc_clk =
(mmcsd_regs->mmc_clk & ~(0xFF)) | mmc_push_pull_freq;
}
host->bus_mode = ios->bus_mode;
if (ios->power_mode == MMC_POWER_UP) {
/* Send clock cycles, poll completion */
mmcsd_regs->mmc_arghl = 0x0;
mmcsd_regs->mmc_cmd = 0x4000;
status = 0;
while (!(status & (MMCSD_EVENT_EOFCMD))) {
status = mmcsd_regs->mmc_st0;
}
}
}
static void mmc_davinci_xfer_done(struct mmc_davinci_host *host,
struct mmc_data *data)
{
unsigned long flags;
host->data = NULL;
host->data_dir = DAVINCI_MMC_DATADIR_NONE;
if (data->error == MMC_ERR_NONE)
data->bytes_xfered += data->blocks * (1 << data->blksz_bits);
if (data->error == MMC_ERR_TIMEOUT) {
spin_lock_irqsave(&mmc_lock, flags);
is_card_busy = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
mmc_request_done(host->mmc, data->mrq);
return;
}
if (!data->stop) {
host->req = NULL;
spin_lock_irqsave(&mmc_lock, flags);
is_card_busy = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
mmc_request_done(host->mmc, data->mrq);
return;
}
mmc_davinci_start_command(host, data->stop);
}
static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
struct mmc_command *cmd)
{
unsigned long flags;
host->cmd = NULL;
switch (cmd->flags & MMC_RSP_MASK) {
case MMC_RSP_NONE:
/* resp 0 */
break;
case MMC_RSP_SHORT:
/* response types 1, 1b, 3, 4, 5, 6 */
cmd->resp[0] = mmcsd_regs->mmc_rsp67;
break;
case MMC_RSP_LONG:
/* response type 2 */
cmd->resp[3] = mmcsd_regs->mmc_rsp01;
cmd->resp[2] = mmcsd_regs->mmc_rsp23;
cmd->resp[1] = mmcsd_regs->mmc_rsp45;
cmd->resp[0] = mmcsd_regs->mmc_rsp67;
break;
}
if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
host->req = NULL;
if (cmd->error == MMC_ERR_TIMEOUT) {
cmd->mrq->cmd->retries = 0;
}
spin_lock_irqsave(&mmc_lock, flags);
is_card_busy = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
mmc_request_done(host->mmc, cmd->mrq);
}
}
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
u16 status;
int end_command;
int end_transfer;
int byte_cnt = 0, i = 0;
unsigned long flags;
if (host->is_core_command) {
if (host->cmd == NULL && host->data == NULL) {
status = mmcsd_regs->mmc_st0;
dev_dbg(&mmc_dev, "Spurious interrupt 0x%04x\r\n",
status);
/* Disable the interrupt from mmcsd */
mmcsd_regs->mmc_im = 0;
return IRQ_HANDLED;
}
}
end_command = 0;
end_transfer = 0;
status = mmcsd_regs->mmc_st0;
if (status == 0) {
return IRQ_HANDLED;
}
if (host->is_core_command) {
if (is_card_initialized) {
if (new_card_state == 0) {
if (host->cmd) {
host->cmd->error |= MMC_ERR_TIMEOUT;
mmc_davinci_cmd_done(host, host->cmd);
}
dev_dbg(&mmc_dev,
"From code segment excuted when card removed\n");
return IRQ_HANDLED;
}
}
while (status != 0) {
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
if (status & MMCSD_EVENT_WRITE) {
/* Buffer almost empty */
if (host->bytes_left > 0) {
byte_cnt =
mmcsd_cfg.rw_threshold;
host->bytes_left -=
mmcsd_cfg.rw_threshold;
for (i = 0; i < (byte_cnt / 4);
i++) {
mmcsd_regs->mmc_dxr =
*host->buffer;
host->buffer++;
}
}
}
}
if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
if (status & MMCSD_EVENT_READ) {
/* Buffer almost empty */
if (host->bytes_left > 0) {
byte_cnt =
mmcsd_cfg.rw_threshold;
host->bytes_left -=
mmcsd_cfg.rw_threshold;
for (i = 0; i < (byte_cnt / 4);
i++) {
*host->buffer =
mmcsd_regs->mmc_drr;
host->buffer++;
}
}
}
}
if (status & MMCSD_EVENT_BLOCK_XFERRED) {
/* Block sent/received */
if (host->data != NULL) {
end_transfer = 1;
}
}
if (status & MMCSD_EVENT_ERROR_DATATIMEOUT) {
/* Data timeout */
if ((host->data) && (new_card_state != 0)) {
host->data->error |= MMC_ERR_TIMEOUT;
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 0;
is_card_initialized = 0;
spin_unlock_irqrestore(&mmc_lock,
flags);
dev_dbg(&mmc_dev,
"MMCSD: Data timeout, CMD%d and status is %x\r\n",
host->cmd->opcode, status);
end_transfer = 1;
host->cmd->error |= MMC_ERR_TIMEOUT;
}
dev_dbg(&mmc_dev,
"MMCSD: Data timeout, CMD%d and status is %x\r\n",
host->cmd->opcode, status);
}
if (status & MMCSD_EVENT_ERROR_DATACRC) {
/* Data CRC error */
if (host->data) {
host->data->error |= MMC_ERR_BADCRC;
dev_dbg(&mmc_dev,
"MMCSD: Data CRC error, bytes left %d\r\n",
host->bytes_left);
end_transfer = 1;
} else {
dev_dbg(&mmc_dev,
"MMCSD: Data CRC error\r\n");
}
}
if (status & MMCSD_EVENT_ERROR_CMDTIMEOUT) {
/* Command timeout */
if (host->cmd) {
/* Timeouts are normal in case of
* MMC_SEND_STATUS
*/
if (host->cmd->opcode !=
MMC_ALL_SEND_CID) {
dev_dbg(&mmc_dev,
"MMCSD: Command timeout, CMD%d and status is %x\r\n",
host->cmd->opcode,
status);
spin_lock_irqsave(&mmc_lock,
flags);
new_card_state = 0;
is_card_initialized = 0;
spin_unlock_irqrestore
(&mmc_lock, flags);
}
host->cmd->error |= MMC_ERR_TIMEOUT;
end_command = 1;
}
}
if (status & MMCSD_EVENT_ERROR_CMDCRC) {
/* Command CRC error */
dev_dbg(&mmc_dev, "Command CRC error\r\n");
if (host->cmd) {
host->cmd->error |= MMC_ERR_BADCRC;
end_command = 1;
}
}
if (status & MMCSD_EVENT_EOFCMD) {
/* End of command phase */
end_command = 1;
}
if (host->data == NULL) {
status = mmcsd_regs->mmc_st0;
if (status != 0) {
dev_dbg(&mmc_dev,
"Status is %x at end of ISR when host->data is NULL",
status);
status = 0;
}
} else {
status = mmcsd_regs->mmc_st0;
}
}
if (end_command) {
mmc_davinci_cmd_done(host, host->cmd);
}
if (end_transfer) {
mmc_davinci_xfer_done(host, host->data);
}
} else {
if (host->cmd_code == 13) {
if (status & MMCSD_EVENT_EOFCMD) {
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 1;
spin_unlock_irqrestore(&mmc_lock, flags);
} else {
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 0;
is_card_initialized = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
}
spin_lock_irqsave(&mmc_lock, flags);
is_card_detect_progress = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
if (is_req_queued_up) {
mmc_davinci_request(que_mmc_host,
que_mmc_request);
spin_lock_irqsave(&mmc_lock, flags);
is_req_queued_up = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
}
}
if (host->cmd_code == 1) {
if (status & MMCSD_EVENT_EOFCMD) {
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 1;
is_card_initialized = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
} else {
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 0;
is_card_initialized = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
}
spin_lock_irqsave(&mmc_lock, flags);
is_card_detect_progress = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
if (is_req_queued_up) {
mmc_davinci_request(que_mmc_host,
que_mmc_request);
spin_lock_irqsave(&mmc_lock, flags);
is_req_queued_up = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
}
}
if (host->cmd_code == 0) {
if (status & MMCSD_EVENT_EOFCMD) {
host->is_core_command = 0;
host->cmd_code = 1;
dev_dbg(&mmc_dev,
"MMC-Probing mmc with cmd1\n");
/* Issue cmd1 */
mmcsd_regs->mmc_arghl = 0x80300000;
mmcsd_regs->mmc_cmd = 0x00000601;
} else {
spin_lock_irqsave(&mmc_lock, flags);
new_card_state = 0;
is_card_initialized = 0;
is_card_detect_progress = 0;
spin_unlock_irqrestore(&mmc_lock, flags);
}
}
}
return IRQ_HANDLED;
}
static struct mmc_host_ops mmc_davinci_ops = {
.request = mmc_davinci_request,
.set_ios = mmc_davinci_set_ios,
};
void mmc_check_card(unsigned long data)
{
struct mmc_davinci_host *host = (struct mmc_davinci_host *)data;
unsigned long flags;
if ((!is_card_detect_progress) || (!is_init_progress)) {
if (is_card_initialized) {
host->is_core_command = 0;
host->cmd_code = 13;
spin_lock_irqsave(&mmc_lock, flags);
is_card_detect_progress = 1;
spin_unlock_irqrestore(&mmc_lock, flags);
/* Issue cmd13 */
mmcsd_regs->mmc_arghl = 0x10000;
mmcsd_regs->mmc_cmd = 0x0000028D;
} else {
host->is_core_command = 0;
host->cmd_code = 0;
spin_lock_irqsave(&mmc_lock, flags);
is_card_detect_progress = 1;
spin_unlock_irqrestore(&mmc_lock, flags);
/* Issue cmd0 */
mmcsd_regs->mmc_arghl = 0;
mmcsd_regs->mmc_cmd = 0x4000;
}
mmcsd_regs->mmc_im = (MMCSD_EVENT_EOFCMD |
MMCSD_EVENT_ERROR_CMDCRC |
MMCSD_EVENT_ERROR_DATACRC |
MMCSD_EVENT_ERROR_CMDTIMEOUT |
MMCSD_EVENT_ERROR_DATATIMEOUT);
}
}
static void davinci_mmc_check_status(unsigned long data)
{
unsigned long flags;
struct mmc_davinci_host *host = (struct mmc_davinci_host *)data;
if (!is_card_busy) {
if (host->old_card_state ^ new_card_state) {
mmc_detect_change(host->mmc, 0);
spin_lock_irqsave(&mmc_lock, flags);
host->old_card_state = new_card_state;
spin_unlock_irqrestore(&mmc_lock, flags);
} else {
mmc_check_card(data);
}
}
mod_timer(&host->timer, jiffies + MULTIPILER_TO_HZ * HZ);
}
static void init_mmcsd_host(void)
{
/* CMD line portion is diabled and in reset state */
mmcsd_regs->mmc_ctl = mmcsd_regs->mmc_ctl | 0x1;
/* DAT line portion is diabled and in reset state */
mmcsd_regs->mmc_ctl = mmcsd_regs->mmc_ctl | (1 << 1);
mmcsd_regs->mmc_clk = 0x0;
mmcsd_regs->mmc_clk = mmcsd_regs->mmc_clk | (1 << 8);
mmcsd_regs->mmc_tor = 0xFFFF;
mmcsd_regs->mmc_tod = 0xFFFF;
mmcsd_regs->mmc_ctl = mmcsd_regs->mmc_ctl & ~(0x1);
mmcsd_regs->mmc_ctl = mmcsd_regs->mmc_ctl & ~(1 << 1);
}
static int davinci_mmcsd_probe(struct device *dev)
{
struct mmc_davinci_host *host;
struct mmc_host *mmc;
int ret;
mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), dev);
if (!mmc) {
ret = -ENOMEM;
goto out;
}
mmcsd_regs =
(volatile mmcsd_regs_base *)IO_ADDRESS(MMCSD_REGS_BASE_ADDR);
init_mmcsd_host();
mmc->ops = &mmc_davinci_ops;
mmc->f_min = 312500;
mmc->f_max = 20000000;
mmc->ocr_avail = MMC_VDD_32_33;
host = mmc_priv(mmc);
host->mmc = mmc; /* Important */
host->use_dma = mmcsd_cfg.use_dma;
host->irq = MMCINT_INTERRUPT;
host->sd_support = 1;
ret =
request_irq(MMCINT_INTERRUPT, mmc_davinci_irq, 0, DRIVER_NAME,
host);
if (ret)
goto out;
dev_set_drvdata(dev, host);
mmc_add_host(mmc);
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
host->timer.function = davinci_mmc_check_status;
host->timer.expires = jiffies + MULTIPILER_TO_HZ * HZ;
add_timer(&host->timer);
return 0;
out:
/* TBD: Free other resources too. */
return ret;
}
static int davinci_mmcsd_remove(struct device *dev)
{
struct mmc_davinci_host *host = dev_get_drvdata(dev);
dev_set_drvdata(dev, NULL);
if (host) {
mmc_remove_host(host->mmc);
free_irq(host->irq, host);
}
del_timer(&host->timer);
davinci_free_dma(DAVINCI_DMA_MMCTXEVT);
davinci_free_dma(DAVINCI_DMA_MMCRXEVT);
return 0;
}
#ifdef CONFIG_PM
static int davinci_mmcsd_suspend(struct device *dev, u32 state, u32 level)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
int ret = 0;
if (mmc && level == SUSPEND_DISABLE)
ret = mmc_suspend_host(mmc, state);
return ret;
}
static int davinci_mmcsd_resume(struct device *dev, u32 level)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
int ret = 0;
if (mmc && level == RESUME_ENABLE)
ret = mmc_resume_host(mmc);
return ret;
}
#else
#define davinci_mmcsd_suspend NULL
#define davinci_mmcsd_resume NULL
#endif
static struct device_driver davinci_mmcsd_driver = {
.name = DRIVER_NAME,
.bus = &platform_bus_type,
.probe = davinci_mmcsd_probe,
.remove = davinci_mmcsd_remove,
.suspend = davinci_mmcsd_suspend,
.resume = davinci_mmcsd_resume,
};
static void mmc_release(struct device *dev)
{
/* Nothing to release? */
}
static u64 mmc_dma_mask = 0xffffffff;
static struct resource mmc_resources[] = {
{
.start = IO_ADDRESS(MMCSD_REGS_BASE_ADDR),
.end = IO_ADDRESS((MMCSD_REGS_BASE_ADDR) + 0x74),
.flags = IORESOURCE_MEM,
},
{
.start = MMCINT_INTERRUPT,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mmc_davinci_device = {
.name = DRIVER_NAME,
.id = 1,
.dev = {
.release = mmc_release,
.dma_mask = &mmc_dma_mask,
},
.num_resources = ARRAY_SIZE(&mmc_resources),
.resource = mmc_resources,
};
static int davinci_mmcsd_init(void)
{
int ret = 0;
struct clk *clkp = NULL;
clkp = clk_get(NULL, "MMCSDCLK");
if (clkp != NULL) {
mmc_clkp = clkp;
clk_enable(mmc_clkp);
mmc_input_clk = clk_get_rate(mmc_clkp);
ret = platform_device_register(&mmc_davinci_device);
if (ret != 0)
goto free1;
ret = driver_register(&davinci_mmcsd_driver);
mmc_davinci_device.dev.driver = &davinci_mmcsd_driver;
mmc_dev = mmc_davinci_device.dev;
if (ret == 0)
return 0;
free1:
platform_device_unregister(&mmc_davinci_device);
}
return -ENODEV;
}
static void __exit davinci_mmcsd_exit(void)
{
driver_unregister(&davinci_mmcsd_driver);
platform_device_unregister(&mmc_davinci_device);
clk_disable(mmc_clkp);
}
module_init(davinci_mmcsd_init);
module_exit(davinci_mmcsd_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MMCSD driver for Davinci MMC controller");
/*
* linux/drivers/mmc/davinci.h
*
* BRIEF MODULE DESCRIPTION
* DAVINCI MMC register and other definitions
*
* Copyright (C) 2006 Texas Instruments.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
Modifications:
ver. 1.0: Oct 2005, Purushotam Kumar Initial version
ver 1.2: Jan 2006, Purushotam Kumar Added hot card remove insert support
*
*/
#ifndef DAVINCI_MMC_H_
#define DAVINCI_MMC_H_
/**************************************************************************\
* Register Overlay Structure
\**************************************************************************/
typedef struct {
unsigned short mmc_ctl;
unsigned char rsvd0[2];
unsigned short mmc_clk;
unsigned char rsvd1[2];
unsigned short mmc_st0;
unsigned char rsvd2[2];
unsigned short mmc_st1;
unsigned char rsvd3[2];
unsigned short mmc_im;
unsigned char rsvd4[2];
unsigned short mmc_tor;
unsigned char rsvd5[2];
unsigned short mmc_tod;
unsigned char rsvd6[2];
unsigned short mmc_blen;
unsigned char rsvd7[2];
unsigned short mmc_nblk;
unsigned char rsvd8[2];
unsigned short mmc_nblc;
unsigned char rsvd9[2];
unsigned int mmc_drr;
unsigned int mmc_dxr;
unsigned int mmc_cmd;
unsigned int mmc_arghl;
unsigned int mmc_rsp01;
unsigned int mmc_rsp23;
unsigned int mmc_rsp45;
unsigned int mmc_rsp67;
unsigned short mmc_drsp;
unsigned char rsvd10[2];
unsigned short mmc_etok;
unsigned char rsvd11[2];
unsigned short mmc_cidx;
unsigned char rsvd12[2];
unsigned short mmc_ckc;
unsigned char rsvd13[2];
unsigned short mmc_torc;
unsigned char rsvd14[2];
unsigned short mmc_todc;
unsigned char rsvd15[2];
unsigned short mmc_blnc;
unsigned char rsvd16[2];
unsigned short sdio_ctl;
unsigned char rsvd17[2];
unsigned short sdio_st0;
unsigned char rsvd18[2];
unsigned short sdio_en;
unsigned char rsvd19[2];
unsigned short sdio_st;
unsigned char rsvd20[2];
unsigned short mmc_fifo_ctl;
} mmcsd_regs_base;
/*
* Command types
*/
#define DAVINCI_MMC_CMDTYPE_BC 0
#define DAVINCI_MMC_CMDTYPE_BCR 1
#define DAVINCI_MMC_CMDTYPE_AC 2
#define DAVINCI_MMC_CMDTYPE_ADTC 3
#define EDMA_MAX_LOGICAL_CHA_ALLOWED 1
typedef struct {
unsigned char cnt_chanel;
unsigned int chanel_num[EDMA_MAX_LOGICAL_CHA_ALLOWED];
} edma_ch_mmcsd;
struct mmc_davinci_host {
int initialized;
int suspended;
struct mmc_request *req;
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_host *mmc;
struct device *dev;
unsigned char id;
struct clk *clk;
u32 base;
int irq;
unsigned char bus_mode;
#define DAVINCI_MMC_DATADIR_NONE 0
#define DAVINCI_MMC_DATADIR_READ 1
#define DAVINCI_MMC_DATADIR_WRITE 2
unsigned char data_dir;
u32 *buffer;
u32 bytes_left;
int power_pin;
int use_dma;
struct completion dma_completion;
struct timer_list timer;
unsigned int is_core_command;
unsigned int cmd_code;
unsigned int old_card_state;
unsigned char sd_support;
edma_ch_mmcsd edma_ch_details;
};
typedef struct {
unsigned short rw_threshold;
unsigned short use_dma;
} mmcsd_config_def;
typedef enum {
MMCSD_EVENT_EOFCMD = (1 << 2),
MMCSD_EVENT_READ = (1 << 10),
MMCSD_EVENT_WRITE = (1 << 9),
MMCSD_EVENT_ERROR_CMDCRC = (1 << 7),
MMCSD_EVENT_ERROR_DATACRC = ((1 << 6) | (1 << 5)),
MMCSD_EVENT_ERROR_CMDTIMEOUT = (1 << 4),
MMCSD_EVENT_ERROR_DATATIMEOUT = (1 << 3),
MMCSD_EVENT_CARD_EXITBUSY = (1 << 1),
MMCSD_EVENT_BLOCK_XFERRED = (1 << 0)
} mmcsdevent;
#define MMCSD_EVENT_TIMEOUT_ERROR \
(MMCSD_EVENT_ERROR_DATATIMEOUT | MMCSD_EVENT_ERROR_CMDTIMEOUT )
#define MMCSD_EVENT_CRC_ERROR \
(MMCSD_EVENT_ERROR_DATACRC | MMCSD_EVENT_ERROR_CMDCRC)
#define MMCSD_EVENT_ERROR \
(MMCSD_EVENT_TIMEOUT_ERROR | MMCSD_EVENT_CRC_ERROR)
#endif /* DAVINCI_MMC_H_ */
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