public inbox for linux-mmc@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] mmc: Add support for drive strength for eMMCs
@ 2015-02-05 13:13 Adrian Hunter
  2015-02-05 13:13 ` [PATCH 01/12] mmc: core: Reset driver type to default Adrian Hunter
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Hi

Here are some patches to enable a host controller to select
driver strength for eMMCs using HS200 or HS400. These are based
on top of the re-tuning series.

There can be some confusion over the term "driver strength".
SD calls it "drive strength" for the card but "driver type"
for the host. Whereas JEDEC calls it both "driver strength"
and "driver type". The values are the same for both SD
and eMMC:

  Value   Driver Type   Relative strength
    0       B             x1	default and mandatory support
    1       A             x1.5
    2       C             x0.75
    3       D             x0.5

Except eMMC also defines value 4:

  Value   Driver Type   Relative strength
    4                     x1.2


Adrian Hunter (12):
      mmc: core: Reset driver type to default
      mmc: core: Allow card drive strength to be different to host
      mmc: core: Simplify card drive strength mask
      mmc: core: Add 'card' to drive strength selection callback
      mmc: core: Factor out common code in drive strength selection
      mmc: core: Record card drive strength
      mmc: mmc: Read card's valid driver strength mask
      mmc: mmc: Add driver strength selection
      mmc: core: Add function to read driver-strength device property
      mmc: sdhci: Add a callback to select drive strength
      mmc: sdhci-pci: Add support for drive strength selection for SPT
      mmc: sdhci-pci: Enable HS400 for some Intel host controllers

 Documentation/devicetree/bindings/mmc/mmc.txt |  4 ++
 drivers/mmc/core/core.c                       | 39 ++++++++++++
 drivers/mmc/core/core.h                       |  2 +
 drivers/mmc/core/host.c                       | 21 ++++++
 drivers/mmc/core/mmc.c                        | 46 ++++++++++++--
 drivers/mmc/core/sd.c                         | 69 ++++++--------------
 drivers/mmc/core/sdio.c                       | 77 +++++++---------------
 drivers/mmc/host/sdhci-pci-data.c             |  3 +
 drivers/mmc/host/sdhci-pci.c                  | 92 +++++++++++++++++++++++++++
 drivers/mmc/host/sdhci-pci.h                  |  4 ++
 drivers/mmc/host/sdhci.c                      | 13 ++++
 drivers/mmc/host/sdhci.h                      |  4 ++
 include/linux/mmc/card.h                      |  2 +
 include/linux/mmc/host.h                      |  6 +-
 include/linux/mmc/mmc.h                       |  4 ++
 include/linux/mmc/sdhci-pci-data.h            |  2 +
 16 files changed, 275 insertions(+), 113 deletions(-)


Regards
Adrian

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 01/12] mmc: core: Reset driver type to default
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 02/12] mmc: core: Allow card drive strength to be different to host Adrian Hunter
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

IO state variable drv_type could be set during card
initialization. Consequently, it must be reset to the
default value when setting the initial state.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/core.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index cd46c0e..dbc40c5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1179,6 +1179,7 @@ void mmc_set_initial_state(struct mmc_host *host)
 	host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
+	host->ios.drv_type = 0;
 
 	mmc_set_ios(host);
 }
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 02/12] mmc: core: Allow card drive strength to be different to host
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
  2015-02-05 13:13 ` [PATCH 01/12] mmc: core: Reset driver type to default Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 03/12] mmc: core: Simplify card drive strength mask Adrian Hunter
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Initialization of UHS-I modes for SD and SDIO cards
employs a callback to allow the host driver to
choose a drive strength value. Currently that
assumes the card drive strength and host driver
type must be the same value. Change to let the
callback make that decision and return both the
card drive strength and host driver type.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/sd.c    | 33 +++++++++++++--------------------
 drivers/mmc/core/sdio.c  | 43 ++++++++++++++++++-------------------------
 include/linux/mmc/host.h |  3 ++-
 3 files changed, 33 insertions(+), 46 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ad4d43e..cbee29a 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -388,18 +388,9 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 {
 	int host_drv_type = SD_DRIVER_TYPE_B;
 	int card_drv_type = SD_DRIVER_TYPE_B;
-	int drive_strength;
+	int drive_strength, drv_type;
 	int err;
 
-	/*
-	 * If the host doesn't support any of the Driver Types A,C or D,
-	 * or there is no board specific handler then default Driver
-	 * Type B is used.
-	 */
-	if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
-	    | MMC_CAP_DRIVER_TYPE_D)))
-		return 0;
-
 	if (!card->host->ops->select_drive_strength)
 		return 0;
 
@@ -430,20 +421,22 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 	mmc_host_clk_hold(card->host);
 	drive_strength = card->host->ops->select_drive_strength(
 		card->sw_caps.uhs_max_dtr,
-		host_drv_type, card_drv_type);
+		host_drv_type, card_drv_type, &drv_type);
 	mmc_host_clk_release(card->host);
 
-	err = mmc_sd_switch(card, 1, 2, drive_strength, status);
-	if (err)
-		return err;
-
-	if ((status[15] & 0xF) != drive_strength) {
-		pr_warn("%s: Problem setting drive strength!\n",
-			mmc_hostname(card->host));
-		return 0;
+	if (drive_strength) {
+		err = mmc_sd_switch(card, 1, 2, drive_strength, status);
+		if (err)
+			return err;
+		if ((status[15] & 0xF) != drive_strength) {
+			pr_warn("%s: Problem setting drive strength!\n",
+				mmc_hostname(card->host));
+			return 0;
+		}
 	}
 
-	mmc_set_driver_type(card->host, drive_strength);
+	if (drv_type)
+		mmc_set_driver_type(card->host, drv_type);
 
 	return 0;
 }
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index ce6cc47..d7309ab 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -401,21 +401,10 @@ static void sdio_select_driver_type(struct mmc_card *card)
 {
 	int host_drv_type = SD_DRIVER_TYPE_B;
 	int card_drv_type = SD_DRIVER_TYPE_B;
-	int drive_strength;
+	int drive_strength, drv_type;
 	unsigned char card_strength;
 	int err;
 
-	/*
-	 * If the host doesn't support any of the Driver Types A,C or D,
-	 * or there is no board specific handler then default Driver
-	 * Type B is used.
-	 */
-	if (!(card->host->caps &
-		(MMC_CAP_DRIVER_TYPE_A |
-		 MMC_CAP_DRIVER_TYPE_C |
-		 MMC_CAP_DRIVER_TYPE_D)))
-		return;
-
 	if (!card->host->ops->select_drive_strength)
 		return;
 
@@ -445,23 +434,27 @@ static void sdio_select_driver_type(struct mmc_card *card)
 	 */
 	drive_strength = card->host->ops->select_drive_strength(
 		card->sw_caps.uhs_max_dtr,
-		host_drv_type, card_drv_type);
+		host_drv_type, card_drv_type, &drv_type);
 
-	/* if error just use default for drive strength B */
-	err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
-		&card_strength);
-	if (err)
-		return;
+	if (drive_strength) {
+		/* if error just use default for drive strength B */
+		err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
+				       &card_strength);
+		if (err)
+			return;
 
-	card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
-	card_strength |= host_drive_to_sdio_drive(drive_strength);
+		card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
+		card_strength |= host_drive_to_sdio_drive(drive_strength);
 
-	err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
-		card_strength, NULL);
+		/* if error default to drive strength B */
+		err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
+				       card_strength, NULL);
+		if (err)
+			return;
+	}
 
-	/* if error default to drive strength B */
-	if (!err)
-		mmc_set_driver_type(card->host, drive_strength);
+	if (drv_type)
+		mmc_set_driver_type(card->host, drv_type);
 }
 
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index e9a7470..d984c55 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -138,7 +138,8 @@ struct mmc_host_ops {
 
 	/* Prepare HS400 target operating frequency depending host driver */
 	int	(*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
-	int	(*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
+	int	(*select_drive_strength)(unsigned int max_dtr, int host_drv,
+					 int card_drv, int *drv_type);
 	void	(*hw_reset)(struct mmc_host *host);
 	void	(*card_event)(struct mmc_host *host);
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 03/12] mmc: core: Simplify card drive strength mask
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
  2015-02-05 13:13 ` [PATCH 01/12] mmc: core: Reset driver type to default Adrian Hunter
  2015-02-05 13:13 ` [PATCH 02/12] mmc: core: Allow card drive strength to be different to host Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 04/12] mmc: core: Add 'card' to drive strength selection callback Adrian Hunter
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Card drive strength selection uses a callback to
which a mask of supported drive strengths is passed.
Currently, the bits are checked against the values
in the SD specifications. That is not necessary
because the callback will anyway match the mask
against a valid value. Simplify by taking the mask
as is but still ensuring that the default mandatory
value (type B) is always supported.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/sd.c   | 12 ++----------
 drivers/mmc/core/sdio.c | 12 ++----------
 2 files changed, 4 insertions(+), 20 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index cbee29a..fab83e8 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -387,8 +387,7 @@ out:
 static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 {
 	int host_drv_type = SD_DRIVER_TYPE_B;
-	int card_drv_type = SD_DRIVER_TYPE_B;
-	int drive_strength, drv_type;
+	int card_drv_type, drive_strength, drv_type;
 	int err;
 
 	if (!card->host->ops->select_drive_strength)
@@ -403,14 +402,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 	if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
 		host_drv_type |= SD_DRIVER_TYPE_D;
 
-	if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
-		card_drv_type |= SD_DRIVER_TYPE_A;
-
-	if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
-		card_drv_type |= SD_DRIVER_TYPE_C;
-
-	if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
-		card_drv_type |= SD_DRIVER_TYPE_D;
+	card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
 	/*
 	 * The drive strength that the hardware can support
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index d7309ab..76e4dde 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -400,8 +400,7 @@ static unsigned char host_drive_to_sdio_drive(int host_strength)
 static void sdio_select_driver_type(struct mmc_card *card)
 {
 	int host_drv_type = SD_DRIVER_TYPE_B;
-	int card_drv_type = SD_DRIVER_TYPE_B;
-	int drive_strength, drv_type;
+	int card_drv_type, drive_strength, drv_type;
 	unsigned char card_strength;
 	int err;
 
@@ -417,14 +416,7 @@ static void sdio_select_driver_type(struct mmc_card *card)
 	if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
 		host_drv_type |= SD_DRIVER_TYPE_D;
 
-	if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
-		card_drv_type |= SD_DRIVER_TYPE_A;
-
-	if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
-		card_drv_type |= SD_DRIVER_TYPE_C;
-
-	if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
-		card_drv_type |= SD_DRIVER_TYPE_D;
+	card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
 	/*
 	 * The drive strength that the hardware can support
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 04/12] mmc: core: Add 'card' to drive strength selection callback
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (2 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 03/12] mmc: core: Simplify card drive strength mask Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 05/12] mmc: core: Factor out common code in drive strength selection Adrian Hunter
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

In preparation for supporting also eMMC drive strength,
add the 'card' as a parameter so that the callback can
distinguish different types of cards if necessary.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/sd.c    | 2 +-
 drivers/mmc/core/sdio.c  | 2 +-
 include/linux/mmc/host.h | 3 ++-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index fab83e8..dba1acf 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -411,7 +411,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 	 * return what is possible given the options
 	 */
 	mmc_host_clk_hold(card->host);
-	drive_strength = card->host->ops->select_drive_strength(
+	drive_strength = card->host->ops->select_drive_strength(card,
 		card->sw_caps.uhs_max_dtr,
 		host_drv_type, card_drv_type, &drv_type);
 	mmc_host_clk_release(card->host);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 76e4dde..de92ffe 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -424,7 +424,7 @@ static void sdio_select_driver_type(struct mmc_card *card)
 	 * information and let the hardware specific code
 	 * return what is possible given the options
 	 */
-	drive_strength = card->host->ops->select_drive_strength(
+	drive_strength = card->host->ops->select_drive_strength(card,
 		card->sw_caps.uhs_max_dtr,
 		host_drv_type, card_drv_type, &drv_type);
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index d984c55..967e7db 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -138,7 +138,8 @@ struct mmc_host_ops {
 
 	/* Prepare HS400 target operating frequency depending host driver */
 	int	(*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
-	int	(*select_drive_strength)(unsigned int max_dtr, int host_drv,
+	int	(*select_drive_strength)(struct mmc_card *card,
+					 unsigned int max_dtr, int host_drv,
 					 int card_drv, int *drv_type);
 	void	(*hw_reset)(struct mmc_host *host);
 	void	(*card_event)(struct mmc_host *host);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 05/12] mmc: core: Factor out common code in drive strength selection
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (3 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 04/12] mmc: core: Add 'card' to drive strength selection callback Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 06/12] mmc: core: Record card drive strength Adrian Hunter
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Make a new function out of common code used for drive
strength selection.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/core.c | 38 ++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/core.h |  2 ++
 drivers/mmc/core/sd.c   | 27 +++------------------------
 drivers/mmc/core/sdio.c | 25 +++----------------------
 4 files changed, 46 insertions(+), 46 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index dbc40c5..373a1cf 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1634,6 +1634,44 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
 	mmc_host_clk_release(host);
 }
 
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+			      int card_drv_type, int *drv_type)
+{
+	struct mmc_host *host = card->host;
+	int host_drv_type = SD_DRIVER_TYPE_B;
+	int drive_strength;
+
+	*drv_type = 0;
+
+	if (!host->ops->select_drive_strength)
+		return 0;
+
+	/* Use SD definition of driver strength for hosts */
+	if (host->caps & MMC_CAP_DRIVER_TYPE_A)
+		host_drv_type |= SD_DRIVER_TYPE_A;
+
+	if (host->caps & MMC_CAP_DRIVER_TYPE_C)
+		host_drv_type |= SD_DRIVER_TYPE_C;
+
+	if (host->caps & MMC_CAP_DRIVER_TYPE_D)
+		host_drv_type |= SD_DRIVER_TYPE_D;
+
+	/*
+	 * The drive strength that the hardware can support
+	 * depends on the board design.  Pass the appropriate
+	 * information and let the hardware specific code
+	 * return what is possible given the options
+	 */
+	mmc_host_clk_hold(host);
+	drive_strength = host->ops->select_drive_strength(card, max_dtr,
+							  host_drv_type,
+							  card_drv_type,
+							  drv_type);
+	mmc_host_clk_release(host);
+
+	return drive_strength;
+}
+
 /*
  * Apply power to the MMC stack.  This is a two-stage process.
  * First, we enable power to the card without the clock running.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index e6f2de7..1a22a82 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -50,6 +50,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
 int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
 void mmc_set_timing(struct mmc_host *host, unsigned int timing);
 void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+			      int card_drv_type, int *drv_type);
 void mmc_power_up(struct mmc_host *host, u32 ocr);
 void mmc_power_off(struct mmc_host *host);
 void mmc_power_cycle(struct mmc_host *host, u32 ocr);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index dba1acf..e1a98b1 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -386,35 +386,14 @@ out:
 
 static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 {
-	int host_drv_type = SD_DRIVER_TYPE_B;
 	int card_drv_type, drive_strength, drv_type;
 	int err;
 
-	if (!card->host->ops->select_drive_strength)
-		return 0;
-
-	if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
-		host_drv_type |= SD_DRIVER_TYPE_A;
-
-	if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
-		host_drv_type |= SD_DRIVER_TYPE_C;
-
-	if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
-		host_drv_type |= SD_DRIVER_TYPE_D;
-
 	card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
-	/*
-	 * The drive strength that the hardware can support
-	 * depends on the board design.  Pass the appropriate
-	 * information and let the hardware specific code
-	 * return what is possible given the options
-	 */
-	mmc_host_clk_hold(card->host);
-	drive_strength = card->host->ops->select_drive_strength(card,
-		card->sw_caps.uhs_max_dtr,
-		host_drv_type, card_drv_type, &drv_type);
-	mmc_host_clk_release(card->host);
+	drive_strength = mmc_select_drive_strength(card,
+						   card->sw_caps.uhs_max_dtr,
+						   card_drv_type, &drv_type);
 
 	if (drive_strength) {
 		err = mmc_sd_switch(card, 1, 2, drive_strength, status);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index de92ffe..8cc1793 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -399,34 +399,15 @@ static unsigned char host_drive_to_sdio_drive(int host_strength)
 
 static void sdio_select_driver_type(struct mmc_card *card)
 {
-	int host_drv_type = SD_DRIVER_TYPE_B;
 	int card_drv_type, drive_strength, drv_type;
 	unsigned char card_strength;
 	int err;
 
-	if (!card->host->ops->select_drive_strength)
-		return;
-
-	if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
-		host_drv_type |= SD_DRIVER_TYPE_A;
-
-	if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
-		host_drv_type |= SD_DRIVER_TYPE_C;
-
-	if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
-		host_drv_type |= SD_DRIVER_TYPE_D;
-
 	card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
-	/*
-	 * The drive strength that the hardware can support
-	 * depends on the board design.  Pass the appropriate
-	 * information and let the hardware specific code
-	 * return what is possible given the options
-	 */
-	drive_strength = card->host->ops->select_drive_strength(card,
-		card->sw_caps.uhs_max_dtr,
-		host_drv_type, card_drv_type, &drv_type);
+	drive_strength = mmc_select_drive_strength(card,
+						   card->sw_caps.uhs_max_dtr,
+						   card_drv_type, &drv_type);
 
 	if (drive_strength) {
 		/* if error just use default for drive strength B */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 06/12] mmc: core: Record card drive strength
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (4 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 05/12] mmc: core: Factor out common code in drive strength selection Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 07/12] mmc: mmc: Read card's valid driver strength mask Adrian Hunter
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

In preparation for adding drive strength support
for eMMC, add drive_strength to struct mmc_card
to record the card drive strength for UHS-I modes
and HS200 / HS400. For eMMC this will be needed
when switching between HS200 and HS400.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/sd.c    | 3 +++
 drivers/mmc/core/sdio.c  | 3 +++
 include/linux/mmc/card.h | 1 +
 3 files changed, 7 insertions(+)

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index e1a98b1..2fdb41b 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -389,6 +389,8 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 	int card_drv_type, drive_strength, drv_type;
 	int err;
 
+	card->drive_strength = 0;
+
 	card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
 	drive_strength = mmc_select_drive_strength(card,
@@ -404,6 +406,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 				mmc_hostname(card->host));
 			return 0;
 		}
+		card->drive_strength = drive_strength;
 	}
 
 	if (drv_type)
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 8cc1793..fa81ba53 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -403,6 +403,8 @@ static void sdio_select_driver_type(struct mmc_card *card)
 	unsigned char card_strength;
 	int err;
 
+	card->drive_strength = 0;
+
 	card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
 	drive_strength = mmc_select_drive_strength(card,
@@ -424,6 +426,7 @@ static void sdio_select_driver_type(struct mmc_card *card)
 				       card_strength, NULL);
 		if (err)
 			return;
+		card->drive_strength = drive_strength;
 	}
 
 	if (drv_type)
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index a6cf4c0..ad54861 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -305,6 +305,7 @@ struct mmc_card {
 
 	unsigned int		sd_bus_speed;	/* Bus Speed Mode set for the card */
 	unsigned int		mmc_avail_type;	/* supported device type by both host and card */
+	unsigned int		drive_strength;	/* for UHS-I, HS200 or HS400 */
 
 	struct dentry		*debugfs_root;
 	struct mmc_part	part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 07/12] mmc: mmc: Read card's valid driver strength mask
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (5 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 06/12] mmc: core: Record card drive strength Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 08/12] mmc: mmc: Add driver strength selection Adrian Hunter
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

In preparation for supporing drive strength selection
for eMMC, read the card's valid driver strengths.

Note that though the SD spec uses the term "drive strength",
the JEDEC eMMC spec uses the term "driver strength".

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/mmc.c   | 1 +
 include/linux/mmc/card.h | 1 +
 include/linux/mmc/mmc.h  | 1 +
 3 files changed, 3 insertions(+)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 03577be..3222d4a 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -426,6 +426,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
 	card->ext_csd.raw_trim_mult =
 		ext_csd[EXT_CSD_TRIM_MULT];
 	card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
+	card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
 	if (card->ext_csd.rev >= 4) {
 		if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
 		    EXT_CSD_PART_SETTING_COMPLETED)
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index ad54861..ebfb3c9 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -97,6 +97,7 @@ struct mmc_ext_csd {
 	u8			raw_erased_mem_count;	/* 181 */
 	u8			raw_ext_csd_structure;	/* 194 */
 	u8			raw_card_type;		/* 196 */
+	u8			raw_driver_strength;	/* 197 */
 	u8			out_of_int_time;	/* 198 */
 	u8			raw_pwr_cl_52_195;	/* 200 */
 	u8			raw_pwr_cl_26_195;	/* 201 */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 124f562..4819cfb 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -302,6 +302,7 @@ struct _mmc_csd {
 #define EXT_CSD_REV			192	/* RO */
 #define EXT_CSD_STRUCTURE		194	/* RO */
 #define EXT_CSD_CARD_TYPE		196	/* RO */
+#define EXT_CSD_DRIVER_STRENGTH		197	/* RO */
 #define EXT_CSD_OUT_OF_INTERRUPT_TIME	198	/* RO */
 #define EXT_CSD_PART_SWITCH_TIME        199     /* RO */
 #define EXT_CSD_PWR_CL_52_195		200	/* RO */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 08/12] mmc: mmc: Add driver strength selection
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (6 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 07/12] mmc: mmc: Read card's valid driver strength mask Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 09/12] mmc: core: Add function to read driver-strength device property Adrian Hunter
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Add the ability to set eMMC driver strength
for HS200 and HS400.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/core/mmc.c  | 45 ++++++++++++++++++++++++++++++++++++++-------
 include/linux/mmc/mmc.h |  3 +++
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3222d4a..23cc737 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1033,6 +1033,7 @@ static int mmc_select_hs400(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
 	int err = 0;
+	u8 val;
 
 	/*
 	 * HS400 mode requires 8-bit bus width
@@ -1048,8 +1049,10 @@ static int mmc_select_hs400(struct mmc_card *card)
 	mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
 	mmc_set_bus_speed(card);
 
+	val = EXT_CSD_TIMING_HS |
+	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
 	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
+			   EXT_CSD_HS_TIMING, val,
 			   card->ext_csd.generic_cmd6_time,
 			   true, true, true);
 	if (err) {
@@ -1068,8 +1071,10 @@ static int mmc_select_hs400(struct mmc_card *card)
 		return err;
 	}
 
+	val = EXT_CSD_TIMING_HS400 |
+	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
 	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
+			   EXT_CSD_HS_TIMING, val,
 			   card->ext_csd.generic_cmd6_time,
 			   true, true, true);
 	if (err) {
@@ -1108,6 +1113,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
 	bool send_status = true;
 	unsigned int max_dtr;
 	int err;
+	u8 val;
 
 	if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
 		send_status = false;
@@ -1117,8 +1123,10 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
 	mmc_set_clock(host, max_dtr);
 
 	/* Switch HS400 to HS DDR */
+	val = EXT_CSD_TIMING_HS |
+	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
 	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
-			   EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time,
+			   val, card->ext_csd.generic_cmd6_time,
 			   true, send_status, true);
 	if (err)
 		goto out_err;
@@ -1147,10 +1155,11 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
 	}
 
 	/* Switch HS to HS200 */
+	val = EXT_CSD_TIMING_HS200 |
+	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
 	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
-			   EXT_CSD_TIMING_HS200,
-			   card->ext_csd.generic_cmd6_time, true, send_status,
-			   true);
+			   val, card->ext_csd.generic_cmd6_time, true,
+			   send_status, true);
 	if (err)
 		goto out_err;
 
@@ -1172,6 +1181,23 @@ out_err:
 	return err;
 }
 
+static void mmc_select_driver_type(struct mmc_card *card)
+{
+	int card_drv_type, drive_strength, drv_type;
+
+	card_drv_type = card->ext_csd.raw_driver_strength |
+			mmc_driver_type_mask(0);
+
+	drive_strength = mmc_select_drive_strength(card,
+						   card->ext_csd.hs200_max_dtr,
+						   card_drv_type, &drv_type);
+
+	card->drive_strength = drive_strength;
+
+	if (drv_type)
+		mmc_set_driver_type(card->host, drv_type);
+}
+
 /*
  * For device supporting HS200 mode, the following sequence
  * should be done before executing the tuning process.
@@ -1183,6 +1209,7 @@ static int mmc_select_hs200(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
 	int err = -EINVAL;
+	u8 val;
 
 	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
 		err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
@@ -1194,14 +1221,18 @@ static int mmc_select_hs200(struct mmc_card *card)
 	if (err)
 		goto err;
 
+	mmc_select_driver_type(card);
+
 	/*
 	 * Set the bus width(4 or 8) with host's support and
 	 * switch to HS200 mode if bus width is set successfully.
 	 */
 	err = mmc_select_bus_width(card);
 	if (!IS_ERR_VALUE(err)) {
+		val = EXT_CSD_TIMING_HS200 |
+		      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
 		err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-				   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
+				   EXT_CSD_HS_TIMING, val,
 				   card->ext_csd.generic_cmd6_time,
 				   true, true, true);
 		if (!err)
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 4819cfb..15f2c4a 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -391,6 +391,7 @@ struct _mmc_csd {
 #define EXT_CSD_TIMING_HS	1	/* High speed */
 #define EXT_CSD_TIMING_HS200	2	/* HS200 */
 #define EXT_CSD_TIMING_HS400	3	/* HS400 */
+#define EXT_CSD_DRV_STR_SHIFT	4	/* Driver Strength shift */
 
 #define EXT_CSD_SEC_ER_EN	BIT(0)
 #define EXT_CSD_SEC_BD_BLK_EN	BIT(2)
@@ -442,4 +443,6 @@ struct _mmc_csd {
 #define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1 in value */
 #define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
 
+#define mmc_driver_type_mask(n)		(1 << (n))
+
 #endif /* LINUX_MMC_MMC_H */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 09/12] mmc: core: Add function to read driver-strength device property
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (7 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 08/12] mmc: mmc: Add driver strength selection Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 10/12] mmc: sdhci: Add a callback to select drive strength Adrian Hunter
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball
  Cc: linux-mmc, Philip Rakity, Adrian Hunter, Rafael J. Wysocki,
	Mika Westerberg

Add a function to read a new "driver-strength" device property.

Currently there is no documentation for device properties,
so add "driver-strength" to mmc devicetree bindings.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 Documentation/devicetree/bindings/mmc/mmc.txt |  4 ++++
 drivers/mmc/core/host.c                       | 21 +++++++++++++++++++++
 include/linux/mmc/host.h                      |  2 ++
 3 files changed, 27 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 438899e..cfcb66b 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -43,6 +43,10 @@ Optional properties:
 - dsr: Value the card's (optional) Driver Stage Register (DSR) should be
   programmed with. Valid range: [0 .. 0xffff].
 
+Device properties (use device_property_ API):
+- driver-strength: Optional driver strength value for a card using UHS-I or
+  HS200/HS400
+
 *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
 polarity properties, we have to fix the meaning of the "normal" and "inverted"
 line levels. We choose to follow the SDHCI standard, which specifies both those
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 20354a5..bf19d14 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -22,6 +22,7 @@
 #include <linux/leds.h>
 #include <linux/slab.h>
 #include <linux/suspend.h>
+#include <linux/property.h>
 
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
@@ -516,6 +517,26 @@ int mmc_of_parse(struct mmc_host *host)
 
 EXPORT_SYMBOL(mmc_of_parse);
 
+int mmc_host_read_drive_strength(struct mmc_host *host)
+{
+	struct device *dev = host->parent;
+	u8 drive_strength;
+	int err;
+
+	if (!device_property_present(dev, "driver-strength"))
+		return -ENODATA;
+
+	err = device_property_read_u8(dev, "driver-strength", &drive_strength);
+	if (err) {
+		dev_err(dev, "Bad \"driver-strength\" property, error %d\n",
+			err);
+		return -EINVAL;
+	}
+
+	return drive_strength;
+}
+EXPORT_SYMBOL(mmc_host_read_drive_strength);
+
 /**
  *	mmc_alloc_host - initialise the per-host structure.
  *	@extra: sizeof private data structure
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 967e7db..d876892 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -394,6 +394,8 @@ void mmc_remove_host(struct mmc_host *);
 void mmc_free_host(struct mmc_host *);
 int mmc_of_parse(struct mmc_host *host);
 
+int mmc_host_read_drive_strength(struct mmc_host *host);
+
 static inline void *mmc_priv(struct mmc_host *host)
 {
 	return (void *)host->private;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 10/12] mmc: sdhci: Add a callback to select drive strength
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (8 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 09/12] mmc: core: Add function to read driver-strength device property Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 11/12] mmc: sdhci-pci: Add support for drive strength selection for SPT Adrian Hunter
  2015-02-05 13:13 ` [PATCH 12/12] mmc: sdhci-pci: Enable HS400 for some Intel host controllers Adrian Hunter
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Add a callbak to let host drivers select drive
strength.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/host/sdhci.c | 13 +++++++++++++
 drivers/mmc/host/sdhci.h |  4 ++++
 2 files changed, 17 insertions(+)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8848e06..fe1b00d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2054,6 +2054,18 @@ out_unlock:
 	return err;
 }
 
+static int sdhci_select_drive_strength(struct mmc_card *card,
+				       unsigned int max_dtr, int host_drv,
+				       int card_drv, int *drv_type)
+{
+	struct sdhci_host *host = mmc_priv(card->host);
+
+	if (!host->ops->select_drive_strength)
+		return 0;
+
+	return host->ops->select_drive_strength(host, card, max_dtr, host_drv,
+						card_drv, drv_type);
+}
 
 static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
 {
@@ -2198,6 +2210,7 @@ static const struct mmc_host_ops sdhci_ops = {
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
 	.prepare_hs400_tuning		= sdhci_prepare_hs400_tuning,
 	.execute_tuning			= sdhci_execute_tuning,
+	.select_drive_strength		= sdhci_select_drive_strength,
 	.card_event			= sdhci_card_event,
 	.card_busy	= sdhci_card_busy,
 };
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 0315e18..57e7bad 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -340,6 +340,10 @@ struct sdhci_ops {
 	void	(*platform_init)(struct sdhci_host *host);
 	void    (*card_event)(struct sdhci_host *host);
 	void	(*voltage_switch)(struct sdhci_host *host);
+	int	(*select_drive_strength)(struct sdhci_host *host,
+					 struct mmc_card *card,
+					 unsigned int max_dtr, int host_drv,
+					 int card_drv, int *drv_type);
 };
 
 #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 11/12] mmc: sdhci-pci: Add support for drive strength selection for SPT
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (9 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 10/12] mmc: sdhci: Add a callback to select drive strength Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  2015-02-05 13:13 ` [PATCH 12/12] mmc: sdhci-pci: Enable HS400 for some Intel host controllers Adrian Hunter
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Implement the select_drive_strength callback to provide
drive strength selection for Intel SPT.

The driver strength will be provided by a device property
otherwise an attempt will be made to read it from the card.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/host/sdhci-pci-data.c  |  3 ++
 drivers/mmc/host/sdhci-pci.c       | 91 ++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/sdhci-pci.h       |  4 ++
 include/linux/mmc/sdhci-pci-data.h |  2 +
 4 files changed, 100 insertions(+)

diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c
index a611217..56fddc6 100644
--- a/drivers/mmc/host/sdhci-pci-data.c
+++ b/drivers/mmc/host/sdhci-pci-data.c
@@ -3,3 +3,6 @@
 
 struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
 EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
+
+int sdhci_pci_spt_drive_strength;
+EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 29eaff7..580f5df 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
 #include <linux/scatterlist.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
@@ -266,6 +267,77 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
 	usleep_range(300, 1000);
 }
 
+static int spt_select_drive_strength(struct sdhci_host *host,
+				     struct mmc_card *card,
+				     unsigned int max_dtr,
+				     int host_drv, int card_drv, int *drv_type)
+{
+	int drive_strength = mmc_host_read_drive_strength(host->mmc);
+
+	if (drive_strength < 0 ||
+	    (mmc_driver_type_mask(drive_strength) & card_drv) == 0) {
+		if (sdhci_pci_spt_drive_strength > 0)
+			drive_strength = sdhci_pci_spt_drive_strength & 0xf;
+		else
+			drive_strength = 1; /* 33-ohm */
+	}
+
+	if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0)
+		drive_strength = 0; /* Default 50-ohm */
+
+	return drive_strength;
+}
+
+/* Try to read the drive strength from the card */
+static void spt_read_drive_strength(struct sdhci_host *host)
+{
+	int drive_strength;
+	u32 val, i, t;
+	u16 m;
+
+	if (sdhci_pci_spt_drive_strength)
+		return;
+
+	sdhci_pci_spt_drive_strength = -1;
+
+	drive_strength = mmc_host_read_drive_strength(host->mmc);
+	if (drive_strength >= 0)
+		return;
+
+	m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7;
+	if (m != 3 && m != 5)
+		return;
+	val = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	if (val & 0x3)
+		return;
+	sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE);
+	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+	sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE);
+	sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
+	sdhci_writew(host, 512, SDHCI_BLOCK_SIZE);
+	sdhci_writew(host, 1, SDHCI_BLOCK_COUNT);
+	sdhci_writel(host, 0, SDHCI_ARGUMENT);
+	sdhci_writew(host, 0x83b, SDHCI_COMMAND);
+	for (i = 0; i < 1000; i++) {
+		val = sdhci_readl(host, SDHCI_INT_STATUS);
+		if (val & 0xffff8000)
+			return;
+		if (val & 0x20)
+			break;
+		udelay(1);
+	}
+	val = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	if (!(val & 0x800))
+		return;
+	for (i = 0; i < 47; i++)
+		val = sdhci_readl(host, SDHCI_BUFFER);
+	t = val & 0xf00;
+	if (t != 0x200 && t != 0x300)
+		return;
+
+	sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf);
+}
+
 static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
 {
 	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
@@ -276,6 +348,10 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
 	slot->hw_reset = sdhci_pci_int_hw_reset;
 	if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
 		slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
+	if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) {
+		spt_read_drive_strength(slot->host);
+		slot->select_drive_strength = spt_select_drive_strength;
+	}
 	return 0;
 }
 
@@ -1202,6 +1278,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
 		slot->hw_reset(host);
 }
 
+static int sdhci_pci_select_drive_strength(struct sdhci_host *host,
+					   struct mmc_card *card,
+					   unsigned int max_dtr, int host_drv,
+					   int card_drv, int *drv_type)
+{
+	struct sdhci_pci_slot *slot = sdhci_priv(host);
+
+	if (!slot->select_drive_strength)
+		return 0;
+
+	return slot->select_drive_strength(host, card, max_dtr, host_drv,
+					   card_drv, drv_type);
+}
+
 static const struct sdhci_ops sdhci_pci_ops = {
 	.set_clock	= sdhci_set_clock,
 	.enable_dma	= sdhci_pci_enable_dma,
@@ -1209,6 +1299,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
 	.reset		= sdhci_reset,
 	.set_uhs_signaling = sdhci_set_uhs_signaling,
 	.hw_reset		= sdhci_pci_hw_reset,
+	.select_drive_strength	= sdhci_pci_select_drive_strength,
 };
 
 /*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 1ec684d..541f1ca 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -72,6 +72,10 @@ struct sdhci_pci_slot {
 	bool			cd_override_level;
 
 	void (*hw_reset)(struct sdhci_host *host);
+	int (*select_drive_strength)(struct sdhci_host *host,
+				     struct mmc_card *card,
+				     unsigned int max_dtr, int host_drv,
+				     int card_drv, int *drv_type);
 };
 
 struct sdhci_pci_chip {
diff --git a/include/linux/mmc/sdhci-pci-data.h b/include/linux/mmc/sdhci-pci-data.h
index 8959604..fda15b6 100644
--- a/include/linux/mmc/sdhci-pci-data.h
+++ b/include/linux/mmc/sdhci-pci-data.h
@@ -15,4 +15,6 @@ struct sdhci_pci_data {
 extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
 				int slotno);
 
+extern int sdhci_pci_spt_drive_strength;
+
 #endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 12/12] mmc: sdhci-pci: Enable HS400 for some Intel host controllers
  2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
                   ` (10 preceding siblings ...)
  2015-02-05 13:13 ` [PATCH 11/12] mmc: sdhci-pci: Add support for drive strength selection for SPT Adrian Hunter
@ 2015-02-05 13:13 ` Adrian Hunter
  11 siblings, 0 replies; 13+ messages in thread
From: Adrian Hunter @ 2015-02-05 13:13 UTC (permalink / raw)
  To: Ulf Hansson, Chris Ball; +Cc: linux-mmc, Philip Rakity, Adrian Hunter

Enable detection of HS400 support via capability bit-63
for some Intel host controllers.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/host/sdhci-pci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 580f5df..1409fe1 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -378,6 +378,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
 	.probe_slot	= byt_emmc_probe_slot,
 	.quirks		= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
 	.quirks2	= SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+			  SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
 			  SDHCI_QUIRK2_STOP_WITH_TC,
 };
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2015-02-05 13:18 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-05 13:13 [PATCH 00/12] mmc: Add support for drive strength for eMMCs Adrian Hunter
2015-02-05 13:13 ` [PATCH 01/12] mmc: core: Reset driver type to default Adrian Hunter
2015-02-05 13:13 ` [PATCH 02/12] mmc: core: Allow card drive strength to be different to host Adrian Hunter
2015-02-05 13:13 ` [PATCH 03/12] mmc: core: Simplify card drive strength mask Adrian Hunter
2015-02-05 13:13 ` [PATCH 04/12] mmc: core: Add 'card' to drive strength selection callback Adrian Hunter
2015-02-05 13:13 ` [PATCH 05/12] mmc: core: Factor out common code in drive strength selection Adrian Hunter
2015-02-05 13:13 ` [PATCH 06/12] mmc: core: Record card drive strength Adrian Hunter
2015-02-05 13:13 ` [PATCH 07/12] mmc: mmc: Read card's valid driver strength mask Adrian Hunter
2015-02-05 13:13 ` [PATCH 08/12] mmc: mmc: Add driver strength selection Adrian Hunter
2015-02-05 13:13 ` [PATCH 09/12] mmc: core: Add function to read driver-strength device property Adrian Hunter
2015-02-05 13:13 ` [PATCH 10/12] mmc: sdhci: Add a callback to select drive strength Adrian Hunter
2015-02-05 13:13 ` [PATCH 11/12] mmc: sdhci-pci: Add support for drive strength selection for SPT Adrian Hunter
2015-02-05 13:13 ` [PATCH 12/12] mmc: sdhci-pci: Enable HS400 for some Intel host controllers Adrian Hunter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox