#include <asm/io.h>
#define DRIVER_NAME "tifm_sd"
-#define DRIVER_VERSION "0.7"
+#define DRIVER_VERSION "0.8"
static int no_dma = 0;
static int fixed_timeout = 0;
#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */
#define TIFM_MMCSD_READ 0x8000
-#define TIFM_MMCSD_DATAMASK 0x001d /* set bits: EOFB, BRS, CB, EOC */
-#define TIFM_MMCSD_ERRMASK 0x41e0 /* set bits: CERR, CCRC, CTO, DCRC, DTO */
+#define TIFM_MMCSD_DATAMASK 0x401d /* set bits: CERR, EOFB, BRS, CB, EOC */
+#define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */
#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */
#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */
#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */
enum {
FIFO_RDY = 0x0001, /* hardware dependent value */
EJECT = 0x0004,
- EJECT_DONE = 0x0008,
CARD_BUSY = 0x0010,
OPENDRAIN = 0x0040, /* hardware dependent value */
CARD_EVENT = 0x0100, /* hardware dependent value */
struct tasklet_struct finish_tasklet;
struct timer_list timer;
struct mmc_request *req;
- wait_queue_head_t notify;
size_t written_blocks;
size_t buffer_size;
};
-static char* tifm_sd_kmap_atomic(struct mmc_data *data)
+static char* tifm_sd_data_buffer(struct mmc_data *data)
{
- return kmap_atomic(data->sg->page, KM_BIO_SRC_IRQ) + data->sg->offset;
-}
-
-static void tifm_sd_kunmap_atomic(char *buffer, struct mmc_data *data)
-{
- kunmap_atomic(buffer - data->sg->offset, KM_BIO_SRC_IRQ);
+ return page_address(data->sg->page) + data->sg->offset;
}
static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host,
if (host_status & TIFM_MMCSD_BRS) {
/* in non-dma rx mode BRS fires when fifo is still not empty */
if (no_dma && (cmd->data->flags & MMC_DATA_READ)) {
- buffer = tifm_sd_kmap_atomic(host->req->data);
+ buffer = tifm_sd_data_buffer(host->req->data);
while (host->buffer_size > host->buffer_pos) {
t_val = readl(sock->addr + SOCK_MMCSD_DATA);
buffer[host->buffer_pos++] = t_val & 0xff;
buffer[host->buffer_pos++] =
(t_val >> 8) & 0xff;
}
- tifm_sd_kunmap_atomic(buffer, host->req->data);
}
return 1;
} else if (no_dma) {
- buffer = tifm_sd_kmap_atomic(host->req->data);
+ buffer = tifm_sd_data_buffer(host->req->data);
if ((cmd->data->flags & MMC_DATA_READ) &&
(host_status & TIFM_MMCSD_AF)) {
for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) {
}
}
}
- tifm_sd_kunmap_atomic(buffer, host->req->data);
}
return 0;
}
case IDLE:
return;
case CMD:
- if (host_status & TIFM_MMCSD_EOC) {
+ if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) {
tifm_sd_fetch_resp(cmd, sock);
if (cmd->data) {
host->state = BRS;
}
/* Called from interrupt handler */
-static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock,
- unsigned int sock_irq_status)
+static void tifm_sd_data_event(struct tifm_dev *sock)
{
struct tifm_sd *host;
- unsigned int host_status = 0, fifo_status = 0;
- int error_code = 0;
+ unsigned int fifo_status = 0;
spin_lock(&sock->lock);
host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
- if (sock_irq_status & FIFO_EVENT) {
- fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
- writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
+ fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
+ writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
+
+ host->flags |= fifo_status & FIFO_RDY;
+
+ if (host->req)
+ tifm_sd_process_cmd(sock, host, 0);
+
+ dev_dbg(&sock->dev, "fifo_status %x\n", fifo_status);
+ spin_unlock(&sock->lock);
+
+}
+
+/* Called from interrupt handler */
+static void tifm_sd_card_event(struct tifm_dev *sock)
+{
+ struct tifm_sd *host;
+ unsigned int host_status = 0;
+ int error_code = 0;
+
+ spin_lock(&sock->lock);
+ host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
- host->flags |= fifo_status & FIFO_RDY;
- }
- if (sock_irq_status & CARD_EVENT) {
host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
goto done;
if (host_status & TIFM_MMCSD_ERRMASK) {
- if (host_status & TIFM_MMCSD_CERR)
- error_code = MMC_ERR_FAILED;
- else if (host_status
- & (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO))
+ if (host_status & (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO))
error_code = MMC_ERR_TIMEOUT;
else if (host_status
& (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC))
host->written_blocks++;
host->flags &= ~CARD_BUSY;
}
- }
if (host->req)
tifm_sd_process_cmd(sock, host, host_status);
done:
- dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n",
- host_status, fifo_status);
+ dev_dbg(&sock->dev, "host_status %x\n", host_status);
spin_unlock(&sock->lock);
- return sock_irq_status;
}
static void tifm_sd_prepare_data(struct tifm_sd *host, struct mmc_command *cmd)
mmc_request_done(mmc, mrq);
}
-static void tifm_sd_terminate(struct tifm_sd *host)
-{
- struct tifm_dev *sock = host->dev;
- unsigned long flags;
-
- writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
- mmiowb();
- spin_lock_irqsave(&sock->lock, flags);
- host->flags |= EJECT;
- if (host->req) {
- writel(TIFM_FIFO_INT_SETALL,
- sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
- writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
- tasklet_schedule(&host->finish_tasklet);
- }
- spin_unlock_irqrestore(&sock->lock, flags);
-}
-
static void tifm_sd_abort(unsigned long data)
{
struct tifm_sd *host = (struct tifm_sd*)data;
printk(KERN_ERR DRIVER_NAME
- ": card failed to respond for a long period of time");
+ ": card failed to respond for a long period of time\n");
- tifm_sd_terminate(host);
tifm_eject(host->dev);
}
/* chip_select : maybe later */
//vdd
//power is set before probe / after remove
- //I believe, power_off when already marked for eject is sufficient to
- // allow removal.
- if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) {
- host->flags |= EJECT_DONE;
- wake_up_all(&host->notify);
- }
spin_unlock_irqrestore(&sock->lock, flags);
}
host->dev = sock;
host->timeout_jiffies = msecs_to_jiffies(1000);
- init_waitqueue_head(&host->notify);
tasklet_init(&host->finish_tasklet,
no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd,
(unsigned long)host);
mmc->f_max = 24000000;
mmc->max_hw_segs = 1;
mmc->max_phys_segs = 1;
- mmc->max_sectors = 127;
- mmc->max_seg_size = mmc->max_sectors << 11; //2k maximum hw block length
- sock->signal_irq = tifm_sd_signal_irq;
+ // limited by DMA counter - it's safer to stick with
+ // block counter has 11 bits though
+ mmc->max_blk_count = 256;
+ // 2k maximum hw block length
+ mmc->max_blk_size = 2048;
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_seg_size = mmc->max_req_size;
+ sock->card_event = tifm_sd_card_event;
+ sock->data_event = tifm_sd_data_event;
rc = tifm_sd_initialize_host(host);
if (!rc)
{
struct mmc_host *mmc = tifm_get_drvdata(sock);
struct tifm_sd *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sock->lock, flags);
+ host->flags |= EJECT;
+ writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
+ mmiowb();
+ spin_unlock_irqrestore(&sock->lock, flags);
- del_timer_sync(&host->timer);
- tifm_sd_terminate(host);
- wait_event_timeout(host->notify, host->flags & EJECT_DONE,
- host->timeout_jiffies);
tasklet_kill(&host->finish_tasklet);
+
+ spin_lock_irqsave(&sock->lock, flags);
+ if (host->req) {
+ writel(TIFM_FIFO_INT_SETALL,
+ sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+ writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+ host->req->cmd->error = MMC_ERR_TIMEOUT;
+ if (host->req->stop)
+ host->req->stop->error = MMC_ERR_TIMEOUT;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ spin_unlock_irqrestore(&sock->lock, flags);
mmc_remove_host(mmc);
+ dev_dbg(&sock->dev, "after remove\n");
/* The meaning of the bit majority in this constant is unknown. */
writel(0xfff8 & readl(sock->addr + SOCK_CONTROL),
sock->addr + SOCK_CONTROL);
- tifm_set_drvdata(sock, NULL);
mmc_free_host(mmc);
}
-static tifm_media_id tifm_sd_id_tbl[] = {
- FM_SD, 0
+#ifdef CONFIG_PM
+
+static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)
+{
+ struct mmc_host *mmc = tifm_get_drvdata(sock);
+ int rc;
+
+ rc = mmc_suspend_host(mmc, state);
+ /* The meaning of the bit majority in this constant is unknown. */
+ writel(0xfff8 & readl(sock->addr + SOCK_CONTROL),
+ sock->addr + SOCK_CONTROL);
+ return rc;
+}
+
+static int tifm_sd_resume(struct tifm_dev *sock)
+{
+ struct mmc_host *mmc = tifm_get_drvdata(sock);
+ struct tifm_sd *host = mmc_priv(mmc);
+
+ if (sock->type != TIFM_TYPE_SD
+ || tifm_sd_initialize_host(host)) {
+ tifm_eject(sock);
+ return 0;
+ } else {
+ return mmc_resume_host(mmc);
+ }
+}
+
+#else
+
+#define tifm_sd_suspend NULL
+#define tifm_sd_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct tifm_device_id tifm_sd_id_tbl[] = {
+ { TIFM_TYPE_SD }, { }
};
static struct tifm_driver tifm_sd_driver = {
},
.id_table = tifm_sd_id_tbl,
.probe = tifm_sd_probe,
- .remove = tifm_sd_remove
+ .remove = tifm_sd_remove,
+ .suspend = tifm_sd_suspend,
+ .resume = tifm_sd_resume
};
static int __init tifm_sd_init(void)