|
@@ -45,65 +45,62 @@ static unsigned int debug_quirks2;
|
|
|
|
|
|
static void sdhci_finish_data(struct sdhci_host *);
|
|
static void sdhci_finish_data(struct sdhci_host *);
|
|
|
|
|
|
-static void sdhci_finish_command(struct sdhci_host *);
|
|
|
|
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
|
|
|
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
|
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
|
-static int sdhci_get_cd(struct mmc_host *mmc);
|
|
|
|
|
|
|
|
static void sdhci_dumpregs(struct sdhci_host *host)
|
|
static void sdhci_dumpregs(struct sdhci_host *host)
|
|
{
|
|
{
|
|
- pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
|
|
|
|
- mmc_hostname(host->mmc));
|
|
|
|
-
|
|
|
|
- pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
|
|
|
|
- sdhci_readl(host, SDHCI_DMA_ADDRESS),
|
|
|
|
- sdhci_readw(host, SDHCI_HOST_VERSION));
|
|
|
|
- pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
|
|
|
|
- sdhci_readw(host, SDHCI_BLOCK_SIZE),
|
|
|
|
- sdhci_readw(host, SDHCI_BLOCK_COUNT));
|
|
|
|
- pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
|
|
|
|
- sdhci_readl(host, SDHCI_ARGUMENT),
|
|
|
|
- sdhci_readw(host, SDHCI_TRANSFER_MODE));
|
|
|
|
- pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
|
|
|
|
- sdhci_readl(host, SDHCI_PRESENT_STATE),
|
|
|
|
- sdhci_readb(host, SDHCI_HOST_CONTROL));
|
|
|
|
- pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
|
|
|
|
- sdhci_readb(host, SDHCI_POWER_CONTROL),
|
|
|
|
- sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
|
|
|
|
- pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
|
|
|
|
- sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
|
|
|
|
- sdhci_readw(host, SDHCI_CLOCK_CONTROL));
|
|
|
|
- pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
|
|
|
|
- sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
|
|
|
|
- sdhci_readl(host, SDHCI_INT_STATUS));
|
|
|
|
- pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
|
|
|
|
- sdhci_readl(host, SDHCI_INT_ENABLE),
|
|
|
|
- sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
|
|
|
|
- pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
|
|
|
|
- sdhci_readw(host, SDHCI_ACMD12_ERR),
|
|
|
|
- sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
|
|
|
- pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
|
|
|
|
- sdhci_readl(host, SDHCI_CAPABILITIES),
|
|
|
|
- sdhci_readl(host, SDHCI_CAPABILITIES_1));
|
|
|
|
- pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
|
|
|
|
- sdhci_readw(host, SDHCI_COMMAND),
|
|
|
|
- sdhci_readl(host, SDHCI_MAX_CURRENT));
|
|
|
|
- pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
|
|
|
|
- sdhci_readw(host, SDHCI_HOST_CONTROL2));
|
|
|
|
|
|
+ pr_err(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+
|
|
|
|
+ pr_err(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
|
|
|
|
+ sdhci_readl(host, SDHCI_DMA_ADDRESS),
|
|
|
|
+ sdhci_readw(host, SDHCI_HOST_VERSION));
|
|
|
|
+ pr_err(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
|
|
|
|
+ sdhci_readw(host, SDHCI_BLOCK_SIZE),
|
|
|
|
+ sdhci_readw(host, SDHCI_BLOCK_COUNT));
|
|
|
|
+ pr_err(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
|
|
|
|
+ sdhci_readl(host, SDHCI_ARGUMENT),
|
|
|
|
+ sdhci_readw(host, SDHCI_TRANSFER_MODE));
|
|
|
|
+ pr_err(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
|
|
|
|
+ sdhci_readl(host, SDHCI_PRESENT_STATE),
|
|
|
|
+ sdhci_readb(host, SDHCI_HOST_CONTROL));
|
|
|
|
+ pr_err(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
|
|
|
|
+ sdhci_readb(host, SDHCI_POWER_CONTROL),
|
|
|
|
+ sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
|
|
|
|
+ pr_err(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
|
|
|
|
+ sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
|
|
|
|
+ sdhci_readw(host, SDHCI_CLOCK_CONTROL));
|
|
|
|
+ pr_err(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
|
|
|
|
+ sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
|
|
|
|
+ sdhci_readl(host, SDHCI_INT_STATUS));
|
|
|
|
+ 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),
|
|
|
|
+ sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
|
|
|
|
+ pr_err(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
|
|
|
|
+ sdhci_readl(host, SDHCI_CAPABILITIES),
|
|
|
|
+ sdhci_readl(host, SDHCI_CAPABILITIES_1));
|
|
|
|
+ pr_err(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
|
|
|
|
+ sdhci_readw(host, SDHCI_COMMAND),
|
|
|
|
+ sdhci_readl(host, SDHCI_MAX_CURRENT));
|
|
|
|
+ pr_err(DRIVER_NAME ": Host ctl2: 0x%08x\n",
|
|
|
|
+ sdhci_readw(host, SDHCI_HOST_CONTROL2));
|
|
|
|
|
|
if (host->flags & SDHCI_USE_ADMA) {
|
|
if (host->flags & SDHCI_USE_ADMA) {
|
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
|
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
|
- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
|
|
|
|
- readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
|
|
|
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
|
|
|
|
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
|
|
|
|
|
+ pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
|
|
|
|
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
|
|
|
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
|
|
|
|
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
|
else
|
|
else
|
|
- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
|
|
|
|
- readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
|
|
|
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
|
|
|
|
|
+ pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
|
|
|
|
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
|
|
|
|
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
|
|
}
|
|
}
|
|
|
|
|
|
- pr_debug(DRIVER_NAME ": ===========================================\n");
|
|
|
|
|
|
+ pr_err(DRIVER_NAME ": ===========================================\n");
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
/*****************************************************************************\
|
|
@@ -112,12 +109,17 @@ static void sdhci_dumpregs(struct sdhci_host *host)
|
|
* *
|
|
* *
|
|
\*****************************************************************************/
|
|
\*****************************************************************************/
|
|
|
|
|
|
|
|
+static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
|
|
|
|
+{
|
|
|
|
+ return cmd->data || cmd->flags & MMC_RSP_BUSY;
|
|
|
|
+}
|
|
|
|
+
|
|
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
|
|
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
|
|
{
|
|
{
|
|
u32 present;
|
|
u32 present;
|
|
|
|
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
|
- (host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
|
|
|
|
|
+ !mmc_card_is_removable(host->mmc))
|
|
return;
|
|
return;
|
|
|
|
|
|
if (enable) {
|
|
if (enable) {
|
|
@@ -193,7 +195,9 @@ EXPORT_SYMBOL_GPL(sdhci_reset);
|
|
static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
|
static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
|
{
|
|
{
|
|
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
|
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
|
- if (!sdhci_get_cd(host->mmc))
|
|
|
|
|
|
+ struct mmc_host *mmc = host->mmc;
|
|
|
|
+
|
|
|
|
+ if (!mmc->ops->get_cd(mmc))
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -210,10 +214,10 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
|
|
|
|
-
|
|
|
|
static void sdhci_init(struct sdhci_host *host, int soft)
|
|
static void sdhci_init(struct sdhci_host *host, int soft)
|
|
{
|
|
{
|
|
|
|
+ struct mmc_host *mmc = host->mmc;
|
|
|
|
+
|
|
if (soft)
|
|
if (soft)
|
|
sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
|
|
sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
|
|
else
|
|
else
|
|
@@ -225,13 +229,17 @@ static void sdhci_init(struct sdhci_host *host, int soft)
|
|
SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
|
|
SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
|
|
SDHCI_INT_RESPONSE;
|
|
SDHCI_INT_RESPONSE;
|
|
|
|
|
|
|
|
+ if (host->tuning_mode == SDHCI_TUNING_MODE_2 ||
|
|
|
|
+ host->tuning_mode == SDHCI_TUNING_MODE_3)
|
|
|
|
+ host->ier |= SDHCI_INT_RETUNE;
|
|
|
|
+
|
|
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
|
|
|
|
if (soft) {
|
|
if (soft) {
|
|
/* force clock reconfiguration */
|
|
/* force clock reconfiguration */
|
|
host->clock = 0;
|
|
host->clock = 0;
|
|
- sdhci_set_ios(host->mmc, &host->mmc->ios);
|
|
|
|
|
|
+ mmc->ops->set_ios(mmc, &mmc->ios);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -429,8 +437,6 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
|
|
{
|
|
{
|
|
u32 mask;
|
|
u32 mask;
|
|
|
|
|
|
- BUG_ON(!host->data);
|
|
|
|
-
|
|
|
|
if (host->blocks == 0)
|
|
if (host->blocks == 0)
|
|
return;
|
|
return;
|
|
|
|
|
|
@@ -747,14 +753,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|
u8 ctrl;
|
|
u8 ctrl;
|
|
struct mmc_data *data = cmd->data;
|
|
struct mmc_data *data = cmd->data;
|
|
|
|
|
|
- WARN_ON(host->data);
|
|
|
|
-
|
|
|
|
- if (data || (cmd->flags & MMC_RSP_BUSY))
|
|
|
|
|
|
+ if (sdhci_data_line_cmd(cmd))
|
|
sdhci_set_timeout(host, cmd);
|
|
sdhci_set_timeout(host, cmd);
|
|
|
|
|
|
if (!data)
|
|
if (!data)
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ WARN_ON(host->data);
|
|
|
|
+
|
|
/* Sanity checks */
|
|
/* Sanity checks */
|
|
BUG_ON(data->blksz * data->blocks > 524288);
|
|
BUG_ON(data->blksz * data->blocks > 524288);
|
|
BUG_ON(data->blksz > host->mmc->max_blk_size);
|
|
BUG_ON(data->blksz > host->mmc->max_blk_size);
|
|
@@ -879,6 +885,12 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
|
|
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
|
|
|
|
+ struct mmc_request *mrq)
|
|
|
|
+{
|
|
|
|
+ return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12);
|
|
|
|
+}
|
|
|
|
+
|
|
static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|
static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|
struct mmc_command *cmd)
|
|
struct mmc_command *cmd)
|
|
{
|
|
{
|
|
@@ -909,12 +921,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|
* If we are sending CMD23, CMD12 never gets sent
|
|
* If we are sending CMD23, CMD12 never gets sent
|
|
* on successful completion (so no Auto-CMD12).
|
|
* on successful completion (so no Auto-CMD12).
|
|
*/
|
|
*/
|
|
- if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) &&
|
|
|
|
|
|
+ if (sdhci_auto_cmd12(host, cmd->mrq) &&
|
|
(cmd->opcode != SD_IO_RW_EXTENDED))
|
|
(cmd->opcode != SD_IO_RW_EXTENDED))
|
|
mode |= SDHCI_TRNS_AUTO_CMD12;
|
|
mode |= SDHCI_TRNS_AUTO_CMD12;
|
|
- else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
|
|
|
|
|
+ else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
|
|
mode |= SDHCI_TRNS_AUTO_CMD23;
|
|
mode |= SDHCI_TRNS_AUTO_CMD23;
|
|
- sdhci_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2);
|
|
|
|
|
|
+ sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -926,14 +938,63 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
|
|
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
|
|
}
|
|
}
|
|
|
|
|
|
-static void sdhci_finish_data(struct sdhci_host *host)
|
|
|
|
|
|
+static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
|
|
|
|
+{
|
|
|
|
+ return (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
|
|
|
+ ((mrq->cmd && mrq->cmd->error) ||
|
|
|
|
+ (mrq->sbc && mrq->sbc->error) ||
|
|
|
|
+ (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
|
|
|
|
+ (mrq->data->stop && mrq->data->stop->error))) ||
|
|
|
|
+ (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
|
|
|
|
+ if (host->mrqs_done[i] == mrq) {
|
|
|
|
+ WARN_ON(1);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
|
|
|
|
+ if (!host->mrqs_done[i]) {
|
|
|
|
+ host->mrqs_done[i] = mrq;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ WARN_ON(i >= SDHCI_MAX_MRQS);
|
|
|
|
+
|
|
|
|
+ tasklet_schedule(&host->finish_tasklet);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
|
{
|
|
{
|
|
- struct mmc_data *data;
|
|
|
|
|
|
+ if (host->cmd && host->cmd->mrq == mrq)
|
|
|
|
+ host->cmd = NULL;
|
|
|
|
+
|
|
|
|
+ if (host->data_cmd && host->data_cmd->mrq == mrq)
|
|
|
|
+ host->data_cmd = NULL;
|
|
|
|
+
|
|
|
|
+ if (host->data && host->data->mrq == mrq)
|
|
|
|
+ host->data = NULL;
|
|
|
|
|
|
- BUG_ON(!host->data);
|
|
|
|
|
|
+ if (sdhci_needs_reset(host, mrq))
|
|
|
|
+ host->pending_reset = true;
|
|
|
|
+
|
|
|
|
+ __sdhci_finish_mrq(host, mrq);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_finish_data(struct sdhci_host *host)
|
|
|
|
+{
|
|
|
|
+ struct mmc_command *data_cmd = host->data_cmd;
|
|
|
|
+ struct mmc_data *data = host->data;
|
|
|
|
|
|
- data = host->data;
|
|
|
|
host->data = NULL;
|
|
host->data = NULL;
|
|
|
|
+ host->data_cmd = NULL;
|
|
|
|
|
|
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
|
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
|
|
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
|
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
|
|
@@ -958,20 +1019,41 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|
*/
|
|
*/
|
|
if (data->stop &&
|
|
if (data->stop &&
|
|
(data->error ||
|
|
(data->error ||
|
|
- !host->mrq->sbc)) {
|
|
|
|
|
|
+ !data->mrq->sbc)) {
|
|
|
|
|
|
/*
|
|
/*
|
|
* The controller needs a reset of internal state machines
|
|
* The controller needs a reset of internal state machines
|
|
* upon error conditions.
|
|
* upon error conditions.
|
|
*/
|
|
*/
|
|
if (data->error) {
|
|
if (data->error) {
|
|
- sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
|
|
|
|
+ if (!host->cmd || host->cmd == data_cmd)
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* Avoid triggering warning in sdhci_send_command() */
|
|
|
|
+ host->cmd = NULL;
|
|
sdhci_send_command(host, data->stop);
|
|
sdhci_send_command(host, data->stop);
|
|
- } else
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ } else {
|
|
|
|
+ sdhci_finish_mrq(host, data->mrq);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
|
|
|
|
+ unsigned long timeout)
|
|
|
|
+{
|
|
|
|
+ if (sdhci_data_line_cmd(mrq->cmd))
|
|
|
|
+ mod_timer(&host->data_timer, timeout);
|
|
|
|
+ else
|
|
|
|
+ mod_timer(&host->timer, timeout);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
|
|
|
|
+{
|
|
|
|
+ if (sdhci_data_line_cmd(mrq->cmd))
|
|
|
|
+ del_timer(&host->data_timer);
|
|
|
|
+ else
|
|
|
|
+ del_timer(&host->timer);
|
|
}
|
|
}
|
|
|
|
|
|
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
@@ -989,12 +1071,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
timeout = 10;
|
|
timeout = 10;
|
|
|
|
|
|
mask = SDHCI_CMD_INHIBIT;
|
|
mask = SDHCI_CMD_INHIBIT;
|
|
- if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
|
|
|
|
|
|
+ if (sdhci_data_line_cmd(cmd))
|
|
mask |= SDHCI_DATA_INHIBIT;
|
|
mask |= SDHCI_DATA_INHIBIT;
|
|
|
|
|
|
/* We shouldn't wait for data inihibit for stop commands, even
|
|
/* We shouldn't wait for data inihibit for stop commands, even
|
|
though they might use busy signaling */
|
|
though they might use busy signaling */
|
|
- if (host->mrq->data && (cmd == host->mrq->data->stop))
|
|
|
|
|
|
+ if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
|
|
mask &= ~SDHCI_DATA_INHIBIT;
|
|
mask &= ~SDHCI_DATA_INHIBIT;
|
|
|
|
|
|
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
|
|
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
|
|
@@ -1003,7 +1085,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
mmc_hostname(host->mmc));
|
|
mmc_hostname(host->mmc));
|
|
sdhci_dumpregs(host);
|
|
sdhci_dumpregs(host);
|
|
cmd->error = -EIO;
|
|
cmd->error = -EIO;
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ sdhci_finish_mrq(host, cmd->mrq);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
timeout--;
|
|
timeout--;
|
|
@@ -1015,10 +1097,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
|
|
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
|
|
else
|
|
else
|
|
timeout += 10 * HZ;
|
|
timeout += 10 * HZ;
|
|
- mod_timer(&host->timer, timeout);
|
|
|
|
|
|
+ sdhci_mod_timer(host, cmd->mrq, timeout);
|
|
|
|
|
|
host->cmd = cmd;
|
|
host->cmd = cmd;
|
|
- host->busy_handle = 0;
|
|
|
|
|
|
+ if (sdhci_data_line_cmd(cmd)) {
|
|
|
|
+ WARN_ON(host->data_cmd);
|
|
|
|
+ host->data_cmd = cmd;
|
|
|
|
+ }
|
|
|
|
|
|
sdhci_prepare_data(host, cmd);
|
|
sdhci_prepare_data(host, cmd);
|
|
|
|
|
|
@@ -1030,7 +1115,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
pr_err("%s: Unsupported response type!\n",
|
|
pr_err("%s: Unsupported response type!\n",
|
|
mmc_hostname(host->mmc));
|
|
mmc_hostname(host->mmc));
|
|
cmd->error = -EINVAL;
|
|
cmd->error = -EINVAL;
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ sdhci_finish_mrq(host, cmd->mrq);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1059,40 +1144,58 @@ EXPORT_SYMBOL_GPL(sdhci_send_command);
|
|
|
|
|
|
static void sdhci_finish_command(struct sdhci_host *host)
|
|
static void sdhci_finish_command(struct sdhci_host *host)
|
|
{
|
|
{
|
|
|
|
+ struct mmc_command *cmd = host->cmd;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- BUG_ON(host->cmd == NULL);
|
|
|
|
|
|
+ host->cmd = NULL;
|
|
|
|
|
|
- if (host->cmd->flags & MMC_RSP_PRESENT) {
|
|
|
|
- if (host->cmd->flags & MMC_RSP_136) {
|
|
|
|
|
|
+ if (cmd->flags & MMC_RSP_PRESENT) {
|
|
|
|
+ if (cmd->flags & MMC_RSP_136) {
|
|
/* CRC is stripped so we need to do some shifting. */
|
|
/* CRC is stripped so we need to do some shifting. */
|
|
for (i = 0;i < 4;i++) {
|
|
for (i = 0;i < 4;i++) {
|
|
- host->cmd->resp[i] = sdhci_readl(host,
|
|
|
|
|
|
+ cmd->resp[i] = sdhci_readl(host,
|
|
SDHCI_RESPONSE + (3-i)*4) << 8;
|
|
SDHCI_RESPONSE + (3-i)*4) << 8;
|
|
if (i != 3)
|
|
if (i != 3)
|
|
- host->cmd->resp[i] |=
|
|
|
|
|
|
+ cmd->resp[i] |=
|
|
sdhci_readb(host,
|
|
sdhci_readb(host,
|
|
SDHCI_RESPONSE + (3-i)*4-1);
|
|
SDHCI_RESPONSE + (3-i)*4-1);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
|
|
|
|
|
|
+ cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The host can send and interrupt when the busy state has
|
|
|
|
+ * ended, allowing us to wait without wasting CPU cycles.
|
|
|
|
+ * The busy signal uses DAT0 so this is similar to waiting
|
|
|
|
+ * for data to complete.
|
|
|
|
+ *
|
|
|
|
+ * Note: The 1.0 specification is a bit ambiguous about this
|
|
|
|
+ * feature so there might be some problems with older
|
|
|
|
+ * controllers.
|
|
|
|
+ */
|
|
|
|
+ if (cmd->flags & MMC_RSP_BUSY) {
|
|
|
|
+ if (cmd->data) {
|
|
|
|
+ DBG("Cannot wait for busy signal when also doing a data transfer");
|
|
|
|
+ } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
|
|
|
|
+ cmd == host->data_cmd) {
|
|
|
|
+ /* Command complete before busy is ended */
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Finished CMD23, now send actual command. */
|
|
/* Finished CMD23, now send actual command. */
|
|
- if (host->cmd == host->mrq->sbc) {
|
|
|
|
- host->cmd = NULL;
|
|
|
|
- sdhci_send_command(host, host->mrq->cmd);
|
|
|
|
|
|
+ if (cmd == cmd->mrq->sbc) {
|
|
|
|
+ sdhci_send_command(host, cmd->mrq->cmd);
|
|
} else {
|
|
} else {
|
|
|
|
|
|
/* Processed actual command. */
|
|
/* Processed actual command. */
|
|
if (host->data && host->data_early)
|
|
if (host->data && host->data_early)
|
|
sdhci_finish_data(host);
|
|
sdhci_finish_data(host);
|
|
|
|
|
|
- if (!host->cmd->data)
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
-
|
|
|
|
- host->cmd = NULL;
|
|
|
|
|
|
+ if (!cmd->data)
|
|
|
|
+ sdhci_finish_mrq(host, cmd->mrq);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1373,26 +1476,22 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
- WARN_ON(host->mrq != NULL);
|
|
|
|
-
|
|
|
|
sdhci_led_activate(host);
|
|
sdhci_led_activate(host);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
|
|
* Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
|
|
* requests if Auto-CMD12 is enabled.
|
|
* requests if Auto-CMD12 is enabled.
|
|
*/
|
|
*/
|
|
- if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) {
|
|
|
|
|
|
+ if (sdhci_auto_cmd12(host, mrq)) {
|
|
if (mrq->stop) {
|
|
if (mrq->stop) {
|
|
mrq->data->stop = NULL;
|
|
mrq->data->stop = NULL;
|
|
mrq->stop = NULL;
|
|
mrq->stop = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- host->mrq = mrq;
|
|
|
|
-
|
|
|
|
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
|
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
|
- host->mrq->cmd->error = -ENOMEDIUM;
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ mrq->cmd->error = -ENOMEDIUM;
|
|
|
|
+ sdhci_finish_mrq(host, mrq);
|
|
} else {
|
|
} else {
|
|
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
|
|
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
|
|
sdhci_send_command(host, mrq->sbc);
|
|
sdhci_send_command(host, mrq->sbc);
|
|
@@ -1617,7 +1716,7 @@ static int sdhci_get_cd(struct mmc_host *mmc)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
/* If nonremovable, assume that the card is always present. */
|
|
/* If nonremovable, assume that the card is always present. */
|
|
- if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
|
|
|
|
|
+ if (!mmc_card_is_removable(host->mmc))
|
|
return 1;
|
|
return 1;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1733,13 +1832,14 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|
|
|
|
|
switch (ios->signal_voltage) {
|
|
switch (ios->signal_voltage) {
|
|
case MMC_SIGNAL_VOLTAGE_330:
|
|
case MMC_SIGNAL_VOLTAGE_330:
|
|
|
|
+ if (!(host->flags & SDHCI_SIGNALING_330))
|
|
|
|
+ return -EINVAL;
|
|
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
|
|
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
|
|
ctrl &= ~SDHCI_CTRL_VDD_180;
|
|
ctrl &= ~SDHCI_CTRL_VDD_180;
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
|
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
- ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000,
|
|
|
|
- 3600000);
|
|
|
|
|
|
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
|
|
if (ret) {
|
|
if (ret) {
|
|
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
|
|
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
|
|
mmc_hostname(mmc));
|
|
mmc_hostname(mmc));
|
|
@@ -1759,9 +1859,10 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|
|
|
|
|
return -EAGAIN;
|
|
return -EAGAIN;
|
|
case MMC_SIGNAL_VOLTAGE_180:
|
|
case MMC_SIGNAL_VOLTAGE_180:
|
|
|
|
+ if (!(host->flags & SDHCI_SIGNALING_180))
|
|
|
|
+ return -EINVAL;
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
- ret = regulator_set_voltage(mmc->supply.vqmmc,
|
|
|
|
- 1700000, 1950000);
|
|
|
|
|
|
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
|
|
if (ret) {
|
|
if (ret) {
|
|
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
|
|
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
|
|
mmc_hostname(mmc));
|
|
mmc_hostname(mmc));
|
|
@@ -1790,9 +1891,10 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|
|
|
|
|
return -EAGAIN;
|
|
return -EAGAIN;
|
|
case MMC_SIGNAL_VOLTAGE_120:
|
|
case MMC_SIGNAL_VOLTAGE_120:
|
|
|
|
+ if (!(host->flags & SDHCI_SIGNALING_120))
|
|
|
|
+ return -EINVAL;
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
- ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
|
|
|
|
- 1300000);
|
|
|
|
|
|
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
|
|
if (ret) {
|
|
if (ret) {
|
|
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
|
|
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
|
|
mmc_hostname(mmc));
|
|
mmc_hostname(mmc));
|
|
@@ -1811,10 +1913,10 @@ static int sdhci_card_busy(struct mmc_host *mmc)
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
u32 present_state;
|
|
u32 present_state;
|
|
|
|
|
|
- /* Check whether DAT[3:0] is 0000 */
|
|
|
|
|
|
+ /* Check whether DAT[0] is 0 */
|
|
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
|
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
|
|
|
|
|
- return !(present_state & SDHCI_DATA_LVL_MASK);
|
|
|
|
|
|
+ return !(present_state & SDHCI_DATA_0_LVL_MASK);
|
|
}
|
|
}
|
|
|
|
|
|
static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
@@ -1909,7 +2011,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
|
|
|
/*
|
|
/*
|
|
* Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
|
|
* Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
|
|
- * of loops reaches 40 times or a timeout of 150ms occurs.
|
|
|
|
|
|
+ * of loops reaches 40 times.
|
|
*/
|
|
*/
|
|
do {
|
|
do {
|
|
struct mmc_command cmd = {0};
|
|
struct mmc_command cmd = {0};
|
|
@@ -1920,13 +2022,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
cmd.retries = 0;
|
|
cmd.retries = 0;
|
|
cmd.data = NULL;
|
|
cmd.data = NULL;
|
|
|
|
+ cmd.mrq = &mrq;
|
|
cmd.error = 0;
|
|
cmd.error = 0;
|
|
|
|
|
|
if (tuning_loop_counter-- == 0)
|
|
if (tuning_loop_counter-- == 0)
|
|
break;
|
|
break;
|
|
|
|
|
|
mrq.cmd = &cmd;
|
|
mrq.cmd = &cmd;
|
|
- host->mrq = &mrq;
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
* In response to CMD19, the card sends 64 bytes of tuning
|
|
* In response to CMD19, the card sends 64 bytes of tuning
|
|
@@ -1956,7 +2058,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
sdhci_send_command(host, &cmd);
|
|
sdhci_send_command(host, &cmd);
|
|
|
|
|
|
host->cmd = NULL;
|
|
host->cmd = NULL;
|
|
- host->mrq = NULL;
|
|
|
|
|
|
+ sdhci_del_timer(host, &mrq);
|
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
/* Wait for Buffer Read Ready interrupt */
|
|
/* Wait for Buffer Read Ready interrupt */
|
|
@@ -2086,6 +2188,24 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
|
|
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline bool sdhci_has_requests(struct sdhci_host *host)
|
|
|
|
+{
|
|
|
|
+ return host->cmd || host->data_cmd;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_error_out_mrqs(struct sdhci_host *host, int err)
|
|
|
|
+{
|
|
|
|
+ if (host->data_cmd) {
|
|
|
|
+ host->data_cmd->error = err;
|
|
|
|
+ sdhci_finish_mrq(host, host->data_cmd->mrq);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (host->cmd) {
|
|
|
|
+ host->cmd->error = err;
|
|
|
|
+ sdhci_finish_mrq(host, host->cmd->mrq);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void sdhci_card_event(struct mmc_host *mmc)
|
|
static void sdhci_card_event(struct mmc_host *mmc)
|
|
{
|
|
{
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
@@ -2096,12 +2216,12 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
|
if (host->ops->card_event)
|
|
if (host->ops->card_event)
|
|
host->ops->card_event(host);
|
|
host->ops->card_event(host);
|
|
|
|
|
|
- present = sdhci_get_cd(host->mmc);
|
|
|
|
|
|
+ present = mmc->ops->get_cd(mmc);
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
- /* Check host->mrq first in case we are runtime suspended */
|
|
|
|
- if (host->mrq && !present) {
|
|
|
|
|
|
+ /* Check sdhci_has_requests() first in case we are runtime suspended */
|
|
|
|
+ if (sdhci_has_requests(host) && !present) {
|
|
pr_err("%s: Card removed during transfer!\n",
|
|
pr_err("%s: Card removed during transfer!\n",
|
|
mmc_hostname(host->mmc));
|
|
mmc_hostname(host->mmc));
|
|
pr_err("%s: Resetting controller.\n",
|
|
pr_err("%s: Resetting controller.\n",
|
|
@@ -2110,8 +2230,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
|
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
|
|
|
|
- host->mrq->cmd->error = -ENOMEDIUM;
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ sdhci_error_out_mrqs(host, -ENOMEDIUM);
|
|
}
|
|
}
|
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
@@ -2140,28 +2259,28 @@ static const struct mmc_host_ops sdhci_ops = {
|
|
* *
|
|
* *
|
|
\*****************************************************************************/
|
|
\*****************************************************************************/
|
|
|
|
|
|
-static void sdhci_tasklet_finish(unsigned long param)
|
|
|
|
|
|
+static bool sdhci_request_done(struct sdhci_host *host)
|
|
{
|
|
{
|
|
- struct sdhci_host *host;
|
|
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
struct mmc_request *mrq;
|
|
struct mmc_request *mrq;
|
|
-
|
|
|
|
- host = (struct sdhci_host*)param;
|
|
|
|
|
|
+ int i;
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
- /*
|
|
|
|
- * If this tasklet gets rescheduled while running, it will
|
|
|
|
- * be run again afterwards but without any active request.
|
|
|
|
- */
|
|
|
|
- if (!host->mrq) {
|
|
|
|
- spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
- return;
|
|
|
|
|
|
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
|
|
|
|
+ mrq = host->mrqs_done[i];
|
|
|
|
+ if (mrq) {
|
|
|
|
+ host->mrqs_done[i] = NULL;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- del_timer(&host->timer);
|
|
|
|
|
|
+ if (!mrq) {
|
|
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
|
|
- mrq = host->mrq;
|
|
|
|
|
|
+ sdhci_del_timer(host, mrq);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Always unmap the data buffers if they were mapped by
|
|
* Always unmap the data buffers if they were mapped by
|
|
@@ -2183,13 +2302,7 @@ static void sdhci_tasklet_finish(unsigned long param)
|
|
* The controller needs a reset of internal state machines
|
|
* The controller needs a reset of internal state machines
|
|
* upon error conditions.
|
|
* upon error conditions.
|
|
*/
|
|
*/
|
|
- if (!(host->flags & SDHCI_DEVICE_DEAD) &&
|
|
|
|
- ((mrq->cmd && mrq->cmd->error) ||
|
|
|
|
- (mrq->sbc && mrq->sbc->error) ||
|
|
|
|
- (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
|
|
|
|
- (mrq->data->stop && mrq->data->stop->error))) ||
|
|
|
|
- (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
|
|
|
|
-
|
|
|
|
|
|
+ if (sdhci_needs_reset(host, mrq)) {
|
|
/* Some controllers need this kick or reset won't work here */
|
|
/* Some controllers need this kick or reset won't work here */
|
|
if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
|
|
if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
|
|
/* This is to force an update */
|
|
/* This is to force an update */
|
|
@@ -2197,20 +2310,31 @@ static void sdhci_tasklet_finish(unsigned long param)
|
|
|
|
|
|
/* Spec says we should do both at the same time, but Ricoh
|
|
/* Spec says we should do both at the same time, but Ricoh
|
|
controllers do not like that. */
|
|
controllers do not like that. */
|
|
- sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
|
|
- sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
|
|
- }
|
|
|
|
|
|
+ if (!host->cmd)
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
|
|
+ if (!host->data_cmd)
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
|
|
|
|
- host->mrq = NULL;
|
|
|
|
- host->cmd = NULL;
|
|
|
|
- host->data = NULL;
|
|
|
|
|
|
+ host->pending_reset = false;
|
|
|
|
+ }
|
|
|
|
|
|
- sdhci_led_deactivate(host);
|
|
|
|
|
|
+ if (!sdhci_has_requests(host))
|
|
|
|
+ sdhci_led_deactivate(host);
|
|
|
|
|
|
mmiowb();
|
|
mmiowb();
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
mmc_request_done(host->mmc, mrq);
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_tasklet_finish(unsigned long param)
|
|
|
|
+{
|
|
|
|
+ struct sdhci_host *host = (struct sdhci_host *)param;
|
|
|
|
+
|
|
|
|
+ while (!sdhci_request_done(host))
|
|
|
|
+ ;
|
|
}
|
|
}
|
|
|
|
|
|
static void sdhci_timeout_timer(unsigned long data)
|
|
static void sdhci_timeout_timer(unsigned long data)
|
|
@@ -2222,7 +2346,30 @@ static void sdhci_timeout_timer(unsigned long data)
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
- if (host->mrq) {
|
|
|
|
|
|
+ if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
|
|
|
|
+ pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ sdhci_dumpregs(host);
|
|
|
|
+
|
|
|
|
+ host->cmd->error = -ETIMEDOUT;
|
|
|
|
+ sdhci_finish_mrq(host, host->cmd->mrq);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mmiowb();
|
|
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sdhci_timeout_data_timer(unsigned long data)
|
|
|
|
+{
|
|
|
|
+ struct sdhci_host *host;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ host = (struct sdhci_host *)data;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&host->lock, flags);
|
|
|
|
+
|
|
|
|
+ if (host->data || host->data_cmd ||
|
|
|
|
+ (host->cmd && sdhci_data_line_cmd(host->cmd))) {
|
|
pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
|
pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
|
mmc_hostname(host->mmc));
|
|
mmc_hostname(host->mmc));
|
|
sdhci_dumpregs(host);
|
|
sdhci_dumpregs(host);
|
|
@@ -2230,13 +2377,12 @@ static void sdhci_timeout_timer(unsigned long data)
|
|
if (host->data) {
|
|
if (host->data) {
|
|
host->data->error = -ETIMEDOUT;
|
|
host->data->error = -ETIMEDOUT;
|
|
sdhci_finish_data(host);
|
|
sdhci_finish_data(host);
|
|
|
|
+ } else if (host->data_cmd) {
|
|
|
|
+ host->data_cmd->error = -ETIMEDOUT;
|
|
|
|
+ sdhci_finish_mrq(host, host->data_cmd->mrq);
|
|
} else {
|
|
} else {
|
|
- if (host->cmd)
|
|
|
|
- host->cmd->error = -ETIMEDOUT;
|
|
|
|
- else
|
|
|
|
- host->mrq->cmd->error = -ETIMEDOUT;
|
|
|
|
-
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ host->cmd->error = -ETIMEDOUT;
|
|
|
|
+ sdhci_finish_mrq(host, host->cmd->mrq);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2252,9 +2398,14 @@ static void sdhci_timeout_timer(unsigned long data)
|
|
|
|
|
|
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
|
|
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
|
|
{
|
|
{
|
|
- BUG_ON(intmask == 0);
|
|
|
|
-
|
|
|
|
if (!host->cmd) {
|
|
if (!host->cmd) {
|
|
|
|
+ /*
|
|
|
|
+ * SDHCI recovers from errors by resetting the cmd and data
|
|
|
|
+ * circuits. Until that is done, there very well might be more
|
|
|
|
+ * interrupts, so ignore them in that case.
|
|
|
|
+ */
|
|
|
|
+ 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);
|
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
|
sdhci_dumpregs(host);
|
|
sdhci_dumpregs(host);
|
|
@@ -2285,37 +2436,14 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ sdhci_finish_mrq(host, host->cmd->mrq);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- /*
|
|
|
|
- * The host can send and interrupt when the busy state has
|
|
|
|
- * ended, allowing us to wait without wasting CPU cycles.
|
|
|
|
- * Unfortunately this is overloaded on the "data complete"
|
|
|
|
- * interrupt, so we need to take some care when handling
|
|
|
|
- * it.
|
|
|
|
- *
|
|
|
|
- * Note: The 1.0 specification is a bit ambiguous about this
|
|
|
|
- * feature so there might be some problems with older
|
|
|
|
- * controllers.
|
|
|
|
- */
|
|
|
|
- if (host->cmd->flags & MMC_RSP_BUSY) {
|
|
|
|
- if (host->cmd->data)
|
|
|
|
- DBG("Cannot wait for busy signal when also doing a data transfer");
|
|
|
|
- else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)
|
|
|
|
- && !host->busy_handle) {
|
|
|
|
- /* Mark that command complete before busy is ended */
|
|
|
|
- host->busy_handle = 1;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* The controller does not support the end-of-busy IRQ,
|
|
|
|
- * fall through and take the SDHCI_INT_RESPONSE */
|
|
|
|
- } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
|
|
|
|
- host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) {
|
|
|
|
|
|
+ if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
|
|
|
|
+ !(host->cmd->flags & MMC_RSP_BUSY) && !host->data &&
|
|
|
|
+ host->cmd->opcode == MMC_STOP_TRANSMISSION)
|
|
*mask &= ~SDHCI_INT_DATA_END;
|
|
*mask &= ~SDHCI_INT_DATA_END;
|
|
- }
|
|
|
|
|
|
|
|
if (intmask & SDHCI_INT_RESPONSE)
|
|
if (intmask & SDHCI_INT_RESPONSE)
|
|
sdhci_finish_command(host);
|
|
sdhci_finish_command(host);
|
|
@@ -2357,7 +2485,6 @@ static void sdhci_adma_show_error(struct sdhci_host *host) { }
|
|
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
{
|
|
{
|
|
u32 command;
|
|
u32 command;
|
|
- BUG_ON(intmask == 0);
|
|
|
|
|
|
|
|
/* CMD19 generates _only_ Buffer Read Ready interrupt */
|
|
/* CMD19 generates _only_ Buffer Read Ready interrupt */
|
|
if (intmask & SDHCI_INT_DATA_AVAIL) {
|
|
if (intmask & SDHCI_INT_DATA_AVAIL) {
|
|
@@ -2371,15 +2498,20 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
}
|
|
}
|
|
|
|
|
|
if (!host->data) {
|
|
if (!host->data) {
|
|
|
|
+ struct mmc_command *data_cmd = host->data_cmd;
|
|
|
|
+
|
|
|
|
+ if (data_cmd)
|
|
|
|
+ host->data_cmd = NULL;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* The "data complete" interrupt is also used to
|
|
* The "data complete" interrupt is also used to
|
|
* indicate that a busy state has ended. See comment
|
|
* indicate that a busy state has ended. See comment
|
|
* above in sdhci_cmd_irq().
|
|
* above in sdhci_cmd_irq().
|
|
*/
|
|
*/
|
|
- if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
|
|
|
|
|
|
+ if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) {
|
|
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
|
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
|
- host->cmd->error = -ETIMEDOUT;
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ data_cmd->error = -ETIMEDOUT;
|
|
|
|
+ sdhci_finish_mrq(host, data_cmd->mrq);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
if (intmask & SDHCI_INT_DATA_END) {
|
|
if (intmask & SDHCI_INT_DATA_END) {
|
|
@@ -2388,14 +2520,22 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
* before the command completed, so make
|
|
* before the command completed, so make
|
|
* sure we do things in the proper order.
|
|
* sure we do things in the proper order.
|
|
*/
|
|
*/
|
|
- if (host->busy_handle)
|
|
|
|
- sdhci_finish_command(host);
|
|
|
|
- else
|
|
|
|
- host->busy_handle = 1;
|
|
|
|
|
|
+ if (host->cmd == data_cmd)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ sdhci_finish_mrq(host, data_cmd->mrq);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * SDHCI recovers from errors by resetting the cmd and data
|
|
|
|
+ * circuits. Until that is done, there very well might be more
|
|
|
|
+ * interrupts, so ignore them in that case.
|
|
|
|
+ */
|
|
|
|
+ if (host->pending_reset)
|
|
|
|
+ return;
|
|
|
|
+
|
|
pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
|
|
pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
|
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
|
sdhci_dumpregs(host);
|
|
sdhci_dumpregs(host);
|
|
@@ -2453,7 +2593,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
}
|
|
}
|
|
|
|
|
|
if (intmask & SDHCI_INT_DATA_END) {
|
|
if (intmask & SDHCI_INT_DATA_END) {
|
|
- if (host->cmd) {
|
|
|
|
|
|
+ if (host->cmd == host->data_cmd) {
|
|
/*
|
|
/*
|
|
* Data managed to finish before the
|
|
* Data managed to finish before the
|
|
* command completed. Make sure we do
|
|
* command completed. Make sure we do
|
|
@@ -2537,6 +2677,9 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|
pr_err("%s: Card is consuming too much power!\n",
|
|
pr_err("%s: Card is consuming too much power!\n",
|
|
mmc_hostname(host->mmc));
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
|
|
|
+ if (intmask & SDHCI_INT_RETUNE)
|
|
|
|
+ mmc_retune_needed(host->mmc);
|
|
|
|
+
|
|
if (intmask & SDHCI_INT_CARD_INT) {
|
|
if (intmask & SDHCI_INT_CARD_INT) {
|
|
sdhci_enable_sdio_irq_nolock(host, false);
|
|
sdhci_enable_sdio_irq_nolock(host, false);
|
|
host->thread_isr |= SDHCI_INT_CARD_INT;
|
|
host->thread_isr |= SDHCI_INT_CARD_INT;
|
|
@@ -2546,7 +2689,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
|
|
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
|
|
SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
|
|
SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
|
|
SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
|
|
SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
|
|
- SDHCI_INT_CARD_INT);
|
|
|
|
|
|
+ SDHCI_INT_RETUNE | SDHCI_INT_CARD_INT);
|
|
|
|
|
|
if (intmask) {
|
|
if (intmask) {
|
|
unexpected |= intmask;
|
|
unexpected |= intmask;
|
|
@@ -2582,8 +2725,10 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
|
|
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
|
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
|
- sdhci_card_event(host->mmc);
|
|
|
|
- mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
|
|
|
|
|
+ struct mmc_host *mmc = host->mmc;
|
|
|
|
+
|
|
|
|
+ mmc->ops->card_event(mmc);
|
|
|
|
+ mmc_detect_change(mmc, msecs_to_jiffies(200));
|
|
}
|
|
}
|
|
|
|
|
|
if (isr & SDHCI_INT_CARD_INT) {
|
|
if (isr & SDHCI_INT_CARD_INT) {
|
|
@@ -2605,18 +2750,31 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
|
\*****************************************************************************/
|
|
\*****************************************************************************/
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
#ifdef CONFIG_PM
|
|
|
|
+/*
|
|
|
|
+ * To enable wakeup events, the corresponding events have to be enabled in
|
|
|
|
+ * the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
|
|
|
|
+ * Table' in the SD Host Controller Standard Specification.
|
|
|
|
+ * It is useless to restore SDHCI_INT_ENABLE state in
|
|
|
|
+ * sdhci_disable_irq_wakeups() since it will be set by
|
|
|
|
+ * sdhci_enable_card_detection() or sdhci_init().
|
|
|
|
+ */
|
|
void sdhci_enable_irq_wakeups(struct sdhci_host *host)
|
|
void sdhci_enable_irq_wakeups(struct sdhci_host *host)
|
|
{
|
|
{
|
|
u8 val;
|
|
u8 val;
|
|
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
|
|
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
|
|
| SDHCI_WAKE_ON_INT;
|
|
| SDHCI_WAKE_ON_INT;
|
|
|
|
+ u32 irq_val = SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
|
|
|
|
+ SDHCI_INT_CARD_INT;
|
|
|
|
|
|
val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
|
|
val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
|
|
val |= mask ;
|
|
val |= mask ;
|
|
/* Avoid fake wake up */
|
|
/* Avoid fake wake up */
|
|
- if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
|
|
|
|
|
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) {
|
|
val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE);
|
|
val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE);
|
|
|
|
+ irq_val &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
|
|
|
|
+ }
|
|
sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
|
|
sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
|
|
|
|
+ sdhci_writel(host, irq_val, SDHCI_INT_ENABLE);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
|
|
EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
|
|
|
|
|
|
@@ -2636,7 +2794,8 @@ int sdhci_suspend_host(struct sdhci_host *host)
|
|
sdhci_disable_card_detection(host);
|
|
sdhci_disable_card_detection(host);
|
|
|
|
|
|
mmc_retune_timer_stop(host->mmc);
|
|
mmc_retune_timer_stop(host->mmc);
|
|
- mmc_retune_needed(host->mmc);
|
|
|
|
|
|
+ if (host->tuning_mode != SDHCI_TUNING_MODE_3)
|
|
|
|
+ mmc_retune_needed(host->mmc);
|
|
|
|
|
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
|
host->ier = 0;
|
|
host->ier = 0;
|
|
@@ -2654,6 +2813,7 @@ EXPORT_SYMBOL_GPL(sdhci_suspend_host);
|
|
|
|
|
|
int sdhci_resume_host(struct sdhci_host *host)
|
|
int sdhci_resume_host(struct sdhci_host *host)
|
|
{
|
|
{
|
|
|
|
+ struct mmc_host *mmc = host->mmc;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
|
@@ -2667,7 +2827,7 @@ int sdhci_resume_host(struct sdhci_host *host)
|
|
sdhci_init(host, 0);
|
|
sdhci_init(host, 0);
|
|
host->pwr = 0;
|
|
host->pwr = 0;
|
|
host->clock = 0;
|
|
host->clock = 0;
|
|
- sdhci_set_ios(host->mmc, &host->mmc->ios);
|
|
|
|
|
|
+ mmc->ops->set_ios(mmc, &mmc->ios);
|
|
} else {
|
|
} else {
|
|
sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
|
|
sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
|
|
mmiowb();
|
|
mmiowb();
|
|
@@ -2696,7 +2856,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
mmc_retune_timer_stop(host->mmc);
|
|
mmc_retune_timer_stop(host->mmc);
|
|
- mmc_retune_needed(host->mmc);
|
|
|
|
|
|
+ if (host->tuning_mode != SDHCI_TUNING_MODE_3)
|
|
|
|
+ mmc_retune_needed(host->mmc);
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
host->ier &= SDHCI_INT_CARD_INT;
|
|
host->ier &= SDHCI_INT_CARD_INT;
|
|
@@ -2716,6 +2877,7 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
|
|
|
|
|
|
int sdhci_runtime_resume_host(struct sdhci_host *host)
|
|
int sdhci_runtime_resume_host(struct sdhci_host *host)
|
|
{
|
|
{
|
|
|
|
+ struct mmc_host *mmc = host->mmc;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
int host_flags = host->flags;
|
|
int host_flags = host->flags;
|
|
|
|
|
|
@@ -2729,8 +2891,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
|
/* Force clock and power re-program */
|
|
/* Force clock and power re-program */
|
|
host->pwr = 0;
|
|
host->pwr = 0;
|
|
host->clock = 0;
|
|
host->clock = 0;
|
|
- sdhci_start_signal_voltage_switch(host->mmc, &host->mmc->ios);
|
|
|
|
- sdhci_set_ios(host->mmc, &host->mmc->ios);
|
|
|
|
|
|
+ mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios);
|
|
|
|
+ mmc->ops->set_ios(mmc, &mmc->ios);
|
|
|
|
|
|
if ((host_flags & SDHCI_PV_ENABLED) &&
|
|
if ((host_flags & SDHCI_PV_ENABLED) &&
|
|
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
|
|
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
|
|
@@ -2781,6 +2943,8 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
|
|
host->mmc_host_ops = sdhci_ops;
|
|
host->mmc_host_ops = sdhci_ops;
|
|
mmc->ops = &host->mmc_host_ops;
|
|
mmc->ops = &host->mmc_host_ops;
|
|
|
|
|
|
|
|
+ host->flags = SDHCI_SIGNALING_330;
|
|
|
|
+
|
|
return host;
|
|
return host;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2816,10 +2980,41 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-int sdhci_add_host(struct sdhci_host *host)
|
|
|
|
|
|
+void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
|
|
|
|
+{
|
|
|
|
+ u16 v;
|
|
|
|
+
|
|
|
|
+ if (host->read_caps)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ host->read_caps = true;
|
|
|
|
+
|
|
|
|
+ if (debug_quirks)
|
|
|
|
+ host->quirks = debug_quirks;
|
|
|
|
+
|
|
|
|
+ if (debug_quirks2)
|
|
|
|
+ host->quirks2 = debug_quirks2;
|
|
|
|
+
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
|
|
|
|
+
|
|
|
|
+ v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
|
|
|
|
+ host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
|
|
|
|
+
|
|
|
|
+ if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES);
|
|
|
|
+
|
|
|
|
+ if (host->version < SDHCI_SPEC_300)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ host->caps1 = caps1 ? *caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(__sdhci_read_caps);
|
|
|
|
+
|
|
|
|
+int sdhci_setup_host(struct sdhci_host *host)
|
|
{
|
|
{
|
|
struct mmc_host *mmc;
|
|
struct mmc_host *mmc;
|
|
- u32 caps[2] = {0, 0};
|
|
|
|
u32 max_current_caps;
|
|
u32 max_current_caps;
|
|
unsigned int ocr_avail;
|
|
unsigned int ocr_avail;
|
|
unsigned int override_timeout_clk;
|
|
unsigned int override_timeout_clk;
|
|
@@ -2832,34 +3027,28 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
|
|
|
|
mmc = host->mmc;
|
|
mmc = host->mmc;
|
|
|
|
|
|
- if (debug_quirks)
|
|
|
|
- host->quirks = debug_quirks;
|
|
|
|
- if (debug_quirks2)
|
|
|
|
- host->quirks2 = debug_quirks2;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If there are external regulators, get them. Note this must be done
|
|
|
|
+ * early before resetting the host and reading the capabilities so that
|
|
|
|
+ * the host can take the appropriate action if regulators are not
|
|
|
|
+ * available.
|
|
|
|
+ */
|
|
|
|
+ ret = mmc_regulator_get_supply(mmc);
|
|
|
|
+ if (ret == -EPROBE_DEFER)
|
|
|
|
+ return ret;
|
|
|
|
|
|
- override_timeout_clk = host->timeout_clk;
|
|
|
|
|
|
+ sdhci_read_caps(host);
|
|
|
|
|
|
- sdhci_do_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
+ override_timeout_clk = host->timeout_clk;
|
|
|
|
|
|
- host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
|
|
|
|
- host->version = (host->version & SDHCI_SPEC_VER_MASK)
|
|
|
|
- >> SDHCI_SPEC_VER_SHIFT;
|
|
|
|
if (host->version > SDHCI_SPEC_300) {
|
|
if (host->version > SDHCI_SPEC_300) {
|
|
pr_err("%s: Unknown controller version (%d). You may experience problems.\n",
|
|
pr_err("%s: Unknown controller version (%d). You may experience problems.\n",
|
|
mmc_hostname(mmc), host->version);
|
|
mmc_hostname(mmc), host->version);
|
|
}
|
|
}
|
|
|
|
|
|
- caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
|
|
|
|
- sdhci_readl(host, SDHCI_CAPABILITIES);
|
|
|
|
-
|
|
|
|
- if (host->version >= SDHCI_SPEC_300)
|
|
|
|
- caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ?
|
|
|
|
- host->caps1 :
|
|
|
|
- sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
|
|
|
-
|
|
|
|
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
|
|
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
|
|
host->flags |= SDHCI_USE_SDMA;
|
|
host->flags |= SDHCI_USE_SDMA;
|
|
- else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
|
|
|
|
|
|
+ else if (!(host->caps & SDHCI_CAN_DO_SDMA))
|
|
DBG("Controller doesn't have SDMA capability\n");
|
|
DBG("Controller doesn't have SDMA capability\n");
|
|
else
|
|
else
|
|
host->flags |= SDHCI_USE_SDMA;
|
|
host->flags |= SDHCI_USE_SDMA;
|
|
@@ -2871,7 +3060,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
|
|
|
|
if ((host->version >= SDHCI_SPEC_200) &&
|
|
if ((host->version >= SDHCI_SPEC_200) &&
|
|
- (caps[0] & SDHCI_CAN_DO_ADMA2))
|
|
|
|
|
|
+ (host->caps & SDHCI_CAN_DO_ADMA2))
|
|
host->flags |= SDHCI_USE_ADMA;
|
|
host->flags |= SDHCI_USE_ADMA;
|
|
|
|
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
|
|
@@ -2887,7 +3076,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
|
|
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
|
|
* implement.
|
|
* implement.
|
|
*/
|
|
*/
|
|
- if (caps[0] & SDHCI_CAN_64BIT)
|
|
|
|
|
|
+ if (host->caps & SDHCI_CAN_64BIT)
|
|
host->flags |= SDHCI_USE_64_BIT_DMA;
|
|
host->flags |= SDHCI_USE_64_BIT_DMA;
|
|
|
|
|
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
|
@@ -2963,10 +3152,10 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
|
|
|
|
if (host->version >= SDHCI_SPEC_300)
|
|
if (host->version >= SDHCI_SPEC_300)
|
|
- host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
|
|
|
|
|
|
+ host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK)
|
|
>> SDHCI_CLOCK_BASE_SHIFT;
|
|
>> SDHCI_CLOCK_BASE_SHIFT;
|
|
else
|
|
else
|
|
- host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
|
|
|
|
|
|
+ host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
|
|
>> SDHCI_CLOCK_BASE_SHIFT;
|
|
>> SDHCI_CLOCK_BASE_SHIFT;
|
|
|
|
|
|
host->max_clk *= 1000000;
|
|
host->max_clk *= 1000000;
|
|
@@ -2985,7 +3174,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
* In case of Host Controller v3.00, find out whether clock
|
|
* In case of Host Controller v3.00, find out whether clock
|
|
* multiplier is supported.
|
|
* multiplier is supported.
|
|
*/
|
|
*/
|
|
- host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >>
|
|
|
|
|
|
+ host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >>
|
|
SDHCI_CLOCK_MUL_SHIFT;
|
|
SDHCI_CLOCK_MUL_SHIFT;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -3017,7 +3206,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
mmc->f_max = max_clk;
|
|
mmc->f_max = max_clk;
|
|
|
|
|
|
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
|
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
|
- host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
|
|
|
|
|
|
+ host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >>
|
|
SDHCI_TIMEOUT_CLK_SHIFT;
|
|
SDHCI_TIMEOUT_CLK_SHIFT;
|
|
if (host->timeout_clk == 0) {
|
|
if (host->timeout_clk == 0) {
|
|
if (host->ops->get_timeout_clock) {
|
|
if (host->ops->get_timeout_clock) {
|
|
@@ -3031,7 +3220,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
|
|
|
|
|
|
+ if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
|
|
host->timeout_clk *= 1000;
|
|
host->timeout_clk *= 1000;
|
|
|
|
|
|
if (override_timeout_clk)
|
|
if (override_timeout_clk)
|
|
@@ -3072,27 +3261,22 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
|
|
if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
|
|
mmc->caps &= ~MMC_CAP_CMD23;
|
|
mmc->caps &= ~MMC_CAP_CMD23;
|
|
|
|
|
|
- if (caps[0] & SDHCI_CAN_DO_HISPD)
|
|
|
|
|
|
+ if (host->caps & SDHCI_CAN_DO_HISPD)
|
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
|
|
|
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
|
- !(mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
|
|
|
|
|
+ mmc_card_is_removable(mmc) &&
|
|
mmc_gpio_get_cd(host->mmc) < 0)
|
|
mmc_gpio_get_cd(host->mmc) < 0)
|
|
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
|
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
|
|
|
|
|
- /* If there are external regulators, get them */
|
|
|
|
- ret = mmc_regulator_get_supply(mmc);
|
|
|
|
- if (ret == -EPROBE_DEFER)
|
|
|
|
- goto undma;
|
|
|
|
-
|
|
|
|
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
|
|
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
ret = regulator_enable(mmc->supply.vqmmc);
|
|
ret = regulator_enable(mmc->supply.vqmmc);
|
|
if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
|
|
if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
|
|
1950000))
|
|
1950000))
|
|
- caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
|
|
|
|
- SDHCI_SUPPORT_SDR50 |
|
|
|
|
- SDHCI_SUPPORT_DDR50);
|
|
|
|
|
|
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR104 |
|
|
|
|
+ SDHCI_SUPPORT_SDR50 |
|
|
|
|
+ SDHCI_SUPPORT_DDR50);
|
|
if (ret) {
|
|
if (ret) {
|
|
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
|
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
|
|
mmc_hostname(mmc), ret);
|
|
mmc_hostname(mmc), ret);
|
|
@@ -3100,28 +3284,30 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
|
|
|
|
- caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
|
|
|
- SDHCI_SUPPORT_DDR50);
|
|
|
|
|
|
+ if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
|
|
|
|
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
|
|
|
+ SDHCI_SUPPORT_DDR50);
|
|
|
|
+ }
|
|
|
|
|
|
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
|
|
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
|
|
- if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
|
|
|
- SDHCI_SUPPORT_DDR50))
|
|
|
|
|
|
+ if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
|
|
|
|
+ SDHCI_SUPPORT_DDR50))
|
|
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
|
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
|
|
|
|
|
/* SDR104 supports also implies SDR50 support */
|
|
/* SDR104 supports also implies SDR50 support */
|
|
- if (caps[1] & SDHCI_SUPPORT_SDR104) {
|
|
|
|
|
|
+ if (host->caps1 & SDHCI_SUPPORT_SDR104) {
|
|
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
|
|
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
|
|
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
|
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
|
* field can be promoted to support HS200.
|
|
* field can be promoted to support HS200.
|
|
*/
|
|
*/
|
|
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
|
|
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
|
|
mmc->caps2 |= MMC_CAP2_HS200;
|
|
mmc->caps2 |= MMC_CAP2_HS200;
|
|
- } else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
|
|
|
|
|
+ } else if (host->caps1 & SDHCI_SUPPORT_SDR50) {
|
|
mmc->caps |= MMC_CAP_UHS_SDR50;
|
|
mmc->caps |= MMC_CAP_UHS_SDR50;
|
|
|
|
+ }
|
|
|
|
|
|
if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
|
|
if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
|
|
- (caps[1] & SDHCI_SUPPORT_HS400))
|
|
|
|
|
|
+ (host->caps1 & SDHCI_SUPPORT_HS400))
|
|
mmc->caps2 |= MMC_CAP2_HS400;
|
|
mmc->caps2 |= MMC_CAP2_HS400;
|
|
|
|
|
|
if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
|
|
if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
|
|
@@ -3130,25 +3316,25 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
1300000)))
|
|
1300000)))
|
|
mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
|
|
mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
|
|
|
|
|
|
- if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
|
|
|
|
- !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
|
|
|
|
|
|
+ if ((host->caps1 & SDHCI_SUPPORT_DDR50) &&
|
|
|
|
+ !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
|
|
mmc->caps |= MMC_CAP_UHS_DDR50;
|
|
mmc->caps |= MMC_CAP_UHS_DDR50;
|
|
|
|
|
|
/* Does the host need tuning for SDR50? */
|
|
/* Does the host need tuning for SDR50? */
|
|
- if (caps[1] & SDHCI_USE_SDR50_TUNING)
|
|
|
|
|
|
+ if (host->caps1 & SDHCI_USE_SDR50_TUNING)
|
|
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
|
|
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
|
|
|
|
|
|
/* Driver Type(s) (A, C, D) supported by the host */
|
|
/* Driver Type(s) (A, C, D) supported by the host */
|
|
- if (caps[1] & SDHCI_DRIVER_TYPE_A)
|
|
|
|
|
|
+ if (host->caps1 & SDHCI_DRIVER_TYPE_A)
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
|
|
- if (caps[1] & SDHCI_DRIVER_TYPE_C)
|
|
|
|
|
|
+ if (host->caps1 & SDHCI_DRIVER_TYPE_C)
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
|
|
- if (caps[1] & SDHCI_DRIVER_TYPE_D)
|
|
|
|
|
|
+ if (host->caps1 & SDHCI_DRIVER_TYPE_D)
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
|
|
|
|
|
|
/* Initial value for re-tuning timer count */
|
|
/* Initial value for re-tuning timer count */
|
|
- host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
|
|
|
|
- SDHCI_RETUNING_TIMER_COUNT_SHIFT;
|
|
|
|
|
|
+ host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
|
|
|
|
+ SDHCI_RETUNING_TIMER_COUNT_SHIFT;
|
|
|
|
|
|
/*
|
|
/*
|
|
* In case Re-tuning Timer is not disabled, the actual value of
|
|
* In case Re-tuning Timer is not disabled, the actual value of
|
|
@@ -3158,7 +3344,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
host->tuning_count = 1 << (host->tuning_count - 1);
|
|
host->tuning_count = 1 << (host->tuning_count - 1);
|
|
|
|
|
|
/* Re-tuning mode supported by the Host Controller */
|
|
/* Re-tuning mode supported by the Host Controller */
|
|
- host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >>
|
|
|
|
|
|
+ host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >>
|
|
SDHCI_RETUNING_MODE_SHIFT;
|
|
SDHCI_RETUNING_MODE_SHIFT;
|
|
|
|
|
|
ocr_avail = 0;
|
|
ocr_avail = 0;
|
|
@@ -3187,7 +3373,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (caps[0] & SDHCI_CAN_VDD_330) {
|
|
|
|
|
|
+ if (host->caps & SDHCI_CAN_VDD_330) {
|
|
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
|
|
|
mmc->max_current_330 = ((max_current_caps &
|
|
mmc->max_current_330 = ((max_current_caps &
|
|
@@ -3195,7 +3381,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
SDHCI_MAX_CURRENT_330_SHIFT) *
|
|
SDHCI_MAX_CURRENT_330_SHIFT) *
|
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
}
|
|
}
|
|
- if (caps[0] & SDHCI_CAN_VDD_300) {
|
|
|
|
|
|
+ if (host->caps & SDHCI_CAN_VDD_300) {
|
|
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
|
|
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
|
|
|
|
|
|
mmc->max_current_300 = ((max_current_caps &
|
|
mmc->max_current_300 = ((max_current_caps &
|
|
@@ -3203,7 +3389,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
SDHCI_MAX_CURRENT_300_SHIFT) *
|
|
SDHCI_MAX_CURRENT_300_SHIFT) *
|
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
}
|
|
}
|
|
- if (caps[0] & SDHCI_CAN_VDD_180) {
|
|
|
|
|
|
+ if (host->caps & SDHCI_CAN_VDD_180) {
|
|
ocr_avail |= MMC_VDD_165_195;
|
|
ocr_avail |= MMC_VDD_165_195;
|
|
|
|
|
|
mmc->max_current_180 = ((max_current_caps &
|
|
mmc->max_current_180 = ((max_current_caps &
|
|
@@ -3240,6 +3426,15 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
goto unreg;
|
|
goto unreg;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
|
|
|
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
|
|
|
|
+ MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) ||
|
|
|
|
+ (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
|
|
|
|
+ host->flags |= SDHCI_SIGNALING_180;
|
|
|
|
+
|
|
|
|
+ if (mmc->caps2 & MMC_CAP2_HSX00_1_2V)
|
|
|
|
+ host->flags |= SDHCI_SIGNALING_120;
|
|
|
|
+
|
|
spin_lock_init(&host->lock);
|
|
spin_lock_init(&host->lock);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -3281,7 +3476,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
|
|
if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
|
|
mmc->max_blk_size = 2;
|
|
mmc->max_blk_size = 2;
|
|
} else {
|
|
} else {
|
|
- mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
|
|
|
|
|
|
+ mmc->max_blk_size = (host->caps & SDHCI_MAX_BLOCK_MASK) >>
|
|
SDHCI_MAX_BLOCK_SHIFT;
|
|
SDHCI_MAX_BLOCK_SHIFT;
|
|
if (mmc->max_blk_size >= 3) {
|
|
if (mmc->max_blk_size >= 3) {
|
|
pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n",
|
|
pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n",
|
|
@@ -3297,6 +3492,28 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
*/
|
|
*/
|
|
mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
|
|
mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
|
|
|
|
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+unreg:
|
|
|
|
+ if (!IS_ERR(mmc->supply.vqmmc))
|
|
|
|
+ regulator_disable(mmc->supply.vqmmc);
|
|
|
|
+undma:
|
|
|
|
+ if (host->align_buffer)
|
|
|
|
+ dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
|
|
|
+ host->adma_table_sz, host->align_buffer,
|
|
|
|
+ host->align_addr);
|
|
|
|
+ host->adma_table = NULL;
|
|
|
|
+ host->align_buffer = NULL;
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_setup_host);
|
|
|
|
+
|
|
|
|
+int __sdhci_add_host(struct sdhci_host *host)
|
|
|
|
+{
|
|
|
|
+ struct mmc_host *mmc = host->mmc;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Init tasklets.
|
|
* Init tasklets.
|
|
*/
|
|
*/
|
|
@@ -3304,6 +3521,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
sdhci_tasklet_finish, (unsigned long)host);
|
|
sdhci_tasklet_finish, (unsigned long)host);
|
|
|
|
|
|
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
|
|
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
|
|
|
|
+ setup_timer(&host->data_timer, sdhci_timeout_data_timer,
|
|
|
|
+ (unsigned long)host);
|
|
|
|
|
|
init_waitqueue_head(&host->buf_ready_int);
|
|
init_waitqueue_head(&host->buf_ready_int);
|
|
|
|
|
|
@@ -3353,10 +3572,10 @@ unirq:
|
|
free_irq(host->irq, host);
|
|
free_irq(host->irq, host);
|
|
untasklet:
|
|
untasklet:
|
|
tasklet_kill(&host->finish_tasklet);
|
|
tasklet_kill(&host->finish_tasklet);
|
|
-unreg:
|
|
|
|
|
|
+
|
|
if (!IS_ERR(mmc->supply.vqmmc))
|
|
if (!IS_ERR(mmc->supply.vqmmc))
|
|
regulator_disable(mmc->supply.vqmmc);
|
|
regulator_disable(mmc->supply.vqmmc);
|
|
-undma:
|
|
|
|
|
|
+
|
|
if (host->align_buffer)
|
|
if (host->align_buffer)
|
|
dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
|
dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
|
host->adma_table_sz, host->align_buffer,
|
|
host->adma_table_sz, host->align_buffer,
|
|
@@ -3366,7 +3585,18 @@ undma:
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL_GPL(__sdhci_add_host);
|
|
|
|
+
|
|
|
|
+int sdhci_add_host(struct sdhci_host *host)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
|
|
|
|
+ ret = sdhci_setup_host(host);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return __sdhci_add_host(host);
|
|
|
|
+}
|
|
EXPORT_SYMBOL_GPL(sdhci_add_host);
|
|
EXPORT_SYMBOL_GPL(sdhci_add_host);
|
|
|
|
|
|
void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|
void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|
@@ -3379,12 +3609,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|
|
|
|
|
host->flags |= SDHCI_DEVICE_DEAD;
|
|
host->flags |= SDHCI_DEVICE_DEAD;
|
|
|
|
|
|
- if (host->mrq) {
|
|
|
|
|
|
+ if (sdhci_has_requests(host)) {
|
|
pr_err("%s: Controller removed during "
|
|
pr_err("%s: Controller removed during "
|
|
" transfer!\n", mmc_hostname(mmc));
|
|
" transfer!\n", mmc_hostname(mmc));
|
|
-
|
|
|
|
- host->mrq->cmd->error = -ENOMEDIUM;
|
|
|
|
- tasklet_schedule(&host->finish_tasklet);
|
|
|
|
|
|
+ sdhci_error_out_mrqs(host, -ENOMEDIUM);
|
|
}
|
|
}
|
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
@@ -3404,6 +3632,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|
free_irq(host->irq, host);
|
|
free_irq(host->irq, host);
|
|
|
|
|
|
del_timer_sync(&host->timer);
|
|
del_timer_sync(&host->timer);
|
|
|
|
+ del_timer_sync(&host->data_timer);
|
|
|
|
|
|
tasklet_kill(&host->finish_tasklet);
|
|
tasklet_kill(&host->finish_tasklet);
|
|
|
|
|