mirror of https://github.com/OpenIPC/firmware.git
780 lines
21 KiB
Diff
780 lines
21 KiB
Diff
--- 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 <linux/mmc/slot-gpio.h>
|
|
|
|
#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);
|