/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Efinix eMMC Host Controller Core Operations * * Copyright (C) 2025 Efinix, Inc. * Author: Teoh Choon Zone */ #include #include #include #include #include #include #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; }