--- linux-4.9.37/drivers/mmc/host/sdhci.c 2017-07-12 16:42:41.000000000 +0300 +++ linux-4.9.y/drivers/mmc/host/sdhci.c 2021-06-07 13:01:33.000000000 +0300 @@ -32,6 +32,7 @@ #include #include "sdhci.h" +#include "sdhci-cmdq.h" #define DRIVER_NAME "sdhci" @@ -76,8 +77,8 @@ pr_err(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", sdhci_readl(host, SDHCI_INT_ENABLE), sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); - pr_err(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", - sdhci_readw(host, SDHCI_ACMD12_ERR), + pr_err(DRIVER_NAME ": ACMD err: 0x%08x | Slot int: 0x%08x\n", + sdhci_readw(host, SDHCI_AUTO_CMD_ERR), sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); pr_err(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", sdhci_readl(host, SDHCI_CAPABILITIES), @@ -227,7 +228,7 @@ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | - SDHCI_INT_RESPONSE; + SDHCI_INT_RESPONSE | SDHCI_INT_ACMD_ERR; if (host->tuning_mode == SDHCI_TUNING_MODE_2 || host->tuning_mode == SDHCI_TUNING_MODE_3) @@ -240,11 +241,17 @@ /* force clock reconfiguration */ host->clock = 0; mmc->ops->set_ios(mmc, &mmc->ios); + } else { + if (host->ops->extra_init) + host->ops->extra_init(host); } } static void sdhci_reinit(struct sdhci_host *host) { + if (host->ops->pre_init) + host->ops->pre_init(host); + sdhci_init(host, 0); sdhci_enable_card_detection(host); } @@ -522,6 +529,60 @@ dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32); } +static void sdhci_write_cmd_table(u8 *desc, u32 reg, u32 attr) +{ + __le32 *reg_addr = (__le32 __force *)(desc + 4); + __le32 *attr_addr = (__le32 __force *)desc; + + attr_addr[0] = cpu_to_le32(attr); + reg_addr[0] = cpu_to_le32(reg); +} + +static void sdhci_write_adma3_desc(struct sdhci_host *host, u8 *desc, + dma_addr_t addr, unsigned int attr) +{ + __le32 *attr_addr = (__le32 __force *)desc; + + attr_addr[0] = cpu_to_le32(attr); + + if (host->flags & SDHCI_USE_64_BIT_DMA) { + __le64 *cmd_ddr = (__le64 __force *)(desc + 4); + cmd_ddr[0] = cpu_to_le64(addr); + } else { + __le32 *cmd_ddr = (__le32 __force *)(desc + 4); + cmd_ddr[0] = cpu_to_le32(addr); + } +} + +static void sdhci_prep_adma3_desc(struct sdhci_host *host, + struct mmc_command *cmd, int flags) +{ + struct mmc_data *data = cmd->data; + unsigned int ctrl_2, cmd_xfer, blksz; + u16 mode; + + blksz = SDHCI_MAKE_BLKSZ(0, data->blksz); + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); + cmd_xfer = (SDHCI_MAKE_CMD(cmd->opcode, flags) << 16) | mode; + + sdhci_write_cmd_table(host->cmd_table, data->blocks, ADMA3_CMD_VALID); + sdhci_write_cmd_table(host->cmd_table + 0x8, blksz, ADMA3_CMD_VALID); + sdhci_write_cmd_table(host->cmd_table + 0x10, + cmd->arg, ADMA3_CMD_VALID); + sdhci_write_cmd_table(host->cmd_table + 0x18, + cmd_xfer, ADMA3_CMD_VALID); + sdhci_adma_write_desc(host, host->cmd_table + 0x20, + host->adma_addr, 0x0, ADMA2_LINK_VALID); + sdhci_write_adma3_desc(host, host->adma3_table, + host->cmd_addr, ADMA3_END); + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl_2 |= SDHCI_CTRL_HOST_VER4_ENABLE; + if (host->flags & SDHCI_USE_64_BIT_DMA) + ctrl_2 |= SDHCI_CTRL_ADDRESSING_64BIT; + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + static void sdhci_adma_mark_end(void *desc) { struct sdhci_adma2_64_desc *dma_desc = desc; @@ -589,6 +650,17 @@ BUG_ON(len > 65536); if (len) { + /* work around for buffer across 128M boundary, split the buffer */ + if (((addr & (SDHCI_DMA_BOUNDARY_SIZE - 1)) + len) > + SDHCI_DMA_BOUNDARY_SIZE) { + offset = SDHCI_DMA_BOUNDARY_SIZE - + (addr & (SDHCI_DMA_BOUNDARY_SIZE - 1)); + sdhci_adma_write_desc(host, desc, addr, offset, + ADMA2_TRAN_VALID); + desc += host->desc_sz; + addr += offset; + len -= offset; + } /* tran, valid */ sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID); @@ -855,10 +927,14 @@ ctrl &= ~SDHCI_CTRL_DMA_MASK; if ((host->flags & SDHCI_REQ_USE_DMA) && (host->flags & SDHCI_USE_ADMA)) { - if (host->flags & SDHCI_USE_64_BIT_DMA) - ctrl |= SDHCI_CTRL_ADMA64; - else - ctrl |= SDHCI_CTRL_ADMA32; + if (host->flags & SDHCI_USE_ADMA3) { + ctrl |= SDHCI_CTRL_ADMA3; + } else { + if (host->flags & SDHCI_USE_64_BIT_DMA) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + } } else { ctrl |= SDHCI_CTRL_SDMA; } @@ -1121,7 +1197,8 @@ sdhci_prepare_data(host, cmd); - sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); + if (!(host->flags & SDHCI_USE_ADMA3) || !cmd->data) + sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); sdhci_set_transfer_mode(host, cmd); @@ -1152,10 +1229,29 @@ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) flags |= SDHCI_CMD_DATA; - sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); + if (host->flags & SDHCI_USE_ADMA3 && cmd->data) { + sdhci_prep_adma3_desc(host, cmd, flags); + + sdhci_writel(host, (u32)host->adma3_addr, + SDHCI_ADMA3_ID_ADDR_LOW); + if (host->flags & SDHCI_USE_64_BIT_DMA) + sdhci_writel(host, (u32)((u64)host->adma3_addr >> 32), + SDHCI_ADMA3_ID_ADDR_HI); + } else { + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), + SDHCI_COMMAND); + } } EXPORT_SYMBOL_GPL(sdhci_send_command); +#define CMD_ERRORS \ + (R1_OUT_OF_RANGE | /* Command argument out of range */ \ + R1_ADDRESS_ERROR | /* Misaligned address */ \ + R1_BLOCK_LEN_ERROR | /* Transferred block length incorrect */\ + R1_WP_VIOLATION | /* Tried to write to protected block */ \ + R1_CC_ERROR | /* Card controller error */ \ + R1_ERROR) /* General/unknown error */ + static void sdhci_finish_command(struct sdhci_host *host) { struct mmc_command *cmd = host->cmd; @@ -1177,6 +1273,15 @@ } else { cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE); } + + if (((cmd->flags & MMC_RSP_R1) == MMC_RSP_R1) && + ((cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR)) { + if ((cmd->resp[0] & CMD_ERRORS) && !host->is_tuning) { + host->error_count++; + cmd->mrq->cmd->error = -EACCES; + pr_err("The status of the card is abnormal, cmd->resp[0]: %x", cmd->resp[0]); + } + } } if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd) @@ -1430,6 +1535,12 @@ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) sdhci_runtime_pm_bus_off(host); + /* + * Controllers need an extra 100ms delay to ensure power off + * completely + */ + msleep(100); + } else { /* * Spec says that we should clear the power reg before setting @@ -1568,13 +1679,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sdhci_host *host = mmc_priv(mmc); - unsigned long flags; u8 ctrl; - spin_lock_irqsave(&host->lock, flags); - if (host->flags & SDHCI_DEVICE_DEAD) { - spin_unlock_irqrestore(&host->lock, flags); if (!IS_ERR(mmc->supply.vmmc) && ios->power_mode == MMC_POWER_OFF) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); @@ -1710,7 +1817,9 @@ } /* Re-enable SD Clock */ - host->ops->set_clock(host, host->clock); + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -1723,7 +1832,6 @@ sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); } static int sdhci_get_cd(struct mmc_host *mmc) @@ -1846,6 +1954,9 @@ u16 ctrl; int ret; + if (host->ops->start_signal_voltage_switch) + return host->ops->start_signal_voltage_switch(host, ios); + /* * Signal Voltage Switching is only applicable for Host Controllers * v3.00 and above. @@ -2281,6 +2392,33 @@ spin_unlock_irqrestore(&host->lock, flags); } +static int sdhci_card_info_save(struct mmc_host *mmc) +{ + struct mmc_card *card = mmc->card; + struct sdhci_host *host= mmc_priv(mmc); + struct card_info *c_info = &host->c_info; + + if (!card) { + memset(c_info,0,sizeof(struct card_info)); + c_info->card_connect = CARD_DISCONNECT; + goto out; + } + + c_info->card_type = card->type; + c_info->card_state = card->state; + + c_info->timing = mmc->ios.timing; + c_info->card_support_clock = mmc->ios.clock; + + c_info->sd_bus_speed = card->sd_bus_speed; + + memcpy(c_info->ssr, card->raw_ssr, ARRAY_SIZE(c_info->ssr)); + + c_info->card_connect = CARD_CONNECT; +out: + return 0; +} + static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .post_req = sdhci_post_req, @@ -2296,6 +2434,7 @@ .select_drive_strength = sdhci_select_drive_strength, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, + .card_info_save = sdhci_card_info_save, }; /*****************************************************************************\ @@ -2370,6 +2509,9 @@ host->pending_reset = false; } + if (mrq->data && mrq->data->error && !host->is_tuning) + host->error_count++; + if (!sdhci_has_requests(host)) sdhci_led_deactivate(host); @@ -2460,17 +2602,32 @@ */ if (host->pending_reset) return; - pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", + + /*pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); - sdhci_dumpregs(host); + sdhci_dumpregs(host);*/ + return; } if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | - SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) { + SDHCI_INT_END_BIT | SDHCI_INT_INDEX | + SDHCI_INT_ACMD_ERR)) { if (intmask & SDHCI_INT_TIMEOUT) host->cmd->error = -ETIMEDOUT; - else + else if (intmask & SDHCI_INT_ACMD_ERR) { + u16 acmd_stat = sdhci_readw(host, SDHCI_AUTO_CMD_ERR); + + if (acmd_stat & (SDHCI_AUTO_CMD12_NOT_EXEC | + SDHCI_AUTO_CMD_INDEX_ERR | + SDHCI_AUTO_CMD12_NOT_ISSUED)) + host->cmd->error = -EIO; + else if (acmd_stat & SDHCI_AUTO_CMD_TIMEOUT_ERR) + host->cmd->error = -ETIMEDOUT; + else + host->cmd->error = -EILSEQ; + + } else host->cmd->error = -EILSEQ; /* @@ -2533,6 +2690,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { + +#ifndef SDHCI_GOKE_EDGE_TUNING u32 command; /* CMD19 generates _only_ Buffer Read Ready interrupt */ @@ -2545,6 +2704,7 @@ return; } } +#endif if (!host->data) { struct mmc_command *data_cmd = host->data_cmd; @@ -2584,6 +2744,9 @@ if (host->pending_reset) return; + if (host->is_tuning) + return; + pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); sdhci_dumpregs(host); @@ -2655,6 +2818,58 @@ } } +#ifdef CONFIG_MMC_CQ_HCI +static int sdhci_get_cmd_err(u32 intmask) +{ + if (intmask & SDHCI_INT_TIMEOUT) + return -ETIMEDOUT; + else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | + SDHCI_INT_INDEX)) + return -EILSEQ; + return 0; +} + +static int sdhci_get_data_err(u32 intmask) +{ + if (intmask & SDHCI_INT_DATA_TIMEOUT) + return -ETIMEDOUT; + else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) + return -EILSEQ; + else if (intmask & SDHCI_INT_ADMA_ERROR) + return -EIO; + return 0; +} + +static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask) +{ + int err = 0; + u32 mask = 0; + irqreturn_t ret; + + if (intmask & SDHCI_INT_CMD_MASK) + err = sdhci_get_cmd_err(intmask); + else if (intmask & SDHCI_INT_DATA_MASK) + err = sdhci_get_data_err(intmask); + + ret = cmdq_irq(host->mmc, err); + if (err) { + /* Clear the error interrupts */ + mask = intmask & SDHCI_INT_ERROR_MASK; + sdhci_writel(host, mask, SDHCI_INT_STATUS); + } + return ret; + +} + +#else +static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask) +{ + pr_err("%s: Received cmdq-irq when disabled !!!!\n", + mmc_hostname(host->mmc)); + return IRQ_NONE; +} +#endif + static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result = IRQ_NONE; @@ -2676,6 +2891,19 @@ } do { + if (host->mmc->card && mmc_card_cmdq(host->mmc->card) && + !mmc_host_halt(host->mmc) && !mmc_host_cq_disable(host->mmc)) { + DBG("*** %s: cmdq intr: 0x%08x\n", + mmc_hostname(host->mmc), + intmask); + result = sdhci_cmdq_irq(host, intmask); + if (result == IRQ_HANDLED) { + mask = intmask & SDHCI_INT_CQE; + sdhci_writel(host, mask, SDHCI_INT_STATUS); + goto out; + } + } + /* Clear selected interrupts. */ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | SDHCI_INT_BUS_POWER); @@ -3093,7 +3321,7 @@ override_timeout_clk = host->timeout_clk; - if (host->version > SDHCI_SPEC_300) { + if (host->version > SDHCI_SPEC_420) { pr_err("%s: Unknown controller version (%d). You may experience problems.\n", mmc_hostname(mmc), host->version); } @@ -3121,6 +3349,15 @@ host->flags &= ~SDHCI_USE_ADMA; } + if ((host->version >= SDHCI_SPEC_400) && + (host->caps1 & SDHCI_CAN_DO_ADMA3)) + host->flags |= SDHCI_USE_ADMA3 | SDHCI_HOST_VER4_ENABLE; + + if ((host->quirks2 & SDHCI_QUIRK2_BROKEN_ADMA3) && + (host->flags & SDHCI_USE_ADMA3)) { + DBG("Disabling ADMA3 as it is marked broken\n"); + host->flags &= ~(SDHCI_USE_ADMA3 | SDHCI_HOST_VER4_ENABLE); + } /* * It is assumed that a 64-bit capable device has set a 64-bit DMA mask * and *must* do 64-bit DMA. A driver has the opportunity to change @@ -3161,14 +3398,14 @@ * all multipled by the descriptor size. */ if (host->flags & SDHCI_USE_64_BIT_DMA) { - host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * - SDHCI_ADMA2_64_DESC_SZ; - host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; - } else { - host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * - SDHCI_ADMA2_32_DESC_SZ; + if (host->flags & SDHCI_HOST_VER4_ENABLE) + host->desc_sz = 16; + else + host->desc_sz = SDHCI_ADMA2_64_DESC_SZ; + } else host->desc_sz = SDHCI_ADMA2_32_DESC_SZ; - } + + host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) * host->desc_sz; host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN; buf = dma_alloc_coherent(mmc_dev(mmc), host->align_buffer_sz + @@ -3191,6 +3428,36 @@ host->adma_table = buf + host->align_buffer_sz; host->adma_addr = dma + host->align_buffer_sz; } + + if (!(host->flags & SDHCI_USE_ADMA)) + host->flags &= ~SDHCI_USE_ADMA3; + + if (host->flags & SDHCI_USE_ADMA3) { +#define MAX_CMD_NUM 32 +#define SDHCI_CMD_DESC_SZ 16 + if (host->flags & SDHCI_USE_64_BIT_DMA) + host->adma3_desc_sz = SDHCI_ADMA3_64_DESC_SZ; + else + host->adma3_desc_sz = SDHCI_ADMA3_32_DESC_SZ; + + host->adma3_table_sz = MAX_CMD_NUM * host->adma3_desc_sz; + host->cmd_table_sz = MAX_CMD_NUM * + (SDHCI_CMD_DESC_SZ + 16); + buf = dma_alloc_coherent(mmc_dev(mmc), host->adma3_table_sz + + host->cmd_table_sz, &dma, GFP_KERNEL); + if (!buf) { + pr_warn("%s: Unable to allocate ADMA3 buffers - falling back to standard DMA\n", + mmc_hostname(mmc)); + host->flags &= ~SDHCI_USE_ADMA3; + } else { + host->adma3_table = buf; + host->adma3_addr = dma; + + host->cmd_table = buf + host->adma3_desc_sz; + host->cmd_addr = dma + host->adma3_desc_sz; + } + } + } /* @@ -3557,10 +3824,165 @@ host->adma_table = NULL; host->align_buffer = NULL; + if (host->adma3_table) + dma_free_coherent(mmc_dev(mmc), host->adma3_table_sz + + host->cmd_table_sz, host->adma3_table, + host->adma3_addr); + + host->adma3_table = NULL; + host->cmd_table = NULL; + return ret; } EXPORT_SYMBOL_GPL(sdhci_setup_host); +#ifdef CONFIG_MMC_CQ_HCI +static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + u8 ctrl; + u16 mode, ctrl2; + + if (host->version >= SDHCI_SPEC_200) { + + ctrl2 = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + ctrl2 |= SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, ctrl2, SDHCI_CLOCK_CONTROL); + + ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl2 |= SDHCI_CTRL_ADDRESSING_64BIT; + ctrl2 |= SDHCI_CTRL_HOST_VER4_ENABLE; + sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (host->flags & SDHCI_USE_64_BIT_DMA) + ctrl |= SDHCI_CTRL_ADMA64; + else + ctrl |= SDHCI_CTRL_ADMA32; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); + sdhci_writew(host, mode | SDHCI_TRNS_MULTI, SDHCI_TRANSFER_MODE); + } +} + +static void sdhci_cmdq_clear_set_irqs(struct mmc_host *mmc, bool clear) +{ + struct sdhci_host *host = mmc_priv(mmc); + u32 ier = 0; + + ier &= ~SDHCI_INT_ALL_MASK; + + if (clear) { + ier = SDHCI_INT_CQE | SDHCI_INT_ERROR_MASK; + sdhci_writel(host, ier, SDHCI_INT_ENABLE); + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); + } else { + ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | + SDHCI_INT_INDEX | SDHCI_INT_END_BIT | + SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | + SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | + SDHCI_INT_ACMD_ERR; + sdhci_writel(host, ier, SDHCI_INT_ENABLE); + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); + } +} + +static void sdhci_cmdq_set_data_timeout(struct mmc_host *mmc, u32 val) +{ + struct sdhci_host *host = mmc_priv(mmc); + + val = 0xe; + sdhci_writeb(host, val, SDHCI_TIMEOUT_CONTROL); +} + +static void sdhci_cmdq_dump_goke_regs(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_dumpregs(host); +} + +static int sdhci_cmdq_init(struct sdhci_host *host, struct mmc_host *mmc, + bool dma64) +{ + struct cmdq_host *cq_host; + + cq_host = kzalloc(sizeof(*cq_host), GFP_KERNEL); + if (!cq_host) { + pr_err("failed to allocate memory for CMDQ\n"); + host->cq_host = NULL; + return -ENOMEM; + } else { + cq_host->mmio = host->ioaddr + 0x180; + host->cq_host = cq_host; + } + + return cmdq_init(host->cq_host, mmc, dma64); +} + +static void sdhci_cmdq_set_block_size(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_writew(host, SDHCI_MAKE_BLKSZ(0, 512), SDHCI_BLOCK_SIZE); +} + +static void sdhci_cmdq_post_cqe_halt(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + sdhci_writel(host, sdhci_readl(host, SDHCI_INT_ENABLE) | + SDHCI_INT_RESPONSE, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); +} +#else +static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc) +{ + +} +static void sdhci_cmdq_clear_set_irqs(struct mmc_host *mmc, bool clear) +{ + +} + +static void sdhci_cmdq_set_data_timeout(struct mmc_host *mmc, u32 val) +{ + +} + +static void sdhci_cmdq_dump_goke_regs(struct mmc_host *mmc) +{ + +} + +static int sdhci_cmdq_init(struct sdhci_host *host, struct mmc_host *mmc, + bool dma64) +{ + return -ENOSYS; +} + +static void sdhci_cmdq_set_block_size(struct mmc_host *mmc) +{ + +} + +static void sdhci_cmdq_post_cqe_halt(struct mmc_host *mmc) +{ +} +#endif + +static const struct cmdq_host_ops sdhci_cmdq_ops = { + .clear_set_irqs = sdhci_cmdq_clear_set_irqs, + .set_data_timeout = sdhci_cmdq_set_data_timeout, + .dump_goke_regs = sdhci_cmdq_dump_goke_regs, + .set_block_size = sdhci_cmdq_set_block_size, + .post_cqe_halt = sdhci_cmdq_post_cqe_halt, + .set_transfer_params = sdhci_cmdq_set_transfer_params, +}; + int __sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; @@ -3605,11 +4027,25 @@ if (ret) goto unled; - pr_info("%s: SDHCI controller on %s [%s] using %s\n", + if (mmc->caps2 & MMC_CAP2_CMD_QUEUE) { + bool dma64 = (host->flags & SDHCI_USE_64_BIT_DMA) ? + true : false; + ret = sdhci_cmdq_init(host, mmc, dma64); + if (ret) + pr_err("%s: CMDQ init: failed (%d)\n", + mmc_hostname(host->mmc), ret); + else + host->cq_host->ops = &sdhci_cmdq_ops; + } + + pr_info("%s: SDHCI controller on %s [%s] using %s in %s mode\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), + (host->flags & SDHCI_USE_ADMA3) ? "ADMA3" : (host->flags & SDHCI_USE_ADMA) ? (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : - (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); + ((host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"), + ((mmc->caps2 & MMC_CAP2_CMD_QUEUE) && !ret) ? + "CMDQ" : "legacy"); sdhci_enable_card_detection(host); @@ -3635,6 +4071,14 @@ host->adma_table = NULL; host->align_buffer = NULL; + if (host->adma3_table) + dma_free_coherent(mmc_dev(mmc), host->adma3_table_sz + + host->cmd_table_sz, host->adma3_table, + host->adma3_addr); + + host->adma3_table = NULL; + host->cmd_table = NULL; + return ret; } EXPORT_SYMBOL_GPL(__sdhci_add_host); @@ -3672,6 +4116,8 @@ sdhci_disable_card_detection(host); + free_irq(host->irq, host); + mmc_remove_host(mmc); sdhci_led_unregister(host); @@ -3681,7 +4127,6 @@ sdhci_writel(host, 0, SDHCI_INT_ENABLE); sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); - free_irq(host->irq, host); del_timer_sync(&host->timer); del_timer_sync(&host->data_timer); @@ -3698,6 +4143,14 @@ host->adma_table = NULL; host->align_buffer = NULL; + + if (host->adma3_table) + dma_free_coherent(mmc_dev(mmc), host->adma3_table_sz + + host->cmd_table_sz, host->adma3_table, + host->adma3_addr); + + host->adma3_table = NULL; + host->cmd_table = NULL; } EXPORT_SYMBOL_GPL(sdhci_remove_host);