|
@@ -134,6 +134,9 @@ struct mmc_omap_host {
|
|
unsigned char bus_mode;
|
|
unsigned char bus_mode;
|
|
unsigned char hw_bus_mode;
|
|
unsigned char hw_bus_mode;
|
|
|
|
|
|
|
|
+ struct work_struct cmd_abort;
|
|
|
|
+ struct timer_list cmd_timer;
|
|
|
|
+
|
|
unsigned int sg_len;
|
|
unsigned int sg_len;
|
|
int sg_idx;
|
|
int sg_idx;
|
|
u16 * buffer;
|
|
u16 * buffer;
|
|
@@ -314,6 +317,8 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
|
|
if (host->data && !(host->data->flags & MMC_DATA_WRITE))
|
|
if (host->data && !(host->data->flags & MMC_DATA_WRITE))
|
|
cmdreg |= 1 << 15;
|
|
cmdreg |= 1 << 15;
|
|
|
|
|
|
|
|
+ mod_timer(&host->cmd_timer, jiffies + HZ/2);
|
|
|
|
+
|
|
OMAP_MMC_WRITE(host, CTO, 200);
|
|
OMAP_MMC_WRITE(host, CTO, 200);
|
|
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
|
|
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
|
|
OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
|
|
OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
|
|
@@ -372,10 +377,38 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
|
|
mmc_omap_start_command(host, data->stop);
|
|
mmc_omap_start_command(host, data->stop);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void
|
|
|
|
+mmc_omap_send_abort(struct mmc_omap_host *host)
|
|
|
|
+{
|
|
|
|
+ struct mmc_omap_slot *slot = host->current_slot;
|
|
|
|
+ unsigned int restarts, passes, timeout;
|
|
|
|
+ u16 stat = 0;
|
|
|
|
+
|
|
|
|
+ /* Sending abort takes 80 clocks. Have some extra and round up */
|
|
|
|
+ timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
|
|
|
|
+ restarts = 0;
|
|
|
|
+ while (restarts < 10000) {
|
|
|
|
+ OMAP_MMC_WRITE(host, STAT, 0xFFFF);
|
|
|
|
+ OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
|
|
|
|
+
|
|
|
|
+ passes = 0;
|
|
|
|
+ while (passes < timeout) {
|
|
|
|
+ stat = OMAP_MMC_READ(host, STAT);
|
|
|
|
+ if (stat & OMAP_MMC_STAT_END_OF_CMD)
|
|
|
|
+ goto out;
|
|
|
|
+ udelay(1);
|
|
|
|
+ passes++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ restarts++;
|
|
|
|
+ }
|
|
|
|
+out:
|
|
|
|
+ OMAP_MMC_WRITE(host, STAT, stat);
|
|
|
|
+}
|
|
|
|
+
|
|
static void
|
|
static void
|
|
mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
|
|
mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
|
|
{
|
|
{
|
|
- int loops;
|
|
|
|
u16 ie;
|
|
u16 ie;
|
|
|
|
|
|
if (host->dma_in_use)
|
|
if (host->dma_in_use)
|
|
@@ -386,16 +419,8 @@ mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
|
|
|
|
|
|
ie = OMAP_MMC_READ(host, IE);
|
|
ie = OMAP_MMC_READ(host, IE);
|
|
OMAP_MMC_WRITE(host, IE, 0);
|
|
OMAP_MMC_WRITE(host, IE, 0);
|
|
- OMAP_MMC_WRITE(host, CMD, 1 << 7);
|
|
|
|
- loops = 0;
|
|
|
|
- while (!(OMAP_MMC_READ(host, STAT) & OMAP_MMC_STAT_END_OF_CMD)) {
|
|
|
|
- udelay(1);
|
|
|
|
- loops++;
|
|
|
|
- if (loops == 100000)
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- OMAP_MMC_WRITE(host, STAT, OMAP_MMC_STAT_END_OF_CMD);
|
|
|
|
OMAP_MMC_WRITE(host, IE, ie);
|
|
OMAP_MMC_WRITE(host, IE, ie);
|
|
|
|
+ mmc_omap_send_abort(host);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|
|
@@ -451,6 +476,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
|
|
{
|
|
{
|
|
host->cmd = NULL;
|
|
host->cmd = NULL;
|
|
|
|
|
|
|
|
+ del_timer(&host->cmd_timer);
|
|
|
|
+
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
if (cmd->flags & MMC_RSP_136) {
|
|
if (cmd->flags & MMC_RSP_136) {
|
|
/* response type 2 */
|
|
/* response type 2 */
|
|
@@ -486,6 +513,47 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Abort stuck command. Can occur when card is removed while it is being
|
|
|
|
+ * read.
|
|
|
|
+ */
|
|
|
|
+static void mmc_omap_abort_command(struct work_struct *work)
|
|
|
|
+{
|
|
|
|
+ struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
|
|
|
|
+ cmd_abort);
|
|
|
|
+ u16 ie;
|
|
|
|
+
|
|
|
|
+ ie = OMAP_MMC_READ(host, IE);
|
|
|
|
+ OMAP_MMC_WRITE(host, IE, 0);
|
|
|
|
+
|
|
|
|
+ if (!host->cmd) {
|
|
|
|
+ OMAP_MMC_WRITE(host, IE, ie);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
|
|
|
|
+ host->cmd->opcode);
|
|
|
|
+
|
|
|
|
+ if (host->data && host->dma_in_use)
|
|
|
|
+ mmc_omap_release_dma(host, host->data, 1);
|
|
|
|
+
|
|
|
|
+ host->data = NULL;
|
|
|
|
+ host->sg_len = 0;
|
|
|
|
+
|
|
|
|
+ mmc_omap_send_abort(host);
|
|
|
|
+ host->cmd->error = -ETIMEDOUT;
|
|
|
|
+ mmc_omap_cmd_done(host, host->cmd);
|
|
|
|
+ OMAP_MMC_WRITE(host, IE, ie);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+mmc_omap_cmd_timer(unsigned long data)
|
|
|
|
+{
|
|
|
|
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
|
|
|
|
+
|
|
|
|
+ schedule_work(&host->cmd_abort);
|
|
|
|
+}
|
|
|
|
+
|
|
/* PIO only */
|
|
/* PIO only */
|
|
static void
|
|
static void
|
|
mmc_omap_sg_to_buf(struct mmc_omap_host *host)
|
|
mmc_omap_sg_to_buf(struct mmc_omap_host *host)
|
|
@@ -1233,6 +1301,11 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
|
|
goto err_free_mem_region;
|
|
goto err_free_mem_region;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ INIT_WORK(&host->cmd_abort, mmc_omap_abort_command);
|
|
|
|
+ init_timer(&host->cmd_timer);
|
|
|
|
+ host->cmd_timer.function = mmc_omap_cmd_timer;
|
|
|
|
+ host->cmd_timer.data = (unsigned long) host;
|
|
|
|
+
|
|
spin_lock_init(&host->dma_lock);
|
|
spin_lock_init(&host->dma_lock);
|
|
init_timer(&host->dma_timer);
|
|
init_timer(&host->dma_timer);
|
|
spin_lock_init(&host->slot_lock);
|
|
spin_lock_init(&host->slot_lock);
|