Files
efinix-linux/drivers/mmc/host/efx_emmc_core.c
2026-08-09 22:42:42 -07:00

976 lines
26 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Efinix eMMC Host Controller Core Operations
*
* Copyright (C) 2025 Efinix, Inc.
* Author: Teoh Choon Zone <czteoh@efinixinc.com>
*/
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/jiffies.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include "efx_emmc.h"
static void efx_emmc_complete_pending_request(struct efx_emmc_host *host)
{
struct mmc_request *mrq;
mrq = READ_ONCE(host->mrq_done);
if (!mrq)
return;
WRITE_ONCE(host->mrq_done, NULL);
mmc_request_done(host->mmc, mrq);
}
bool efx_emmc_card_busy(struct efx_emmc_host *host)
{
return !!(efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE) &
EFX_EMMC_DAT_LINE_ACTIVE);
}
static bool efx_emmc_need_stop_command(struct mmc_data *data,
struct mmc_request *mrq)
{
if (data->blocks <= 1)
return false;
if (!data->stop)
return false;
if (mrq->sbc)
return false;
return true;
}
void efx_emmc_finish_request(struct efx_emmc_host *host,
struct mmc_request *mrq)
{
if (host->data) {
efx_emmc_cleanup_dma(host, host->data);
}
host->mrq = NULL;
host->cmd = NULL;
host->data = NULL;
host->bytes_to_transfer = 0;
host->blocks_done = 0;
host->sg_offset = 0;
host->use_dma = false;
if (mrq)
WRITE_ONCE(host->mrq_done, mrq);
}
static void efx_emmc_transfer_pio_read(struct efx_emmc_host *host)
{
struct mmc_data *data;
struct scatterlist *sg;
u32 *buf;
int words_in_fifo, words_transferred, i;
unsigned int sg_offset, remaining_in_sg, words_to_transfer;
u32 present_state;
data = host->data;
if (!data) {
return;
}
sg = data->sg;
if (!sg) {
dev_err(&host->pdev->dev, "No scatter-gather list for read\n");
return;
}
words_in_fifo = data->blksz / sizeof(u32);
sg_offset = host->sg_offset;
words_transferred = 0;
while (words_transferred < words_in_fifo &&
host->bytes_to_transfer > 0) {
while (sg && sg_offset >= sg->length) {
sg_offset -= sg->length;
sg = sg_next(sg);
}
if (!sg) {
dev_err(&host->pdev->dev,
"No more scatter-gather entries\n");
break;
}
buf = (u32 *)(sg_virt(sg) + sg_offset);
remaining_in_sg = sg->length - sg_offset;
words_to_transfer = min3(remaining_in_sg / sizeof(u32),
(unsigned int)(words_in_fifo -
words_transferred),
(unsigned int)(host->bytes_to_transfer /
sizeof(u32)));
if (words_to_transfer == 0) {
break;
}
for (i = 0; i < words_to_transfer; i++) {
buf[i] = efx_emmc_readl(host, EFX_EMMC_BUFFER_DATA_PORT);
}
words_transferred += words_to_transfer;
sg_offset += words_to_transfer * 4;
host->bytes_to_transfer -= words_to_transfer * 4;
efx_emmc_dbg_pio(host,
"PIO read: %d words, %d bytes remaining\n",
words_to_transfer, host->bytes_to_transfer);
}
host->sg_offset += words_transferred * 4;
host->blocks_done = (data->blksz * data->blocks -
host->bytes_to_transfer) / data->blksz;
efx_emmc_dbg_pio(host,
"PIO read completed: %d words total, "
"%d blocks done, %d bytes remaining\n",
words_transferred, host->blocks_done,
host->bytes_to_transfer);
present_state = efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE);
efx_emmc_dbg_pio(host, "Present state after PIO read: 0x%08x\n",
present_state);
}
static void efx_emmc_transfer_pio_write(struct efx_emmc_host *host)
{
struct mmc_data *data;
struct scatterlist *sg;
u32 *buf;
int words_in_fifo, words_transferred, i;
unsigned int sg_offset, remaining_in_sg, words_to_transfer;
u32 present_state;
data = host->data;
if (!data) {
return;
}
sg = data->sg;
if (!sg) {
dev_err(&host->pdev->dev, "No scatter-gather list for write\n");
return;
}
words_in_fifo = data->blksz / sizeof(u32);
sg_offset = host->sg_offset;
words_transferred = 0;
while (words_transferred < words_in_fifo &&
host->bytes_to_transfer > 0) {
while (sg && sg_offset >= sg->length) {
sg_offset -= sg->length;
sg = sg_next(sg);
}
if (!sg) {
dev_err(&host->pdev->dev,
"No more scatter-gather entries\n");
break;
}
buf = (u32 *)(sg_virt(sg) + sg_offset);
remaining_in_sg = sg->length - sg_offset;
words_to_transfer = min3(remaining_in_sg / sizeof(u32),
(unsigned int)(words_in_fifo -
words_transferred),
(unsigned int)(host->bytes_to_transfer /
sizeof(u32)));
if (words_to_transfer == 0) {
break;
}
for (i = 0; i < words_to_transfer; i++) {
efx_emmc_writel(host, buf[i], EFX_EMMC_BUFFER_DATA_PORT);
}
words_transferred += words_to_transfer;
sg_offset += words_to_transfer * 4;
host->bytes_to_transfer -= words_to_transfer * 4;
efx_emmc_dbg_pio(host,
"PIO write: %d words, %d bytes remaining\n",
words_to_transfer, host->bytes_to_transfer);
}
host->sg_offset += words_transferred * 4;
host->blocks_done = (data->blksz * data->blocks -
host->bytes_to_transfer) / data->blksz;
efx_emmc_dbg_pio(host,
"PIO write completed: %d words total, %d blocks done, "
"%d bytes remaining\\n",
words_transferred, host->blocks_done,
host->bytes_to_transfer);
present_state = efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE);
efx_emmc_dbg_pio(host, "Present state after PIO write: 0x%08x\n",
present_state);
}
void efx_emmc_transfer_pio(struct efx_emmc_host *host)
{
struct mmc_data *data;
u32 present_state;
data = host->data;
if (!data) {
return;
}
present_state = efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE);
if (data->flags & MMC_DATA_READ) {
if (present_state & EFX_EMMC_BUFFER_READ_EN) {
efx_emmc_transfer_pio_read(host);
}
} else {
if (present_state & EFX_EMMC_BUFFER_WRITE_EN) {
efx_emmc_transfer_pio_write(host);
}
}
}
void efx_emmc_finish_data(struct efx_emmc_host *host)
{
struct mmc_data *data;
u32 present_state;
int retry_count;
data = host->data;
if (!data) {
return;
}
if (data->error == 0) {
data->bytes_xfered = data->blksz * data->blocks;
efx_emmc_dbg_cmd(host,
"Data transfer completed: %d bytes (DMA: %s)\n",
data->bytes_xfered,
host->use_dma ? "enabled" : "disabled");
} else {
data->bytes_xfered = 0;
dev_err(&host->pdev->dev,
"Data transfer failed with error %d\n",
data->error);
}
efx_emmc_cleanup_dma(host, data);
host->data = NULL;
host->bytes_to_transfer = 0;
host->blocks_done = 0;
host->sg_offset = 0;
if (efx_emmc_need_stop_command(data, host->mrq) && !data->error) {
retry_count = 0;
while (retry_count < 100) {
present_state = efx_emmc_readl(host,
EFX_EMMC_PRESENT_STATE);
if (!(present_state & (EFX_EMMC_DAT_LINE_ACTIVE |
EFX_EMMC_READ_XFER_ACTIVE |
EFX_EMMC_WRITE_XFER_ACTIVE))) {
break;
}
udelay(10);
retry_count++;
}
if (retry_count >= 100) {
dev_warn(&host->pdev->dev,
"Data lines still active before CMD12, proceeding anyway\n");
}
udelay(100);
efx_emmc_dbg_cmd(host,
"Sending CMD12 (STOP) for %d-block transfer\n",
data->blocks);
host->cmd = data->stop;
efx_emmc_send_command(host, data->stop);
} else {
efx_emmc_dbg_cmd(host, "Finishing request\n");
if (host->mrq) {
efx_emmc_finish_request(host, host->mrq);
}
}
}
void efx_emmc_finish_command(struct efx_emmc_host *host)
{
struct mmc_command *cmd;
u32 resp[4];
u32 present_state;
cmd = host->cmd;
if (!cmd) {
return;
}
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
resp[0] = efx_emmc_readl(host, EFX_EMMC_RESPONSE0);
resp[1] = efx_emmc_readl(host, EFX_EMMC_RESPONSE1);
resp[2] = efx_emmc_readl(host, EFX_EMMC_RESPONSE2);
resp[3] = efx_emmc_readl(host, EFX_EMMC_RESPONSE3) & 0xFFFFFF;
cmd->resp[0] = resp[3] << 8 | resp[2] >> 24;
cmd->resp[1] = resp[2] << 8 | resp[1] >> 24;
cmd->resp[2] = resp[1] << 8 | resp[0] >> 24;
cmd->resp[3] = resp[0] << 8;
efx_emmc_dbg_cmd(host,
"CMD%d 136-bit response: %08x %08x %08x %08x\n",
cmd->opcode, cmd->resp[0], cmd->resp[1],
cmd->resp[2], cmd->resp[3]);
} else {
cmd->resp[0] = efx_emmc_readl(host, EFX_EMMC_RESPONSE0);
efx_emmc_dbg_cmd(host, "CMD%d response: 0x%08x\n",
cmd->opcode, cmd->resp[0]);
}
}
if (cmd->opcode == 6 && cmd->error == 0) {
u32 arg = cmd->arg;
u8 index = (arg >> 16) & 0xFF;
u8 value = (arg >> 8) & 0xFF;
if (index == 185) {
switch (value) {
case 0:
case 1:
dev_dbg(&host->pdev->dev,
"CMD6 mode switch to legacy/HS completed\n");
efx_emmc_set_timing_config(host, 0, 0);
break;
case 2:
dev_dbg(&host->pdev->dev,
"CMD6 mode switch to DDR52 completed\n");
efx_emmc_set_timing_config(host, 2, 2);
break;
case 3:
dev_dbg(&host->pdev->dev,
"CMD6 mode switch to HS200 completed\n");
break;
default:
efx_emmc_dbg_cmd(host,
"CMD6 mode switch to timing value %u\n",
value);
break;
}
efx_emmc_writel(host, 0xFFFFFFFF, EFX_EMMC_INT_STATUS);
}
}
if (cmd->opcode == 23 && host->mrq && host->mrq->cmd) {
efx_emmc_dbg_cmd(host,
"CMD23 complete, sending main command CMD%d\n",
host->mrq->cmd->opcode);
host->cmd = host->mrq->cmd;
efx_emmc_send_command(host, host->mrq->cmd);
return;
}
host->cmd = NULL;
if (!host->data) {
efx_emmc_dbg_cmd(host, "Command complete, finishing request\n");
if (host->mrq) {
efx_emmc_finish_request(host, host->mrq);
}
} else {
efx_emmc_dbg_cmd(host,
"Command complete, data transfer continues (DMA: %s)\n",
host->use_dma ? "enabled" : "disabled");
if (!host->use_dma) {
bool buffer_ready_read, buffer_ready_write;
present_state = efx_emmc_readl(host,
EFX_EMMC_PRESENT_STATE);
efx_emmc_dbg_cmd(host,
"Present state after command: 0x%08x\n",
present_state);
buffer_ready_read = !!(present_state & EFX_EMMC_BUFFER_READ_EN);
buffer_ready_write = !!(present_state & EFX_EMMC_BUFFER_WRITE_EN);
if (host->data->flags & MMC_DATA_READ) {
if (buffer_ready_read) {
efx_emmc_dbg_pio(host,
"Buffer immediately ready for read, starting PIO\n");
efx_emmc_transfer_pio(host);
}
} else {
if (buffer_ready_write) {
efx_emmc_dbg_pio(host,
"Buffer immediately ready for write, starting PIO\n");
efx_emmc_transfer_pio(host);
}
}
}
}
}
void efx_emmc_send_command(struct efx_emmc_host *host, struct mmc_command *cmd)
{
u32 command, present_state;
unsigned long timeout;
struct mmc_data *data;
u16 cmd_timeout;
data = cmd->data;
host->cmd = cmd;
efx_emmc_dbg_cmd(host, "Sending CMD%d, arg=0x%08x%s\n",
cmd->opcode, cmd->arg, (cmd->opcode == 12) ? " (STOP)" : "");
if (cmd->opcode == 6) {
u32 arg = cmd->arg;
u8 index = (arg >> 16) & 0xFF;
u8 value = (arg >> 8) & 0xFF;
if (index == 185) {
dev_dbg(&host->pdev->dev,
"CMD6 mode switch detected (index=%u, value=%u)\n",
index, value);
efx_emmc_set_timing_config(host, 0, 0);
msleep(10);
efx_emmc_writel(host, 0xFFFFFFFF, EFX_EMMC_INT_STATUS);
}
}
switch (cmd->opcode) {
case 6:
cmd_timeout = 2000;
break;
case 12:
cmd_timeout = 1000;
break;
case 23:
cmd_timeout = 500;
break;
default:
cmd_timeout = 500;
break;
}
timeout = jiffies + msecs_to_jiffies(cmd_timeout);
while (time_before(jiffies, timeout)) {
present_state = efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE);
if (!(present_state & EFX_EMMC_CMD_INHIBIT_CMD)) {
if (!data || !(present_state & EFX_EMMC_CMD_INHIBIT_DAT)) {
break;
}
}
cpu_relax();
}
if (time_after_eq(jiffies, timeout)) {
dev_err(&host->pdev->dev,
"Command line timeout, present_state=0x%08x\n",
efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE));
cmd->error = -ETIMEDOUT;
if (host->mrq) {
efx_emmc_finish_request(host, host->mrq);
}
return;
}
efx_emmc_writel(host, cmd->arg, EFX_EMMC_ARG1);
command = (cmd->opcode << EFX_EMMC_CMD_INDEX_SHIFT) &
EFX_EMMC_CMD_INDEX_MASK;
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
command |= (EFX_EMMC_RESP_TYPE_136 << EFX_EMMC_RESP_TYPE_SHIFT);
} else if (cmd->flags & MMC_RSP_BUSY) {
command |= (EFX_EMMC_RESP_TYPE_48_BUSY << EFX_EMMC_RESP_TYPE_SHIFT);
} else {
command |= (EFX_EMMC_RESP_TYPE_48 << EFX_EMMC_RESP_TYPE_SHIFT);
}
if (cmd->flags & MMC_RSP_CRC) {
command |= EFX_EMMC_CMD_CRC_CHECK_EN;
}
if (cmd->flags & MMC_RSP_OPCODE) {
command |= EFX_EMMC_CMD_INDEX_CHECK_EN;
}
}
if (data) {
command |= EFX_EMMC_DATA_PRESENT;
host->use_dma = (data->blksz * data->blocks >= 512);
if (host->use_dma) {
efx_emmc_prepare_dma(host, data);
}
efx_emmc_writel(host, (data->blocks << EFX_EMMC_BLOCK_COUNT_SHIFT) |
(data->blksz & EFX_EMMC_BLOCK_SIZE_MASK),
EFX_EMMC_BLOCK_SIZE);
if (data->blocks > 1) {
command |= EFX_EMMC_MULTI_BLOCK_SEL;
command |= EFX_EMMC_BLOCK_COUNT_EN;
if (host->mrq && host->mrq->sbc) {
efx_emmc_dbg_cmd(host,
"Multi-block transfer: CMD23 used, no CMD12 needed\n");
} else {
efx_emmc_dbg_cmd(host,
"Multi-block transfer: manual CMD12 will be used\n");
}
}
if (data->flags & MMC_DATA_READ) {
command |= EFX_EMMC_DATA_XFER_DIR;
}
if (host->use_dma) {
command |= EFX_EMMC_DMA_EN;
efx_emmc_dbg_cmd(host, "DMA enabled for data transfer\n");
} else {
host->bytes_to_transfer = data->blksz * data->blocks;
host->blocks_done = 0;
host->sg_offset = 0;
efx_emmc_dbg_cmd(host, "Using PIO for data transfer\n");
}
host->data = data;
efx_emmc_dbg_cmd(host, "Data transfer: %d blocks of %d bytes, %s, %s\n",
data->blocks, data->blksz,
(data->flags & MMC_DATA_READ) ? "read" : "write",
host->use_dma ? "DMA" : "PIO");
}
efx_emmc_dbg_cmd(host, "Command register: 0x%08x\n", command);
efx_emmc_writel(host, command, EFX_EMMC_TRANSFER_MODE);
}
void efx_emmc_set_clock(struct efx_emmc_host *host, unsigned int clock)
{
u32 div, reg;
unsigned long timeout;
if (clock == 0) {
reg = efx_emmc_readl(host, EFX_EMMC_BASE_REG0);
reg &= ~EFX_EMMC_BASE_REG0_CLK_EN;
efx_emmc_writel(host, reg, EFX_EMMC_BASE_REG0);
host->current_clk = 0;
return;
}
if (clock > EFX_EMMC_MAX_FREQ) {
clock = EFX_EMMC_MAX_FREQ;
}
if (clock >= host->base_clk) {
div = 1;
} else {
div = (host->base_clk + clock - 1) / clock;
if (div > 1 && (div & 1)) {
div += 1;
}
}
reg = efx_emmc_readl(host, EFX_EMMC_BASE_REG0);
reg &= ~EFX_EMMC_BASE_REG0_CLK_EN;
efx_emmc_writel(host, reg, EFX_EMMC_BASE_REG0);
reg = (reg & ~EFX_EMMC_BASE_REG0_CLK_DIV_MASK) |
(div & EFX_EMMC_BASE_REG0_CLK_DIV_MASK);
efx_emmc_writel(host, reg, EFX_EMMC_BASE_REG0);
reg |= EFX_EMMC_BASE_REG0_CLK_EN;
efx_emmc_writel(host, reg, EFX_EMMC_BASE_REG0);
if (clock <= 400000) {
usleep_range(500, 1000);
} else if (clock <= 25000000) {
usleep_range(100, 200);
} else {
usleep_range(50, 100);
}
timeout = jiffies + msecs_to_jiffies(50);
while (time_before(jiffies, timeout)) {
if (!(efx_emmc_readl(host, EFX_EMMC_BASE_STATUS_REG0) &
(EFX_EMMC_BASE_STATUS_CMD_BUSY |
EFX_EMMC_BASE_STATUS_DAT_BUSY))) {
break;
}
cpu_relax();
}
host->current_clk = host->base_clk / div;
host->clk_div = div;
dev_dbg(&host->pdev->dev, "Set clock to %u Hz (div=%u, actual=%u)\n",
clock, div, host->current_clk);
}
void efx_emmc_set_bus_width(struct efx_emmc_host *host, int width)
{
u32 reg;
reg = efx_emmc_readl(host, EFX_EMMC_HOST_CONTROL);
reg &= ~EFX_EMMC_DATA_WIDTH_MASK;
switch (width) {
case MMC_BUS_WIDTH_1:
reg |= (EFX_EMMC_DATA_WIDTH_1BIT << EFX_EMMC_DATA_WIDTH_SHIFT);
break;
case MMC_BUS_WIDTH_4:
reg |= (EFX_EMMC_DATA_WIDTH_4BIT << EFX_EMMC_DATA_WIDTH_SHIFT);
break;
case MMC_BUS_WIDTH_8:
reg |= (EFX_EMMC_DATA_WIDTH_8BIT << EFX_EMMC_DATA_WIDTH_SHIFT);
break;
default:
dev_warn(&host->pdev->dev, "Unsupported bus width: %d\n", width);
return;
}
efx_emmc_writel(host, reg, EFX_EMMC_HOST_CONTROL);
dev_dbg(&host->pdev->dev, "Set bus width to %d bits\n", width);
}
void efx_emmc_set_timing(struct efx_emmc_host *host, unsigned int timing)
{
u32 reg;
bool needs_tuning = false;
reg = efx_emmc_readl(host, EFX_EMMC_HOST_CONTROL);
switch (timing) {
case MMC_TIMING_LEGACY:
case MMC_TIMING_MMC_HS:
reg &= ~EFX_EMMC_DATA_SAMPLING_MODE;
break;
case MMC_TIMING_MMC_DDR52:
reg |= EFX_EMMC_DATA_SAMPLING_MODE;
host->tuning_done = true;
set_bit(timing, &host->tuned_timing_modes);
break;
case MMC_TIMING_MMC_HS200:
reg &= ~EFX_EMMC_DATA_SAMPLING_MODE;
if (!test_bit(timing, &host->tuned_timing_modes)) {
needs_tuning = true;
host->tuning_done = false;
dev_dbg(&host->pdev->dev, "HS200 mode - tuning required\n");
} else {
efx_emmc_set_timing_config(host, host->hs200_sample_count,
host->hs200_pll_shift);
dev_dbg(&host->pdev->dev,
"HS200 mode - already tuned, restored timing (sample=%u, pll=%u)\n",
host->hs200_sample_count, host->hs200_pll_shift);
}
break;
case MMC_TIMING_MMC_HS400:
reg |= EFX_EMMC_DATA_SAMPLING_MODE;
if (!test_bit(timing, &host->tuned_timing_modes)) {
u32 initial_sample = host->hs400_default_sample;
u32 initial_pll = host->hs400_default_pll;
efx_emmc_set_timing_config(host, initial_sample, initial_pll);
needs_tuning = true;
host->tuning_done = false;
host->hs400_retune_pending = true;
dev_info(&host->pdev->dev,
"HS400 mode - using default sample=%u pll=%u, clock=%u Hz\n",
initial_sample, initial_pll, host->mmc->ios.clock);
dev_info(&host->pdev->dev,
"HS400 tuning scheduled (clock=%u Hz)\n",
host->mmc->ios.clock);
schedule_delayed_work(&host->hs400_retune_work,
msecs_to_jiffies(10));
} else {
efx_emmc_set_timing_config(host, host->hs400_sample_count,
host->hs400_pll_shift);
dev_info(&host->pdev->dev,
"HS400 mode - already tuned, restored timing (sample=%u, pll=%u)\n",
host->hs400_sample_count, host->hs400_pll_shift);
}
break;
default:
dev_warn(&host->pdev->dev, "Unsupported timing: %d\n", timing);
return;
}
efx_emmc_writel(host, reg, EFX_EMMC_HOST_CONTROL);
dev_dbg(&host->pdev->dev, "Set timing mode: %s (%d) - %s\n",
timing == MMC_TIMING_LEGACY ? "Legacy" :
timing == MMC_TIMING_MMC_HS ? "High Speed" :
timing == MMC_TIMING_MMC_DDR52 ? "DDR52" :
timing == MMC_TIMING_MMC_HS200 ? "HS200" :
timing == MMC_TIMING_MMC_HS400 ? "HS400" : "Unknown", timing,
needs_tuning ? "NEEDS TUNING" : "NO TUNING NEEDED");
host->prev_timing = timing;
}
irqreturn_t efx_emmc_irq(int irq, void *dev_id)
{
struct efx_emmc_host *host;
u32 intstat, present_state;
irqreturn_t result;
host = dev_id;
result = IRQ_NONE;
spin_lock(&host->lock);
intstat = efx_emmc_readl(host, EFX_EMMC_INT_STATUS);
if (!intstat) {
goto out;
}
present_state = efx_emmc_readl(host, EFX_EMMC_PRESENT_STATE);
efx_emmc_dbg_irq(host, "IRQ: status=0x%08x, present=0x%08x\n",
intstat, present_state);
efx_emmc_writel(host, intstat, EFX_EMMC_INT_STATUS);
result = IRQ_HANDLED;
if (intstat & EFX_EMMC_INT_ERROR_MASK) {
if (intstat & EFX_EMMC_INT_ADMA_ERROR) {
dev_err(&host->pdev->dev, "ADMA error detected\n");
if (host->data) {
host->data->error = -EIO;
}
}
if (host->cmd && (host->cmd->opcode == 52 || host->cmd->opcode == 8 ||
host->cmd->opcode == 5 || host->cmd->opcode == 55)) {
efx_emmc_dbg_irq(host,
"Expected timeout for CMD%d during card detection\n",
host->cmd->opcode);
} else if (host->tuning_in_progress) {
efx_emmc_dbg_irq(host, "Tuning error (expected): 0x%08x\n",
(unsigned int)(intstat & EFX_EMMC_INT_ERROR_MASK));
} else {
dev_err(&host->pdev->dev, "Error interrupt: 0x%08x\n",
(unsigned int)(intstat & EFX_EMMC_INT_ERROR_MASK));
}
if (host->cmd) {
if (intstat & EFX_EMMC_INT_CMD_TIMEOUT_ERR) {
host->cmd->error = -ETIMEDOUT;
if (host->cmd->opcode == 12) {
dev_warn(&host->pdev->dev,
"CMD12 timeout - data may have completed normally\n");
host->cmd = NULL;
if (host->mrq) {
efx_emmc_finish_request(host, host->mrq);
}
goto out;
} else if (host->cmd->opcode == 6) {
dev_err(&host->pdev->dev,
"CMD6 (mode switch) timeout - arg=0x%08x\n",
host->cmd->arg);
} else if (!(host->cmd->opcode == 52 ||
host->cmd->opcode == 8 ||
host->cmd->opcode == 5 ||
host->cmd->opcode == 55)) {
dev_err(&host->pdev->dev, "CMD%d timeout\n",
host->cmd->opcode);
}
} else if (intstat & (EFX_EMMC_INT_CMD_CRC_ERR |
EFX_EMMC_INT_CMD_END_BIT_ERR |
EFX_EMMC_INT_CMD_INDEX_ERR)) {
host->cmd->error = -EILSEQ;
if (host->cmd->opcode == 6) {
dev_err(&host->pdev->dev,
"CMD6 (mode switch) CRC/protocol error - arg=0x%08x\n",
host->cmd->arg);
} else {
dev_err(&host->pdev->dev,
"CMD%d CRC/protocol error\n",
host->cmd->opcode);
}
}
}
if (host->data) {
if (intstat & EFX_EMMC_INT_DATA_TIMEOUT_ERR) {
host->data->error = -ETIMEDOUT;
dev_err(&host->pdev->dev, "Data timeout error\n");
}
if (intstat & EFX_EMMC_INT_DATA_CRC_ERR) {
host->data->error = -EILSEQ;
dev_err(&host->pdev->dev, "Data CRC error\n");
}
if (intstat & EFX_EMMC_INT_DATA_END_BIT_ERR) {
host->data->error = -EILSEQ;
dev_err(&host->pdev->dev, "Data end bit error\n");
}
}
if (!(host->cmd && host->cmd->opcode == 12 &&
(intstat & EFX_EMMC_INT_CMD_TIMEOUT_ERR))) {
if (host->mrq) {
efx_emmc_finish_request(host, host->mrq);
}
}
goto out;
}
if (intstat & EFX_EMMC_INT_CMD_COMPLETE) {
efx_emmc_dbg_irq(host, "Command complete\n");
efx_emmc_finish_command(host);
}
if (likely(!host->use_dma) &&
(intstat & (EFX_EMMC_INT_BUFFER_READ_RDY |
EFX_EMMC_INT_BUFFER_WRITE_RDY))) {
efx_emmc_dbg_irq(host, "Buffer ready for %s\n",
(intstat & EFX_EMMC_INT_BUFFER_READ_RDY) ? "read" : "write");
efx_emmc_transfer_pio(host);
if (host->data && host->bytes_to_transfer > 0) {
efx_emmc_dbg_pio(host,
"Waiting for more data: %d bytes remaining\n",
host->bytes_to_transfer);
} else if (host->data && host->bytes_to_transfer == 0) {
efx_emmc_dbg_pio(host,
"All data transferred via PIO, completing transfer\n");
efx_emmc_finish_data(host);
}
}
if (intstat & EFX_EMMC_INT_XFER_COMPLETE) {
efx_emmc_dbg_irq(host, "Transfer complete interrupt (DMA: %s)\n",
host->use_dma ? "enabled" : "disabled");
efx_emmc_finish_data(host);
}
if (intstat & ~(EFX_EMMC_INT_CMD_COMPLETE | EFX_EMMC_INT_BUFFER_READ_RDY |
EFX_EMMC_INT_BUFFER_WRITE_RDY |
EFX_EMMC_INT_XFER_COMPLETE | EFX_EMMC_INT_ERROR_MASK)) {
efx_emmc_dbg_irq(host, "Unhandled interrupt bits: 0x%08x\n",
(unsigned int)(intstat &
~(EFX_EMMC_INT_CMD_COMPLETE |
EFX_EMMC_INT_BUFFER_READ_RDY |
EFX_EMMC_INT_BUFFER_WRITE_RDY |
EFX_EMMC_INT_XFER_COMPLETE |
EFX_EMMC_INT_ERROR_MASK)));
}
out:
spin_unlock(&host->lock);
efx_emmc_complete_pending_request(host);
return result;
}
void efx_emmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct efx_emmc_host *host;
unsigned long flags;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
if (!host->clk) {
dev_err(&host->pdev->dev, "No clock available\n");
mrq->cmd->error = -ENODEV;
spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(mmc, mrq);
return;
}
efx_emmc_dbg_cmd(host, "New request: CMD%d\n", mrq->cmd->opcode);
host->mrq = mrq;
if (mrq->sbc) {
efx_emmc_dbg_cmd(host,
"Sending CMD23 (SET_BLOCK_COUNT) first, blocks=%u\n",
mrq->sbc->arg);
host->cmd = mrq->sbc;
efx_emmc_send_command(host, mrq->sbc);
} else {
efx_emmc_send_command(host, mrq->cmd);
}
spin_unlock_irqrestore(&host->lock, flags);
efx_emmc_complete_pending_request(host);
}
void efx_emmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct efx_emmc_host *host;
unsigned long flags;
bool need_host_control_update = false;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
if (ios->clock != host->current_clk) {
efx_emmc_set_clock(host, ios->clock);
}
if (ios->bus_width != MMC_BUS_WIDTH_1) {
efx_emmc_set_bus_width(host, ios->bus_width);
need_host_control_update = true;
}
if (ios->timing != host->prev_timing) {
dev_dbg(&host->pdev->dev, "Timing mode: %u -> %u, Clock: %u Hz\n",
host->prev_timing, ios->timing, ios->clock);
}
efx_emmc_set_timing(host, ios->timing);
spin_unlock_irqrestore(&host->lock, flags);
}
int efx_emmc_get_cd(struct mmc_host *mmc)
{
return 1;
}
int efx_emmc_card_busy_wrapper(struct mmc_host *mmc)
{
struct efx_emmc_host *host;
unsigned long flags;
bool busy;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
busy = efx_emmc_card_busy(host);
spin_unlock_irqrestore(&host->lock, flags);
return busy;
}
int efx_emmc_get_ro(struct mmc_host *mmc)
{
return 0;
}