* [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit
@ 2016-06-28 13:23 Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 1/5] mmc: sdhci: Do not call implementations of mmc host ops directly Adrian Hunter
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Adrian Hunter @ 2016-06-28 13:23 UTC (permalink / raw)
To: linux-mmc; +Cc: Ulf Hansson, Jon Hunter, Dong Aisheng, Dong Aisheng
Hi
Here are some patches to make it easier for drivers to set capabilities
particularly with respect to signal voltage support. Not tested thoroughly
yet so RFC for now.
Adrian Hunter (5):
mmc: sdhci: Do not call implementations of mmc host ops directly
mmc: sdhci: Split sdhci_add_host()
mmc: sdhci: Make signal voltage support explicit
mmc: sdhci: Tidy caps variables in sdhci_setup_host()
mmc: sdhci: Add sdhci_read_caps()
drivers/mmc/host/sdhci.c | 203 ++++++++++++++++++++++++++++++++---------------
drivers/mmc/host/sdhci.h | 17 +++-
2 files changed, 152 insertions(+), 68 deletions(-)
Regards
Adrian
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH RFC 1/5] mmc: sdhci: Do not call implementations of mmc host ops directly
2016-06-28 13:23 [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
@ 2016-06-28 13:23 ` Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host() Adrian Hunter
` (3 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Adrian Hunter @ 2016-06-28 13:23 UTC (permalink / raw)
To: linux-mmc; +Cc: Ulf Hansson, Jon Hunter, Dong Aisheng, Dong Aisheng
Drivers must be able to provide their own implementations for mmc host
operations. Consequently, SDHCI should call those not the default
implementations. Do that by calling indirectly through the mmc host ops
function pointers.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 28 ++++++++++++++++------------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 15bdbcb99170..7009c862ca9b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -46,9 +46,7 @@ static unsigned int debug_quirks2;
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 int sdhci_get_cd(struct mmc_host *mmc);
static void sdhci_dumpregs(struct sdhci_host *host)
{
@@ -193,7 +191,9 @@ 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_get_cd(host->mmc))
+ struct mmc_host *mmc = host->mmc;
+
+ if (!mmc->ops->get_cd(mmc))
return;
}
@@ -210,10 +210,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)
{
+ struct mmc_host *mmc = host->mmc;
+
if (soft)
sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
else
@@ -231,7 +231,7 @@ static void sdhci_init(struct sdhci_host *host, int soft)
if (soft) {
/* force clock reconfiguration */
host->clock = 0;
- sdhci_set_ios(host->mmc, &host->mmc->ios);
+ mmc->ops->set_ios(mmc, &mmc->ios);
}
}
@@ -2096,7 +2096,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
if (host->ops->card_event)
host->ops->card_event(host);
- present = sdhci_get_cd(host->mmc);
+ present = mmc->ops->get_cd(mmc);
spin_lock_irqsave(&host->lock, flags);
@@ -2582,8 +2582,10 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
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));
+ 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) {
@@ -2667,6 +2669,7 @@ EXPORT_SYMBOL_GPL(sdhci_suspend_host);
int sdhci_resume_host(struct sdhci_host *host)
{
+ struct mmc_host *mmc = host->mmc;
int ret = 0;
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -2680,7 +2683,7 @@ int sdhci_resume_host(struct sdhci_host *host)
sdhci_init(host, 0);
host->pwr = 0;
host->clock = 0;
- sdhci_set_ios(host->mmc, &host->mmc->ios);
+ mmc->ops->set_ios(mmc, &mmc->ios);
} else {
sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
mmiowb();
@@ -2729,6 +2732,7 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
int sdhci_runtime_resume_host(struct sdhci_host *host)
{
+ struct mmc_host *mmc = host->mmc;
unsigned long flags;
int host_flags = host->flags;
@@ -2742,8 +2746,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
/* Force clock and power re-program */
host->pwr = 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) &&
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host()
2016-06-28 13:23 [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 1/5] mmc: sdhci: Do not call implementations of mmc host ops directly Adrian Hunter
@ 2016-06-28 13:23 ` Adrian Hunter
2016-07-05 9:02 ` Jon Hunter
2016-06-28 13:23 ` [PATCH RFC 3/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
` (2 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Adrian Hunter @ 2016-06-28 13:23 UTC (permalink / raw)
To: linux-mmc; +Cc: Ulf Hansson, Jon Hunter, Dong Aisheng, Dong Aisheng
Split sdhci-add_host() in order to further our objective to make
sdhci into a library.
The split divides code that sets up mmc and sdhci parameters, from
code that actually activates things - such as tasklet initialization,
requesting the irq, and adding (and starting) the host.
This gives drivers an opportunity to change various settings before
committing to start the host.
Drivers can continue to call sdhci_add_host() but drivers that want
to take advantage of the split instead call sdhci_setup_host() followed
by __sdhci_add_host().
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 39 ++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 7009c862ca9b..22061faf7fa4 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2833,7 +2833,7 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
return ret;
}
-int sdhci_add_host(struct sdhci_host *host)
+int sdhci_setup_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
u32 caps[2] = {0, 0};
@@ -3314,6 +3314,28 @@ int sdhci_add_host(struct sdhci_host *host)
*/
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.
*/
@@ -3370,10 +3392,10 @@ unirq:
free_irq(host->irq, host);
untasklet:
tasklet_kill(&host->finish_tasklet);
-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,
@@ -3383,7 +3405,18 @@ undma:
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);
void sdhci_remove_host(struct sdhci_host *host, int dead)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 609f87ca536b..b16aec2faa2e 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -645,6 +645,8 @@ static inline void *sdhci_priv(struct sdhci_host *host)
}
extern void sdhci_card_detect(struct sdhci_host *host);
+extern int sdhci_setup_host(struct sdhci_host *host);
+extern int __sdhci_add_host(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
extern void sdhci_send_command(struct sdhci_host *host,
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH RFC 3/5] mmc: sdhci: Make signal voltage support explicit
2016-06-28 13:23 [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 1/5] mmc: sdhci: Do not call implementations of mmc host ops directly Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host() Adrian Hunter
@ 2016-06-28 13:23 ` Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 4/5] mmc: sdhci: Tidy caps variables in sdhci_setup_host() Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 5/5] mmc: sdhci: Add sdhci_read_caps() Adrian Hunter
4 siblings, 0 replies; 9+ messages in thread
From: Adrian Hunter @ 2016-06-28 13:23 UTC (permalink / raw)
To: linux-mmc; +Cc: Ulf Hansson, Jon Hunter, Dong Aisheng, Dong Aisheng
Signal voltage support is not a quirk, it is a capability. According to the
SDHCI specification, support for 1.8V signaling is determined by the
presence of one of the capability bits SDHCI_SUPPORT_SDR50,
SDHCI_SUPPORT_SDR104, or SDHCI_SUPPORT_DDR50. This is complicated by also
supporting eMMC which has 1.8V modes and 1.2V modes. It would be possible
to use the transfer mode to determine signal voltage support, except for
eMMC DDR52 mode which uses the same capability (MMC_CAP_1_8V_DDR) for 1.8V
signaling and 3V signaling.
In addition, the mmc core will fail over from one signaling voltage to the
next (refer mmc_power_up()) which means SDHCI really needs to validate
which voltages are actually supported.
Introduce SDHCI flags for signal voltage support and set them based on the
supported transfer modes. In general, drivers should prefer to set the
supported transfer modes correctly rather than change the signal voltage
capability, except in the case where 3V DDR52 is supported but 1.8V is
not.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 17 +++++++++++++++++
drivers/mmc/host/sdhci.h | 3 +++
2 files changed, 20 insertions(+)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 22061faf7fa4..8ac5a9df2c01 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1733,6 +1733,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
switch (ios->signal_voltage) {
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 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
@@ -1759,6 +1761,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_180:
+ if (!(host->flags & SDHCI_SIGNALING_180))
+ return -EINVAL;
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc,
1700000, 1950000);
@@ -1790,6 +1794,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_120:
+ if (!(host->flags & SDHCI_SIGNALING_120))
+ return -EINVAL;
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
1300000);
@@ -2798,6 +2804,8 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->mmc_host_ops = sdhci_ops;
mmc->ops = &host->mmc_host_ops;
+ host->flags = SDHCI_SIGNALING_330;
+
return host;
}
@@ -3257,6 +3265,15 @@ int sdhci_setup_host(struct sdhci_host *host)
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);
/*
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index b16aec2faa2e..419911f107d3 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -447,6 +447,9 @@ struct sdhci_host {
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
+#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
+#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */
+#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */
unsigned int version; /* SDHCI spec. version */
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH RFC 4/5] mmc: sdhci: Tidy caps variables in sdhci_setup_host()
2016-06-28 13:23 [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
` (2 preceding siblings ...)
2016-06-28 13:23 ` [PATCH RFC 3/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
@ 2016-06-28 13:23 ` Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 5/5] mmc: sdhci: Add sdhci_read_caps() Adrian Hunter
4 siblings, 0 replies; 9+ messages in thread
From: Adrian Hunter @ 2016-06-28 13:23 UTC (permalink / raw)
To: linux-mmc; +Cc: Ulf Hansson, Jon Hunter, Dong Aisheng, Dong Aisheng
In preparation for adding a function to read the capability registers.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 81 ++++++++++++++++++++++++------------------------
drivers/mmc/host/sdhci.h | 4 +--
2 files changed, 42 insertions(+), 43 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8ac5a9df2c01..185dbf27689e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2844,7 +2844,6 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
int sdhci_setup_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
- u32 caps[2] = {0, 0};
u32 max_current_caps;
unsigned int ocr_avail;
unsigned int override_timeout_clk;
@@ -2874,17 +2873,15 @@ int sdhci_setup_host(struct sdhci_host *host)
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_MISSING_CAPS)) {
+ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ if (host->version >= SDHCI_SPEC_300)
+ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ }
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
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");
else
host->flags |= SDHCI_USE_SDMA;
@@ -2896,7 +2893,7 @@ int sdhci_setup_host(struct sdhci_host *host)
}
if ((host->version >= SDHCI_SPEC_200) &&
- (caps[0] & SDHCI_CAN_DO_ADMA2))
+ (host->caps & SDHCI_CAN_DO_ADMA2))
host->flags |= SDHCI_USE_ADMA;
if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
@@ -2912,7 +2909,7 @@ int sdhci_setup_host(struct sdhci_host *host)
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
* implement.
*/
- if (caps[0] & SDHCI_CAN_64BIT)
+ if (host->caps & SDHCI_CAN_64BIT)
host->flags |= SDHCI_USE_64_BIT_DMA;
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -2988,10 +2985,10 @@ int sdhci_setup_host(struct sdhci_host *host)
}
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;
else
- host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
+ host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
host->max_clk *= 1000000;
@@ -3010,7 +3007,7 @@ int sdhci_setup_host(struct sdhci_host *host)
* In case of Host Controller v3.00, find out whether clock
* 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;
/*
@@ -3042,7 +3039,7 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc->f_max = max_clk;
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;
if (host->timeout_clk == 0) {
if (host->ops->get_timeout_clock) {
@@ -3056,7 +3053,7 @@ int sdhci_setup_host(struct sdhci_host *host)
}
}
- if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
+ if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
if (override_timeout_clk)
@@ -3097,7 +3094,7 @@ int sdhci_setup_host(struct sdhci_host *host)
if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_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;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
@@ -3115,9 +3112,9 @@ int sdhci_setup_host(struct sdhci_host *host)
ret = regulator_enable(mmc->supply.vqmmc);
if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
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) {
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
mmc_hostname(mmc), ret);
@@ -3125,28 +3122,30 @@ int sdhci_setup_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. */
- 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;
/* 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;
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
* field can be promoted to support HS200.
*/
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_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;
+ }
if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
- (caps[1] & SDHCI_SUPPORT_HS400))
+ (host->caps1 & SDHCI_SUPPORT_HS400))
mmc->caps2 |= MMC_CAP2_HS400;
if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
@@ -3155,25 +3154,25 @@ int sdhci_setup_host(struct sdhci_host *host)
1300000)))
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;
/* 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;
/* 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;
- if (caps[1] & SDHCI_DRIVER_TYPE_C)
+ if (host->caps1 & SDHCI_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;
/* 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
@@ -3183,7 +3182,7 @@ int sdhci_setup_host(struct sdhci_host *host)
host->tuning_count = 1 << (host->tuning_count - 1);
/* 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;
ocr_avail = 0;
@@ -3212,7 +3211,7 @@ int sdhci_setup_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;
mmc->max_current_330 = ((max_current_caps &
@@ -3220,7 +3219,7 @@ int sdhci_setup_host(struct sdhci_host *host)
SDHCI_MAX_CURRENT_330_SHIFT) *
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;
mmc->max_current_300 = ((max_current_caps &
@@ -3228,7 +3227,7 @@ int sdhci_setup_host(struct sdhci_host *host)
SDHCI_MAX_CURRENT_300_SHIFT) *
SDHCI_MAX_CURRENT_MULTIPLIER;
}
- if (caps[0] & SDHCI_CAN_VDD_180) {
+ if (host->caps & SDHCI_CAN_VDD_180) {
ocr_avail |= MMC_VDD_165_195;
mmc->max_current_180 = ((max_current_caps &
@@ -3315,7 +3314,7 @@ int sdhci_setup_host(struct sdhci_host *host)
if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
mmc->max_blk_size = 2;
} 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;
if (mmc->max_blk_size >= 3) {
pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n",
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 419911f107d3..8696c9365ef2 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -490,8 +490,8 @@ struct sdhci_host {
struct timer_list timer; /* Timer for timeouts */
- u32 caps; /* Alternative CAPABILITY_0 */
- u32 caps1; /* Alternative CAPABILITY_1 */
+ u32 caps; /* CAPABILITY_0 */
+ u32 caps1; /* CAPABILITY_1 */
unsigned int ocr_avail_sdio; /* OCR bit masks */
unsigned int ocr_avail_sd;
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH RFC 5/5] mmc: sdhci: Add sdhci_read_caps()
2016-06-28 13:23 [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
` (3 preceding siblings ...)
2016-06-28 13:23 ` [PATCH RFC 4/5] mmc: sdhci: Tidy caps variables in sdhci_setup_host() Adrian Hunter
@ 2016-06-28 13:23 ` Adrian Hunter
4 siblings, 0 replies; 9+ messages in thread
From: Adrian Hunter @ 2016-06-28 13:23 UTC (permalink / raw)
To: linux-mmc; +Cc: Ulf Hansson, Jon Hunter, Dong Aisheng, Dong Aisheng
Add sdhci_read_caps() and __sdhci_read_caps() to make it easier for drivers
to fix the version and capabilities registers.
Pedantically, the SDHCI specification states that the capabilities
registers are valid when the host controller resets the Software Reset For
All bit. That requirement has always been satisfied by performing a reset
at the start of initialization, and consequently that is now part of the
new functions.
Although the SDHCI_QUIRK_MISSING_CAPS quirk has not yet been removed,
drivers that want to provide their own caps can now use these functions
instead of that quirk.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 48 +++++++++++++++++++++++++++++++++---------------
drivers/mmc/host/sdhci.h | 8 ++++++++
2 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 185dbf27689e..44cdb6d035f2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2841,6 +2841,38 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
return ret;
}
+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;
@@ -2856,29 +2888,15 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc = host->mmc;
- if (debug_quirks)
- host->quirks = debug_quirks;
- if (debug_quirks2)
- host->quirks2 = debug_quirks2;
+ sdhci_read_caps(host);
override_timeout_clk = host->timeout_clk;
- sdhci_do_reset(host, SDHCI_RESET_ALL);
-
- 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) {
pr_err("%s: Unknown controller version (%d). You may experience problems.\n",
mmc_hostname(mmc), host->version);
}
- if (!(host->quirks & SDHCI_QUIRK_MISSING_CAPS)) {
- host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
- if (host->version >= SDHCI_SPEC_300)
- host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
- }
-
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
else if (!(host->caps & SDHCI_CAN_DO_SDMA))
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 8696c9365ef2..e332e4a40823 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -492,6 +492,7 @@ struct sdhci_host {
u32 caps; /* CAPABILITY_0 */
u32 caps1; /* CAPABILITY_1 */
+ bool read_caps; /* Capability flags have been read */
unsigned int ocr_avail_sdio; /* OCR bit masks */
unsigned int ocr_avail_sd;
@@ -648,6 +649,8 @@ static inline void *sdhci_priv(struct sdhci_host *host)
}
extern void sdhci_card_detect(struct sdhci_host *host);
+extern void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps,
+ u32 *caps1);
extern int sdhci_setup_host(struct sdhci_host *host);
extern int __sdhci_add_host(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
@@ -655,6 +658,11 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead);
extern void sdhci_send_command(struct sdhci_host *host,
struct mmc_command *cmd);
+static inline void sdhci_read_caps(struct sdhci_host *host)
+{
+ __sdhci_read_caps(host, NULL, NULL, NULL);
+}
+
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
{
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host()
2016-06-28 13:23 ` [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host() Adrian Hunter
@ 2016-07-05 9:02 ` Jon Hunter
2016-07-05 9:13 ` Adrian Hunter
0 siblings, 1 reply; 9+ messages in thread
From: Jon Hunter @ 2016-07-05 9:02 UTC (permalink / raw)
To: Adrian Hunter, linux-mmc; +Cc: Ulf Hansson, Dong Aisheng, Dong Aisheng
On 28/06/16 14:23, Adrian Hunter wrote:
> Split sdhci-add_host() in order to further our objective to make
> sdhci into a library.
s/sdhci-add_host/sdhci_add_host
> The split divides code that sets up mmc and sdhci parameters, from
> code that actually activates things - such as tasklet initialization,
> requesting the irq, and adding (and starting) the host.
>
> This gives drivers an opportunity to change various settings before
> committing to start the host.
>
> Drivers can continue to call sdhci_add_host() but drivers that want
> to take advantage of the split instead call sdhci_setup_host() followed
> by __sdhci_add_host().
If sdhci_setup_host() is successful, but then the subsequent call to
__sdhci_add_host() fails, then what should be called to clean-up? Does
not look like we can still call sdhci_remove_host() in this case.
Cheers
Jon
--
nvpublic
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host()
2016-07-05 9:02 ` Jon Hunter
@ 2016-07-05 9:13 ` Adrian Hunter
2016-07-05 9:21 ` Adrian Hunter
0 siblings, 1 reply; 9+ messages in thread
From: Adrian Hunter @ 2016-07-05 9:13 UTC (permalink / raw)
To: Jon Hunter, linux-mmc; +Cc: Ulf Hansson, Dong Aisheng, Dong Aisheng
On 05/07/16 12:02, Jon Hunter wrote:
>
> On 28/06/16 14:23, Adrian Hunter wrote:
>> Split sdhci-add_host() in order to further our objective to make
>> sdhci into a library.
>
> s/sdhci-add_host/sdhci_add_host
Thanks I'll make that change
>
>> The split divides code that sets up mmc and sdhci parameters, from
>> code that actually activates things - such as tasklet initialization,
>> requesting the irq, and adding (and starting) the host.
>>
>> This gives drivers an opportunity to change various settings before
>> committing to start the host.
>>
>> Drivers can continue to call sdhci_add_host() but drivers that want
>> to take advantage of the split instead call sdhci_setup_host() followed
>> by __sdhci_add_host().
>
> If sdhci_setup_host() is successful, but then the subsequent call to
> __sdhci_add_host() fails, then what should be called to clean-up? Does
> not look like we can still call sdhci_remove_host() in this case.
Nothing needs to be called because __sdhci_add_host() cleans up for
sdhci_setup_host() and itself.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host()
2016-07-05 9:13 ` Adrian Hunter
@ 2016-07-05 9:21 ` Adrian Hunter
0 siblings, 0 replies; 9+ messages in thread
From: Adrian Hunter @ 2016-07-05 9:21 UTC (permalink / raw)
To: Adrian Hunter, Jon Hunter, linux-mmc
Cc: Ulf Hansson, Dong Aisheng, Dong Aisheng
On 05/07/16 12:13, Adrian Hunter wrote:
> On 05/07/16 12:02, Jon Hunter wrote:
>>
>> On 28/06/16 14:23, Adrian Hunter wrote:
>>> Split sdhci-add_host() in order to further our objective to make
>>> sdhci into a library.
>>
>> s/sdhci-add_host/sdhci_add_host
>
> Thanks I'll make that change
>
>>
>>> The split divides code that sets up mmc and sdhci parameters, from
>>> code that actually activates things - such as tasklet initialization,
>>> requesting the irq, and adding (and starting) the host.
>>>
>>> This gives drivers an opportunity to change various settings before
>>> committing to start the host.
>>>
>>> Drivers can continue to call sdhci_add_host() but drivers that want
>>> to take advantage of the split instead call sdhci_setup_host() followed
>>> by __sdhci_add_host().
>>
>> If sdhci_setup_host() is successful, but then the subsequent call to
>> __sdhci_add_host() fails, then what should be called to clean-up? Does
>> not look like we can still call sdhci_remove_host() in this case.
>
> Nothing needs to be called because __sdhci_add_host() cleans up for
> sdhci_setup_host() and itself.
I should have written nothing extra needs to be called because you still
need to free the host (i.e. call sdhci_free_host()), but that is the same
whether you use sdhci_add_host() or sdhci_setup_host()+__sdhci_add_host().
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2016-07-05 9:25 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-06-28 13:23 [PATCH RFC 0/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 1/5] mmc: sdhci: Do not call implementations of mmc host ops directly Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 2/5] mmc: sdhci: Split sdhci_add_host() Adrian Hunter
2016-07-05 9:02 ` Jon Hunter
2016-07-05 9:13 ` Adrian Hunter
2016-07-05 9:21 ` Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 3/5] mmc: sdhci: Make signal voltage support explicit Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 4/5] mmc: sdhci: Tidy caps variables in sdhci_setup_host() Adrian Hunter
2016-06-28 13:23 ` [PATCH RFC 5/5] mmc: sdhci: Add sdhci_read_caps() Adrian Hunter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).