diff mbox

[v2] sdhci: Advertise 2.0v on SDIO host interface

Message ID 20180105203743.35201-1-andriy.shevchenko@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andy Shevchenko Jan. 5, 2018, 8:37 p.m. UTC
On Intel Edison the Broadcom WiFi card, which is connected to SDIO,
requires 2.0v, while the host, according to Intel Merrifield TRM,
supports 1.8v I/O only.

The card announces itself as

  mmc2: new ultra high speed DDR50 SDIO card at address 0001

Introduce a custom OCR mask and ->set_power() callback to override 2.0v
signaling on Intel Merrifield platforms by enforcing 1.8v power choice.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
- quirk free approach
 drivers/mmc/host/sdhci-pci-core.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

Comments

Ulf Hansson Jan. 6, 2018, 12:44 p.m. UTC | #1
On 5 January 2018 at 21:37, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> On Intel Edison the Broadcom WiFi card, which is connected to SDIO,
> requires 2.0v, while the host, according to Intel Merrifield TRM,
> supports 1.8v I/O only.
>
> The card announces itself as
>
>   mmc2: new ultra high speed DDR50 SDIO card at address 0001
>
> Introduce a custom OCR mask and ->set_power() callback to override 2.0v
> signaling on Intel Merrifield platforms by enforcing 1.8v power choice.

This seems to be about VDD (vmmc) rather than about signaling voltage?

Could you verify that is the case? I think we have had too many cases
historically, which mixing the two voltages together and thus we end
up by luck getting things to work.

>
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
> - quirk free approach
>  drivers/mmc/host/sdhci-pci-core.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 3e4f04fd5175..029fcb19eb91 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -778,6 +778,7 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
>                 slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
>                 break;
>         case INTEL_MRFLD_SDIO:
> +               slot->host->ocr_mask = MMC_VDD_20_21 | MMC_VDD_165_195;

So this is about VDD and what VDD voltage levels the host support. If
there a way to find out these values dynamically, that should be done
instead.

>                 slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE |
>                                          MMC_CAP_POWER_OFF_CARD;
>                 break;
> @@ -789,10 +790,37 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
>         return 0;
>  }
>
> +static void intel_mrfld_sdhci_set_power(struct sdhci_host *host,
> +                                       unsigned char mode, unsigned short vdd)
> +{
> +       if (IS_ERR(host->mmc->supply.vmmc)) {
> +               switch (1 << vdd) {
> +               case MMC_VDD_20_21:
> +                       sdhci_set_power_noreg(host, mode, ilog2(MMC_VDD_165_195));

This looks weird.

I don't think you should need to treat the MMC_VDD_20_21 in a special way.

Or perhaps this is because this is about signal voltage after all?

> +                       break;
> +               default:
> +                       sdhci_set_power_noreg(host, mode, vdd);
> +                       break;
> +               }
> +       } else {
> +               sdhci_set_power(host, mode, vdd);
> +       }
> +}
> +
> +static const struct sdhci_ops intel_mrfld_sdhci_pci_ops = {
> +       .set_clock              = sdhci_set_clock,
> +       .set_power              = intel_mrfld_sdhci_set_power,
> +       .enable_dma             = sdhci_pci_enable_dma,
> +       .set_bus_width          = sdhci_set_bus_width,
> +       .reset                  = sdhci_reset,
> +       .set_uhs_signaling      = sdhci_set_uhs_signaling,
> +};
> +
>  static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
>         .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
>         .quirks2        = SDHCI_QUIRK2_BROKEN_HS200 |
>                         SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
> +       .ops            = &intel_mrfld_sdhci_pci_ops,
>         .allow_runtime_pm = true,
>         .probe_slot     = intel_mrfld_mmc_probe_slot,
>  };
> --
> 2.15.1
>

Kind regards
Uffe
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko Jan. 8, 2018, 1:05 p.m. UTC | #2
On Sat, 2018-01-06 at 13:44 +0100, Ulf Hansson wrote:
> On 5 January 2018 at 21:37, Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Intel Edison the Broadcom WiFi card, which is connected to SDIO,
> > requires 2.0v, while the host, according to Intel Merrifield TRM,
> > supports 1.8v I/O only.
> > 
> > The card announces itself as
> > 
> >   mmc2: new ultra high speed DDR50 SDIO card at address 0001
> > 
> > Introduce a custom OCR mask and ->set_power() callback to override
> > 2.0v
> > signaling on Intel Merrifield platforms by enforcing 1.8v power
> > choice.
> 
> This seems to be about VDD (vmmc) rather than about signaling voltage?

I'm not sure the case is the following:
1) SDHCI host reports (via OCR) that supported voltage is 1.8v (only)
2) BCM Wi-Fi card tells to the driver that it supports 2.0v (only)

In the result there is no OCR which has at least one supported voltage
by both.

Keep in mind that there is no regulator case.

> 
> Could you verify that is the case? I think we have had too many cases
> historically, which mixing the two voltages together and thus we end
> up by luck getting things to work.

If you tell me what to add to debug print this or alike, I will provide
you the answer. 

> >         case INTEL_MRFLD_SDIO:
> > +               slot->host->ocr_mask = MMC_VDD_20_21 |
> > MMC_VDD_165_195;
> 
> So this is about VDD and what VDD voltage levels the host support. If
> there a way to find out these values dynamically, that should be done
> instead.

Then we can't find a compatible voltage.

> > +       if (IS_ERR(host->mmc->supply.vmmc)) {
> > +               switch (1 << vdd) {
> > +               case MMC_VDD_20_21:
> > +                       sdhci_set_power_noreg(host, mode,
> > ilog2(MMC_VDD_165_195));
> 
> This looks weird.

Yes, it does. I have no idea how to do this better. I can't fix neither
hardware nor firmware to do something about this.

> I don't think you should need to treat the MMC_VDD_20_21 in a special
> way.
> 
> Or perhaps this is because this is about signal voltage after all?

See above.
Adrian Hunter Jan. 10, 2018, 2:56 p.m. UTC | #3
On 05/01/18 22:37, Andy Shevchenko wrote:
> On Intel Edison the Broadcom WiFi card, which is connected to SDIO,
> requires 2.0v, while the host, according to Intel Merrifield TRM,
> supports 1.8v I/O only.

	I/O -> supply

> 
> The card announces itself as
> 
>   mmc2: new ultra high speed DDR50 SDIO card at address 0001
> 
> Introduce a custom OCR mask and ->set_power() callback to override 2.0v
> signaling on Intel Merrifield platforms by enforcing 1.8v power choice.

	signaling -> supply

> 
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
> - quirk free approach
>  drivers/mmc/host/sdhci-pci-core.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
> 
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 3e4f04fd5175..029fcb19eb91 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -778,6 +778,7 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
>  		slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
>  		break;
>  	case INTEL_MRFLD_SDIO:

Maybe add a comment here:

		/* Advertise 2.0v for compatibility with the SDIO card's OCR */

> +		slot->host->ocr_mask = MMC_VDD_20_21 | MMC_VDD_165_195;
>  		slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE |
>  					 MMC_CAP_POWER_OFF_CARD;
>  		break;
> @@ -789,10 +790,37 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
>  	return 0;
>  }
>  
> +static void intel_mrfld_sdhci_set_power(struct sdhci_host *host,
> +					unsigned char mode, unsigned short vdd)
> +{
> +	if (IS_ERR(host->mmc->supply.vmmc)) {
> +		switch (1 << vdd) {

Maybe add a comment:

	/*
	 * Without a regulator, SDHCI does not support 2.0v but we get here
	 * because we advertised 2.0v support for compatibility with the SDIO
	 * card's OCR. Map it to 1.8v for the purpose of turning on the power.
	 */

> +		case MMC_VDD_20_21:
> +			sdhci_set_power_noreg(host, mode, ilog2(MMC_VDD_165_195));
> +			break;
> +		default:
> +			sdhci_set_power_noreg(host, mode, vdd);
> +			break;
> +		}
> +	} else {
> +		sdhci_set_power(host, mode, vdd);
> +	}

How about this instead:

	if (IS_ERR(host->mmc->supply.vmmc) && vdd == ilog2(MMC_VDD_20_21)) {
		sdhci_set_power_noreg(host, mode, ilog2(MMC_VDD_165_195));
		return;
	}

	sdhci_set_power(host, mode, vdd);

> +}
> +
> +static const struct sdhci_ops intel_mrfld_sdhci_pci_ops = {
> +	.set_clock		= sdhci_set_clock,
> +	.set_power		= intel_mrfld_sdhci_set_power,
> +	.enable_dma		= sdhci_pci_enable_dma,
> +	.set_bus_width		= sdhci_set_bus_width,
> +	.reset			= sdhci_reset,
> +	.set_uhs_signaling	= sdhci_set_uhs_signaling,
> +};
> +
>  static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
>  	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
>  	.quirks2	= SDHCI_QUIRK2_BROKEN_HS200 |
>  			SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
> +	.ops		= &intel_mrfld_sdhci_pci_ops,
>  	.allow_runtime_pm = true,
>  	.probe_slot	= intel_mrfld_mmc_probe_slot,
>  };
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 3e4f04fd5175..029fcb19eb91 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -778,6 +778,7 @@  static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
 		slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
 		break;
 	case INTEL_MRFLD_SDIO:
+		slot->host->ocr_mask = MMC_VDD_20_21 | MMC_VDD_165_195;
 		slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE |
 					 MMC_CAP_POWER_OFF_CARD;
 		break;
@@ -789,10 +790,37 @@  static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
 	return 0;
 }
 
+static void intel_mrfld_sdhci_set_power(struct sdhci_host *host,
+					unsigned char mode, unsigned short vdd)
+{
+	if (IS_ERR(host->mmc->supply.vmmc)) {
+		switch (1 << vdd) {
+		case MMC_VDD_20_21:
+			sdhci_set_power_noreg(host, mode, ilog2(MMC_VDD_165_195));
+			break;
+		default:
+			sdhci_set_power_noreg(host, mode, vdd);
+			break;
+		}
+	} else {
+		sdhci_set_power(host, mode, vdd);
+	}
+}
+
+static const struct sdhci_ops intel_mrfld_sdhci_pci_ops = {
+	.set_clock		= sdhci_set_clock,
+	.set_power		= intel_mrfld_sdhci_set_power,
+	.enable_dma		= sdhci_pci_enable_dma,
+	.set_bus_width		= sdhci_set_bus_width,
+	.reset			= sdhci_reset,
+	.set_uhs_signaling	= sdhci_set_uhs_signaling,
+};
+
 static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
 	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
 	.quirks2	= SDHCI_QUIRK2_BROKEN_HS200 |
 			SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+	.ops		= &intel_mrfld_sdhci_pci_ops,
 	.allow_runtime_pm = true,
 	.probe_slot	= intel_mrfld_mmc_probe_slot,
 };