/*
* tifm_sd.c - TI FlashMedia driver
*
* Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
*
* 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.
*
* Special thanks to Brad Campbell for extensive testing of this driver.
*
*/
#include <linux/tifm.h>
#include <linux/mmc/host.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_sd"
#define DRIVER_VERSION "0.8"
static int no_dma = 0;
static int fixed_timeout = 0;
module_param(no_dma, bool, 0644);
module_param(fixed_timeout, bool, 0644);
/* Constants here are mostly from OMAP5912 datasheet */
#define TIFM_MMCSD_RESET 0x0002
#define TIFM_MMCSD_CLKMASK 0x03ff
#define TIFM_MMCSD_POWER 0x0800
#define TIFM_MMCSD_4BBUS 0x8000
#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */
#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */
#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */
#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */
#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */
#define TIFM_MMCSD_READ 0x8000
#define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */
#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */
#define TIFM_MMCSD_CD 0x0002 /* card detect */
#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */
#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */
#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */
#define TIFM_MMCSD_DTO 0x0020 /* data time-out */
#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */
#define TIFM_MMCSD_CTO 0x0080 /* command time-out */
#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */
#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */
#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */
#define TIFM_MMCSD_OCRB 0x1000 /* OCR busy */
#define TIFM_MMCSD_CIRQ 0x2000 /* card irq (cmd40/sdio) */
#define TIFM_MMCSD_CERR 0x4000 /* card status error */
#define TIFM_MMCSD_ODTO 0x0040 /* open drain / extended timeout */
#define TIFM_MMCSD_CARD_RO 0x0200 /* card is read-only */
#define TIFM_MMCSD_FIFO_SIZE 0x0020
#define TIFM_MMCSD_RSP_R0 0x0000
#define TIFM_MMCSD_RSP_R1 0x0100
#define TIFM_MMCSD_RSP_R2 0x0200
#define TIFM_MMCSD_RSP_R3 0x0300
#define TIFM_MMCSD_RSP_R4 0x0400
#define TIFM_MMCSD_RSP_R5 0x0500
#define TIFM_MMCSD_RSP_R6 0x0600
#define TIFM_MMCSD_RSP_BUSY 0x0800
#define TIFM_MMCSD_CMD_BC 0x0000
#define TIFM_MMCSD_CMD_BCR 0x1000
#define TIFM_MMCSD_CMD_AC 0x2000
#define TIFM_MMCSD_CMD_ADTC 0x3000
#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL
enum {
CMD_READY = 0x0001,
FIFO_READY = 0x0002,
BRS_READY = 0x0004,
SCMD_ACTIVE = 0x0008,
SCMD_READY = 0x0010,
CARD_BUSY = 0x0020,
DATA_CARRY = 0x0040
};
struct tifm_sd {
struct tifm_dev *dev;
unsigned short eject:1,
open_drain:1,
no_dma:1;
unsigned short cmd_flags;
unsigned int clk_freq;
unsigned int clk_div;
unsigned long timeout_jiffies;
struct tasklet_struct finish_tasklet;
struct timer_list timer;
struct mmc_request *req;
int sg_len;
int sg_pos;
unsigned int block_pos;
struct scatterlist bounce_buf;
unsigned char bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE];
};
/* for some reason, host won't respond correctly to readw/writew */
static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
unsigned int off, unsigned int cnt)
{
struct tifm_dev *sock = host->dev;
unsigned char *buf;
unsigned int pos = 0, val;
buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off;
if (host->cmd_flags & DATA_CARRY) {
buf[pos++] = host->bounce_buf_data[0];
host->cmd_flags &= ~DATA_CARRY;
}
while (pos < cnt) {
val = readl(sock->addr + SOCK_MMCSD_DATA);
buf[pos++] = val & 0xff;
if (pos == cnt) {
host->bounce_buf_data[0] = (val >> 8) & 0xff;
host->cmd_flags |= DATA_CARRY;
break;
}
buf[pos++] = (val >> 8) & 0xff;
}
kunmap_atomic(buf - off, KM_BIO_DST_IRQ);
}
static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
unsigned int off, unsigned int cnt)
{
struct tifm_dev *sock = host->dev;
unsigned char *buf;
unsigned int pos = 0, val;
buf = kmap_atomic