diff mbox

[RESEND,V4] mmc: core: HS200 mode support for eMMC 4.5

Message ID 1319605188-2909-1-git-send-email-girish.shivananjappa@linaro.org
State New
Headers show

Commit Message

Girish K S Oct. 26, 2011, 4:59 a.m. UTC
This patch adds the support of the HS200 bus speed for eMMC 4.5 devices.
The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core and
host modules have been touched to add support for this module.

It is necessary to know the card type in the sdhci.c file to add support
for eMMC tuning function. So card.h file is included to import the card
data structure.

cc: Chris Ball <cjb@laptop.org>
Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
---
Changes in v4:
	Rebased onto chris-mmc/mmc-next branch. This patch is successfully 
	applied on commit with id de022ed3fdc14808299b2fa66dbb1ed5ab921912.
Changes in v3:
	In the previous commits of chris-mmc/mmc-next branch, the patch with
	commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines caps2 for
	more capabilities. This patch version deletes the member ext_caps(created
	in my earlier patch) from struct mmc_host and reuses already accepted
	caps2 member.
Changes in v2:
	Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
	problems identified in review. This patch has to be applied before
	the patch released for modifying the printk messages.
Changes in v1:
	Case statements in switch that produce same result have
	been combined to reduce repeated assignments.
	patch recreated after rebase to chris balls mmc-next branch.

 drivers/mmc/core/bus.c    |    3 +-
 drivers/mmc/core/mmc.c    |   92 ++++++++++++++++++++++++++++++++++++++++----
 drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
 include/linux/mmc/card.h  |    3 +
 include/linux/mmc/host.h  |    6 +++
 include/linux/mmc/mmc.h   |    8 +++-
 include/linux/mmc/sdhci.h |    1 +
 7 files changed, 132 insertions(+), 17 deletions(-)

Comments

Chris Ball Oct. 26, 2011, 7:53 p.m. UTC | #1
Hi Girish,

On Wed, Oct 26 2011, Girish K S wrote:
> This patch adds the support of the HS200 bus speed for eMMC 4.5 devices.
> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core and
> host modules have been touched to add support for this module.
>
> It is necessary to know the card type in the sdhci.c file to add support
> for eMMC tuning function. So card.h file is included to import the card
> data structure.
>
> cc: Chris Ball <cjb@laptop.org>
> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>

The long case statements and conditionals in this patch make it pretty
dense/ugly -- can you think of a way to avoid doing this to the code?
Also, it would be good to hear if anyone else has tested/is using this
patch.

Is having a new mmc_card_hs200() test desirable, compared to just
reusing mmc_card_highspeed() and checking the ext_csd.card_type?

Thanks,

- Chris.
Girish K S Oct. 28, 2011, 6:54 a.m. UTC | #2
On 27 October 2011 01:23, Chris Ball <cjb@laptop.org> wrote:
> Hi Girish,
>
> On Wed, Oct 26 2011, Girish K S wrote:
>> This patch adds the support of the HS200 bus speed for eMMC 4.5 devices.
>> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core and
>> host modules have been touched to add support for this module.
>>
>> It is necessary to know the card type in the sdhci.c file to add support
>> for eMMC tuning function. So card.h file is included to import the card
>> data structure.
>>
>> cc: Chris Ball <cjb@laptop.org>
>> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
>
> The long case statements and conditionals in this patch make it pretty
> dense/ugly -- can you think of a way to avoid doing this to the code?
Definitely will workout.
> Also, it would be good to hear if anyone else has tested/is using this
> patch.
>
> Is having a new mmc_card_hs200() test desirable, compared to just
> reusing mmc_card_highspeed() and checking the ext_csd.card_type?
The specification classifies from 26 to 52MHz as high speed. And
anything above it upto 200 as hs200. I added the macro for
clarity and to be inclined to the specification.
> Thanks,
>
> - Chris.
> --
> Chris Ball   <cjb@laptop.org>   <http://printf.net/>
> One Laptop Per Child
>
Sahitya Tummala Nov. 8, 2011, 8:39 a.m. UTC | #3
Hi Girish,

On 10/26/2011 10:29 AM, Girish K S wrote:
> This patch adds the support of the HS200 bus speed for eMMC 4.5 devices.
> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core and
> host modules have been touched to add support for this module.
>
> It is necessary to know the card type in the sdhci.c file to add support
> for eMMC tuning function. So card.h file is included to import the card
> data structure.
>
> cc: Chris Ball<cjb@laptop.org>
> Signed-off-by: Girish K S<girish.shivananjappa@linaro.org>
> ---
> Changes in v4:
> 	Rebased onto chris-mmc/mmc-next branch. This patch is successfully
> 	applied on commit with id de022ed3fdc14808299b2fa66dbb1ed5ab921912.
> Changes in v3:
> 	In the previous commits of chris-mmc/mmc-next branch, the patch with
> 	commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines caps2 for
> 	more capabilities. This patch version deletes the member ext_caps(created
> 	in my earlier patch) from struct mmc_host and reuses already accepted
> 	caps2 member.
> Changes in v2:
> 	Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
> 	problems identified in review. This patch has to be applied before
> 	the patch released for modifying the printk messages.
> Changes in v1:
> 	Case statements in switch that produce same result have
> 	been combined to reduce repeated assignments.
> 	patch recreated after rebase to chris balls mmc-next branch.
>
>   drivers/mmc/core/bus.c    |    3 +-
>   drivers/mmc/core/mmc.c    |   92 ++++++++++++++++++++++++++++++++++++++++----
>   drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
>   include/linux/mmc/card.h  |    3 +
>   include/linux/mmc/host.h  |    6 +++
>   include/linux/mmc/mmc.h   |    8 +++-
>   include/linux/mmc/sdhci.h |    1 +
>   7 files changed, 132 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index 46b6e84..2f82f6b 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
>   			mmc_card_ddr_mode(card) ? "DDR " : "",
>   			type);
>   	} else {
> -		printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
> +		pr_info("%s: new %s%s%s%s card at address %04x\n",
>   			mmc_hostname(card->host),
>   			mmc_sd_card_uhs(card) ? "ultra high speed " :
>   			(mmc_card_highspeed(card) ? "high speed " : ""),
> +			(mmc_card_hs200(card) ? "HS200 " : ""),
>   			mmc_card_ddr_mode(card) ? "DDR " : "",
>   			type, card->rca);
>   	}
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 3627044..4db248c 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
>   	}
>   	card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
>   	switch (ext_csd[EXT_CSD_CARD_TYPE]&  EXT_CSD_CARD_TYPE_MASK) {
> +	case EXT_CSD_CARD_TYPE_SDR_200 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +		card->ext_csd.hs_max_dtr = 200000000;
> +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> +		break;
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +		card->ext_csd.hs_max_dtr = 200000000;
> +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> +		break;
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +		card->ext_csd.hs_max_dtr = 200000000;
> +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> +		break;
>   	case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
>   	     EXT_CSD_CARD_TYPE_26:
>   		card->ext_csd.hs_max_dtr = 52000000;
> @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   {
>   	struct mmc_card *card;
>   	int err, ddr = 0;
> +	int hs_sdr = 0;
>   	u32 cid[4];
>   	unsigned int max_dtr;
>   	u32 rocr;
> @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   	/*
>   	 * Activate high speed (if supported)
>   	 */
> -	if ((card->ext_csd.hs_max_dtr != 0)&&
> -		(host->caps&  MMC_CAP_MMC_HIGHSPEED)) {
> -		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> -				 EXT_CSD_HS_TIMING, 1,
> -				 card->ext_csd.generic_cmd6_time);
> +	if (card->ext_csd.hs_max_dtr != 0) {
> +		if ((card->ext_csd.hs_max_dtr>  52000000)&&
> +		    (host->caps2&  MMC_CAP2_HIGHSPEED_200))
> +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +					 EXT_CSD_HS_TIMING, 2, 0);
> +		else if	(host->caps&  MMC_CAP_MMC_HIGHSPEED)
> +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +					 EXT_CSD_HS_TIMING, 1, 0);
> +
>   		if (err&&  err != -EBADMSG)
>   			goto free_card;
>
> @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   			       mmc_hostname(card->host));
>   			err = 0;
>   		} else {
> -			mmc_card_set_highspeed(card);
> +			if (card->ext_csd.hs_max_dtr>  52000000)
> +				mmc_card_set_hs200(card);
> +			else
> +				mmc_card_set_highspeed(card);
>   			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>   		}
>   	}
> @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   	 */
>   	max_dtr = (unsigned int)-1;
>
> -	if (mmc_card_highspeed(card)) {
> +	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
>   		if (max_dtr>  card->ext_csd.hs_max_dtr)
>   			max_dtr = card->ext_csd.hs_max_dtr;
>   	} else if (max_dtr>  card->csd.max_dtr) {
> @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   	}
>
>   	/*
> +	 * Indicate HS200 SDR mode (if supported).
> +	 */
> +	if (mmc_card_hs200(card)) {
> +		if ((card->ext_csd.card_type&  EXT_CSD_CARD_TYPE_SDR_1_8V)
> +			&&  ((host->caps2&  (MMC_CAP2_HS200_1_8V_SDR |
> +			     MMC_CAP2_HS200))
> +				== (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200)))
> +				hs_sdr = MMC_1_8V_SDR_MODE;
> +		else if ((card->ext_csd.card_type&  EXT_CSD_CARD_TYPE_SDR_1_2V)
> +			&&  ((host->caps2&  (MMC_CAP2_HS200_1_2V_SDR |
> +			      MMC_CAP2_HS200))
> +				== (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS200)))
> +				hs_sdr = MMC_1_2V_SDR_MODE;
> +	}
> +
> +	/*
>   	 * Activate wide bus and DDR (if supported).
>   	 */
>   	if ((card->csd.mmca_vsn>= CSD_SPEC_VER_4)&&
> @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   			if (!err) {
>   				mmc_set_bus_width(card->host, bus_width);
>
> +				if ((host->caps2&  MMC_CAP2_HS200)&&
> +				    card->host->ops->execute_tuning)
> +					err = card->host->ops->	\
> +					      execute_tuning(card->host);
> +
>   				/*
>   				 * If controller can't handle bus width test,
>   				 * compare ext_csd previously read in 1 bit mode
>   				 * against ext_csd at new bus width
>   				 */
> -				if (!(host->caps&  MMC_CAP_BUS_WIDTH_TEST))
> +				if (!(host->caps&  MMC_CAP_BUS_WIDTH_TEST)&&
> +				    !err)
>   					err = mmc_compare_ext_csds(card,
>   						bus_width);
> -				else
> +				else if (!err)
>   					err = mmc_bus_test(card, bus_width);
> +				else
> +					pr_warning("tuning execution failed\n");
>   				if (!err)
>   					break;
>   			}
> @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
>   			mmc_card_set_ddr_mode(card);
>   			mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
>   			mmc_set_bus_width(card->host, bus_width);
> +		} else if (hs_sdr) {
> +			if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
> +				err = mmc_set_signal_voltage(host,
> +					MMC_SIGNAL_VOLTAGE_120, 0);
> +				if (err)
> +					goto err;
> +			}
> +			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
Can you define a new timing mode for HS200 instead of using the same 
high speed
timing mode MMC_TIMING_MMC_HS?  The host driver might need to configure
it's controller with a different timing mode for HS200 cards.
> +			mmc_set_bus_width(card->host, bus_width);
>   		}
>   	}
Thanks,
Sahitya.

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum.
Subhash Jadavani Nov. 17, 2011, 5:36 a.m. UTC | #4
Hi Girish,

> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Sahitya Tummala
> Sent: Tuesday, November 08, 2011 2:10 PM
> To: Girish K S
> Cc: linux-mmc@vger.kernel.org; cjb@laptop.org; patches@linaro.org;
> linux-samsung-soc@vger.kernel.org
> Subject: Re: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC
> 4.5
> 
> Hi Girish,
> 
> On 10/26/2011 10:29 AM, Girish K S wrote:
> > This patch adds the support of the HS200 bus speed for eMMC 4.5
> devices.
> > The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core
> and
> > host modules have been touched to add support for this module.
> >
> > It is necessary to know the card type in the sdhci.c file to add
> support
> > for eMMC tuning function. So card.h file is included to import the
> card
> > data structure.
> >
> > cc: Chris Ball<cjb@laptop.org>
> > Signed-off-by: Girish K S<girish.shivananjappa@linaro.org>
> > ---
> > Changes in v4:
> > 	Rebased onto chris-mmc/mmc-next branch. This patch is
> successfully
> > 	applied on commit with id
> de022ed3fdc14808299b2fa66dbb1ed5ab921912.
> > Changes in v3:
> > 	In the previous commits of chris-mmc/mmc-next branch, the patch
> with
> > 	commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines
> caps2 for
> > 	more capabilities. This patch version deletes the member
> ext_caps(created
> > 	in my earlier patch) from struct mmc_host and reuses already
> accepted
> > 	caps2 member.
> > Changes in v2:
> > 	Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
> > 	problems identified in review. This patch has to be applied
> before
> > 	the patch released for modifying the printk messages.
> > Changes in v1:
> > 	Case statements in switch that produce same result have
> > 	been combined to reduce repeated assignments.
> > 	patch recreated after rebase to chris balls mmc-next branch.
> >
> >   drivers/mmc/core/bus.c    |    3 +-
> >   drivers/mmc/core/mmc.c    |   92
> ++++++++++++++++++++++++++++++++++++++++----
> >   drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
> >   include/linux/mmc/card.h  |    3 +
> >   include/linux/mmc/host.h  |    6 +++
> >   include/linux/mmc/mmc.h   |    8 +++-
> >   include/linux/mmc/sdhci.h |    1 +
> >   7 files changed, 132 insertions(+), 17 deletions(-)
> >
> > diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> > index 46b6e84..2f82f6b 100644
> > --- a/drivers/mmc/core/bus.c
> > +++ b/drivers/mmc/core/bus.c
> > @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
> >   			mmc_card_ddr_mode(card) ? "DDR " : "",
> >   			type);
> >   	} else {
> > -		printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
> > +		pr_info("%s: new %s%s%s%s card at address %04x\n",
> >   			mmc_hostname(card->host),
> >   			mmc_sd_card_uhs(card) ? "ultra high speed " :
> >   			(mmc_card_highspeed(card) ? "high speed " : ""),
> > +			(mmc_card_hs200(card) ? "HS200 " : ""),
> >   			mmc_card_ddr_mode(card) ? "DDR " : "",
> >   			type, card->rca);
> >   	}
> > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> > index 3627044..4db248c 100644
> > --- a/drivers/mmc/core/mmc.c
> > +++ b/drivers/mmc/core/mmc.c
> > @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card
> *card, u8 *ext_csd)
> >   	}
> >   	card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
> >   	switch (ext_csd[EXT_CSD_CARD_TYPE]&  EXT_CSD_CARD_TYPE_MASK) {
> > +	case EXT_CSD_CARD_TYPE_SDR_200 |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +		card->ext_csd.hs_max_dtr = 200000000;
> > +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> > +		break;
> > +	case EXT_CSD_CARD_TYPE_SDR_1_2V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +		card->ext_csd.hs_max_dtr = 200000000;
> > +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> > +		break;
> > +	case EXT_CSD_CARD_TYPE_SDR_1_8V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
> > +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> > +		card->ext_csd.hs_max_dtr = 200000000;
> > +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> > +		break;
> >   	case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
> >   	     EXT_CSD_CARD_TYPE_26:
> >   		card->ext_csd.hs_max_dtr = 52000000;
> > @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> >   {
> >   	struct mmc_card *card;
> >   	int err, ddr = 0;
> > +	int hs_sdr = 0;
> >   	u32 cid[4];
> >   	unsigned int max_dtr;
> >   	u32 rocr;
> > @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> >   	/*
> >   	 * Activate high speed (if supported)
> >   	 */
> > -	if ((card->ext_csd.hs_max_dtr != 0)&&
> > -		(host->caps&  MMC_CAP_MMC_HIGHSPEED)) {
> > -		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> > -				 EXT_CSD_HS_TIMING, 1,
> > -				 card->ext_csd.generic_cmd6_time);
> > +	if (card->ext_csd.hs_max_dtr != 0) {
> > +		if ((card->ext_csd.hs_max_dtr>  52000000)&&
> > +		    (host->caps2&  MMC_CAP2_HIGHSPEED_200))
> > +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> > +					 EXT_CSD_HS_TIMING, 2, 0);
> > +		else if	(host->caps&  MMC_CAP_MMC_HIGHSPEED)
> > +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> > +					 EXT_CSD_HS_TIMING, 1, 0);
> > +
> >   		if (err&&  err != -EBADMSG)
> >   			goto free_card;
> >
> > @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> >   			       mmc_hostname(card->host));
> >   			err = 0;
> >   		} else {
> > -			mmc_card_set_highspeed(card);
> > +			if (card->ext_csd.hs_max_dtr>  52000000)
> > +				mmc_card_set_hs200(card);
> > +			else
> > +				mmc_card_set_highspeed(card);
> >   			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> >   		}
> >   	}
> > @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> >   	 */
> >   	max_dtr = (unsigned int)-1;
> >
> > -	if (mmc_card_highspeed(card)) {
> > +	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
> >   		if (max_dtr>  card->ext_csd.hs_max_dtr)
> >   			max_dtr = card->ext_csd.hs_max_dtr;
> >   	} else if (max_dtr>  card->csd.max_dtr) {
> > @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> >   	}
> >
> >   	/*
> > +	 * Indicate HS200 SDR mode (if supported).
> > +	 */
> > +	if (mmc_card_hs200(card)) {
> > +		if ((card->ext_csd.card_type&  EXT_CSD_CARD_TYPE_SDR_1_8V)
> > +			&&  ((host->caps2&  (MMC_CAP2_HS200_1_8V_SDR |
> > +			     MMC_CAP2_HS200))
> > +				== (MMC_CAP2_HS200_1_8V_SDR |
MMC_CAP2_HS200)))
> > +				hs_sdr = MMC_1_8V_SDR_MODE;
> > +		else if ((card->ext_csd.card_type&
> EXT_CSD_CARD_TYPE_SDR_1_2V)
> > +			&&  ((host->caps2&  (MMC_CAP2_HS200_1_2V_SDR |
> > +			      MMC_CAP2_HS200))
> > +				== (MMC_CAP2_HS200_1_2V_SDR |
MMC_CAP2_HS200)))
> > +				hs_sdr = MMC_1_2V_SDR_MODE;
> > +	}
> > +
> > +	/*
> >   	 * Activate wide bus and DDR (if supported).
> >   	 */
> >   	if ((card->csd.mmca_vsn>= CSD_SPEC_VER_4)&&
> > @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host
> *host, u32 ocr,
> >   			if (!err) {
> >   				mmc_set_bus_width(card->host, bus_width);
> >
> > +				if ((host->caps2&  MMC_CAP2_HS200)&&
> > +				    card->host->ops->execute_tuning)
> > +					err = card->host->ops->	\
> > +					      execute_tuning(card->host);
> > +
> >   				/*
> >   				 * If controller can't handle bus width
test,
> >   				 * compare ext_csd previously read in 1 bit
> mode
> >   				 * against ext_csd at new bus width
> >   				 */
> > -				if (!(host->caps&  MMC_CAP_BUS_WIDTH_TEST))
> > +				if (!(host->caps&  MMC_CAP_BUS_WIDTH_TEST)&&
> > +				    !err)
> >   					err = mmc_compare_ext_csds(card,
> >   						bus_width);
> > -				else
> > +				else if (!err)
> >   					err = mmc_bus_test(card, bus_width);
> > +				else
> > +					pr_warning("tuning execution
failed\n");
> >   				if (!err)
> >   					break;
> >   			}
> > @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host
> *host, u32 ocr,
> >   			mmc_card_set_ddr_mode(card);
> >   			mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
> >   			mmc_set_bus_width(card->host, bus_width);
> > +		} else if (hs_sdr) {
> > +			if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
> > +				err = mmc_set_signal_voltage(host,
> > +					MMC_SIGNAL_VOLTAGE_120, 0);
> > +				if (err)
> > +					goto err;
> > +			}
> > +			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> Can you define a new timing mode for HS200 instead of using the same
> high speed
> timing mode MMC_TIMING_MMC_HS?  The host driver might need to configure
> it's controller with a different timing mode for HS200 cards.
> > +			mmc_set_bus_width(card->host, bus_width);
> >   		}
> >   	}
Any comment on using the new timing mode for HS200?

Regards,
Subhash

> Thanks,
> Sahitya.
> 
> --
> Sent by a consultant of the Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
> Forum.
> 
> 
> --
> 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
Girish K S Nov. 17, 2011, 12:09 p.m. UTC | #5
On 17 November 2011 11:06, Subhash Jadavani <subhashj@codeaurora.org> wrote:
> Hi Girish,
>
>> -----Original Message-----
>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> owner@vger.kernel.org] On Behalf Of Sahitya Tummala
>> Sent: Tuesday, November 08, 2011 2:10 PM
>> To: Girish K S
>> Cc: linux-mmc@vger.kernel.org; cjb@laptop.org; patches@linaro.org;
>> linux-samsung-soc@vger.kernel.org
>> Subject: Re: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC
>> 4.5
>>
>> Hi Girish,
>>
>> On 10/26/2011 10:29 AM, Girish K S wrote:
>> > This patch adds the support of the HS200 bus speed for eMMC 4.5
>> devices.
>> > The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core
>> and
>> > host modules have been touched to add support for this module.
>> >
>> > It is necessary to know the card type in the sdhci.c file to add
>> support
>> > for eMMC tuning function. So card.h file is included to import the
>> card
>> > data structure.
>> >
>> > cc: Chris Ball<cjb@laptop.org>
>> > Signed-off-by: Girish K S<girish.shivananjappa@linaro.org>
>> > ---
>> > Changes in v4:
>> >     Rebased onto chris-mmc/mmc-next branch. This patch is
>> successfully
>> >     applied on commit with id
>> de022ed3fdc14808299b2fa66dbb1ed5ab921912.
>> > Changes in v3:
>> >     In the previous commits of chris-mmc/mmc-next branch, the patch
>> with
>> >     commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines
>> caps2 for
>> >     more capabilities. This patch version deletes the member
>> ext_caps(created
>> >     in my earlier patch) from struct mmc_host and reuses already
>> accepted
>> >     caps2 member.
>> > Changes in v2:
>> >     Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
>> >     problems identified in review. This patch has to be applied
>> before
>> >     the patch released for modifying the printk messages.
>> > Changes in v1:
>> >     Case statements in switch that produce same result have
>> >     been combined to reduce repeated assignments.
>> >     patch recreated after rebase to chris balls mmc-next branch.
>> >
>> >   drivers/mmc/core/bus.c    |    3 +-
>> >   drivers/mmc/core/mmc.c    |   92
>> ++++++++++++++++++++++++++++++++++++++++----
>> >   drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
>> >   include/linux/mmc/card.h  |    3 +
>> >   include/linux/mmc/host.h  |    6 +++
>> >   include/linux/mmc/mmc.h   |    8 +++-
>> >   include/linux/mmc/sdhci.h |    1 +
>> >   7 files changed, 132 insertions(+), 17 deletions(-)
>> >
>> > diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
>> > index 46b6e84..2f82f6b 100644
>> > --- a/drivers/mmc/core/bus.c
>> > +++ b/drivers/mmc/core/bus.c
>> > @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
>> >                     mmc_card_ddr_mode(card) ? "DDR " : "",
>> >                     type);
>> >     } else {
>> > -           printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
>> > +           pr_info("%s: new %s%s%s%s card at address %04x\n",
>> >                     mmc_hostname(card->host),
>> >                     mmc_sd_card_uhs(card) ? "ultra high speed " :
>> >                     (mmc_card_highspeed(card) ? "high speed " : ""),
>> > +                   (mmc_card_hs200(card) ? "HS200 " : ""),
>> >                     mmc_card_ddr_mode(card) ? "DDR " : "",
>> >                     type, card->rca);
>> >     }
>> > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> > index 3627044..4db248c 100644
>> > --- a/drivers/mmc/core/mmc.c
>> > +++ b/drivers/mmc/core/mmc.c
>> > @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card
>> *card, u8 *ext_csd)
>> >     }
>> >     card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
>> >     switch (ext_csd[EXT_CSD_CARD_TYPE]&  EXT_CSD_CARD_TYPE_MASK) {
>> > +   case EXT_CSD_CARD_TYPE_SDR_200 |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +           card->ext_csd.hs_max_dtr = 200000000;
>> > +           card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
>> > +           break;
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_2V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +           card->ext_csd.hs_max_dtr = 200000000;
>> > +           card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
>> > +           break;
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_8V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +   case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
>> > +        EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> > +           card->ext_csd.hs_max_dtr = 200000000;
>> > +           card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
>> > +           break;
>> >     case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
>> >          EXT_CSD_CARD_TYPE_26:
>> >             card->ext_csd.hs_max_dtr = 52000000;
>> > @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> >   {
>> >     struct mmc_card *card;
>> >     int err, ddr = 0;
>> > +   int hs_sdr = 0;
>> >     u32 cid[4];
>> >     unsigned int max_dtr;
>> >     u32 rocr;
>> > @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> >     /*
>> >      * Activate high speed (if supported)
>> >      */
>> > -   if ((card->ext_csd.hs_max_dtr != 0)&&
>> > -           (host->caps&  MMC_CAP_MMC_HIGHSPEED)) {
>> > -           err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> > -                            EXT_CSD_HS_TIMING, 1,
>> > -                            card->ext_csd.generic_cmd6_time);
>> > +   if (card->ext_csd.hs_max_dtr != 0) {
>> > +           if ((card->ext_csd.hs_max_dtr>  52000000)&&
>> > +               (host->caps2&  MMC_CAP2_HIGHSPEED_200))
>> > +                   err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> > +                                    EXT_CSD_HS_TIMING, 2, 0);
>> > +           else if (host->caps&  MMC_CAP_MMC_HIGHSPEED)
>> > +                   err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> > +                                    EXT_CSD_HS_TIMING, 1, 0);
>> > +
>> >             if (err&&  err != -EBADMSG)
>> >                     goto free_card;
>> >
>> > @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> >                            mmc_hostname(card->host));
>> >                     err = 0;
>> >             } else {
>> > -                   mmc_card_set_highspeed(card);
>> > +                   if (card->ext_csd.hs_max_dtr>  52000000)
>> > +                           mmc_card_set_hs200(card);
>> > +                   else
>> > +                           mmc_card_set_highspeed(card);
>> >                     mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> >             }
>> >     }
>> > @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> >      */
>> >     max_dtr = (unsigned int)-1;
>> >
>> > -   if (mmc_card_highspeed(card)) {
>> > +   if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
>> >             if (max_dtr>  card->ext_csd.hs_max_dtr)
>> >                     max_dtr = card->ext_csd.hs_max_dtr;
>> >     } else if (max_dtr>  card->csd.max_dtr) {
>> > @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> >     }
>> >
>> >     /*
>> > +    * Indicate HS200 SDR mode (if supported).
>> > +    */
>> > +   if (mmc_card_hs200(card)) {
>> > +           if ((card->ext_csd.card_type&  EXT_CSD_CARD_TYPE_SDR_1_8V)
>> > +                   &&  ((host->caps2&  (MMC_CAP2_HS200_1_8V_SDR |
>> > +                        MMC_CAP2_HS200))
>> > +                           == (MMC_CAP2_HS200_1_8V_SDR |
> MMC_CAP2_HS200)))
>> > +                           hs_sdr = MMC_1_8V_SDR_MODE;
>> > +           else if ((card->ext_csd.card_type&
>> EXT_CSD_CARD_TYPE_SDR_1_2V)
>> > +                   &&  ((host->caps2&  (MMC_CAP2_HS200_1_2V_SDR |
>> > +                         MMC_CAP2_HS200))
>> > +                           == (MMC_CAP2_HS200_1_2V_SDR |
> MMC_CAP2_HS200)))
>> > +                           hs_sdr = MMC_1_2V_SDR_MODE;
>> > +   }
>> > +
>> > +   /*
>> >      * Activate wide bus and DDR (if supported).
>> >      */
>> >     if ((card->csd.mmca_vsn>= CSD_SPEC_VER_4)&&
>> > @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host
>> *host, u32 ocr,
>> >                     if (!err) {
>> >                             mmc_set_bus_width(card->host, bus_width);
>> >
>> > +                           if ((host->caps2&  MMC_CAP2_HS200)&&
>> > +                               card->host->ops->execute_tuning)
>> > +                                   err = card->host->ops-> \
>> > +                                         execute_tuning(card->host);
>> > +
>> >                             /*
>> >                              * If controller can't handle bus width
> test,
>> >                              * compare ext_csd previously read in 1 bit
>> mode
>> >                              * against ext_csd at new bus width
>> >                              */
>> > -                           if (!(host->caps&  MMC_CAP_BUS_WIDTH_TEST))
>> > +                           if (!(host->caps&  MMC_CAP_BUS_WIDTH_TEST)&&
>> > +                               !err)
>> >                                     err = mmc_compare_ext_csds(card,
>> >                                             bus_width);
>> > -                           else
>> > +                           else if (!err)
>> >                                     err = mmc_bus_test(card, bus_width);
>> > +                           else
>> > +                                   pr_warning("tuning execution
> failed\n");
>> >                             if (!err)
>> >                                     break;
>> >                     }
>> > @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host
>> *host, u32 ocr,
>> >                     mmc_card_set_ddr_mode(card);
>> >                     mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
>> >                     mmc_set_bus_width(card->host, bus_width);
>> > +           } else if (hs_sdr) {
>> > +                   if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
>> > +                           err = mmc_set_signal_voltage(host,
>> > +                                   MMC_SIGNAL_VOLTAGE_120, 0);
>> > +                           if (err)
>> > +                                   goto err;
>> > +                   }
>> > +                   mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> Can you define a new timing mode for HS200 instead of using the same
>> high speed
>> timing mode MMC_TIMING_MMC_HS?  The host driver might need to configure
>> it's controller with a different timing mode for HS200 cards.
>> > +                   mmc_set_bus_width(card->host, bus_width);
>> >             }
>> >     }
> Any comment on using the new timing mode for HS200?
> Will look into it tomorrow and let you know
> Regards,
> Subhash
>
>> Thanks,
>> Sahitya.
>>
>> --
>> Sent by a consultant of the Qualcomm Innovation Center, Inc.
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
>> Forum.
>>
>>
>> --
>> 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
>
>
Subhash Jadavani Nov. 18, 2011, 6:59 p.m. UTC | #6
Hi Girish,

> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Girish K S
> Sent: Wednesday, October 26, 2011 10:30 AM
> To: linux-mmc@vger.kernel.org
> Cc: cjb@laptop.org; patches@linaro.org; linux-samsung-
> soc@vger.kernel.org; Girish K S
> Subject: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC 4.5
> 
> This patch adds the support of the HS200 bus speed for eMMC 4.5
> devices.
> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core and
> host modules have been touched to add support for this module.
> 
> It is necessary to know the card type in the sdhci.c file to add
> support
> for eMMC tuning function. So card.h file is included to import the card
> data structure.

We don't need to know the card type in the host controller driver if we pass
the command opcode as the 2nd argument to execute_tuning() ops. Command
opcode can have have either CMD19 or CMD21 as it's value depending on the
card type (whether it's SD3.0 card or eMMC4.5 card).


> 
> cc: Chris Ball <cjb@laptop.org>
> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> ---
> Changes in v4:
> 	Rebased onto chris-mmc/mmc-next branch. This patch is
> successfully
> 	applied on commit with id
> de022ed3fdc14808299b2fa66dbb1ed5ab921912.
> Changes in v3:
> 	In the previous commits of chris-mmc/mmc-next branch, the patch
> with
> 	commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines
> caps2 for
> 	more capabilities. This patch version deletes the member
> ext_caps(created
> 	in my earlier patch) from struct mmc_host and reuses already
> accepted
> 	caps2 member.
> Changes in v2:
> 	Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
> 	problems identified in review. This patch has to be applied
> before
> 	the patch released for modifying the printk messages.
> Changes in v1:
> 	Case statements in switch that produce same result have
> 	been combined to reduce repeated assignments.
> 	patch recreated after rebase to chris balls mmc-next branch.
> 
>  drivers/mmc/core/bus.c    |    3 +-
>  drivers/mmc/core/mmc.c    |   92
> ++++++++++++++++++++++++++++++++++++++++----
>  drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
>  include/linux/mmc/card.h  |    3 +
>  include/linux/mmc/host.h  |    6 +++
>  include/linux/mmc/mmc.h   |    8 +++-
>  include/linux/mmc/sdhci.h |    1 +
>  7 files changed, 132 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index 46b6e84..2f82f6b 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
>  			mmc_card_ddr_mode(card) ? "DDR " : "",
>  			type);
>  	} else {
> -		printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
> +		pr_info("%s: new %s%s%s%s card at address %04x\n",
>  			mmc_hostname(card->host),
>  			mmc_sd_card_uhs(card) ? "ultra high speed " :
>  			(mmc_card_highspeed(card) ? "high speed " : ""),
> +			(mmc_card_hs200(card) ? "HS200 " : ""),
>  			mmc_card_ddr_mode(card) ? "DDR " : "",
>  			type, card->rca);
>  	}
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 3627044..4db248c 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card *card,
> u8 *ext_csd)
>  	}
>  	card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
>  	switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
> +	case EXT_CSD_CARD_TYPE_SDR_200 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +		card->ext_csd.hs_max_dtr = 200000000;
> +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> +		break;
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +		card->ext_csd.hs_max_dtr = 200000000;
> +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> +		break;
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
> +	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> +		card->ext_csd.hs_max_dtr = 200000000;
> +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> +		break;

Can't we optimize this? This looks like too many case statements.

Basically we can have checks like this:

temp = ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK;
if ((temp & EXT_CSD_CARD_TYPE_SDR_1_2V == EXT_CSD_CARD_TYPE_SDR_1_2V) ||
	(temp & EXT_CSD_CARD_TYPE_SDR_1_8V == EXT_CSD_CARD_TYPE_SDR_1_8V)) {
	card->ext_csd.hs_max_dtr = 200000000;
	card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V; /* we can have
some logic to identify the card type correctly */
}


>  	case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
>  	     EXT_CSD_CARD_TYPE_26:
>  		card->ext_csd.hs_max_dtr = 52000000;
> @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host, u32
> ocr,
>  {
>  	struct mmc_card *card;
>  	int err, ddr = 0;
> +	int hs_sdr = 0;
>  	u32 cid[4];
>  	unsigned int max_dtr;
>  	u32 rocr;
> @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
>  	/*
>  	 * Activate high speed (if supported)
>  	 */
> -	if ((card->ext_csd.hs_max_dtr != 0) &&
> -		(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
> -		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> -				 EXT_CSD_HS_TIMING, 1,
> -				 card->ext_csd.generic_cmd6_time);
> +	if (card->ext_csd.hs_max_dtr != 0) {

We should enter into this if condition only if one of the this cap
(MMC_CAP2_HIGHSPEED_200 or MMC_CAP_MMC_HIGHSPEED) is set.

> +		if ((card->ext_csd.hs_max_dtr > 52000000) &&
> +		    (host->caps2 & MMC_CAP2_HIGHSPEED_200))
> +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +					 EXT_CSD_HS_TIMING, 2, 0);
> +		else if	(host->caps & MMC_CAP_MMC_HIGHSPEED)
> +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +					 EXT_CSD_HS_TIMING, 1, 0);
> +
>  		if (err && err != -EBADMSG)
>  			goto free_card;
> 
> @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
>  			       mmc_hostname(card->host));
>  			err = 0;
>  		} else {
> -			mmc_card_set_highspeed(card);
> +			if (card->ext_csd.hs_max_dtr > 52000000)

I think there should be AND condition with (host->caps2 &
MMC_CAP2_HIGHSPEED_200) for setting the card as HS200.

> +				mmc_card_set_hs200(card);
> +			else
> +				mmc_card_set_highspeed(card);
>  			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);

As mentioned earlier, there should be new timing mode defined for HS200.

>  		}
>  	}
> @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host, u32
> ocr,
>  	 */
>  	max_dtr = (unsigned int)-1;
> 
> -	if (mmc_card_highspeed(card)) {
> +	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
>  		if (max_dtr > card->ext_csd.hs_max_dtr)
>  			max_dtr = card->ext_csd.hs_max_dtr;
>  	} else if (max_dtr > card->csd.max_dtr) {
> @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
>  	}
> 
>  	/*
> +	 * Indicate HS200 SDR mode (if supported).
> +	 */
> +	if (mmc_card_hs200(card)) {
> +		if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V)
> +			&& ((host->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
> +			     MMC_CAP2_HS200))
> +				== (MMC_CAP2_HS200_1_8V_SDR |
MMC_CAP2_HS200)))
> +				hs_sdr = MMC_1_8V_SDR_MODE;
> +		else if ((card->ext_csd.card_type &
> EXT_CSD_CARD_TYPE_SDR_1_2V)
> +			 && ((host->caps2 & (MMC_CAP2_HS200_1_2V_SDR |
> +			      MMC_CAP2_HS200))
> +				== (MMC_CAP2_HS200_1_2V_SDR |
MMC_CAP2_HS200)))
> +				hs_sdr = MMC_1_2V_SDR_MODE;
> +	}
> +
> +	/*
>  	 * Activate wide bus and DDR (if supported).
>  	 */
>  	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
> @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
>  			if (!err) {
>  				mmc_set_bus_width(card->host, bus_width);
> 
> +				if ((host->caps2 & MMC_CAP2_HS200) &&
> +				    card->host->ops->execute_tuning)
> +					err = card->host->ops->	\
> +					      execute_tuning(card->host);

execute_tuning() should be executed immediately after the TIMING is changed
to HS200 and clock speed is changed to 200MHz. But here timing mode and
clock rate is changed and then you are also sending bus width change
command. As host controller timing tuning has not completed, this bus width
change command may fail with CMD/DATA CRC errors.

So basically this should be the sequence.
1. set the bus width depending on the host controller and card capability
2. If card support HS200 timing, change the timing of the card by sending
SWITCH command. Change the clock rate. Change the timing mode of controller.
3. Now call the host controller execute_tuning ops to perform the controller
sampling point tuning.

Also, let's pass the command opcode as the 2nd argument of the
execute_tuning() function. Which means host controller driver need not to
know the card type.

> +
>  				/*
>  				 * If controller can't handle bus width
test,
>  				 * compare ext_csd previously read in 1 bit
> mode
>  				 * against ext_csd at new bus width
>  				 */
> -				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
> +				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)
&&
> +				    !err)
>  					err = mmc_compare_ext_csds(card,
>  						bus_width);
> -				else
> +				else if (!err)
>  					err = mmc_bus_test(card, bus_width);
> +				else
> +					pr_warning("tuning execution
failed\n");
>  				if (!err)
>  					break;
>  			}
> @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
>  			mmc_card_set_ddr_mode(card);
>  			mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
>  			mmc_set_bus_width(card->host, bus_width);
> +		} else if (hs_sdr) {
> +			if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
> +				err = mmc_set_signal_voltage(host,
> +					MMC_SIGNAL_VOLTAGE_120, 0);
> +				if (err)
> +					goto err;
> +			}
> +			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> +			mmc_set_bus_width(card->host, bus_width);
>  		}
>  	}
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index a69d269..28fb747 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -27,6 +27,7 @@
> 
>  #include <linux/mmc/mmc.h>
>  #include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
> 
>  #include "sdhci.h"
> 
> @@ -1016,7 +1017,8 @@ static void sdhci_send_command(struct sdhci_host
> *host, struct mmc_command *cmd)
>  		flags |= SDHCI_CMD_INDEX;
> 
>  	/* CMD19 is special in that the Data Present Select should be set
> */
> -	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
> +	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
> +	    (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
>  		flags |= SDHCI_CMD_DATA;
> 
>  	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
> SDHCI_COMMAND);
> @@ -1684,10 +1686,13 @@ static int sdhci_execute_tuning(struct mmc_host
> *mmc)
>  	 * Host Controller needs tuning only in case of SDR104 mode
>  	 * and for SDR50 mode when Use Tuning for SDR50 is set in
>  	 * Capabilities register.
> +	 * If the Host Controller supports the HS200 mode then tuning
> +	 * function has to be executed.
>  	 */
>  	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
>  	    (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> -	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> +	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
> +	    (host->flags & SDHCI_HS200_NEEDS_TUNING))
>  		ctrl |= SDHCI_CTRL_EXEC_TUNING;
>  	else {
>  		spin_unlock(&host->lock);
> @@ -1723,7 +1728,10 @@ static int sdhci_execute_tuning(struct mmc_host
> *mmc)
>  		if (!tuning_loop_counter && !timeout)
>  			break;
> 
> -		cmd.opcode = MMC_SEND_TUNING_BLOCK;
> +		if (mmc->card->type == MMC_TYPE_MMC)
> +			cmd.opcode = MMC_SEND_TUNING_BLOCK_HS200;
> +		else
> +			cmd.opcode = MMC_SEND_TUNING_BLOCK;
>  		cmd.arg = 0;
>  		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
>  		cmd.retries = 0;
> @@ -1738,7 +1746,17 @@ static int sdhci_execute_tuning(struct mmc_host
> *mmc)
>  		 * block to the Host Controller. So we set the block size
>  		 * to 64 here.
>  		 */
> -		sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> SDHCI_BLOCK_SIZE);
> +		if (mmc->card->type == MMC_TYPE_MMC) {
> +			if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
> +				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
> +					     SDHCI_BLOCK_SIZE);
> +			else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
> +				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> +					     SDHCI_BLOCK_SIZE);
> +		} else {
> +			sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> +				     SDHCI_BLOCK_SIZE);
> +		}
> 
>  		/*
>  		 * The tuning block is sent by the card to the host
> controller.
> @@ -2121,12 +2139,14 @@ static void sdhci_show_adma_error(struct
> sdhci_host *host) { }
> 
>  static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>  {
> +	u32 command;
>  	BUG_ON(intmask == 0);
> 
>  	/* CMD19 generates _only_ Buffer Read Ready interrupt */
>  	if (intmask & SDHCI_INT_DATA_AVAIL) {
> -		if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
> -		    MMC_SEND_TUNING_BLOCK) {
> +		command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
> +		if ((command == MMC_SEND_TUNING_BLOCK) ||
> +		    (command == MMC_SEND_TUNING_BLOCK_HS200)) {
>  			host->tuning_done = 1;
>  			wake_up(&host->buf_ready_int);
>  			return;
> @@ -2731,6 +2751,10 @@ int sdhci_add_host(struct sdhci_host *host)
>  	if (caps[1] & SDHCI_USE_SDR50_TUNING)
>  		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
> 
> +	/* Does the host needs tuning for HS200? */
> +	if (mmc->caps2 & MMC_CAP2_HS200)
> +		host->flags |= SDHCI_HS200_NEEDS_TUNING;
> +
>  	/* Driver Type(s) (A, C, D) supported by the host */
>  	if (caps[1] & SDHCI_DRIVER_TYPE_A)
>  		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 6e04e10..90af438 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -206,6 +206,7 @@ struct mmc_card {
>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)		/* card is in high
> speed mode */
>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)		/* card is in ultra
> high speed mode */
>  #define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
> +#define MMC_STATE_HIGHSPEED_200	(1<<7)	/* card is in HS200
> mode */
>  	unsigned int		quirks; 	/* card quirks */
>  #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0
> writes outside of the VS CCCR range */
>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func-
> >cur_blksize */
> @@ -361,6 +362,7 @@ static inline void __maybe_unused
> remove_quirk(struct mmc_card *card, int data)
>  #define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
>  #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
>  #define mmc_card_highspeed(c)	((c)->state & MMC_STATE_HIGHSPEED)
> +#define mmc_card_hs200(c)	((c)->state & MMC_STATE_HIGHSPEED_200)
>  #define mmc_card_blockaddr(c)	((c)->state & MMC_STATE_BLOCKADDR)
>  #define mmc_card_ddr_mode(c)	((c)->state & MMC_STATE_HIGHSPEED_DDR)
>  #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
> @@ -369,6 +371,7 @@ static inline void __maybe_unused
> remove_quirk(struct mmc_card *card, int data)
>  #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>  #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
> +#define mmc_card_set_hs200(c)	((c)->state |=
> MMC_STATE_HIGHSPEED_200)
>  #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
>  #define mmc_card_set_ddr_mode(c) ((c)->state |=
> MMC_STATE_HIGHSPEED_DDR)
>  #define mmc_sd_card_set_uhs(c) ((c)->state |=
> MMC_STATE_ULTRAHIGHSPEED)
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index a3ac9c4..20551f5 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -60,6 +60,8 @@ struct mmc_ios {
>  #define MMC_SDR_MODE		0
>  #define MMC_1_2V_DDR_MODE	1
>  #define MMC_1_8V_DDR_MODE	2
> +#define MMC_1_2V_SDR_MODE	3
> +#define MMC_1_8V_SDR_MODE	4
> 
>  	unsigned char	signal_voltage;		/* signalling voltage
> (1.8V or 3.3V) */
> 
> @@ -242,6 +244,10 @@ struct mmc_host {
>  #define MMC_CAP2_CACHE_CTRL	(1 << 1)	/* Allow cache control */
>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)	/* Notify poweroff
> supported */
>  #define MMC_CAP2_NO_MULTI_READ	(1 << 3)	/* Multiblock reads
> don't work */
> +#define MMC_CAP2_HS200		(1 << 4)	/* Host supports
HS200 mode
> */

Why is this cap required? You have already defined this 2 caps
MMC_CAP2_HS200_1_8V_SDR & MMC_CAP2_HS200_1_2V_SDR which should be enough.

> +#define MMC_CAP2_HS200_1_8V_SDR	(1 << 5)	/* can support */
> +#define MMC_CAP2_HS200_1_2V_SDR	(1 << 6)	/* can support */
> +#define MMC_CAP2_HIGHSPEED_200	(1 << 7)	/* Can do MMC HS200
Why is this is this cap required?

> timing */
> 
>  	mmc_pm_flag_t		pm_caps;	/* supported pm features */
>  	unsigned int        power_notify_type;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 0e71356..af4fa72 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -51,6 +51,7 @@
>  #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1
> */
>  #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1
> */
>  #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1
> */
> +#define MMC_SEND_TUNING_BLOCK_HS200	21	/* adtc R1  */
> 
>    /* class 3 */
>  #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1
> */
> @@ -333,13 +334,18 @@ struct _mmc_csd {
> 
>  #define EXT_CSD_CARD_TYPE_26	(1<<0)	/* Card can run at 26MHz */
>  #define EXT_CSD_CARD_TYPE_52	(1<<1)	/* Card can run at 52MHz */
> -#define EXT_CSD_CARD_TYPE_MASK	0xF	/* Mask out reserved bits */
> +#define EXT_CSD_CARD_TYPE_MASK	0x3F	/* Mask out reserved bits */
>  #define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at 52MHz
> */
>  					     /* DDR mode @1.8V or 3V I/O */
>  #define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at 52MHz
> */
>  					     /* DDR mode @1.2V I/O */
>  #define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V  \
>  					| EXT_CSD_CARD_TYPE_DDR_1_2V)
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V	(1<<4)	/* Card can run at
> 200MHz */
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V	(1<<5)	/* Card can run at
> 200MHz */
> +						/* SDR mode @1.2V I/O */
> +#define EXT_CSD_CARD_TYPE_SDR_200	(EXT_CSD_CARD_TYPE_SDR_1_8V	\
> +					| EXT_CSD_CARD_TYPE_SDR_1_2V)
> 
>  #define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
>  #define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index e4b6935..d9a2222 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -121,6 +121,7 @@ struct sdhci_host {
>  #define SDHCI_AUTO_CMD23	(1<<7)	/* Auto CMD23 support */
>  #define SDHCI_PV_ENABLED	(1<<8)	/* Preset value enabled */
>  #define SDHCI_SDIO_IRQ_ENABLED	(1<<9)	/* SDIO irq enabled */
> +#define SDHCI_HS200_NEEDS_TUNING (1<<10)	/* HS200 needs tuning */
> 
>  	unsigned int version;	/* SDHCI spec. version */
> 
> --
> 1.7.1
> 
> --
> 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
Girish K S Nov. 21, 2011, 10:41 a.m. UTC | #7
On 19 November 2011 00:29, Subhash Jadavani <subhashj@codeaurora.org> wrote:
> Hi Girish,
>
>> -----Original Message-----
>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> owner@vger.kernel.org] On Behalf Of Girish K S
>> Sent: Wednesday, October 26, 2011 10:30 AM
>> To: linux-mmc@vger.kernel.org
>> Cc: cjb@laptop.org; patches@linaro.org; linux-samsung-
>> soc@vger.kernel.org; Girish K S
>> Subject: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC 4.5
>>
>> This patch adds the support of the HS200 bus speed for eMMC 4.5
>> devices.
>> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core and
>> host modules have been touched to add support for this module.
>>
>> It is necessary to know the card type in the sdhci.c file to add
>> support
>> for eMMC tuning function. So card.h file is included to import the card
>> data structure.
>
> We don't need to know the card type in the host controller driver if we pass
> the command opcode as the 2nd argument to execute_tuning() ops. Command
> opcode can have have either CMD19 or CMD21 as it's value depending on the
> card type (whether it's SD3.0 card or eMMC4.5 card).
>
>
>>
>> cc: Chris Ball <cjb@laptop.org>
>> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
>> ---
>> Changes in v4:
>>       Rebased onto chris-mmc/mmc-next branch. This patch is
>> successfully
>>       applied on commit with id
>> de022ed3fdc14808299b2fa66dbb1ed5ab921912.
>> Changes in v3:
>>       In the previous commits of chris-mmc/mmc-next branch, the patch
>> with
>>       commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines
>> caps2 for
>>       more capabilities. This patch version deletes the member
>> ext_caps(created
>>       in my earlier patch) from struct mmc_host and reuses already
>> accepted
>>       caps2 member.
>> Changes in v2:
>>       Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
>>       problems identified in review. This patch has to be applied
>> before
>>       the patch released for modifying the printk messages.
>> Changes in v1:
>>       Case statements in switch that produce same result have
>>       been combined to reduce repeated assignments.
>>       patch recreated after rebase to chris balls mmc-next branch.
>>
>>  drivers/mmc/core/bus.c    |    3 +-
>>  drivers/mmc/core/mmc.c    |   92
>> ++++++++++++++++++++++++++++++++++++++++----
>>  drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
>>  include/linux/mmc/card.h  |    3 +
>>  include/linux/mmc/host.h  |    6 +++
>>  include/linux/mmc/mmc.h   |    8 +++-
>>  include/linux/mmc/sdhci.h |    1 +
>>  7 files changed, 132 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
>> index 46b6e84..2f82f6b 100644
>> --- a/drivers/mmc/core/bus.c
>> +++ b/drivers/mmc/core/bus.c
>> @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
>>                       mmc_card_ddr_mode(card) ? "DDR " : "",
>>                       type);
>>       } else {
>> -             printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
>> +             pr_info("%s: new %s%s%s%s card at address %04x\n",
>>                       mmc_hostname(card->host),
>>                       mmc_sd_card_uhs(card) ? "ultra high speed " :
>>                       (mmc_card_highspeed(card) ? "high speed " : ""),
>> +                     (mmc_card_hs200(card) ? "HS200 " : ""),
>>                       mmc_card_ddr_mode(card) ? "DDR " : "",
>>                       type, card->rca);
>>       }
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 3627044..4db248c 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card *card,
>> u8 *ext_csd)
>>       }
>>       card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
>>       switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
>> +     case EXT_CSD_CARD_TYPE_SDR_200 |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +             card->ext_csd.hs_max_dtr = 200000000;
>> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
>> +             break;
>> +     case EXT_CSD_CARD_TYPE_SDR_1_2V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +             card->ext_csd.hs_max_dtr = 200000000;
>> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
>> +             break;
>> +     case EXT_CSD_CARD_TYPE_SDR_1_8V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
>> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> +             card->ext_csd.hs_max_dtr = 200000000;
>> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
>> +             break;
>
> Can't we optimize this? This looks like too many case statements.
>
> Basically we can have checks like this:
>
> temp = ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK;
> if ((temp & EXT_CSD_CARD_TYPE_SDR_1_2V == EXT_CSD_CARD_TYPE_SDR_1_2V) ||
>        (temp & EXT_CSD_CARD_TYPE_SDR_1_8V == EXT_CSD_CARD_TYPE_SDR_1_8V)) {
>        card->ext_csd.hs_max_dtr = 200000000;
>        card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V; /* we can have
> some logic to identify the card type correctly */
> }
>
>
>>       case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
>>            EXT_CSD_CARD_TYPE_26:
>>               card->ext_csd.hs_max_dtr = 52000000;
>> @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host, u32
>> ocr,
>>  {
>>       struct mmc_card *card;
>>       int err, ddr = 0;
>> +     int hs_sdr = 0;
>>       u32 cid[4];
>>       unsigned int max_dtr;
>>       u32 rocr;
>> @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>       /*
>>        * Activate high speed (if supported)
>>        */
>> -     if ((card->ext_csd.hs_max_dtr != 0) &&
>> -             (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
>> -             err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> -                              EXT_CSD_HS_TIMING, 1,
>> -                              card->ext_csd.generic_cmd6_time);
>> +     if (card->ext_csd.hs_max_dtr != 0) {
>
> We should enter into this if condition only if one of the this cap
> (MMC_CAP2_HIGHSPEED_200 or MMC_CAP_MMC_HIGHSPEED) is set.
card->ext_csd.hs_max_dtr is non zero for high speed only i.e for
frequency above 26Mhz. For anything less than 26 Mhz  this if
condition is skipped.
So its just enough if we check for less than 52 MHz and >= 52 Mhz.
>
>> +             if ((card->ext_csd.hs_max_dtr > 52000000) &&
>> +                 (host->caps2 & MMC_CAP2_HIGHSPEED_200))
>> +                     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                                      EXT_CSD_HS_TIMING, 2, 0);
>> +             else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
>> +                     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                                      EXT_CSD_HS_TIMING, 1, 0);
>> +
>>               if (err && err != -EBADMSG)
>>                       goto free_card;
>>
>> @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>                              mmc_hostname(card->host));
>>                       err = 0;
>>               } else {
>> -                     mmc_card_set_highspeed(card);
>> +                     if (card->ext_csd.hs_max_dtr > 52000000)
>
> I think there should be AND condition with (host->caps2 &
> MMC_CAP2_HIGHSPEED_200) for setting the card as HS200.
Ok will add it.
>
>> +                             mmc_card_set_hs200(card);
>> +                     else
>> +                             mmc_card_set_highspeed(card);
>>                       mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>
> As mentioned earlier, there should be new timing mode defined for HS200.
can you share Host controller specification will check it.
>
>>               }
>>       }
>> @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host, u32
>> ocr,
>>        */
>>       max_dtr = (unsigned int)-1;
>>
>> -     if (mmc_card_highspeed(card)) {
>> +     if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
>>               if (max_dtr > card->ext_csd.hs_max_dtr)
>>                       max_dtr = card->ext_csd.hs_max_dtr;
>>       } else if (max_dtr > card->csd.max_dtr) {
>> @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>       }
>>
>>       /*
>> +      * Indicate HS200 SDR mode (if supported).
>> +      */
>> +     if (mmc_card_hs200(card)) {
>> +             if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V)
>> +                     && ((host->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
>> +                          MMC_CAP2_HS200))
>> +                             == (MMC_CAP2_HS200_1_8V_SDR |
> MMC_CAP2_HS200)))
>> +                             hs_sdr = MMC_1_8V_SDR_MODE;
>> +             else if ((card->ext_csd.card_type &
>> EXT_CSD_CARD_TYPE_SDR_1_2V)
>> +                      && ((host->caps2 & (MMC_CAP2_HS200_1_2V_SDR |
>> +                           MMC_CAP2_HS200))
>> +                             == (MMC_CAP2_HS200_1_2V_SDR |
> MMC_CAP2_HS200)))
>> +                             hs_sdr = MMC_1_2V_SDR_MODE;
>> +     }
>> +
>> +     /*
>>        * Activate wide bus and DDR (if supported).
>>        */
>>       if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
>> @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>                       if (!err) {
>>                               mmc_set_bus_width(card->host, bus_width);
>>
>> +                             if ((host->caps2 & MMC_CAP2_HS200) &&
>> +                                 card->host->ops->execute_tuning)
>> +                                     err = card->host->ops-> \
>> +                                           execute_tuning(card->host);
>
> execute_tuning() should be executed immediately after the TIMING is changed
> to HS200 and clock speed is changed to 200MHz. But here timing mode and
> clock rate is changed and then you are also sending bus width change
> command. As host controller timing tuning has not completed, this bus width
> change command may fail with CMD/DATA CRC errors.
> pls check the eMMC specification section 6.6.4  it says
"Note that the host should switch to the required bus width before
starting the tuning operation to
allow the tuning sequence to be done using the proper bus operating conditions.
"
> So basically this should be the sequence.
> 1. set the bus width depending on the host controller and card capability
> 2. If card support HS200 timing, change the timing of the card by sending
> SWITCH command. Change the clock rate. Change the timing mode of controller.
> 3. Now call the host controller execute_tuning ops to perform the controller
> sampling point tuning.
>
> Also, let's pass the command opcode as the 2nd argument of the
> execute_tuning() function. Which means host controller driver need not to
> know the card type.
>
>> +
>>                               /*
>>                                * If controller can't handle bus width
> test,
>>                                * compare ext_csd previously read in 1 bit
>> mode
>>                                * against ext_csd at new bus width
>>                                */
>> -                             if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
>> +                             if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)
> &&
>> +                                 !err)
>>                                       err = mmc_compare_ext_csds(card,
>>                                               bus_width);
>> -                             else
>> +                             else if (!err)
>>                                       err = mmc_bus_test(card, bus_width);
>> +                             else
>> +                                     pr_warning("tuning execution
> failed\n");
>>                               if (!err)
>>                                       break;
>>                       }
>> @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>>                       mmc_card_set_ddr_mode(card);
>>                       mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
>>                       mmc_set_bus_width(card->host, bus_width);
>> +             } else if (hs_sdr) {
>> +                     if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
>> +                             err = mmc_set_signal_voltage(host,
>> +                                     MMC_SIGNAL_VOLTAGE_120, 0);
>> +                             if (err)
>> +                                     goto err;
>> +                     }
>> +                     mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> +                     mmc_set_bus_width(card->host, bus_width);
>>               }
>>       }
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index a69d269..28fb747 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -27,6 +27,7 @@
>>
>>  #include <linux/mmc/mmc.h>
>>  #include <linux/mmc/host.h>
>> +#include <linux/mmc/card.h>
>>
>>  #include "sdhci.h"
>>
>> @@ -1016,7 +1017,8 @@ static void sdhci_send_command(struct sdhci_host
>> *host, struct mmc_command *cmd)
>>               flags |= SDHCI_CMD_INDEX;
>>
>>       /* CMD19 is special in that the Data Present Select should be set
>> */
>> -     if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
>> +     if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
>> +         (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
>>               flags |= SDHCI_CMD_DATA;
>>
>>       sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
>> SDHCI_COMMAND);
>> @@ -1684,10 +1686,13 @@ static int sdhci_execute_tuning(struct mmc_host
>> *mmc)
>>        * Host Controller needs tuning only in case of SDR104 mode
>>        * and for SDR50 mode when Use Tuning for SDR50 is set in
>>        * Capabilities register.
>> +      * If the Host Controller supports the HS200 mode then tuning
>> +      * function has to be executed.
>>        */
>>       if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
>>           (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
>> -         (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
>> +         (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
>> +         (host->flags & SDHCI_HS200_NEEDS_TUNING))
>>               ctrl |= SDHCI_CTRL_EXEC_TUNING;
>>       else {
>>               spin_unlock(&host->lock);
>> @@ -1723,7 +1728,10 @@ static int sdhci_execute_tuning(struct mmc_host
>> *mmc)
>>               if (!tuning_loop_counter && !timeout)
>>                       break;
>>
>> -             cmd.opcode = MMC_SEND_TUNING_BLOCK;
>> +             if (mmc->card->type == MMC_TYPE_MMC)
>> +                     cmd.opcode = MMC_SEND_TUNING_BLOCK_HS200;
>> +             else
>> +                     cmd.opcode = MMC_SEND_TUNING_BLOCK;
>>               cmd.arg = 0;
>>               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
>>               cmd.retries = 0;
>> @@ -1738,7 +1746,17 @@ static int sdhci_execute_tuning(struct mmc_host
>> *mmc)
>>                * block to the Host Controller. So we set the block size
>>                * to 64 here.
>>                */
>> -             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> SDHCI_BLOCK_SIZE);
>> +             if (mmc->card->type == MMC_TYPE_MMC) {
>> +                     if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
>> +                             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
>> +                                          SDHCI_BLOCK_SIZE);
>> +                     else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
>> +                             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> +                                          SDHCI_BLOCK_SIZE);
>> +             } else {
>> +                     sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> +                                  SDHCI_BLOCK_SIZE);
>> +             }
>>
>>               /*
>>                * The tuning block is sent by the card to the host
>> controller.
>> @@ -2121,12 +2139,14 @@ static void sdhci_show_adma_error(struct
>> sdhci_host *host) { }
>>
>>  static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>>  {
>> +     u32 command;
>>       BUG_ON(intmask == 0);
>>
>>       /* CMD19 generates _only_ Buffer Read Ready interrupt */
>>       if (intmask & SDHCI_INT_DATA_AVAIL) {
>> -             if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
>> -                 MMC_SEND_TUNING_BLOCK) {
>> +             command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
>> +             if ((command == MMC_SEND_TUNING_BLOCK) ||
>> +                 (command == MMC_SEND_TUNING_BLOCK_HS200)) {
>>                       host->tuning_done = 1;
>>                       wake_up(&host->buf_ready_int);
>>                       return;
>> @@ -2731,6 +2751,10 @@ int sdhci_add_host(struct sdhci_host *host)
>>       if (caps[1] & SDHCI_USE_SDR50_TUNING)
>>               host->flags |= SDHCI_SDR50_NEEDS_TUNING;
>>
>> +     /* Does the host needs tuning for HS200? */
>> +     if (mmc->caps2 & MMC_CAP2_HS200)
>> +             host->flags |= SDHCI_HS200_NEEDS_TUNING;
>> +
>>       /* Driver Type(s) (A, C, D) supported by the host */
>>       if (caps[1] & SDHCI_DRIVER_TYPE_A)
>>               mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 6e04e10..90af438 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -206,6 +206,7 @@ struct mmc_card {
>>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)               /* card is in high
>> speed mode */
>>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)              /* card is in ultra
>> high speed mode */
>>  #define MMC_CARD_SDXC                (1<<6)          /* card is SDXC */
>> +#define MMC_STATE_HIGHSPEED_200      (1<<7)  /* card is in HS200
>> mode */
>>       unsigned int            quirks;         /* card quirks */
>>  #define MMC_QUIRK_LENIENT_FN0        (1<<0)          /* allow SDIO FN0
>> writes outside of the VS CCCR range */
>>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func-
>> >cur_blksize */
>> @@ -361,6 +362,7 @@ static inline void __maybe_unused
>> remove_quirk(struct mmc_card *card, int data)
>>  #define mmc_card_present(c)  ((c)->state & MMC_STATE_PRESENT)
>>  #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
>>  #define mmc_card_highspeed(c)        ((c)->state & MMC_STATE_HIGHSPEED)
>> +#define mmc_card_hs200(c)    ((c)->state & MMC_STATE_HIGHSPEED_200)
>>  #define mmc_card_blockaddr(c)        ((c)->state & MMC_STATE_BLOCKADDR)
>>  #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
>>  #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
>> @@ -369,6 +371,7 @@ static inline void __maybe_unused
>> remove_quirk(struct mmc_card *card, int data)
>>  #define mmc_card_set_present(c)      ((c)->state |= MMC_STATE_PRESENT)
>>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>>  #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
>> +#define mmc_card_set_hs200(c)        ((c)->state |=
>> MMC_STATE_HIGHSPEED_200)
>>  #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
>>  #define mmc_card_set_ddr_mode(c) ((c)->state |=
>> MMC_STATE_HIGHSPEED_DDR)
>>  #define mmc_sd_card_set_uhs(c) ((c)->state |=
>> MMC_STATE_ULTRAHIGHSPEED)
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index a3ac9c4..20551f5 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -60,6 +60,8 @@ struct mmc_ios {
>>  #define MMC_SDR_MODE         0
>>  #define MMC_1_2V_DDR_MODE    1
>>  #define MMC_1_8V_DDR_MODE    2
>> +#define MMC_1_2V_SDR_MODE    3
>> +#define MMC_1_8V_SDR_MODE    4
>>
>>       unsigned char   signal_voltage;         /* signalling voltage
>> (1.8V or 3.3V) */
>>
>> @@ -242,6 +244,10 @@ struct mmc_host {
>>  #define MMC_CAP2_CACHE_CTRL  (1 << 1)        /* Allow cache control */
>>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)    /* Notify poweroff
>> supported */
>>  #define MMC_CAP2_NO_MULTI_READ       (1 << 3)        /* Multiblock reads
>> don't work */
>> +#define MMC_CAP2_HS200               (1 << 4)        /* Host supports
> HS200 mode
>> */
>
> Why is this cap required? You have already defined this 2 caps
> MMC_CAP2_HS200_1_8V_SDR & MMC_CAP2_HS200_1_2V_SDR which should be enough.
>
>> +#define MMC_CAP2_HS200_1_8V_SDR      (1 << 5)        /* can support */
>> +#define MMC_CAP2_HS200_1_2V_SDR      (1 << 6)        /* can support */
>> +#define MMC_CAP2_HIGHSPEED_200       (1 << 7)        /* Can do MMC HS200
> Why is this is this cap required?
>  will remove it
>> timing */
>>
>>       mmc_pm_flag_t           pm_caps;        /* supported pm features */
>>       unsigned int        power_notify_type;
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> index 0e71356..af4fa72 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -51,6 +51,7 @@
>>  #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1
>> */
>>  #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1
>> */
>>  #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1
>> */
>> +#define MMC_SEND_TUNING_BLOCK_HS200  21      /* adtc R1  */
>>
>>    /* class 3 */
>>  #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1
>> */
>> @@ -333,13 +334,18 @@ struct _mmc_csd {
>>
>>  #define EXT_CSD_CARD_TYPE_26 (1<<0)  /* Card can run at 26MHz */
>>  #define EXT_CSD_CARD_TYPE_52 (1<<1)  /* Card can run at 52MHz */
>> -#define EXT_CSD_CARD_TYPE_MASK       0xF     /* Mask out reserved bits */
>> +#define EXT_CSD_CARD_TYPE_MASK       0x3F    /* Mask out reserved bits */
>>  #define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at 52MHz
>> */
>>                                            /* DDR mode @1.8V or 3V I/O */
>>  #define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at 52MHz
>> */
>>                                            /* DDR mode @1.2V I/O */
>>  #define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V  \
>>                                       | EXT_CSD_CARD_TYPE_DDR_1_2V)
>> +#define EXT_CSD_CARD_TYPE_SDR_1_8V   (1<<4)  /* Card can run at
>> 200MHz */
>> +#define EXT_CSD_CARD_TYPE_SDR_1_2V   (1<<5)  /* Card can run at
>> 200MHz */
>> +                                             /* SDR mode @1.2V I/O */
>> +#define EXT_CSD_CARD_TYPE_SDR_200    (EXT_CSD_CARD_TYPE_SDR_1_8V     \
>> +                                     | EXT_CSD_CARD_TYPE_SDR_1_2V)
>>
>>  #define EXT_CSD_BUS_WIDTH_1  0       /* Card is in 1 bit mode */
>>  #define EXT_CSD_BUS_WIDTH_4  1       /* Card is in 4 bit mode */
>> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
>> index e4b6935..d9a2222 100644
>> --- a/include/linux/mmc/sdhci.h
>> +++ b/include/linux/mmc/sdhci.h
>> @@ -121,6 +121,7 @@ struct sdhci_host {
>>  #define SDHCI_AUTO_CMD23     (1<<7)  /* Auto CMD23 support */
>>  #define SDHCI_PV_ENABLED     (1<<8)  /* Preset value enabled */
>>  #define SDHCI_SDIO_IRQ_ENABLED       (1<<9)  /* SDIO irq enabled */
>> +#define SDHCI_HS200_NEEDS_TUNING (1<<10)     /* HS200 needs tuning */
>>
>>       unsigned int version;   /* SDHCI spec. version */
>>
>> --
>> 1.7.1
>>
>> --
>> 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
>
>
Subhash Jadavani Nov. 24, 2011, 6:13 a.m. UTC | #8
Hi Girish,

> -----Original Message-----
> From: Girish K S [mailto:girish.shivananjappa@linaro.org]
> Sent: Monday, November 21, 2011 4:12 PM
> To: Subhash Jadavani
> Cc: linux-mmc@vger.kernel.org; cjb@laptop.org; patches@linaro.org;
> linux-samsung-soc@vger.kernel.org
> Subject: Re: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC
> 4.5
> 
> On 19 November 2011 00:29, Subhash Jadavani <subhashj@codeaurora.org>
> wrote:
> > Hi Girish,
> >
> >> -----Original Message-----
> >> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >> owner@vger.kernel.org] On Behalf Of Girish K S
> >> Sent: Wednesday, October 26, 2011 10:30 AM
> >> To: linux-mmc@vger.kernel.org
> >> Cc: cjb@laptop.org; patches@linaro.org; linux-samsung-
> >> soc@vger.kernel.org; Girish K S
> >> Subject: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC
> 4.5
> >>
> >> This patch adds the support of the HS200 bus speed for eMMC 4.5
> >> devices.
> >> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core
> and
> >> host modules have been touched to add support for this module.
> >>
> >> It is necessary to know the card type in the sdhci.c file to add
> >> support
> >> for eMMC tuning function. So card.h file is included to import the
> card
> >> data structure.
> >
> > We don't need to know the card type in the host controller driver if
> we pass
> > the command opcode as the 2nd argument to execute_tuning() ops.
> Command
> > opcode can have have either CMD19 or CMD21 as it's value depending on
> the
> > card type (whether it's SD3.0 card or eMMC4.5 card).

Can you see if we can implement this?

> >
> >
> >>
> >> cc: Chris Ball <cjb@laptop.org>
> >> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> >> ---
> >> Changes in v4:
> >>       Rebased onto chris-mmc/mmc-next branch. This patch is
> >> successfully
> >>       applied on commit with id
> >> de022ed3fdc14808299b2fa66dbb1ed5ab921912.
> >> Changes in v3:
> >>       In the previous commits of chris-mmc/mmc-next branch, the
> patch
> >> with
> >>       commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines
> >> caps2 for
> >>       more capabilities. This patch version deletes the member
> >> ext_caps(created
> >>       in my earlier patch) from struct mmc_host and reuses already
> >> accepted
> >>       caps2 member.
> >> Changes in v2:
> >>       Rebased to latest chris-mmc/mmc-next branch. Resolved
> indentation
> >>       problems identified in review. This patch has to be applied
> >> before
> >>       the patch released for modifying the printk messages.
> >> Changes in v1:
> >>       Case statements in switch that produce same result have
> >>       been combined to reduce repeated assignments.
> >>       patch recreated after rebase to chris balls mmc-next branch.
> >>
> >>  drivers/mmc/core/bus.c    |    3 +-
> >>  drivers/mmc/core/mmc.c    |   92
> >> ++++++++++++++++++++++++++++++++++++++++----
> >>  drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
> >>  include/linux/mmc/card.h  |    3 +
> >>  include/linux/mmc/host.h  |    6 +++
> >>  include/linux/mmc/mmc.h   |    8 +++-
> >>  include/linux/mmc/sdhci.h |    1 +
> >>  7 files changed, 132 insertions(+), 17 deletions(-)
> >>
> >> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> >> index 46b6e84..2f82f6b 100644
> >> --- a/drivers/mmc/core/bus.c
> >> +++ b/drivers/mmc/core/bus.c
> >> @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
> >>                       mmc_card_ddr_mode(card) ? "DDR " : "",
> >>                       type);
> >>       } else {
> >> -             printk(KERN_INFO "%s: new %s%s%s card at address
> %04x\n",
> >> +             pr_info("%s: new %s%s%s%s card at address %04x\n",
> >>                       mmc_hostname(card->host),
> >>                       mmc_sd_card_uhs(card) ? "ultra high speed " :
> >>                       (mmc_card_highspeed(card) ? "high speed " :
> ""),
> >> +                     (mmc_card_hs200(card) ? "HS200 " : ""),
> >>                       mmc_card_ddr_mode(card) ? "DDR " : "",
> >>                       type, card->rca);
> >>       }
> >> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> >> index 3627044..4db248c 100644
> >> --- a/drivers/mmc/core/mmc.c
> >> +++ b/drivers/mmc/core/mmc.c
> >> @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card
> *card,
> >> u8 *ext_csd)
> >>       }
> >>       card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
> >>       switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
> >> +     case EXT_CSD_CARD_TYPE_SDR_200 |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +             card->ext_csd.hs_max_dtr = 200000000;
> >> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> >> +             break;
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +             card->ext_csd.hs_max_dtr = 200000000;
> >> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> >> +             break;
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
> >> +             card->ext_csd.hs_max_dtr = 200000000;
> >> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> >> +             break;
> >
> > Can't we optimize this? This looks like too many case statements.
> >
> > Basically we can have checks like this:
> >
> > temp = ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK;
> > if ((temp & EXT_CSD_CARD_TYPE_SDR_1_2V == EXT_CSD_CARD_TYPE_SDR_1_2V)
> ||
> >        (temp & EXT_CSD_CARD_TYPE_SDR_1_8V ==
> EXT_CSD_CARD_TYPE_SDR_1_8V)) {
> >        card->ext_csd.hs_max_dtr = 200000000;
> >        card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V; /* we
> can have
> > some logic to identify the card type correctly */
> > }

Can you please respond to this?

> >
> >
> >>       case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
> >>            EXT_CSD_CARD_TYPE_26:
> >>               card->ext_csd.hs_max_dtr = 52000000;
> >> @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host,
> u32
> >> ocr,
> >>  {
> >>       struct mmc_card *card;
> >>       int err, ddr = 0;
> >> +     int hs_sdr = 0;
> >>       u32 cid[4];
> >>       unsigned int max_dtr;
> >>       u32 rocr;
> >> @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host
> *host,
> >> u32 ocr,
> >>       /*
> >>        * Activate high speed (if supported)
> >>        */
> >> -     if ((card->ext_csd.hs_max_dtr != 0) &&
> >> -             (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
> >> -             err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> -                              EXT_CSD_HS_TIMING, 1,
> >> -                              card->ext_csd.generic_cmd6_time);
> >> +     if (card->ext_csd.hs_max_dtr != 0) {
> >
> > We should enter into this if condition only if one of the this cap
> > (MMC_CAP2_HIGHSPEED_200 or MMC_CAP_MMC_HIGHSPEED) is set.
> card->ext_csd.hs_max_dtr is non zero for high speed only i.e for
> frequency above 26Mhz. For anything less than 26 Mhz  this if
> condition is skipped.

I guess that's not the correct assumption here. Even for
EXT_CSD_CARD_TYPE_26 card type we have non zero value hs_max_dtr. Please
check.

	case EXT_CSD_CARD_TYPE_26:
		card->ext_csd.hs_max_dtr = 26000000;

> So its just enough if we check for less than 52 MHz and >= 52 Mhz.

> >
> >> +             if ((card->ext_csd.hs_max_dtr > 52000000) &&
> >> +                 (host->caps2 & MMC_CAP2_HIGHSPEED_200))
> >> +                     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> +                                      EXT_CSD_HS_TIMING, 2, 0);
> >> +             else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
> >> +                     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> +                                      EXT_CSD_HS_TIMING, 1, 0);
> >> +
> >>               if (err && err != -EBADMSG)
> >>                       goto free_card;
> >>
> >> @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host,
> >> u32 ocr,
> >>                              mmc_hostname(card->host));
> >>                       err = 0;
> >>               } else {
> >> -                     mmc_card_set_highspeed(card);
> >> +                     if (card->ext_csd.hs_max_dtr > 52000000)
> >
> > I think there should be AND condition with (host->caps2 &
> > MMC_CAP2_HIGHSPEED_200) for setting the card as HS200.
> Ok will add it.
> >
> >> +                             mmc_card_set_hs200(card);
> >> +                     else
> >> +                             mmc_card_set_highspeed(card);
> >>                       mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> >
> > As mentioned earlier, there should be new timing mode defined for
> HS200.
> can you share Host controller specification will check it.

Our Qualcomm MSM SDCC host controller is not compliant to SDHCI (Standard
Host controller interface) specification so our host controller
implementation won't be same as controllers compliant to SDHCI. It's not
just Qualcomm SDCC controller, there are many other SDCC controller (like
mmci) which are not compliant to SDHCI standard.

If you check the SD3.0 implementation (sd_set_bus_speed_mode() function in
drivers/mmc/core/sd.c), core layer is setting the different timing modes for
all the different UHS-I bus speed modes (like SDR104, SDR50, DDR50 etc ...).
Then it's upto the host controller driver to see whether they want to make
use this timing mode or not? Our host controller driver does make use of it
for SD3.0 timing modes and similar we need to know HS200 timing mode in
order to program our controller properly.


> >
> >>               }
> >>       }
> >> @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host,
> u32
> >> ocr,
> >>        */
> >>       max_dtr = (unsigned int)-1;
> >>
> >> -     if (mmc_card_highspeed(card)) {
> >> +     if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
> >>               if (max_dtr > card->ext_csd.hs_max_dtr)
> >>                       max_dtr = card->ext_csd.hs_max_dtr;
> >>       } else if (max_dtr > card->csd.max_dtr) {
> >> @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host,
> >> u32 ocr,
> >>       }
> >>
> >>       /*
> >> +      * Indicate HS200 SDR mode (if supported).
> >> +      */
> >> +     if (mmc_card_hs200(card)) {
> >> +             if ((card->ext_csd.card_type &
> EXT_CSD_CARD_TYPE_SDR_1_8V)
> >> +                     && ((host->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
> >> +                          MMC_CAP2_HS200))
> >> +                             == (MMC_CAP2_HS200_1_8V_SDR |
> > MMC_CAP2_HS200)))
> >> +                             hs_sdr = MMC_1_8V_SDR_MODE;
> >> +             else if ((card->ext_csd.card_type &
> >> EXT_CSD_CARD_TYPE_SDR_1_2V)
> >> +                      && ((host->caps2 & (MMC_CAP2_HS200_1_2V_SDR |
> >> +                           MMC_CAP2_HS200))
> >> +                             == (MMC_CAP2_HS200_1_2V_SDR |
> > MMC_CAP2_HS200)))
> >> +                             hs_sdr = MMC_1_2V_SDR_MODE;
> >> +     }
> >> +
> >> +     /*
> >>        * Activate wide bus and DDR (if supported).
> >>        */
> >>       if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
> >> @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host
> *host,
> >> u32 ocr,
> >>                       if (!err) {
> >>                               mmc_set_bus_width(card->host,
> bus_width);
> >>
> >> +                             if ((host->caps2 & MMC_CAP2_HS200) &&
> >> +                                 card->host->ops->execute_tuning)
> >> +                                     err = card->host->ops-> \
> >> +                                           execute_tuning(card-
> >host);
> >
> > execute_tuning() should be executed immediately after the TIMING is
> changed
> > to HS200 and clock speed is changed to 200MHz. But here timing mode
> and
> > clock rate is changed and then you are also sending bus width change
> > command. As host controller timing tuning has not completed, this bus
> width
> > change command may fail with CMD/DATA CRC errors.
> > pls check the eMMC specification section 6.6.4  it says
> "Note that the host should switch to the required bus width before
> starting the tuning operation to
> allow the tuning sequence to be done using the proper bus operating
> conditions.

Agreed. But specification doesn't when exactly the bus width change should
happen. Right before Step.6 (mentioned in section 6.6.4) or before setting
the HS200 mode in HS_TIMING and changing the clock speed.

I would say we should set the bus width before the step.1 (mentioned in
section 6.6.4). then proceed to execute all the step.1 and step.6 in
sequence.

As I had mentioned earlier, let's say at first you set the HS_TIMING=HS200
and set the clock speed to 200MHz. Now if you try to set the bus width (by
sending the CMD6 and then reading the STATUS via CMD13) without executing
tuning, CMD6/CMD13 may fail with CMD/RESPONSE CRC errors as controller
sampling point is still not tuned. We had exact similar issue faced with
SD3.0 as well on our host controller. You can take a look at this patch:
http://www.spinics.net/lists/linux-arm-msm/msg03867.html


> "
> > So basically this should be the sequence.
> > 1. set the bus width depending on the host controller and card
> capability
> > 2. If card support HS200 timing, change the timing of the card by
> sending
> > SWITCH command. Change the clock rate. Change the timing mode of
> controller.
> > 3. Now call the host controller execute_tuning ops to perform the
> controller
> > sampling point tuning.
> >
> > Also, let's pass the command opcode as the 2nd argument of the
> > execute_tuning() function. Which means host controller driver need
> not to
> > know the card type.
> >
> >> +
> >>                               /*
> >>                                * If controller can't handle bus
> width
> > test,
> >>                                * compare ext_csd previously read in
> 1 bit
> >> mode
> >>                                * against ext_csd at new bus width
> >>                                */
> >> -                             if (!(host->caps &
> MMC_CAP_BUS_WIDTH_TEST))
> >> +                             if (!(host->caps &
> MMC_CAP_BUS_WIDTH_TEST)
> > &&
> >> +                                 !err)
> >>                                       err =
> mmc_compare_ext_csds(card,
> >>                                               bus_width);
> >> -                             else
> >> +                             else if (!err)
> >>                                       err = mmc_bus_test(card,
> bus_width);
> >> +                             else
> >> +                                     pr_warning("tuning execution
> > failed\n");
> >>                               if (!err)
> >>                                       break;
> >>                       }
> >> @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host
> *host,
> >> u32 ocr,
> >>                       mmc_card_set_ddr_mode(card);
> >>                       mmc_set_timing(card->host,
> MMC_TIMING_UHS_DDR50);
> >>                       mmc_set_bus_width(card->host, bus_width);
> >> +             } else if (hs_sdr) {
> >> +                     if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
> >> +                             err = mmc_set_signal_voltage(host,
> >> +                                     MMC_SIGNAL_VOLTAGE_120, 0);
> >> +                             if (err)
> >> +                                     goto err;
> >> +                     }
> >> +                     mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> >> +                     mmc_set_bus_width(card->host, bus_width);
> >>               }
> >>       }
> >>
> >> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> >> index a69d269..28fb747 100644
> >> --- a/drivers/mmc/host/sdhci.c
> >> +++ b/drivers/mmc/host/sdhci.c
> >> @@ -27,6 +27,7 @@
> >>
> >>  #include <linux/mmc/mmc.h>
> >>  #include <linux/mmc/host.h>
> >> +#include <linux/mmc/card.h>
> >>
> >>  #include "sdhci.h"
> >>
> >> @@ -1016,7 +1017,8 @@ static void sdhci_send_command(struct
> sdhci_host
> >> *host, struct mmc_command *cmd)
> >>               flags |= SDHCI_CMD_INDEX;
> >>
> >>       /* CMD19 is special in that the Data Present Select should be
> set
> >> */
> >> -     if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
> >> +     if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
> >> +         (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
> >>               flags |= SDHCI_CMD_DATA;
> >>
> >>       sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
> >> SDHCI_COMMAND);
> >> @@ -1684,10 +1686,13 @@ static int sdhci_execute_tuning(struct
> mmc_host
> >> *mmc)
> >>        * Host Controller needs tuning only in case of SDR104 mode
> >>        * and for SDR50 mode when Use Tuning for SDR50 is set in
> >>        * Capabilities register.
> >> +      * If the Host Controller supports the HS200 mode then tuning
> >> +      * function has to be executed.
> >>        */
> >>       if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
> >>           (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> >> -         (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> >> +         (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
> >> +         (host->flags & SDHCI_HS200_NEEDS_TUNING))
> >>               ctrl |= SDHCI_CTRL_EXEC_TUNING;
> >>       else {
> >>               spin_unlock(&host->lock);
> >> @@ -1723,7 +1728,10 @@ static int sdhci_execute_tuning(struct
> mmc_host
> >> *mmc)
> >>               if (!tuning_loop_counter && !timeout)
> >>                       break;
> >>
> >> -             cmd.opcode = MMC_SEND_TUNING_BLOCK;
> >> +             if (mmc->card->type == MMC_TYPE_MMC)
> >> +                     cmd.opcode = MMC_SEND_TUNING_BLOCK_HS200;
> >> +             else
> >> +                     cmd.opcode = MMC_SEND_TUNING_BLOCK;
> >>               cmd.arg = 0;
> >>               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> >>               cmd.retries = 0;
> >> @@ -1738,7 +1746,17 @@ static int sdhci_execute_tuning(struct
> mmc_host
> >> *mmc)
> >>                * block to the Host Controller. So we set the block
> size
> >>                * to 64 here.
> >>                */
> >> -             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> >> SDHCI_BLOCK_SIZE);
> >> +             if (mmc->card->type == MMC_TYPE_MMC) {
> >> +                     if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
> >> +                             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7,
> 128),
> >> +                                          SDHCI_BLOCK_SIZE);
> >> +                     else if (mmc->ios.bus_width ==
> MMC_BUS_WIDTH_4)
> >> +                             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7,
> 64),
> >> +                                          SDHCI_BLOCK_SIZE);
> >> +             } else {
> >> +                     sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> >> +                                  SDHCI_BLOCK_SIZE);
> >> +             }
> >>
> >>               /*
> >>                * The tuning block is sent by the card to the host
> >> controller.
> >> @@ -2121,12 +2139,14 @@ static void sdhci_show_adma_error(struct
> >> sdhci_host *host) { }
> >>
> >>  static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
> >>  {
> >> +     u32 command;
> >>       BUG_ON(intmask == 0);
> >>
> >>       /* CMD19 generates _only_ Buffer Read Ready interrupt */
> >>       if (intmask & SDHCI_INT_DATA_AVAIL) {
> >> -             if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
> >> -                 MMC_SEND_TUNING_BLOCK) {
> >> +             command = SDHCI_GET_CMD(sdhci_readw(host,
> SDHCI_COMMAND));
> >> +             if ((command == MMC_SEND_TUNING_BLOCK) ||
> >> +                 (command == MMC_SEND_TUNING_BLOCK_HS200)) {
> >>                       host->tuning_done = 1;
> >>                       wake_up(&host->buf_ready_int);
> >>                       return;
> >> @@ -2731,6 +2751,10 @@ int sdhci_add_host(struct sdhci_host *host)
> >>       if (caps[1] & SDHCI_USE_SDR50_TUNING)
> >>               host->flags |= SDHCI_SDR50_NEEDS_TUNING;
> >>
> >> +     /* Does the host needs tuning for HS200? */
> >> +     if (mmc->caps2 & MMC_CAP2_HS200)
> >> +             host->flags |= SDHCI_HS200_NEEDS_TUNING;
> >> +
> >>       /* Driver Type(s) (A, C, D) supported by the host */
> >>       if (caps[1] & SDHCI_DRIVER_TYPE_A)
> >>               mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
> >> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> >> index 6e04e10..90af438 100644
> >> --- a/include/linux/mmc/card.h
> >> +++ b/include/linux/mmc/card.h
> >> @@ -206,6 +206,7 @@ struct mmc_card {
> >>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)               /* card is in
> high
> >> speed mode */
> >>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)              /* card is in
> ultra
> >> high speed mode */
> >>  #define MMC_CARD_SDXC                (1<<6)          /* card is
> SDXC */
> >> +#define MMC_STATE_HIGHSPEED_200      (1<<7)  /* card is in HS200
> >> mode */
> >>       unsigned int            quirks;         /* card quirks */
> >>  #define MMC_QUIRK_LENIENT_FN0        (1<<0)          /* allow SDIO
> FN0
> >> writes outside of the VS CCCR range */
> >>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func-
> >> >cur_blksize */
> >> @@ -361,6 +362,7 @@ static inline void __maybe_unused
> >> remove_quirk(struct mmc_card *card, int data)
> >>  #define mmc_card_present(c)  ((c)->state & MMC_STATE_PRESENT)
> >>  #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
> >>  #define mmc_card_highspeed(c)        ((c)->state &
> MMC_STATE_HIGHSPEED)
> >> +#define mmc_card_hs200(c)    ((c)->state & MMC_STATE_HIGHSPEED_200)
> >>  #define mmc_card_blockaddr(c)        ((c)->state &
> MMC_STATE_BLOCKADDR)
> >>  #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
> >>  #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
> >> @@ -369,6 +371,7 @@ static inline void __maybe_unused
> >> remove_quirk(struct mmc_card *card, int data)
> >>  #define mmc_card_set_present(c)      ((c)->state |=
> MMC_STATE_PRESENT)
> >>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
> >>  #define mmc_card_set_highspeed(c) ((c)->state |=
> MMC_STATE_HIGHSPEED)
> >> +#define mmc_card_set_hs200(c)        ((c)->state |=
> >> MMC_STATE_HIGHSPEED_200)
> >>  #define mmc_card_set_blockaddr(c) ((c)->state |=
> MMC_STATE_BLOCKADDR)
> >>  #define mmc_card_set_ddr_mode(c) ((c)->state |=
> >> MMC_STATE_HIGHSPEED_DDR)
> >>  #define mmc_sd_card_set_uhs(c) ((c)->state |=
> >> MMC_STATE_ULTRAHIGHSPEED)
> >> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> >> index a3ac9c4..20551f5 100644
> >> --- a/include/linux/mmc/host.h
> >> +++ b/include/linux/mmc/host.h
> >> @@ -60,6 +60,8 @@ struct mmc_ios {
> >>  #define MMC_SDR_MODE         0
> >>  #define MMC_1_2V_DDR_MODE    1
> >>  #define MMC_1_8V_DDR_MODE    2
> >> +#define MMC_1_2V_SDR_MODE    3
> >> +#define MMC_1_8V_SDR_MODE    4
> >>
> >>       unsigned char   signal_voltage;         /* signalling voltage
> >> (1.8V or 3.3V) */
> >>
> >> @@ -242,6 +244,10 @@ struct mmc_host {
> >>  #define MMC_CAP2_CACHE_CTRL  (1 << 1)        /* Allow cache control
> */
> >>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)    /* Notify poweroff
> >> supported */
> >>  #define MMC_CAP2_NO_MULTI_READ       (1 << 3)        /* Multiblock
> reads
> >> don't work */
> >> +#define MMC_CAP2_HS200               (1 << 4)        /* Host
> supports
> > HS200 mode
> >> */
> >
> > Why is this cap required? You have already defined this 2 caps
> > MMC_CAP2_HS200_1_8V_SDR & MMC_CAP2_HS200_1_2V_SDR which should be
> enough.
> >
> >> +#define MMC_CAP2_HS200_1_8V_SDR      (1 << 5)        /* can support
> */
> >> +#define MMC_CAP2_HS200_1_2V_SDR      (1 << 6)        /* can support
> */
> >> +#define MMC_CAP2_HIGHSPEED_200       (1 << 7)        /* Can do MMC
> HS200
> > Why is this is this cap required?
> >  will remove it

Both MMC_CAP2_HIGHSPEED_200 and MMC_CAP2_HS200 are redundant.

> >> timing */
> >>
> >>       mmc_pm_flag_t           pm_caps;        /* supported pm
> features */
> >>       unsigned int        power_notify_type;
> >> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> >> index 0e71356..af4fa72 100644
> >> --- a/include/linux/mmc/mmc.h
> >> +++ b/include/linux/mmc/mmc.h
> >> @@ -51,6 +51,7 @@
> >>  #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1
> >> */
> >>  #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1
> >> */
> >>  #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1
> >> */
> >> +#define MMC_SEND_TUNING_BLOCK_HS200  21      /* adtc R1  */
> >>
> >>    /* class 3 */
> >>  #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1
> >> */
> >> @@ -333,13 +334,18 @@ struct _mmc_csd {
> >>
> >>  #define EXT_CSD_CARD_TYPE_26 (1<<0)  /* Card can run at 26MHz */
> >>  #define EXT_CSD_CARD_TYPE_52 (1<<1)  /* Card can run at 52MHz */
> >> -#define EXT_CSD_CARD_TYPE_MASK       0xF     /* Mask out reserved
> bits */
> >> +#define EXT_CSD_CARD_TYPE_MASK       0x3F    /* Mask out reserved
> bits */
> >>  #define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at
> 52MHz
> >> */
> >>                                            /* DDR mode @1.8V or 3V
> I/O */
> >>  #define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at
> 52MHz
> >> */
> >>                                            /* DDR mode @1.2V I/O */
> >>  #define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V
>  \
> >>                                       | EXT_CSD_CARD_TYPE_DDR_1_2V)
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V   (1<<4)  /* Card can run at
> >> 200MHz */
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V   (1<<5)  /* Card can run at
> >> 200MHz */
> >> +                                             /* SDR mode @1.2V I/O
> */
> >> +#define EXT_CSD_CARD_TYPE_SDR_200    (EXT_CSD_CARD_TYPE_SDR_1_8V
>   \
> >> +                                     | EXT_CSD_CARD_TYPE_SDR_1_2V)
> >>
> >>  #define EXT_CSD_BUS_WIDTH_1  0       /* Card is in 1 bit mode */
> >>  #define EXT_CSD_BUS_WIDTH_4  1       /* Card is in 4 bit mode */
> >> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> >> index e4b6935..d9a2222 100644
> >> --- a/include/linux/mmc/sdhci.h
> >> +++ b/include/linux/mmc/sdhci.h
> >> @@ -121,6 +121,7 @@ struct sdhci_host {
> >>  #define SDHCI_AUTO_CMD23     (1<<7)  /* Auto CMD23 support */
> >>  #define SDHCI_PV_ENABLED     (1<<8)  /* Preset value enabled */
> >>  #define SDHCI_SDIO_IRQ_ENABLED       (1<<9)  /* SDIO irq enabled */
> >> +#define SDHCI_HS200_NEEDS_TUNING (1<<10)     /* HS200 needs tuning
> */
> >>
> >>       unsigned int version;   /* SDHCI spec. version */
> >>
> >> --
> >> 1.7.1
> >>
> >> --
> >> 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
> >
> >
Girish K S Nov. 24, 2011, 7:10 a.m. UTC | #9
On 24 November 2011 11:43, Subhash Jadavani <subhashj@codeaurora.org> wrote:
> Hi Girish,
>
>> -----Original Message-----
>> From: Girish K S [mailto:girish.shivananjappa@linaro.org]
>> Sent: Monday, November 21, 2011 4:12 PM
>> To: Subhash Jadavani
>> Cc: linux-mmc@vger.kernel.org; cjb@laptop.org; patches@linaro.org;
>> linux-samsung-soc@vger.kernel.org
>> Subject: Re: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC
>> 4.5
>>
>> On 19 November 2011 00:29, Subhash Jadavani <subhashj@codeaurora.org>
>> wrote:
>> > Hi Girish,
>> >
>> >> -----Original Message-----
>> >> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> >> owner@vger.kernel.org] On Behalf Of Girish K S
>> >> Sent: Wednesday, October 26, 2011 10:30 AM
>> >> To: linux-mmc@vger.kernel.org
>> >> Cc: cjb@laptop.org; patches@linaro.org; linux-samsung-
>> >> soc@vger.kernel.org; Girish K S
>> >> Subject: [PATCH RESEND V4] mmc: core: HS200 mode support for eMMC
>> 4.5
>> >>
>> >> This patch adds the support of the HS200 bus speed for eMMC 4.5
>> >> devices.
>> >> The eMMC 4.5 devices have support for 200MHz bus speed.The mmc core
>> and
>> >> host modules have been touched to add support for this module.
>> >>
>> >> It is necessary to know the card type in the sdhci.c file to add
>> >> support
>> >> for eMMC tuning function. So card.h file is included to import the
>> card
>> >> data structure.
>> >
>> > We don't need to know the card type in the host controller driver if
>> we pass
>> > the command opcode as the 2nd argument to execute_tuning() ops.
>> Command
>> > opcode can have have either CMD19 or CMD21 as it's value depending on
>> the
>> > card type (whether it's SD3.0 card or eMMC4.5 card).
>
> Can you see if we can implement this?
I have considered this and made the patch
>
>> >
>> >
>> >>
>> >> cc: Chris Ball <cjb@laptop.org>
>> >> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
>> >> ---
>> >> Changes in v4:
>> >>       Rebased onto chris-mmc/mmc-next branch. This patch is
>> >> successfully
>> >>       applied on commit with id
>> >> de022ed3fdc14808299b2fa66dbb1ed5ab921912.
>> >> Changes in v3:
>> >>       In the previous commits of chris-mmc/mmc-next branch, the
>> patch
>> >> with
>> >>       commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines
>> >> caps2 for
>> >>       more capabilities. This patch version deletes the member
>> >> ext_caps(created
>> >>       in my earlier patch) from struct mmc_host and reuses already
>> >> accepted
>> >>       caps2 member.
>> >> Changes in v2:
>> >>       Rebased to latest chris-mmc/mmc-next branch. Resolved
>> indentation
>> >>       problems identified in review. This patch has to be applied
>> >> before
>> >>       the patch released for modifying the printk messages.
>> >> Changes in v1:
>> >>       Case statements in switch that produce same result have
>> >>       been combined to reduce repeated assignments.
>> >>       patch recreated after rebase to chris balls mmc-next branch.
>> >>
>> >>  drivers/mmc/core/bus.c    |    3 +-
>> >>  drivers/mmc/core/mmc.c    |   92
>> >> ++++++++++++++++++++++++++++++++++++++++----
>> >>  drivers/mmc/host/sdhci.c  |   36 +++++++++++++++---
>> >>  include/linux/mmc/card.h  |    3 +
>> >>  include/linux/mmc/host.h  |    6 +++
>> >>  include/linux/mmc/mmc.h   |    8 +++-
>> >>  include/linux/mmc/sdhci.h |    1 +
>> >>  7 files changed, 132 insertions(+), 17 deletions(-)
>> >>
>> >> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
>> >> index 46b6e84..2f82f6b 100644
>> >> --- a/drivers/mmc/core/bus.c
>> >> +++ b/drivers/mmc/core/bus.c
>> >> @@ -301,10 +301,11 @@ int mmc_add_card(struct mmc_card *card)
>> >>                       mmc_card_ddr_mode(card) ? "DDR " : "",
>> >>                       type);
>> >>       } else {
>> >> -             printk(KERN_INFO "%s: new %s%s%s card at address
>> %04x\n",
>> >> +             pr_info("%s: new %s%s%s%s card at address %04x\n",
>> >>                       mmc_hostname(card->host),
>> >>                       mmc_sd_card_uhs(card) ? "ultra high speed " :
>> >>                       (mmc_card_highspeed(card) ? "high speed " :
>> ""),
>> >> +                     (mmc_card_hs200(card) ? "HS200 " : ""),
>> >>                       mmc_card_ddr_mode(card) ? "DDR " : "",
>> >>                       type, card->rca);
>> >>       }
>> >> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> >> index 3627044..4db248c 100644
>> >> --- a/drivers/mmc/core/mmc.c
>> >> +++ b/drivers/mmc/core/mmc.c
>> >> @@ -285,6 +285,39 @@ static int mmc_read_ext_csd(struct mmc_card
>> *card,
>> >> u8 *ext_csd)
>> >>       }
>> >>       card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
>> >>       switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
>> >> +     case EXT_CSD_CARD_TYPE_SDR_200 |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +             card->ext_csd.hs_max_dtr = 200000000;
>> >> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
>> >> +             break;
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +             card->ext_csd.hs_max_dtr = 200000000;
>> >> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
>> >> +             break;
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +     case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
>> >> +          EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
>> >> +             card->ext_csd.hs_max_dtr = 200000000;
>> >> +             card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
>> >> +             break;
>> >
>> > Can't we optimize this? This looks like too many case statements.
>> >
>> > Basically we can have checks like this:
>> >
>> > temp = ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK;
>> > if ((temp & EXT_CSD_CARD_TYPE_SDR_1_2V == EXT_CSD_CARD_TYPE_SDR_1_2V)
>> ||
>> >        (temp & EXT_CSD_CARD_TYPE_SDR_1_8V ==
>> EXT_CSD_CARD_TYPE_SDR_1_8V)) {
>> >        card->ext_csd.hs_max_dtr = 200000000;
>> >        card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V; /* we
>> can have
>> > some logic to identify the card type correctly */
>> > }
>
> Can you please respond to this?
Its taken care in the current patch (not submitted)
>
>> >
>> >
>> >>       case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
>> >>            EXT_CSD_CARD_TYPE_26:
>> >>               card->ext_csd.hs_max_dtr = 52000000;
>> >> @@ -699,6 +732,7 @@ static int mmc_init_card(struct mmc_host *host,
>> u32
>> >> ocr,
>> >>  {
>> >>       struct mmc_card *card;
>> >>       int err, ddr = 0;
>> >> +     int hs_sdr = 0;
>> >>       u32 cid[4];
>> >>       unsigned int max_dtr;
>> >>       u32 rocr;
>> >> @@ -890,11 +924,15 @@ static int mmc_init_card(struct mmc_host
>> *host,
>> >> u32 ocr,
>> >>       /*
>> >>        * Activate high speed (if supported)
>> >>        */
>> >> -     if ((card->ext_csd.hs_max_dtr != 0) &&
>> >> -             (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
>> >> -             err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> >> -                              EXT_CSD_HS_TIMING, 1,
>> >> -                              card->ext_csd.generic_cmd6_time);
>> >> +     if (card->ext_csd.hs_max_dtr != 0) {
>> >
>> > We should enter into this if condition only if one of the this cap
>> > (MMC_CAP2_HIGHSPEED_200 or MMC_CAP_MMC_HIGHSPEED) is set.
>> card->ext_csd.hs_max_dtr is non zero for high speed only i.e for
>> frequency above 26Mhz. For anything less than 26 Mhz  this if
>> condition is skipped.
>
> I guess that's not the correct assumption here. Even for
> EXT_CSD_CARD_TYPE_26 card type we have non zero value hs_max_dtr. Please
> check.
>
>        case EXT_CSD_CARD_TYPE_26:
>                card->ext_csd.hs_max_dtr = 26000000;
> pls check the category of 26Mhz (it is considered as High speed). anything below 26 is normal speed.
>> So its just enough if we check for less than 52 MHz and >= 52 Mhz.
>
>> >
>> >> +             if ((card->ext_csd.hs_max_dtr > 52000000) &&
>> >> +                 (host->caps2 & MMC_CAP2_HIGHSPEED_200))
>> >> +                     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> >> +                                      EXT_CSD_HS_TIMING, 2, 0);
>> >> +             else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
>> >> +                     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> >> +                                      EXT_CSD_HS_TIMING, 1, 0);
>> >> +
>> >>               if (err && err != -EBADMSG)
>> >>                       goto free_card;
>> >>
>> >> @@ -903,7 +941,10 @@ static int mmc_init_card(struct mmc_host *host,
>> >> u32 ocr,
>> >>                              mmc_hostname(card->host));
>> >>                       err = 0;
>> >>               } else {
>> >> -                     mmc_card_set_highspeed(card);
>> >> +                     if (card->ext_csd.hs_max_dtr > 52000000)
>> >
>> > I think there should be AND condition with (host->caps2 &
>> > MMC_CAP2_HIGHSPEED_200) for setting the card as HS200.
>> Ok will add it.
>> >
>> >> +                             mmc_card_set_hs200(card);
>> >> +                     else
>> >> +                             mmc_card_set_highspeed(card);
>> >>                       mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> >
>> > As mentioned earlier, there should be new timing mode defined for
>> HS200.
>> can you share Host controller specification will check it.
>
> Our Qualcomm MSM SDCC host controller is not compliant to SDHCI (Standard
> Host controller interface) specification so our host controller
> implementation won't be same as controllers compliant to SDHCI. It's not
> just Qualcomm SDCC controller, there are many other SDCC controller (like
> mmci) which are not compliant to SDHCI standard.
>
> If you check the SD3.0 implementation (sd_set_bus_speed_mode() function in
> drivers/mmc/core/sd.c), core layer is setting the different timing modes for
> all the different UHS-I bus speed modes (like SDR104, SDR50, DDR50 etc ...).
> Then it's upto the host controller driver to see whether they want to make
> use this timing mode or not? Our host controller driver does make use of it
> for SD3.0 timing modes and similar we need to know HS200 timing mode in
> order to program our controller properly.
>
>Need to check more on this
>> >
>> >>               }
>> >>       }
>> >> @@ -929,7 +970,7 @@ static int mmc_init_card(struct mmc_host *host,
>> u32
>> >> ocr,
>> >>        */
>> >>       max_dtr = (unsigned int)-1;
>> >>
>> >> -     if (mmc_card_highspeed(card)) {
>> >> +     if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
>> >>               if (max_dtr > card->ext_csd.hs_max_dtr)
>> >>                       max_dtr = card->ext_csd.hs_max_dtr;
>> >>       } else if (max_dtr > card->csd.max_dtr) {
>> >> @@ -955,6 +996,22 @@ static int mmc_init_card(struct mmc_host *host,
>> >> u32 ocr,
>> >>       }
>> >>
>> >>       /*
>> >> +      * Indicate HS200 SDR mode (if supported).
>> >> +      */
>> >> +     if (mmc_card_hs200(card)) {
>> >> +             if ((card->ext_csd.card_type &
>> EXT_CSD_CARD_TYPE_SDR_1_8V)
>> >> +                     && ((host->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
>> >> +                          MMC_CAP2_HS200))
>> >> +                             == (MMC_CAP2_HS200_1_8V_SDR |
>> > MMC_CAP2_HS200)))
>> >> +                             hs_sdr = MMC_1_8V_SDR_MODE;
>> >> +             else if ((card->ext_csd.card_type &
>> >> EXT_CSD_CARD_TYPE_SDR_1_2V)
>> >> +                      && ((host->caps2 & (MMC_CAP2_HS200_1_2V_SDR |
>> >> +                           MMC_CAP2_HS200))
>> >> +                             == (MMC_CAP2_HS200_1_2V_SDR |
>> > MMC_CAP2_HS200)))
>> >> +                             hs_sdr = MMC_1_2V_SDR_MODE;
>> >> +     }
>> >> +
>> >> +     /*
>> >>        * Activate wide bus and DDR (if supported).
>> >>        */
>> >>       if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
>> >> @@ -994,16 +1051,24 @@ static int mmc_init_card(struct mmc_host
>> *host,
>> >> u32 ocr,
>> >>                       if (!err) {
>> >>                               mmc_set_bus_width(card->host,
>> bus_width);
>> >>
>> >> +                             if ((host->caps2 & MMC_CAP2_HS200) &&
>> >> +                                 card->host->ops->execute_tuning)
>> >> +                                     err = card->host->ops-> \
>> >> +                                           execute_tuning(card-
>> >host);
>> >
>> > execute_tuning() should be executed immediately after the TIMING is
>> changed
>> > to HS200 and clock speed is changed to 200MHz. But here timing mode
>> and
>> > clock rate is changed and then you are also sending bus width change
>> > command. As host controller timing tuning has not completed, this bus
>> width
>> > change command may fail with CMD/DATA CRC errors.
>> > pls check the eMMC specification section 6.6.4  it says
>> "Note that the host should switch to the required bus width before
>> starting the tuning operation to
>> allow the tuning sequence to be done using the proper bus operating
>> conditions.
>
> Agreed. But specification doesn't when exactly the bus width change should
> happen. Right before Step.6 (mentioned in section 6.6.4) or before setting
> the HS200 mode in HS_TIMING and changing the clock speed.
>
> I would say we should set the bus width before the step.1 (mentioned in
> section 6.6.4). then proceed to execute all the step.1 and step.6 in
> sequence.
>
> As I had mentioned earlier, let's say at first you set the HS_TIMING=HS200
> and set the clock speed to 200MHz. Now if you try to set the bus width (by
> sending the CMD6 and then reading the STATUS via CMD13) without executing
> tuning, CMD6/CMD13 may fail with CMD/RESPONSE CRC errors as controller
> sampling point is still not tuned. We had exact similar issue faced with
> SD3.0 as well on our host controller. You can take a look at this patch:
> http://www.spinics.net/lists/linux-arm-msm/msg03867.html
> will check again
>
>> "
>> > So basically this should be the sequence.
>> > 1. set the bus width depending on the host controller and card
>> capability
>> > 2. If card support HS200 timing, change the timing of the card by
>> sending
>> > SWITCH command. Change the clock rate. Change the timing mode of
>> controller.
>> > 3. Now call the host controller execute_tuning ops to perform the
>> controller
>> > sampling point tuning.
>> >
>> > Also, let's pass the command opcode as the 2nd argument of the
>> > execute_tuning() function. Which means host controller driver need
>> not to
>> > know the card type.
>> >
>> >> +
>> >>                               /*
>> >>                                * If controller can't handle bus
>> width
>> > test,
>> >>                                * compare ext_csd previously read in
>> 1 bit
>> >> mode
>> >>                                * against ext_csd at new bus width
>> >>                                */
>> >> -                             if (!(host->caps &
>> MMC_CAP_BUS_WIDTH_TEST))
>> >> +                             if (!(host->caps &
>> MMC_CAP_BUS_WIDTH_TEST)
>> > &&
>> >> +                                 !err)
>> >>                                       err =
>> mmc_compare_ext_csds(card,
>> >>                                               bus_width);
>> >> -                             else
>> >> +                             else if (!err)
>> >>                                       err = mmc_bus_test(card,
>> bus_width);
>> >> +                             else
>> >> +                                     pr_warning("tuning execution
>> > failed\n");
>> >>                               if (!err)
>> >>                                       break;
>> >>                       }
>> >> @@ -1052,6 +1117,15 @@ static int mmc_init_card(struct mmc_host
>> *host,
>> >> u32 ocr,
>> >>                       mmc_card_set_ddr_mode(card);
>> >>                       mmc_set_timing(card->host,
>> MMC_TIMING_UHS_DDR50);
>> >>                       mmc_set_bus_width(card->host, bus_width);
>> >> +             } else if (hs_sdr) {
>> >> +                     if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
>> >> +                             err = mmc_set_signal_voltage(host,
>> >> +                                     MMC_SIGNAL_VOLTAGE_120, 0);
>> >> +                             if (err)
>> >> +                                     goto err;
>> >> +                     }
>> >> +                     mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> >> +                     mmc_set_bus_width(card->host, bus_width);
>> >>               }
>> >>       }
>> >>
>> >> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> >> index a69d269..28fb747 100644
>> >> --- a/drivers/mmc/host/sdhci.c
>> >> +++ b/drivers/mmc/host/sdhci.c
>> >> @@ -27,6 +27,7 @@
>> >>
>> >>  #include <linux/mmc/mmc.h>
>> >>  #include <linux/mmc/host.h>
>> >> +#include <linux/mmc/card.h>
>> >>
>> >>  #include "sdhci.h"
>> >>
>> >> @@ -1016,7 +1017,8 @@ static void sdhci_send_command(struct
>> sdhci_host
>> >> *host, struct mmc_command *cmd)
>> >>               flags |= SDHCI_CMD_INDEX;
>> >>
>> >>       /* CMD19 is special in that the Data Present Select should be
>> set
>> >> */
>> >> -     if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
>> >> +     if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
>> >> +         (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
>> >>               flags |= SDHCI_CMD_DATA;
>> >>
>> >>       sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags),
>> >> SDHCI_COMMAND);
>> >> @@ -1684,10 +1686,13 @@ static int sdhci_execute_tuning(struct
>> mmc_host
>> >> *mmc)
>> >>        * Host Controller needs tuning only in case of SDR104 mode
>> >>        * and for SDR50 mode when Use Tuning for SDR50 is set in
>> >>        * Capabilities register.
>> >> +      * If the Host Controller supports the HS200 mode then tuning
>> >> +      * function has to be executed.
>> >>        */
>> >>       if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
>> >>           (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
>> >> -         (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
>> >> +         (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
>> >> +         (host->flags & SDHCI_HS200_NEEDS_TUNING))
>> >>               ctrl |= SDHCI_CTRL_EXEC_TUNING;
>> >>       else {
>> >>               spin_unlock(&host->lock);
>> >> @@ -1723,7 +1728,10 @@ static int sdhci_execute_tuning(struct
>> mmc_host
>> >> *mmc)
>> >>               if (!tuning_loop_counter && !timeout)
>> >>                       break;
>> >>
>> >> -             cmd.opcode = MMC_SEND_TUNING_BLOCK;
>> >> +             if (mmc->card->type == MMC_TYPE_MMC)
>> >> +                     cmd.opcode = MMC_SEND_TUNING_BLOCK_HS200;
>> >> +             else
>> >> +                     cmd.opcode = MMC_SEND_TUNING_BLOCK;
>> >>               cmd.arg = 0;
>> >>               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
>> >>               cmd.retries = 0;
>> >> @@ -1738,7 +1746,17 @@ static int sdhci_execute_tuning(struct
>> mmc_host
>> >> *mmc)
>> >>                * block to the Host Controller. So we set the block
>> size
>> >>                * to 64 here.
>> >>                */
>> >> -             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> >> SDHCI_BLOCK_SIZE);
>> >> +             if (mmc->card->type == MMC_TYPE_MMC) {
>> >> +                     if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
>> >> +                             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7,
>> 128),
>> >> +                                          SDHCI_BLOCK_SIZE);
>> >> +                     else if (mmc->ios.bus_width ==
>> MMC_BUS_WIDTH_4)
>> >> +                             sdhci_writew(host, SDHCI_MAKE_BLKSZ(7,
>> 64),
>> >> +                                          SDHCI_BLOCK_SIZE);
>> >> +             } else {
>> >> +                     sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> >> +                                  SDHCI_BLOCK_SIZE);
>> >> +             }
>> >>
>> >>               /*
>> >>                * The tuning block is sent by the card to the host
>> >> controller.
>> >> @@ -2121,12 +2139,14 @@ static void sdhci_show_adma_error(struct
>> >> sdhci_host *host) { }
>> >>
>> >>  static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>> >>  {
>> >> +     u32 command;
>> >>       BUG_ON(intmask == 0);
>> >>
>> >>       /* CMD19 generates _only_ Buffer Read Ready interrupt */
>> >>       if (intmask & SDHCI_INT_DATA_AVAIL) {
>> >> -             if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
>> >> -                 MMC_SEND_TUNING_BLOCK) {
>> >> +             command = SDHCI_GET_CMD(sdhci_readw(host,
>> SDHCI_COMMAND));
>> >> +             if ((command == MMC_SEND_TUNING_BLOCK) ||
>> >> +                 (command == MMC_SEND_TUNING_BLOCK_HS200)) {
>> >>                       host->tuning_done = 1;
>> >>                       wake_up(&host->buf_ready_int);
>> >>                       return;
>> >> @@ -2731,6 +2751,10 @@ int sdhci_add_host(struct sdhci_host *host)
>> >>       if (caps[1] & SDHCI_USE_SDR50_TUNING)
>> >>               host->flags |= SDHCI_SDR50_NEEDS_TUNING;
>> >>
>> >> +     /* Does the host needs tuning for HS200? */
>> >> +     if (mmc->caps2 & MMC_CAP2_HS200)
>> >> +             host->flags |= SDHCI_HS200_NEEDS_TUNING;
>> >> +
>> >>       /* Driver Type(s) (A, C, D) supported by the host */
>> >>       if (caps[1] & SDHCI_DRIVER_TYPE_A)
>> >>               mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
>> >> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> >> index 6e04e10..90af438 100644
>> >> --- a/include/linux/mmc/card.h
>> >> +++ b/include/linux/mmc/card.h
>> >> @@ -206,6 +206,7 @@ struct mmc_card {
>> >>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)               /* card is in
>> high
>> >> speed mode */
>> >>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)              /* card is in
>> ultra
>> >> high speed mode */
>> >>  #define MMC_CARD_SDXC                (1<<6)          /* card is
>> SDXC */
>> >> +#define MMC_STATE_HIGHSPEED_200      (1<<7)  /* card is in HS200
>> >> mode */
>> >>       unsigned int            quirks;         /* card quirks */
>> >>  #define MMC_QUIRK_LENIENT_FN0        (1<<0)          /* allow SDIO
>> FN0
>> >> writes outside of the VS CCCR range */
>> >>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func-
>> >> >cur_blksize */
>> >> @@ -361,6 +362,7 @@ static inline void __maybe_unused
>> >> remove_quirk(struct mmc_card *card, int data)
>> >>  #define mmc_card_present(c)  ((c)->state & MMC_STATE_PRESENT)
>> >>  #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
>> >>  #define mmc_card_highspeed(c)        ((c)->state &
>> MMC_STATE_HIGHSPEED)
>> >> +#define mmc_card_hs200(c)    ((c)->state & MMC_STATE_HIGHSPEED_200)
>> >>  #define mmc_card_blockaddr(c)        ((c)->state &
>> MMC_STATE_BLOCKADDR)
>> >>  #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
>> >>  #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
>> >> @@ -369,6 +371,7 @@ static inline void __maybe_unused
>> >> remove_quirk(struct mmc_card *card, int data)
>> >>  #define mmc_card_set_present(c)      ((c)->state |=
>> MMC_STATE_PRESENT)
>> >>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>> >>  #define mmc_card_set_highspeed(c) ((c)->state |=
>> MMC_STATE_HIGHSPEED)
>> >> +#define mmc_card_set_hs200(c)        ((c)->state |=
>> >> MMC_STATE_HIGHSPEED_200)
>> >>  #define mmc_card_set_blockaddr(c) ((c)->state |=
>> MMC_STATE_BLOCKADDR)
>> >>  #define mmc_card_set_ddr_mode(c) ((c)->state |=
>> >> MMC_STATE_HIGHSPEED_DDR)
>> >>  #define mmc_sd_card_set_uhs(c) ((c)->state |=
>> >> MMC_STATE_ULTRAHIGHSPEED)
>> >> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> >> index a3ac9c4..20551f5 100644
>> >> --- a/include/linux/mmc/host.h
>> >> +++ b/include/linux/mmc/host.h
>> >> @@ -60,6 +60,8 @@ struct mmc_ios {
>> >>  #define MMC_SDR_MODE         0
>> >>  #define MMC_1_2V_DDR_MODE    1
>> >>  #define MMC_1_8V_DDR_MODE    2
>> >> +#define MMC_1_2V_SDR_MODE    3
>> >> +#define MMC_1_8V_SDR_MODE    4
>> >>
>> >>       unsigned char   signal_voltage;         /* signalling voltage
>> >> (1.8V or 3.3V) */
>> >>
>> >> @@ -242,6 +244,10 @@ struct mmc_host {
>> >>  #define MMC_CAP2_CACHE_CTRL  (1 << 1)        /* Allow cache control
>> */
>> >>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)    /* Notify poweroff
>> >> supported */
>> >>  #define MMC_CAP2_NO_MULTI_READ       (1 << 3)        /* Multiblock
>> reads
>> >> don't work */
>> >> +#define MMC_CAP2_HS200               (1 << 4)        /* Host
>> supports
>> > HS200 mode
>> >> */
>> >
>> > Why is this cap required? You have already defined this 2 caps
>> > MMC_CAP2_HS200_1_8V_SDR & MMC_CAP2_HS200_1_2V_SDR which should be
>> enough.
>> >
>> >> +#define MMC_CAP2_HS200_1_8V_SDR      (1 << 5)        /* can support
>> */
>> >> +#define MMC_CAP2_HS200_1_2V_SDR      (1 << 6)        /* can support
>> */
>> >> +#define MMC_CAP2_HIGHSPEED_200       (1 << 7)        /* Can do MMC
>> HS200
>> > Why is this is this cap required?
>> >  will remove it
>
> Both MMC_CAP2_HIGHSPEED_200 and MMC_CAP2_HS200 are redundant.
>
>> >> timing */
>> >>
>> >>       mmc_pm_flag_t           pm_caps;        /* supported pm
>> features */
>> >>       unsigned int        power_notify_type;
>> >> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> >> index 0e71356..af4fa72 100644
>> >> --- a/include/linux/mmc/mmc.h
>> >> +++ b/include/linux/mmc/mmc.h
>> >> @@ -51,6 +51,7 @@
>> >>  #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1
>> >> */
>> >>  #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1
>> >> */
>> >>  #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1
>> >> */
>> >> +#define MMC_SEND_TUNING_BLOCK_HS200  21      /* adtc R1  */
>> >>
>> >>    /* class 3 */
>> >>  #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1
>> >> */
>> >> @@ -333,13 +334,18 @@ struct _mmc_csd {
>> >>
>> >>  #define EXT_CSD_CARD_TYPE_26 (1<<0)  /* Card can run at 26MHz */
>> >>  #define EXT_CSD_CARD_TYPE_52 (1<<1)  /* Card can run at 52MHz */
>> >> -#define EXT_CSD_CARD_TYPE_MASK       0xF     /* Mask out reserved
>> bits */
>> >> +#define EXT_CSD_CARD_TYPE_MASK       0x3F    /* Mask out reserved
>> bits */
>> >>  #define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at
>> 52MHz
>> >> */
>> >>                                            /* DDR mode @1.8V or 3V
>> I/O */
>> >>  #define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at
>> 52MHz
>> >> */
>> >>                                            /* DDR mode @1.2V I/O */
>> >>  #define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V
>>  \
>> >>                                       | EXT_CSD_CARD_TYPE_DDR_1_2V)
>> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V   (1<<4)  /* Card can run at
>> >> 200MHz */
>> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V   (1<<5)  /* Card can run at
>> >> 200MHz */
>> >> +                                             /* SDR mode @1.2V I/O
>> */
>> >> +#define EXT_CSD_CARD_TYPE_SDR_200    (EXT_CSD_CARD_TYPE_SDR_1_8V
>>   \
>> >> +                                     | EXT_CSD_CARD_TYPE_SDR_1_2V)
>> >>
>> >>  #define EXT_CSD_BUS_WIDTH_1  0       /* Card is in 1 bit mode */
>> >>  #define EXT_CSD_BUS_WIDTH_4  1       /* Card is in 4 bit mode */
>> >> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
>> >> index e4b6935..d9a2222 100644
>> >> --- a/include/linux/mmc/sdhci.h
>> >> +++ b/include/linux/mmc/sdhci.h
>> >> @@ -121,6 +121,7 @@ struct sdhci_host {
>> >>  #define SDHCI_AUTO_CMD23     (1<<7)  /* Auto CMD23 support */
>> >>  #define SDHCI_PV_ENABLED     (1<<8)  /* Preset value enabled */
>> >>  #define SDHCI_SDIO_IRQ_ENABLED       (1<<9)  /* SDIO irq enabled */
>> >> +#define SDHCI_HS200_NEEDS_TUNING (1<<10)     /* HS200 needs tuning
>> */
>> >>
>> >>       unsigned int version;   /* SDHCI spec. version */
>> >>
>> >> --
>> >> 1.7.1
>> >>
>> >> --
>> >> 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/core/bus.c b/drivers/mmc/core/bus.c
index 46b6e84..2f82f6b 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -301,10 +301,11 @@  int mmc_add_card(struct mmc_card *card)
 			mmc_card_ddr_mode(card) ? "DDR " : "",
 			type);
 	} else {
-		printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
+		pr_info("%s: new %s%s%s%s card at address %04x\n",
 			mmc_hostname(card->host),
 			mmc_sd_card_uhs(card) ? "ultra high speed " :
 			(mmc_card_highspeed(card) ? "high speed " : ""),
+			(mmc_card_hs200(card) ? "HS200 " : ""),
 			mmc_card_ddr_mode(card) ? "DDR " : "",
 			type, card->rca);
 	}
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3627044..4db248c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -285,6 +285,39 @@  static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
 	}
 	card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
 	switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
+	case EXT_CSD_CARD_TYPE_SDR_200 |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_8V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_1_2V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_200 | EXT_CSD_CARD_TYPE_DDR_52 |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+		card->ext_csd.hs_max_dtr = 200000000;
+		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
+		break;
+	case EXT_CSD_CARD_TYPE_SDR_1_2V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_8V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_1_2V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_1_2V | EXT_CSD_CARD_TYPE_DDR_52 |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+		card->ext_csd.hs_max_dtr = 200000000;
+		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
+		break;
+	case EXT_CSD_CARD_TYPE_SDR_1_8V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_8V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+	case EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_DDR_52 |
+	     EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+		card->ext_csd.hs_max_dtr = 200000000;
+		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
+		break;
 	case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
 	     EXT_CSD_CARD_TYPE_26:
 		card->ext_csd.hs_max_dtr = 52000000;
@@ -699,6 +732,7 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 {
 	struct mmc_card *card;
 	int err, ddr = 0;
+	int hs_sdr = 0;
 	u32 cid[4];
 	unsigned int max_dtr;
 	u32 rocr;
@@ -890,11 +924,15 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 	/*
 	 * Activate high speed (if supported)
 	 */
-	if ((card->ext_csd.hs_max_dtr != 0) &&
-		(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
-		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-				 EXT_CSD_HS_TIMING, 1,
-				 card->ext_csd.generic_cmd6_time);
+	if (card->ext_csd.hs_max_dtr != 0) {
+		if ((card->ext_csd.hs_max_dtr > 52000000) &&
+		    (host->caps2 & MMC_CAP2_HIGHSPEED_200))
+			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					 EXT_CSD_HS_TIMING, 2, 0);
+		else if	(host->caps & MMC_CAP_MMC_HIGHSPEED)
+			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					 EXT_CSD_HS_TIMING, 1, 0);
+
 		if (err && err != -EBADMSG)
 			goto free_card;
 
@@ -903,7 +941,10 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 			       mmc_hostname(card->host));
 			err = 0;
 		} else {
-			mmc_card_set_highspeed(card);
+			if (card->ext_csd.hs_max_dtr > 52000000)
+				mmc_card_set_hs200(card);
+			else
+				mmc_card_set_highspeed(card);
 			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
 		}
 	}
@@ -929,7 +970,7 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 	 */
 	max_dtr = (unsigned int)-1;
 
-	if (mmc_card_highspeed(card)) {
+	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
 		if (max_dtr > card->ext_csd.hs_max_dtr)
 			max_dtr = card->ext_csd.hs_max_dtr;
 	} else if (max_dtr > card->csd.max_dtr) {
@@ -955,6 +996,22 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 	}
 
 	/*
+	 * Indicate HS200 SDR mode (if supported).
+	 */
+	if (mmc_card_hs200(card)) {
+		if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V)
+			&& ((host->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
+			     MMC_CAP2_HS200))
+				== (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200)))
+				hs_sdr = MMC_1_8V_SDR_MODE;
+		else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)
+			 && ((host->caps2 & (MMC_CAP2_HS200_1_2V_SDR |
+			      MMC_CAP2_HS200))
+				== (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS200)))
+				hs_sdr = MMC_1_2V_SDR_MODE;
+	}
+
+	/*
 	 * Activate wide bus and DDR (if supported).
 	 */
 	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
@@ -994,16 +1051,24 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 			if (!err) {
 				mmc_set_bus_width(card->host, bus_width);
 
+				if ((host->caps2 & MMC_CAP2_HS200) &&
+				    card->host->ops->execute_tuning)
+					err = card->host->ops->	\
+					      execute_tuning(card->host);
+
 				/*
 				 * If controller can't handle bus width test,
 				 * compare ext_csd previously read in 1 bit mode
 				 * against ext_csd at new bus width
 				 */
-				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
+				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST) &&
+				    !err)
 					err = mmc_compare_ext_csds(card,
 						bus_width);
-				else
+				else if (!err)
 					err = mmc_bus_test(card, bus_width);
+				else
+					pr_warning("tuning execution failed\n");
 				if (!err)
 					break;
 			}
@@ -1052,6 +1117,15 @@  static int mmc_init_card(struct mmc_host *host, u32 ocr,
 			mmc_card_set_ddr_mode(card);
 			mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
 			mmc_set_bus_width(card->host, bus_width);
+		} else if (hs_sdr) {
+			if (hs_sdr == EXT_CSD_CARD_TYPE_SDR_1_2V) {
+				err = mmc_set_signal_voltage(host,
+					MMC_SIGNAL_VOLTAGE_120, 0);
+				if (err)
+					goto err;
+			}
+			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+			mmc_set_bus_width(card->host, bus_width);
 		}
 	}
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index a69d269..28fb747 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -27,6 +27,7 @@ 
 
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
 
 #include "sdhci.h"
 
@@ -1016,7 +1017,8 @@  static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 		flags |= SDHCI_CMD_INDEX;
 
 	/* CMD19 is special in that the Data Present Select should be set */
-	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+	    (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
 		flags |= SDHCI_CMD_DATA;
 
 	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1684,10 +1686,13 @@  static int sdhci_execute_tuning(struct mmc_host *mmc)
 	 * Host Controller needs tuning only in case of SDR104 mode
 	 * and for SDR50 mode when Use Tuning for SDR50 is set in
 	 * Capabilities register.
+	 * If the Host Controller supports the HS200 mode then tuning
+	 * function has to be executed.
 	 */
 	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
 	    (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
-	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
+	    (host->flags & SDHCI_HS200_NEEDS_TUNING))
 		ctrl |= SDHCI_CTRL_EXEC_TUNING;
 	else {
 		spin_unlock(&host->lock);
@@ -1723,7 +1728,10 @@  static int sdhci_execute_tuning(struct mmc_host *mmc)
 		if (!tuning_loop_counter && !timeout)
 			break;
 
-		cmd.opcode = MMC_SEND_TUNING_BLOCK;
+		if (mmc->card->type == MMC_TYPE_MMC)
+			cmd.opcode = MMC_SEND_TUNING_BLOCK_HS200;
+		else
+			cmd.opcode = MMC_SEND_TUNING_BLOCK;
 		cmd.arg = 0;
 		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 		cmd.retries = 0;
@@ -1738,7 +1746,17 @@  static int sdhci_execute_tuning(struct mmc_host *mmc)
 		 * block to the Host Controller. So we set the block size
 		 * to 64 here.
 		 */
-		sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+		if (mmc->card->type == MMC_TYPE_MMC) {
+			if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+					     SDHCI_BLOCK_SIZE);
+			else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+					     SDHCI_BLOCK_SIZE);
+		} else {
+			sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+				     SDHCI_BLOCK_SIZE);
+		}
 
 		/*
 		 * The tuning block is sent by the card to the host controller.
@@ -2121,12 +2139,14 @@  static void sdhci_show_adma_error(struct sdhci_host *host) { }
 
 static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 {
+	u32 command;
 	BUG_ON(intmask == 0);
 
 	/* CMD19 generates _only_ Buffer Read Ready interrupt */
 	if (intmask & SDHCI_INT_DATA_AVAIL) {
-		if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
-		    MMC_SEND_TUNING_BLOCK) {
+		command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+		if ((command == MMC_SEND_TUNING_BLOCK) ||
+		    (command == MMC_SEND_TUNING_BLOCK_HS200)) {
 			host->tuning_done = 1;
 			wake_up(&host->buf_ready_int);
 			return;
@@ -2731,6 +2751,10 @@  int sdhci_add_host(struct sdhci_host *host)
 	if (caps[1] & SDHCI_USE_SDR50_TUNING)
 		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
 
+	/* Does the host needs tuning for HS200? */
+	if (mmc->caps2 & MMC_CAP2_HS200)
+		host->flags |= SDHCI_HS200_NEEDS_TUNING;
+
 	/* Driver Type(s) (A, C, D) supported by the host */
 	if (caps[1] & SDHCI_DRIVER_TYPE_A)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 6e04e10..90af438 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -206,6 +206,7 @@  struct mmc_card {
 #define MMC_STATE_HIGHSPEED_DDR (1<<4)		/* card is in high speed mode */
 #define MMC_STATE_ULTRAHIGHSPEED (1<<5)		/* card is in ultra high speed mode */
 #define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
+#define MMC_STATE_HIGHSPEED_200	(1<<7)	/* card is in HS200 mode */
 	unsigned int		quirks; 	/* card quirks */
 #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize */
@@ -361,6 +362,7 @@  static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
 #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
 #define mmc_card_highspeed(c)	((c)->state & MMC_STATE_HIGHSPEED)
+#define mmc_card_hs200(c)	((c)->state & MMC_STATE_HIGHSPEED_200)
 #define mmc_card_blockaddr(c)	((c)->state & MMC_STATE_BLOCKADDR)
 #define mmc_card_ddr_mode(c)	((c)->state & MMC_STATE_HIGHSPEED_DDR)
 #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
@@ -369,6 +371,7 @@  static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
+#define mmc_card_set_hs200(c)	((c)->state |= MMC_STATE_HIGHSPEED_200)
 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
 #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
 #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index a3ac9c4..20551f5 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -60,6 +60,8 @@  struct mmc_ios {
 #define MMC_SDR_MODE		0
 #define MMC_1_2V_DDR_MODE	1
 #define MMC_1_8V_DDR_MODE	2
+#define MMC_1_2V_SDR_MODE	3
+#define MMC_1_8V_SDR_MODE	4
 
 	unsigned char	signal_voltage;		/* signalling voltage (1.8V or 3.3V) */
 
@@ -242,6 +244,10 @@  struct mmc_host {
 #define MMC_CAP2_CACHE_CTRL	(1 << 1)	/* Allow cache control */
 #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)	/* Notify poweroff supported */
 #define MMC_CAP2_NO_MULTI_READ	(1 << 3)	/* Multiblock reads don't work */
+#define MMC_CAP2_HS200		(1 << 4)	/* Host supports HS200 mode */
+#define MMC_CAP2_HS200_1_8V_SDR	(1 << 5)	/* can support */
+#define MMC_CAP2_HS200_1_2V_SDR	(1 << 6)	/* can support */
+#define MMC_CAP2_HIGHSPEED_200	(1 << 7)	/* Can do MMC HS200 timing */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 	unsigned int        power_notify_type;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 0e71356..af4fa72 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -51,6 +51,7 @@ 
 #define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
 #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
 #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1  */
+#define MMC_SEND_TUNING_BLOCK_HS200	21	/* adtc R1  */
 
   /* class 3 */
 #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
@@ -333,13 +334,18 @@  struct _mmc_csd {
 
 #define EXT_CSD_CARD_TYPE_26	(1<<0)	/* Card can run at 26MHz */
 #define EXT_CSD_CARD_TYPE_52	(1<<1)	/* Card can run at 52MHz */
-#define EXT_CSD_CARD_TYPE_MASK	0xF	/* Mask out reserved bits */
+#define EXT_CSD_CARD_TYPE_MASK	0x3F	/* Mask out reserved bits */
 #define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at 52MHz */
 					     /* DDR mode @1.8V or 3V I/O */
 #define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at 52MHz */
 					     /* DDR mode @1.2V I/O */
 #define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V  \
 					| EXT_CSD_CARD_TYPE_DDR_1_2V)
+#define EXT_CSD_CARD_TYPE_SDR_1_8V	(1<<4)	/* Card can run at 200MHz */
+#define EXT_CSD_CARD_TYPE_SDR_1_2V	(1<<5)	/* Card can run at 200MHz */
+						/* SDR mode @1.2V I/O */
+#define EXT_CSD_CARD_TYPE_SDR_200	(EXT_CSD_CARD_TYPE_SDR_1_8V	\
+					| EXT_CSD_CARD_TYPE_SDR_1_2V)
 
 #define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
 #define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index e4b6935..d9a2222 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -121,6 +121,7 @@  struct sdhci_host {
 #define SDHCI_AUTO_CMD23	(1<<7)	/* Auto CMD23 support */
 #define SDHCI_PV_ENABLED	(1<<8)	/* Preset value enabled */
 #define SDHCI_SDIO_IRQ_ENABLED	(1<<9)	/* SDIO irq enabled */
+#define SDHCI_HS200_NEEDS_TUNING (1<<10)	/* HS200 needs tuning */
 
 	unsigned int version;	/* SDHCI spec. version */