|
@@ -44,6 +44,8 @@
|
|
|
|
|
|
#define MAX_TUNING_LOOP 40
|
|
#define MAX_TUNING_LOOP 40
|
|
|
|
|
|
|
|
+#define ADMA_SIZE ((128 * 2 + 1) * 4)
|
|
|
|
+
|
|
static unsigned int debug_quirks = 0;
|
|
static unsigned int debug_quirks = 0;
|
|
static unsigned int debug_quirks2;
|
|
static unsigned int debug_quirks2;
|
|
|
|
|
|
@@ -131,43 +133,26 @@ static void sdhci_dumpregs(struct sdhci_host *host)
|
|
* *
|
|
* *
|
|
\*****************************************************************************/
|
|
\*****************************************************************************/
|
|
|
|
|
|
-static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
|
|
|
|
-{
|
|
|
|
- u32 ier;
|
|
|
|
-
|
|
|
|
- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
|
|
|
|
- ier &= ~clear;
|
|
|
|
- ier |= set;
|
|
|
|
- sdhci_writel(host, ier, SDHCI_INT_ENABLE);
|
|
|
|
- sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
|
|
|
|
-{
|
|
|
|
- sdhci_clear_set_irqs(host, 0, irqs);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
|
|
|
|
-{
|
|
|
|
- sdhci_clear_set_irqs(host, irqs, 0);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
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, irqs;
|
|
|
|
|
|
+ u32 present;
|
|
|
|
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
|
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
|
(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
|
(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
|
return;
|
|
return;
|
|
|
|
|
|
- present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
|
|
- SDHCI_CARD_PRESENT;
|
|
|
|
- irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
|
|
|
|
|
|
+ if (enable) {
|
|
|
|
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
|
|
+ SDHCI_CARD_PRESENT;
|
|
|
|
|
|
- if (enable)
|
|
|
|
- sdhci_unmask_irqs(host, irqs);
|
|
|
|
- else
|
|
|
|
- sdhci_mask_irqs(host, irqs);
|
|
|
|
|
|
+ host->ier |= present ? SDHCI_INT_CARD_REMOVE :
|
|
|
|
+ SDHCI_INT_CARD_INSERT;
|
|
|
|
+ } else {
|
|
|
|
+ host->ier &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
}
|
|
}
|
|
|
|
|
|
static void sdhci_enable_card_detection(struct sdhci_host *host)
|
|
static void sdhci_enable_card_detection(struct sdhci_host *host)
|
|
@@ -180,22 +165,9 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)
|
|
sdhci_set_card_detection(host, false);
|
|
sdhci_set_card_detection(host, false);
|
|
}
|
|
}
|
|
|
|
|
|
-static void sdhci_reset(struct sdhci_host *host, u8 mask)
|
|
|
|
|
|
+void sdhci_reset(struct sdhci_host *host, u8 mask)
|
|
{
|
|
{
|
|
unsigned long timeout;
|
|
unsigned long timeout;
|
|
- u32 uninitialized_var(ier);
|
|
|
|
-
|
|
|
|
- if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
|
|
|
- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
|
|
- SDHCI_CARD_PRESENT))
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
|
|
|
|
- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
|
|
|
|
-
|
|
|
|
- if (host->ops->platform_reset_enter)
|
|
|
|
- host->ops->platform_reset_enter(host, mask);
|
|
|
|
|
|
|
|
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
|
|
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
|
|
|
|
|
|
@@ -220,16 +192,27 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
|
|
timeout--;
|
|
timeout--;
|
|
mdelay(1);
|
|
mdelay(1);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_reset);
|
|
|
|
+
|
|
|
|
+static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
|
|
|
+{
|
|
|
|
+ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
|
|
|
+ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
|
|
+ SDHCI_CARD_PRESENT))
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
- if (host->ops->platform_reset_exit)
|
|
|
|
- host->ops->platform_reset_exit(host, mask);
|
|
|
|
|
|
+ host->ops->reset(host, mask);
|
|
|
|
|
|
- if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
|
|
|
|
- sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
|
|
|
|
|
|
+ if (mask & SDHCI_RESET_ALL) {
|
|
|
|
+ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
|
|
|
+ if (host->ops->enable_dma)
|
|
|
|
+ host->ops->enable_dma(host);
|
|
|
|
+ }
|
|
|
|
|
|
- if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
|
|
|
- if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL))
|
|
|
|
- host->ops->enable_dma(host);
|
|
|
|
|
|
+ /* Resetting the controller clears many */
|
|
|
|
+ host->preset_enabled = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -238,15 +221,18 @@ 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)
|
|
{
|
|
{
|
|
if (soft)
|
|
if (soft)
|
|
- sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
|
|
else
|
|
else
|
|
- sdhci_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
- sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
|
|
|
|
- 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);
|
|
|
|
|
|
+ host->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_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
|
|
|
|
if (soft) {
|
|
if (soft) {
|
|
/* force clock reconfiguration */
|
|
/* force clock reconfiguration */
|
|
@@ -502,11 +488,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|
else
|
|
else
|
|
direction = DMA_TO_DEVICE;
|
|
direction = DMA_TO_DEVICE;
|
|
|
|
|
|
- /*
|
|
|
|
- * The ADMA descriptor table is mapped further down as we
|
|
|
|
- * need to fill it with data first.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
host->align_addr = dma_map_single(mmc_dev(host->mmc),
|
|
host->align_addr = dma_map_single(mmc_dev(host->mmc),
|
|
host->align_buffer, 128 * 4, direction);
|
|
host->align_buffer, 128 * 4, direction);
|
|
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
|
|
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
|
|
@@ -567,7 +548,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|
* If this triggers then we have a calculation bug
|
|
* If this triggers then we have a calculation bug
|
|
* somewhere. :/
|
|
* somewhere. :/
|
|
*/
|
|
*/
|
|
- WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4);
|
|
|
|
|
|
+ WARN_ON((desc - host->adma_desc) > ADMA_SIZE);
|
|
}
|
|
}
|
|
|
|
|
|
if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
|
|
if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
|
|
@@ -595,17 +576,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|
host->align_addr, 128 * 4, direction);
|
|
host->align_addr, 128 * 4, direction);
|
|
}
|
|
}
|
|
|
|
|
|
- host->adma_addr = dma_map_single(mmc_dev(host->mmc),
|
|
|
|
- host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
|
|
|
|
- if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr))
|
|
|
|
- goto unmap_entries;
|
|
|
|
- BUG_ON(host->adma_addr & 0x3);
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
-unmap_entries:
|
|
|
|
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
|
|
|
- data->sg_len, direction);
|
|
|
|
unmap_align:
|
|
unmap_align:
|
|
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
|
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
|
128 * 4, direction);
|
|
128 * 4, direction);
|
|
@@ -623,19 +595,25 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
|
u8 *align;
|
|
u8 *align;
|
|
char *buffer;
|
|
char *buffer;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
+ bool has_unaligned;
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
if (data->flags & MMC_DATA_READ)
|
|
direction = DMA_FROM_DEVICE;
|
|
direction = DMA_FROM_DEVICE;
|
|
else
|
|
else
|
|
direction = DMA_TO_DEVICE;
|
|
direction = DMA_TO_DEVICE;
|
|
|
|
|
|
- dma_unmap_single(mmc_dev(host->mmc), host->adma_addr,
|
|
|
|
- (128 * 2 + 1) * 4, DMA_TO_DEVICE);
|
|
|
|
-
|
|
|
|
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
|
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
|
|
128 * 4, direction);
|
|
128 * 4, direction);
|
|
|
|
|
|
- if (data->flags & MMC_DATA_READ) {
|
|
|
|
|
|
+ /* Do a quick scan of the SG list for any unaligned mappings */
|
|
|
|
+ has_unaligned = false;
|
|
|
|
+ for_each_sg(data->sg, sg, host->sg_count, i)
|
|
|
|
+ if (sg_dma_address(sg) & 3) {
|
|
|
|
+ has_unaligned = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (has_unaligned && data->flags & MMC_DATA_READ) {
|
|
dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
|
|
dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
|
|
data->sg_len, direction);
|
|
data->sg_len, direction);
|
|
|
|
|
|
@@ -721,9 +699,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
|
|
u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
|
|
u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
|
|
|
|
|
|
if (host->flags & SDHCI_REQ_USE_DMA)
|
|
if (host->flags & SDHCI_REQ_USE_DMA)
|
|
- sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
|
|
|
|
|
|
+ host->ier = (host->ier & ~pio_irqs) | dma_irqs;
|
|
else
|
|
else
|
|
- sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
|
|
|
|
|
|
+ host->ier = (host->ier & ~dma_irqs) | pio_irqs;
|
|
|
|
+
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
}
|
|
}
|
|
|
|
|
|
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|
@@ -976,8 +957,8 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|
* upon error conditions.
|
|
* upon error conditions.
|
|
*/
|
|
*/
|
|
if (data->error) {
|
|
if (data->error) {
|
|
- sdhci_reset(host, SDHCI_RESET_CMD);
|
|
|
|
- sdhci_reset(host, SDHCI_RESET_DATA);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
}
|
|
}
|
|
|
|
|
|
sdhci_send_command(host, data->stop);
|
|
sdhci_send_command(host, data->stop);
|
|
@@ -1107,24 +1088,23 @@ static void sdhci_finish_command(struct sdhci_host *host)
|
|
|
|
|
|
static u16 sdhci_get_preset_value(struct sdhci_host *host)
|
|
static u16 sdhci_get_preset_value(struct sdhci_host *host)
|
|
{
|
|
{
|
|
- u16 ctrl, preset = 0;
|
|
|
|
-
|
|
|
|
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
|
|
+ u16 preset = 0;
|
|
|
|
|
|
- switch (ctrl & SDHCI_CTRL_UHS_MASK) {
|
|
|
|
- case SDHCI_CTRL_UHS_SDR12:
|
|
|
|
|
|
+ switch (host->timing) {
|
|
|
|
+ case MMC_TIMING_UHS_SDR12:
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12);
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12);
|
|
break;
|
|
break;
|
|
- case SDHCI_CTRL_UHS_SDR25:
|
|
|
|
|
|
+ case MMC_TIMING_UHS_SDR25:
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25);
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25);
|
|
break;
|
|
break;
|
|
- case SDHCI_CTRL_UHS_SDR50:
|
|
|
|
|
|
+ case MMC_TIMING_UHS_SDR50:
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50);
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50);
|
|
break;
|
|
break;
|
|
- case SDHCI_CTRL_UHS_SDR104:
|
|
|
|
|
|
+ case MMC_TIMING_UHS_SDR104:
|
|
|
|
+ case MMC_TIMING_MMC_HS200:
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104);
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104);
|
|
break;
|
|
break;
|
|
- case SDHCI_CTRL_UHS_DDR50:
|
|
|
|
|
|
+ case MMC_TIMING_UHS_DDR50:
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
|
|
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
@@ -1136,32 +1116,22 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
|
|
return preset;
|
|
return preset;
|
|
}
|
|
}
|
|
|
|
|
|
-static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
|
|
|
|
+void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
{
|
|
{
|
|
int div = 0; /* Initialized for compiler warning */
|
|
int div = 0; /* Initialized for compiler warning */
|
|
int real_div = div, clk_mul = 1;
|
|
int real_div = div, clk_mul = 1;
|
|
u16 clk = 0;
|
|
u16 clk = 0;
|
|
unsigned long timeout;
|
|
unsigned long timeout;
|
|
|
|
|
|
- if (clock && clock == host->clock)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
host->mmc->actual_clock = 0;
|
|
host->mmc->actual_clock = 0;
|
|
|
|
|
|
- if (host->ops->set_clock) {
|
|
|
|
- host->ops->set_clock(host, clock);
|
|
|
|
- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
|
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
|
|
|
|
|
if (clock == 0)
|
|
if (clock == 0)
|
|
- goto out;
|
|
|
|
|
|
+ return;
|
|
|
|
|
|
if (host->version >= SDHCI_SPEC_300) {
|
|
if (host->version >= SDHCI_SPEC_300) {
|
|
- if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
|
|
|
|
- SDHCI_CTRL_PRESET_VAL_ENABLE) {
|
|
|
|
|
|
+ if (host->preset_enabled) {
|
|
u16 pre_val;
|
|
u16 pre_val;
|
|
|
|
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
@@ -1247,26 +1217,16 @@ clock_set:
|
|
|
|
|
|
clk |= SDHCI_CLOCK_CARD_EN;
|
|
clk |= SDHCI_CLOCK_CARD_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
-
|
|
|
|
-out:
|
|
|
|
- host->clock = clock;
|
|
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_set_clock);
|
|
|
|
|
|
-static inline void sdhci_update_clock(struct sdhci_host *host)
|
|
|
|
-{
|
|
|
|
- unsigned int clock;
|
|
|
|
-
|
|
|
|
- clock = host->clock;
|
|
|
|
- host->clock = 0;
|
|
|
|
- sdhci_set_clock(host, clock);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|
|
|
|
|
+static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
|
|
|
|
+ unsigned short vdd)
|
|
{
|
|
{
|
|
u8 pwr = 0;
|
|
u8 pwr = 0;
|
|
|
|
|
|
- if (power != (unsigned short)-1) {
|
|
|
|
- switch (1 << power) {
|
|
|
|
|
|
+ if (mode != MMC_POWER_OFF) {
|
|
|
|
+ switch (1 << vdd) {
|
|
case MMC_VDD_165_195:
|
|
case MMC_VDD_165_195:
|
|
pwr = SDHCI_POWER_180;
|
|
pwr = SDHCI_POWER_180;
|
|
break;
|
|
break;
|
|
@@ -1284,7 +1244,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|
}
|
|
}
|
|
|
|
|
|
if (host->pwr == pwr)
|
|
if (host->pwr == pwr)
|
|
- return -1;
|
|
|
|
|
|
+ return;
|
|
|
|
|
|
host->pwr = pwr;
|
|
host->pwr = pwr;
|
|
|
|
|
|
@@ -1292,38 +1252,43 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
|
|
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
|
|
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
|
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
|
sdhci_runtime_pm_bus_off(host);
|
|
sdhci_runtime_pm_bus_off(host);
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Spec says that we should clear the power reg before setting
|
|
|
|
- * a new value. Some controllers don't seem to like this though.
|
|
|
|
- */
|
|
|
|
- if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
|
|
|
|
- sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
|
|
|
|
|
|
+ vdd = 0;
|
|
|
|
+ } else {
|
|
|
|
+ /*
|
|
|
|
+ * Spec says that we should clear the power reg before setting
|
|
|
|
+ * a new value. Some controllers don't seem to like this though.
|
|
|
|
+ */
|
|
|
|
+ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
|
|
|
|
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
|
|
|
|
|
|
- /*
|
|
|
|
- * At least the Marvell CaFe chip gets confused if we set the voltage
|
|
|
|
- * and set turn on power at the same time, so set the voltage first.
|
|
|
|
- */
|
|
|
|
- if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
|
|
|
|
- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * At least the Marvell CaFe chip gets confused if we set the
|
|
|
|
+ * voltage and set turn on power at the same time, so set the
|
|
|
|
+ * voltage first.
|
|
|
|
+ */
|
|
|
|
+ if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
|
|
|
|
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
|
|
|
|
|
- pwr |= SDHCI_POWER_ON;
|
|
|
|
|
|
+ pwr |= SDHCI_POWER_ON;
|
|
|
|
|
|
- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
|
|
|
|
|
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
|
|
|
|
|
- if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
|
|
|
- sdhci_runtime_pm_bus_on(host);
|
|
|
|
|
|
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
|
|
|
+ sdhci_runtime_pm_bus_on(host);
|
|
|
|
|
|
- /*
|
|
|
|
- * Some controllers need an extra 10ms delay of 10ms before they
|
|
|
|
- * can apply clock after applying power
|
|
|
|
- */
|
|
|
|
- if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
|
|
|
|
- mdelay(10);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Some controllers need an extra 10ms delay of 10ms before
|
|
|
|
+ * they can apply clock after applying power
|
|
|
|
+ */
|
|
|
|
+ if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
|
|
|
|
+ mdelay(10);
|
|
|
|
+ }
|
|
|
|
|
|
- return power;
|
|
|
|
|
|
+ if (host->vmmc) {
|
|
|
|
+ spin_unlock_irq(&host->lock);
|
|
|
|
+ mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd);
|
|
|
|
+ spin_lock_irq(&host->lock);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
/*****************************************************************************\
|
|
@@ -1427,10 +1392,53 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void sdhci_set_bus_width(struct sdhci_host *host, int width)
|
|
|
|
+{
|
|
|
|
+ u8 ctrl;
|
|
|
|
+
|
|
|
|
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
|
|
|
+ if (width == MMC_BUS_WIDTH_8) {
|
|
|
|
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
|
|
|
|
+ if (host->version >= SDHCI_SPEC_300)
|
|
|
|
+ ctrl |= SDHCI_CTRL_8BITBUS;
|
|
|
|
+ } else {
|
|
|
|
+ if (host->version >= SDHCI_SPEC_300)
|
|
|
|
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
|
|
|
|
+ if (width == MMC_BUS_WIDTH_4)
|
|
|
|
+ ctrl |= SDHCI_CTRL_4BITBUS;
|
|
|
|
+ else
|
|
|
|
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
|
|
|
|
+ }
|
|
|
|
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_set_bus_width);
|
|
|
|
+
|
|
|
|
+void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
|
|
|
+{
|
|
|
|
+ u16 ctrl_2;
|
|
|
|
+
|
|
|
|
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
+ /* Select Bus Speed Mode for host */
|
|
|
|
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
|
|
|
+ if ((timing == MMC_TIMING_MMC_HS200) ||
|
|
|
|
+ (timing == MMC_TIMING_UHS_SDR104))
|
|
|
|
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
|
|
|
+ else if (timing == MMC_TIMING_UHS_SDR12)
|
|
|
|
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
|
|
|
|
+ else if (timing == MMC_TIMING_UHS_SDR25)
|
|
|
|
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
|
|
|
+ else if (timing == MMC_TIMING_UHS_SDR50)
|
|
|
|
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
|
|
|
+ else if ((timing == MMC_TIMING_UHS_DDR50) ||
|
|
|
|
+ (timing == MMC_TIMING_MMC_DDR52))
|
|
|
|
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
|
|
|
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
|
|
|
|
+
|
|
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- int vdd_bit = -1;
|
|
|
|
u8 ctrl;
|
|
u8 ctrl;
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
@@ -1456,45 +1464,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
|
|
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
|
|
sdhci_enable_preset_value(host, false);
|
|
sdhci_enable_preset_value(host, false);
|
|
|
|
|
|
- sdhci_set_clock(host, ios->clock);
|
|
|
|
-
|
|
|
|
- if (ios->power_mode == MMC_POWER_OFF)
|
|
|
|
- vdd_bit = sdhci_set_power(host, -1);
|
|
|
|
- else
|
|
|
|
- vdd_bit = sdhci_set_power(host, ios->vdd);
|
|
|
|
-
|
|
|
|
- if (host->vmmc && vdd_bit != -1) {
|
|
|
|
- spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
- mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
|
|
|
|
- spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
+ if (!ios->clock || ios->clock != host->clock) {
|
|
|
|
+ host->ops->set_clock(host, ios->clock);
|
|
|
|
+ host->clock = ios->clock;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ sdhci_set_power(host, ios->power_mode, ios->vdd);
|
|
|
|
+
|
|
if (host->ops->platform_send_init_74_clocks)
|
|
if (host->ops->platform_send_init_74_clocks)
|
|
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
|
|
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
|
|
|
|
|
|
- /*
|
|
|
|
- * If your platform has 8-bit width support but is not a v3 controller,
|
|
|
|
- * or if it requires special setup code, you should implement that in
|
|
|
|
- * platform_bus_width().
|
|
|
|
- */
|
|
|
|
- if (host->ops->platform_bus_width) {
|
|
|
|
- host->ops->platform_bus_width(host, ios->bus_width);
|
|
|
|
- } else {
|
|
|
|
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
|
|
|
- if (ios->bus_width == MMC_BUS_WIDTH_8) {
|
|
|
|
- ctrl &= ~SDHCI_CTRL_4BITBUS;
|
|
|
|
- if (host->version >= SDHCI_SPEC_300)
|
|
|
|
- ctrl |= SDHCI_CTRL_8BITBUS;
|
|
|
|
- } else {
|
|
|
|
- if (host->version >= SDHCI_SPEC_300)
|
|
|
|
- ctrl &= ~SDHCI_CTRL_8BITBUS;
|
|
|
|
- if (ios->bus_width == MMC_BUS_WIDTH_4)
|
|
|
|
- ctrl |= SDHCI_CTRL_4BITBUS;
|
|
|
|
- else
|
|
|
|
- ctrl &= ~SDHCI_CTRL_4BITBUS;
|
|
|
|
- }
|
|
|
|
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
|
|
- }
|
|
|
|
|
|
+ host->ops->set_bus_width(host, ios->bus_width);
|
|
|
|
|
|
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
|
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
|
|
|
|
|
@@ -1510,19 +1490,20 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
|
|
|
|
/* In case of UHS-I modes, set High Speed Enable */
|
|
/* In case of UHS-I modes, set High Speed Enable */
|
|
if ((ios->timing == MMC_TIMING_MMC_HS200) ||
|
|
if ((ios->timing == MMC_TIMING_MMC_HS200) ||
|
|
|
|
+ (ios->timing == MMC_TIMING_MMC_DDR52) ||
|
|
(ios->timing == MMC_TIMING_UHS_SDR50) ||
|
|
(ios->timing == MMC_TIMING_UHS_SDR50) ||
|
|
(ios->timing == MMC_TIMING_UHS_SDR104) ||
|
|
(ios->timing == MMC_TIMING_UHS_SDR104) ||
|
|
(ios->timing == MMC_TIMING_UHS_DDR50) ||
|
|
(ios->timing == MMC_TIMING_UHS_DDR50) ||
|
|
(ios->timing == MMC_TIMING_UHS_SDR25))
|
|
(ios->timing == MMC_TIMING_UHS_SDR25))
|
|
ctrl |= SDHCI_CTRL_HISPD;
|
|
ctrl |= SDHCI_CTRL_HISPD;
|
|
|
|
|
|
- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
- if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
|
|
|
|
|
+ if (!host->preset_enabled) {
|
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
/*
|
|
/*
|
|
* We only need to set Driver Strength if the
|
|
* We only need to set Driver Strength if the
|
|
* preset value enable is not set.
|
|
* preset value enable is not set.
|
|
*/
|
|
*/
|
|
|
|
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
|
|
ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
|
|
if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
|
|
if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
|
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
|
|
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
|
|
@@ -1546,7 +1527,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
|
|
|
|
/* Re-enable SD Clock */
|
|
/* Re-enable SD Clock */
|
|
- sdhci_update_clock(host);
|
|
|
|
|
|
+ host->ops->set_clock(host, host->clock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1555,25 +1536,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
clk &= ~SDHCI_CLOCK_CARD_EN;
|
|
clk &= ~SDHCI_CLOCK_CARD_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
|
|
|
|
- if (host->ops->set_uhs_signaling)
|
|
|
|
- host->ops->set_uhs_signaling(host, ios->timing);
|
|
|
|
- else {
|
|
|
|
- ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
- /* Select Bus Speed Mode for host */
|
|
|
|
- ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
|
|
|
- if ((ios->timing == MMC_TIMING_MMC_HS200) ||
|
|
|
|
- (ios->timing == MMC_TIMING_UHS_SDR104))
|
|
|
|
- ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
|
|
|
- else if (ios->timing == MMC_TIMING_UHS_SDR12)
|
|
|
|
- ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
|
|
|
|
- else if (ios->timing == MMC_TIMING_UHS_SDR25)
|
|
|
|
- ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
|
|
|
- else if (ios->timing == MMC_TIMING_UHS_SDR50)
|
|
|
|
- ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
|
|
|
- else if (ios->timing == MMC_TIMING_UHS_DDR50)
|
|
|
|
- ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
|
|
|
- sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
|
|
|
- }
|
|
|
|
|
|
+ host->ops->set_uhs_signaling(host, ios->timing);
|
|
|
|
+ host->timing = ios->timing;
|
|
|
|
|
|
if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) &&
|
|
if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) &&
|
|
((ios->timing == MMC_TIMING_UHS_SDR12) ||
|
|
((ios->timing == MMC_TIMING_UHS_SDR12) ||
|
|
@@ -1590,7 +1554,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
}
|
|
}
|
|
|
|
|
|
/* Re-enable SD Clock */
|
|
/* Re-enable SD Clock */
|
|
- sdhci_update_clock(host);
|
|
|
|
|
|
+ host->ops->set_clock(host, host->clock);
|
|
} else
|
|
} else
|
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
|
|
|
|
|
@@ -1600,7 +1564,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|
* it on each ios seems to solve the problem.
|
|
* it on each ios seems to solve the problem.
|
|
*/
|
|
*/
|
|
if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
|
if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
|
- sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
|
|
|
|
|
mmiowb();
|
|
mmiowb();
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
@@ -1709,24 +1673,16 @@ static int sdhci_get_ro(struct mmc_host *mmc)
|
|
|
|
|
|
static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
|
|
static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
|
|
{
|
|
{
|
|
- if (host->flags & SDHCI_DEVICE_DEAD)
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
- if (enable)
|
|
|
|
- host->flags |= SDHCI_SDIO_IRQ_ENABLED;
|
|
|
|
- else
|
|
|
|
- host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
|
|
|
|
-
|
|
|
|
- /* SDIO IRQ will be enabled as appropriate in runtime resume */
|
|
|
|
- if (host->runtime_suspended)
|
|
|
|
- goto out;
|
|
|
|
|
|
+ if (!(host->flags & SDHCI_DEVICE_DEAD)) {
|
|
|
|
+ if (enable)
|
|
|
|
+ host->ier |= SDHCI_INT_CARD_INT;
|
|
|
|
+ else
|
|
|
|
+ host->ier &= ~SDHCI_INT_CARD_INT;
|
|
|
|
|
|
- if (enable)
|
|
|
|
- sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
|
|
|
|
- else
|
|
|
|
- sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
|
|
|
|
-out:
|
|
|
|
- mmiowb();
|
|
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
|
|
+ mmiowb();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
@@ -1734,9 +1690,18 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
+ sdhci_runtime_pm_get(host);
|
|
|
|
+
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
+ if (enable)
|
|
|
|
+ host->flags |= SDHCI_SDIO_IRQ_ENABLED;
|
|
|
|
+ else
|
|
|
|
+ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
|
|
|
|
+
|
|
sdhci_enable_sdio_irq_nolock(host, enable);
|
|
sdhci_enable_sdio_irq_nolock(host, enable);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+
|
|
|
|
+ sdhci_runtime_pm_put(host);
|
|
}
|
|
}
|
|
|
|
|
|
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
|
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
|
@@ -1855,22 +1820,15 @@ static int sdhci_card_busy(struct mmc_host *mmc)
|
|
|
|
|
|
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
{
|
|
{
|
|
- struct sdhci_host *host;
|
|
|
|
|
|
+ struct sdhci_host *host = mmc_priv(mmc);
|
|
u16 ctrl;
|
|
u16 ctrl;
|
|
- u32 ier;
|
|
|
|
int tuning_loop_counter = MAX_TUNING_LOOP;
|
|
int tuning_loop_counter = MAX_TUNING_LOOP;
|
|
- unsigned long timeout;
|
|
|
|
int err = 0;
|
|
int err = 0;
|
|
- bool requires_tuning_nonuhs = false;
|
|
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
- host = mmc_priv(mmc);
|
|
|
|
-
|
|
|
|
sdhci_runtime_pm_get(host);
|
|
sdhci_runtime_pm_get(host);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* The Host Controller needs tuning only in case of SDR104 mode
|
|
* The Host Controller needs tuning only in case of SDR104 mode
|
|
* and for SDR50 mode when Use Tuning for SDR50 is set in the
|
|
* and for SDR50 mode when Use Tuning for SDR50 is set in the
|
|
@@ -1878,15 +1836,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
* If the Host Controller supports the HS200 mode then the
|
|
* If the Host Controller supports the HS200 mode then the
|
|
* tuning function has to be executed.
|
|
* tuning function has to be executed.
|
|
*/
|
|
*/
|
|
- if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
|
|
|
|
- (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
|
|
|
|
- host->flags & SDHCI_SDR104_NEEDS_TUNING))
|
|
|
|
- requires_tuning_nonuhs = true;
|
|
|
|
-
|
|
|
|
- if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
|
|
|
|
- requires_tuning_nonuhs)
|
|
|
|
- ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
|
|
|
- else {
|
|
|
|
|
|
+ switch (host->timing) {
|
|
|
|
+ case MMC_TIMING_MMC_HS200:
|
|
|
|
+ case MMC_TIMING_UHS_SDR104:
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case MMC_TIMING_UHS_SDR50:
|
|
|
|
+ if (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
|
|
|
|
+ host->flags & SDHCI_SDR104_NEEDS_TUNING)
|
|
|
|
+ break;
|
|
|
|
+ /* FALLTHROUGH */
|
|
|
|
+
|
|
|
|
+ default:
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
sdhci_runtime_pm_put(host);
|
|
sdhci_runtime_pm_put(host);
|
|
return 0;
|
|
return 0;
|
|
@@ -1899,6 +1860,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1911,21 +1874,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
* to make sure we don't hit a controller bug, we _only_
|
|
* to make sure we don't hit a controller bug, we _only_
|
|
* enable Buffer Read Ready interrupt here.
|
|
* enable Buffer Read Ready interrupt here.
|
|
*/
|
|
*/
|
|
- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
|
|
|
|
- sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL);
|
|
|
|
|
|
+ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
|
|
|
|
|
|
/*
|
|
/*
|
|
* 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 or a timeout of 150ms occurs.
|
|
*/
|
|
*/
|
|
- timeout = 150;
|
|
|
|
do {
|
|
do {
|
|
struct mmc_command cmd = {0};
|
|
struct mmc_command cmd = {0};
|
|
struct mmc_request mrq = {NULL};
|
|
struct mmc_request mrq = {NULL};
|
|
|
|
|
|
- if (!tuning_loop_counter && !timeout)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
cmd.opcode = opcode;
|
|
cmd.opcode = opcode;
|
|
cmd.arg = 0;
|
|
cmd.arg = 0;
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
@@ -1933,6 +1892,9 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
cmd.data = NULL;
|
|
cmd.data = NULL;
|
|
cmd.error = 0;
|
|
cmd.error = 0;
|
|
|
|
|
|
|
|
+ if (tuning_loop_counter-- == 0)
|
|
|
|
+ break;
|
|
|
|
+
|
|
mrq.cmd = &cmd;
|
|
mrq.cmd = &cmd;
|
|
host->mrq = &mrq;
|
|
host->mrq = &mrq;
|
|
|
|
|
|
@@ -1990,26 +1952,25 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
host->tuning_done = 0;
|
|
host->tuning_done = 0;
|
|
|
|
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
- tuning_loop_counter--;
|
|
|
|
- timeout--;
|
|
|
|
- mdelay(1);
|
|
|
|
|
|
+
|
|
|
|
+ /* eMMC spec does not require a delay between tuning cycles */
|
|
|
|
+ if (opcode == MMC_SEND_TUNING_BLOCK)
|
|
|
|
+ mdelay(1);
|
|
} while (ctrl & SDHCI_CTRL_EXEC_TUNING);
|
|
} while (ctrl & SDHCI_CTRL_EXEC_TUNING);
|
|
|
|
|
|
/*
|
|
/*
|
|
* The Host Driver has exhausted the maximum number of loops allowed,
|
|
* The Host Driver has exhausted the maximum number of loops allowed,
|
|
* so use fixed sampling frequency.
|
|
* so use fixed sampling frequency.
|
|
*/
|
|
*/
|
|
- if (!tuning_loop_counter || !timeout) {
|
|
|
|
|
|
+ if (tuning_loop_counter < 0) {
|
|
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
|
|
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
|
+ }
|
|
|
|
+ if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
|
|
|
|
+ pr_info(DRIVER_NAME ": Tuning procedure"
|
|
|
|
+ " failed, falling back to fixed sampling"
|
|
|
|
+ " clock\n");
|
|
err = -EIO;
|
|
err = -EIO;
|
|
- } else {
|
|
|
|
- if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
|
|
|
|
- pr_info(DRIVER_NAME ": Tuning procedure"
|
|
|
|
- " failed, falling back to fixed sampling"
|
|
|
|
- " clock\n");
|
|
|
|
- err = -EIO;
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
out:
|
|
out:
|
|
@@ -2044,7 +2005,8 @@ out:
|
|
if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
|
|
if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
|
|
err = 0;
|
|
err = 0;
|
|
|
|
|
|
- sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
|
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
sdhci_runtime_pm_put(host);
|
|
sdhci_runtime_pm_put(host);
|
|
|
|
|
|
@@ -2054,26 +2016,30 @@ out:
|
|
|
|
|
|
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
|
|
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
|
|
{
|
|
{
|
|
- u16 ctrl;
|
|
|
|
-
|
|
|
|
/* Host Controller v3.00 defines preset value registers */
|
|
/* Host Controller v3.00 defines preset value registers */
|
|
if (host->version < SDHCI_SPEC_300)
|
|
if (host->version < SDHCI_SPEC_300)
|
|
return;
|
|
return;
|
|
|
|
|
|
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* We only enable or disable Preset Value if they are not already
|
|
* We only enable or disable Preset Value if they are not already
|
|
* enabled or disabled respectively. Otherwise, we bail out.
|
|
* enabled or disabled respectively. Otherwise, we bail out.
|
|
*/
|
|
*/
|
|
- if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
|
|
|
- ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
|
|
|
|
- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
|
- host->flags |= SDHCI_PV_ENABLED;
|
|
|
|
- } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
|
|
|
|
- ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
|
|
|
|
|
|
+ if (host->preset_enabled != enable) {
|
|
|
|
+ u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
+
|
|
|
|
+ if (enable)
|
|
|
|
+ ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
|
|
|
|
+ else
|
|
|
|
+ ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
|
|
|
|
+
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
- host->flags &= ~SDHCI_PV_ENABLED;
|
|
|
|
|
|
+
|
|
|
|
+ if (enable)
|
|
|
|
+ host->flags |= SDHCI_PV_ENABLED;
|
|
|
|
+ else
|
|
|
|
+ host->flags &= ~SDHCI_PV_ENABLED;
|
|
|
|
+
|
|
|
|
+ host->preset_enabled = enable;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2095,8 +2061,8 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
|
pr_err("%s: Resetting controller.\n",
|
|
pr_err("%s: Resetting controller.\n",
|
|
mmc_hostname(host->mmc));
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
|
- sdhci_reset(host, SDHCI_RESET_CMD);
|
|
|
|
- sdhci_reset(host, SDHCI_RESET_DATA);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
|
|
|
|
host->mrq->cmd->error = -ENOMEDIUM;
|
|
host->mrq->cmd->error = -ENOMEDIUM;
|
|
tasklet_schedule(&host->finish_tasklet);
|
|
tasklet_schedule(&host->finish_tasklet);
|
|
@@ -2124,15 +2090,6 @@ static const struct mmc_host_ops sdhci_ops = {
|
|
* *
|
|
* *
|
|
\*****************************************************************************/
|
|
\*****************************************************************************/
|
|
|
|
|
|
-static void sdhci_tasklet_card(unsigned long param)
|
|
|
|
-{
|
|
|
|
- struct sdhci_host *host = (struct sdhci_host*)param;
|
|
|
|
-
|
|
|
|
- sdhci_card_event(host->mmc);
|
|
|
|
-
|
|
|
|
- mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void sdhci_tasklet_finish(unsigned long param)
|
|
static void sdhci_tasklet_finish(unsigned long param)
|
|
{
|
|
{
|
|
struct sdhci_host *host;
|
|
struct sdhci_host *host;
|
|
@@ -2169,12 +2126,12 @@ static void sdhci_tasklet_finish(unsigned long param)
|
|
/* 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 */
|
|
- sdhci_update_clock(host);
|
|
|
|
|
|
+ host->ops->set_clock(host, host->clock);
|
|
|
|
|
|
/* 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_reset(host, SDHCI_RESET_CMD);
|
|
|
|
- sdhci_reset(host, SDHCI_RESET_DATA);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
|
|
}
|
|
}
|
|
|
|
|
|
host->mrq = NULL;
|
|
host->mrq = NULL;
|
|
@@ -2424,101 +2381,94 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
|
|
|
|
static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|
static irqreturn_t sdhci_irq(int irq, void *dev_id)
|
|
{
|
|
{
|
|
- irqreturn_t result;
|
|
|
|
|
|
+ irqreturn_t result = IRQ_NONE;
|
|
struct sdhci_host *host = dev_id;
|
|
struct sdhci_host *host = dev_id;
|
|
- u32 intmask, unexpected = 0;
|
|
|
|
- int cardint = 0, max_loops = 16;
|
|
|
|
|
|
+ u32 intmask, mask, unexpected = 0;
|
|
|
|
+ int max_loops = 16;
|
|
|
|
|
|
spin_lock(&host->lock);
|
|
spin_lock(&host->lock);
|
|
|
|
|
|
- if (host->runtime_suspended) {
|
|
|
|
|
|
+ if (host->runtime_suspended && !sdhci_sdio_irq_enabled(host)) {
|
|
spin_unlock(&host->lock);
|
|
spin_unlock(&host->lock);
|
|
return IRQ_NONE;
|
|
return IRQ_NONE;
|
|
}
|
|
}
|
|
|
|
|
|
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
|
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
|
-
|
|
|
|
if (!intmask || intmask == 0xffffffff) {
|
|
if (!intmask || intmask == 0xffffffff) {
|
|
result = IRQ_NONE;
|
|
result = IRQ_NONE;
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
-again:
|
|
|
|
- DBG("*** %s got interrupt: 0x%08x\n",
|
|
|
|
- mmc_hostname(host->mmc), intmask);
|
|
|
|
-
|
|
|
|
- if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
|
|
|
- u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
|
|
- SDHCI_CARD_PRESENT;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * There is a observation on i.mx esdhc. INSERT bit will be
|
|
|
|
- * immediately set again when it gets cleared, if a card is
|
|
|
|
- * inserted. We have to mask the irq to prevent interrupt
|
|
|
|
- * storm which will freeze the system. And the REMOVE gets
|
|
|
|
- * the same situation.
|
|
|
|
- *
|
|
|
|
- * More testing are needed here to ensure it works for other
|
|
|
|
- * platforms though.
|
|
|
|
- */
|
|
|
|
- sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
|
|
|
|
- SDHCI_INT_CARD_REMOVE);
|
|
|
|
- sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
|
|
|
|
- SDHCI_INT_CARD_INSERT);
|
|
|
|
-
|
|
|
|
- sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
|
|
|
|
- SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
|
|
|
|
- intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
|
|
|
|
- tasklet_schedule(&host->card_tasklet);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (intmask & SDHCI_INT_CMD_MASK) {
|
|
|
|
- sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
|
|
|
|
- SDHCI_INT_STATUS);
|
|
|
|
- sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
|
|
|
|
- }
|
|
|
|
|
|
+ do {
|
|
|
|
+ /* Clear selected interrupts. */
|
|
|
|
+ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
|
|
|
|
+ SDHCI_INT_BUS_POWER);
|
|
|
|
+ sdhci_writel(host, mask, SDHCI_INT_STATUS);
|
|
|
|
|
|
- if (intmask & SDHCI_INT_DATA_MASK) {
|
|
|
|
- sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
|
|
|
|
- SDHCI_INT_STATUS);
|
|
|
|
- sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
|
|
|
|
- }
|
|
|
|
|
|
+ DBG("*** %s got interrupt: 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc), intmask);
|
|
|
|
|
|
- intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
|
|
|
|
|
|
+ if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
|
|
|
+ u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
|
|
+ SDHCI_CARD_PRESENT;
|
|
|
|
|
|
- intmask &= ~SDHCI_INT_ERROR;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * There is a observation on i.mx esdhc. INSERT
|
|
|
|
+ * bit will be immediately set again when it gets
|
|
|
|
+ * cleared, if a card is inserted. We have to mask
|
|
|
|
+ * the irq to prevent interrupt storm which will
|
|
|
|
+ * freeze the system. And the REMOVE gets the
|
|
|
|
+ * same situation.
|
|
|
|
+ *
|
|
|
|
+ * More testing are needed here to ensure it works
|
|
|
|
+ * for other platforms though.
|
|
|
|
+ */
|
|
|
|
+ host->ier &= ~(SDHCI_INT_CARD_INSERT |
|
|
|
|
+ SDHCI_INT_CARD_REMOVE);
|
|
|
|
+ host->ier |= present ? SDHCI_INT_CARD_REMOVE :
|
|
|
|
+ SDHCI_INT_CARD_INSERT;
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
|
|
+
|
|
|
|
+ sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
|
|
|
|
+ SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
|
|
|
|
+
|
|
|
|
+ host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT |
|
|
|
|
+ SDHCI_INT_CARD_REMOVE);
|
|
|
|
+ result = IRQ_WAKE_THREAD;
|
|
|
|
+ }
|
|
|
|
|
|
- if (intmask & SDHCI_INT_BUS_POWER) {
|
|
|
|
- pr_err("%s: Card is consuming too much power!\n",
|
|
|
|
- mmc_hostname(host->mmc));
|
|
|
|
- sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
|
|
|
|
- }
|
|
|
|
|
|
+ if (intmask & SDHCI_INT_CMD_MASK)
|
|
|
|
+ sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
|
|
|
|
|
|
- intmask &= ~SDHCI_INT_BUS_POWER;
|
|
|
|
|
|
+ if (intmask & SDHCI_INT_DATA_MASK)
|
|
|
|
+ sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
|
|
|
|
|
|
- if (intmask & SDHCI_INT_CARD_INT)
|
|
|
|
- cardint = 1;
|
|
|
|
|
|
+ if (intmask & SDHCI_INT_BUS_POWER)
|
|
|
|
+ pr_err("%s: Card is consuming too much power!\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
|
|
- intmask &= ~SDHCI_INT_CARD_INT;
|
|
|
|
|
|
+ if (intmask & SDHCI_INT_CARD_INT) {
|
|
|
|
+ sdhci_enable_sdio_irq_nolock(host, false);
|
|
|
|
+ host->thread_isr |= SDHCI_INT_CARD_INT;
|
|
|
|
+ result = IRQ_WAKE_THREAD;
|
|
|
|
+ }
|
|
|
|
|
|
- if (intmask) {
|
|
|
|
- unexpected |= intmask;
|
|
|
|
- sdhci_writel(host, intmask, SDHCI_INT_STATUS);
|
|
|
|
- }
|
|
|
|
|
|
+ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
|
|
|
|
+ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
|
|
|
|
+ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
|
|
|
|
+ SDHCI_INT_CARD_INT);
|
|
|
|
|
|
- result = IRQ_HANDLED;
|
|
|
|
|
|
+ if (intmask) {
|
|
|
|
+ unexpected |= intmask;
|
|
|
|
+ sdhci_writel(host, intmask, SDHCI_INT_STATUS);
|
|
|
|
+ }
|
|
|
|
|
|
- intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
|
|
|
|
|
+ if (result == IRQ_NONE)
|
|
|
|
+ result = IRQ_HANDLED;
|
|
|
|
|
|
- /*
|
|
|
|
- * If we know we'll call the driver to signal SDIO IRQ, disregard
|
|
|
|
- * further indications of Card Interrupt in the status to avoid a
|
|
|
|
- * needless loop.
|
|
|
|
- */
|
|
|
|
- if (cardint)
|
|
|
|
- intmask &= ~SDHCI_INT_CARD_INT;
|
|
|
|
- if (intmask && --max_loops)
|
|
|
|
- goto again;
|
|
|
|
|
|
+ intmask = sdhci_readl(host, SDHCI_INT_STATUS);
|
|
|
|
+ } while (intmask && --max_loops);
|
|
out:
|
|
out:
|
|
spin_unlock(&host->lock);
|
|
spin_unlock(&host->lock);
|
|
|
|
|
|
@@ -2527,15 +2477,38 @@ out:
|
|
mmc_hostname(host->mmc), unexpected);
|
|
mmc_hostname(host->mmc), unexpected);
|
|
sdhci_dumpregs(host);
|
|
sdhci_dumpregs(host);
|
|
}
|
|
}
|
|
- /*
|
|
|
|
- * We have to delay this as it calls back into the driver.
|
|
|
|
- */
|
|
|
|
- if (cardint)
|
|
|
|
- mmc_signal_sdio_irq(host->mmc);
|
|
|
|
|
|
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
|
|
|
+{
|
|
|
|
+ struct sdhci_host *host = dev_id;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ u32 isr;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&host->lock, flags);
|
|
|
|
+ isr = host->thread_isr;
|
|
|
|
+ host->thread_isr = 0;
|
|
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+
|
|
|
|
+ if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
|
|
|
+ sdhci_card_event(host->mmc);
|
|
|
|
+ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (isr & SDHCI_INT_CARD_INT) {
|
|
|
|
+ sdio_run_irqs(host->mmc);
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&host->lock, flags);
|
|
|
|
+ if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
|
|
|
|
+ sdhci_enable_sdio_irq_nolock(host, true);
|
|
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return isr ? IRQ_HANDLED : IRQ_NONE;
|
|
|
|
+}
|
|
|
|
+
|
|
/*****************************************************************************\
|
|
/*****************************************************************************\
|
|
* *
|
|
* *
|
|
* Suspend/resume *
|
|
* Suspend/resume *
|
|
@@ -2572,9 +2545,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
|
|
|
|
|
|
int sdhci_suspend_host(struct sdhci_host *host)
|
|
int sdhci_suspend_host(struct sdhci_host *host)
|
|
{
|
|
{
|
|
- if (host->ops->platform_suspend)
|
|
|
|
- host->ops->platform_suspend(host);
|
|
|
|
-
|
|
|
|
sdhci_disable_card_detection(host);
|
|
sdhci_disable_card_detection(host);
|
|
|
|
|
|
/* Disable tuning since we are suspending */
|
|
/* Disable tuning since we are suspending */
|
|
@@ -2584,7 +2554,9 @@ int sdhci_suspend_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
|
|
|
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
|
- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
|
|
|
|
|
|
+ host->ier = 0;
|
|
|
|
+ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
|
|
free_irq(host->irq, host);
|
|
free_irq(host->irq, host);
|
|
} else {
|
|
} else {
|
|
sdhci_enable_irq_wakeups(host);
|
|
sdhci_enable_irq_wakeups(host);
|
|
@@ -2605,8 +2577,9 @@ int sdhci_resume_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
|
|
|
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
|
if (!device_may_wakeup(mmc_dev(host->mmc))) {
|
|
- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
|
|
|
|
- mmc_hostname(host->mmc), host);
|
|
|
|
|
|
+ ret = request_threaded_irq(host->irq, sdhci_irq,
|
|
|
|
+ sdhci_thread_irq, IRQF_SHARED,
|
|
|
|
+ mmc_hostname(host->mmc), host);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
} else {
|
|
} else {
|
|
@@ -2628,9 +2601,6 @@ int sdhci_resume_host(struct sdhci_host *host)
|
|
|
|
|
|
sdhci_enable_card_detection(host);
|
|
sdhci_enable_card_detection(host);
|
|
|
|
|
|
- if (host->ops->platform_resume)
|
|
|
|
- host->ops->platform_resume(host);
|
|
|
|
-
|
|
|
|
/* Set the re-tuning expiration flag */
|
|
/* Set the re-tuning expiration flag */
|
|
if (host->flags & SDHCI_USING_RETUNING_TIMER)
|
|
if (host->flags & SDHCI_USING_RETUNING_TIMER)
|
|
host->flags |= SDHCI_NEEDS_RETUNING;
|
|
host->flags |= SDHCI_NEEDS_RETUNING;
|
|
@@ -2682,10 +2652,12 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
|
}
|
|
}
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
|
|
|
|
|
|
+ host->ier &= SDHCI_INT_CARD_INT;
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
|
|
- synchronize_irq(host->irq);
|
|
|
|
|
|
+ synchronize_hardirq(host->irq);
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
host->runtime_suspended = true;
|
|
host->runtime_suspended = true;
|
|
@@ -2729,7 +2701,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
|
host->runtime_suspended = false;
|
|
host->runtime_suspended = false;
|
|
|
|
|
|
/* Enable SDIO IRQ */
|
|
/* Enable SDIO IRQ */
|
|
- if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
|
|
|
|
|
|
+ if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
|
|
sdhci_enable_sdio_irq_nolock(host, true);
|
|
sdhci_enable_sdio_irq_nolock(host, true);
|
|
|
|
|
|
/* Enable Card Detection */
|
|
/* Enable Card Detection */
|
|
@@ -2788,7 +2760,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
if (debug_quirks2)
|
|
if (debug_quirks2)
|
|
host->quirks2 = debug_quirks2;
|
|
host->quirks2 = debug_quirks2;
|
|
|
|
|
|
- sdhci_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
|
|
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
|
|
host->version = (host->version & SDHCI_SPEC_VER_MASK)
|
|
host->version = (host->version & SDHCI_SPEC_VER_MASK)
|
|
@@ -2848,15 +2820,29 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
* (128) and potentially one alignment transfer for
|
|
* (128) and potentially one alignment transfer for
|
|
* each of those entries.
|
|
* each of those entries.
|
|
*/
|
|
*/
|
|
- host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);
|
|
|
|
|
|
+ host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc),
|
|
|
|
+ ADMA_SIZE, &host->adma_addr,
|
|
|
|
+ GFP_KERNEL);
|
|
host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
|
|
host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
|
|
if (!host->adma_desc || !host->align_buffer) {
|
|
if (!host->adma_desc || !host->align_buffer) {
|
|
- kfree(host->adma_desc);
|
|
|
|
|
|
+ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
|
|
|
|
+ host->adma_desc, host->adma_addr);
|
|
kfree(host->align_buffer);
|
|
kfree(host->align_buffer);
|
|
pr_warning("%s: Unable to allocate ADMA "
|
|
pr_warning("%s: Unable to allocate ADMA "
|
|
"buffers. Falling back to standard DMA.\n",
|
|
"buffers. Falling back to standard DMA.\n",
|
|
mmc_hostname(mmc));
|
|
mmc_hostname(mmc));
|
|
host->flags &= ~SDHCI_USE_ADMA;
|
|
host->flags &= ~SDHCI_USE_ADMA;
|
|
|
|
+ host->adma_desc = NULL;
|
|
|
|
+ host->align_buffer = NULL;
|
|
|
|
+ } else if (host->adma_addr & 3) {
|
|
|
|
+ pr_warning("%s: unable to allocate aligned ADMA descriptor\n",
|
|
|
|
+ mmc_hostname(mmc));
|
|
|
|
+ host->flags &= ~SDHCI_USE_ADMA;
|
|
|
|
+ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
|
|
|
|
+ host->adma_desc, host->adma_addr);
|
|
|
|
+ kfree(host->align_buffer);
|
|
|
|
+ host->adma_desc = NULL;
|
|
|
|
+ host->align_buffer = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2941,6 +2927,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
|
|
mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
|
|
|
|
|
|
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
|
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
|
|
|
+ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
|
|
|
|
|
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
|
|
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
|
|
host->flags |= SDHCI_AUTO_CMD12;
|
|
host->flags |= SDHCI_AUTO_CMD12;
|
|
@@ -3212,8 +3199,6 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
/*
|
|
/*
|
|
* Init tasklets.
|
|
* Init tasklets.
|
|
*/
|
|
*/
|
|
- tasklet_init(&host->card_tasklet,
|
|
|
|
- sdhci_tasklet_card, (unsigned long)host);
|
|
|
|
tasklet_init(&host->finish_tasklet,
|
|
tasklet_init(&host->finish_tasklet,
|
|
sdhci_tasklet_finish, (unsigned long)host);
|
|
sdhci_tasklet_finish, (unsigned long)host);
|
|
|
|
|
|
@@ -3230,8 +3215,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
|
|
|
|
sdhci_init(host, 0);
|
|
sdhci_init(host, 0);
|
|
|
|
|
|
- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
|
|
|
|
- mmc_hostname(mmc), host);
|
|
|
|
|
|
+ ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
|
|
|
|
+ IRQF_SHARED, mmc_hostname(mmc), host);
|
|
if (ret) {
|
|
if (ret) {
|
|
pr_err("%s: Failed to request IRQ %d: %d\n",
|
|
pr_err("%s: Failed to request IRQ %d: %d\n",
|
|
mmc_hostname(mmc), host->irq, ret);
|
|
mmc_hostname(mmc), host->irq, ret);
|
|
@@ -3273,12 +3258,12 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
|
|
|
|
#ifdef SDHCI_USE_LEDS_CLASS
|
|
#ifdef SDHCI_USE_LEDS_CLASS
|
|
reset:
|
|
reset:
|
|
- sdhci_reset(host, SDHCI_RESET_ALL);
|
|
|
|
- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
|
|
|
|
+ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
|
|
free_irq(host->irq, host);
|
|
free_irq(host->irq, host);
|
|
#endif
|
|
#endif
|
|
untasklet:
|
|
untasklet:
|
|
- tasklet_kill(&host->card_tasklet);
|
|
|
|
tasklet_kill(&host->finish_tasklet);
|
|
tasklet_kill(&host->finish_tasklet);
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
@@ -3315,14 +3300,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
if (!dead)
|
|
if (!dead)
|
|
- sdhci_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
|
|
|
|
|
|
- sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
|
|
|
|
|
|
+ sdhci_writel(host, 0, SDHCI_INT_ENABLE);
|
|
|
|
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
|
|
free_irq(host->irq, host);
|
|
free_irq(host->irq, host);
|
|
|
|
|
|
del_timer_sync(&host->timer);
|
|
del_timer_sync(&host->timer);
|
|
|
|
|
|
- tasklet_kill(&host->card_tasklet);
|
|
|
|
tasklet_kill(&host->finish_tasklet);
|
|
tasklet_kill(&host->finish_tasklet);
|
|
|
|
|
|
if (host->vmmc) {
|
|
if (host->vmmc) {
|
|
@@ -3335,7 +3320,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|
regulator_put(host->vqmmc);
|
|
regulator_put(host->vqmmc);
|
|
}
|
|
}
|
|
|
|
|
|
- kfree(host->adma_desc);
|
|
|
|
|
|
+ if (host->adma_desc)
|
|
|
|
+ dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE,
|
|
|
|
+ host->adma_desc, host->adma_addr);
|
|
kfree(host->align_buffer);
|
|
kfree(host->align_buffer);
|
|
|
|
|
|
host->adma_desc = NULL;
|
|
host->adma_desc = NULL;
|