Re: [PATCH] mmc: sdhci-acpi: Add support for ACPI HID of AMD Controller with HS400

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 7/25/2017 3:28 PM, Adrian Hunter wrote:
> On 28/06/17 13:53, Shah, Nehal-bakulchandra wrote:
>> From: Shah Nehal-Bakulchandra <Nehal-bakulchandra.Shah@xxxxxxx>
>>
>> This patch supports HS400 for AMD upcoming emmc 5.0 controller.The
>> HS400 and HS200 mode requires hardware work around also. This patch
>> adds the quirks for the same.
> 
> I have some comments and we need Ulf's feedback for the core changes.
> 
>>
>> Signed-off-by: Shah Nehal-Bakulchandra <Nehal-bakulchandra.Shah@xxxxxxx>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@xxxxxxx>
>> ---
>>  drivers/mmc/core/mmc.c        | 36 +++++++++++++++++++-----------------
>>  drivers/mmc/host/sdhci-acpi.c | 36 ++++++++++++++++++++++++++++++++++++
>>  drivers/mmc/host/sdhci.c      | 22 ++++++++++++++++++++++
>>  drivers/mmc/host/sdhci.h      |  4 +++-
>>  include/linux/mmc/host.h      |  1 +
> 
> Please split separate the core changes (mmc.c and host.h) from the driver
> changes by moving them to a separate patch.
> 
>>  5 files changed, 81 insertions(+), 18 deletions(-)
>>
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 4ffea14..29c46d7 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -1161,14 +1161,14 @@ static int mmc_select_hs400(struct mmc_card *card)
>>  			mmc_hostname(host), err);
>>  		return err;
>>  	}
>> -
>> -	/* Set host controller to HS timing */
>> -	mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> -
>> -	/* Reduce frequency to HS frequency */
>> -	max_dtr = card->ext_csd.hs_max_dtr;
>> -	mmc_set_clock(host, max_dtr);
>> -
>> +	/*In AMD Platform due to hardware ip issue this fails*/
>> +	if (!host->ops->set_hs400_dll) {
>> +		/* Set host controller to HS timing */
>> +		mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> +		/* Reduce frequency to HS frequency */
>> +		max_dtr = card->ext_csd.hs_max_dtr;
>> +		mmc_set_clock(host, max_dtr);
>> +	}
> 
> Really need to re-consider the host API here.  One possibility is to add
> another member to struct mmc_ios that indicates the transition being made.
> e.g. HS200_TO_HS400_TO_HS in this case i.e. going to HS while on the way
> from HS200 to HS400.  Then the driver can presumably do the right thing (in
> this case nothing) in its ->set_ios() callback.  In any case it is up to Ulf
> to comment on this.
> 
>>  	err = mmc_switch_status(card);
>>  	if (err)
>>  		goto out_err;
>> @@ -1204,7 +1204,8 @@ static int mmc_select_hs400(struct mmc_card *card)
>>  	err = mmc_switch_status(card);
>>  	if (err)
>>  		goto out_err;
>> -
>> +	if (host->ops->set_hs400_dll)
>> +		host->ops->set_hs400_dll(host);
> 
> Why is this after checking switch status instead of before?
> 
>>  	return 0;
>>  
>>  out_err:
>> @@ -1227,8 +1228,8 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
>>  
>>  	/* Reduce frequency to HS */
>>  	max_dtr = card->ext_csd.hs_max_dtr;
>> -	mmc_set_clock(host, max_dtr);
>> -
>> +	if (!host->ops->set_hs400_dll)
>> +		mmc_set_clock(host, max_dtr);
>>  	/* Switch HS400 to HS DDR */
>>  	val = EXT_CSD_TIMING_HS;
>>  	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
>> @@ -1236,12 +1237,13 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
>>  			   true, false, true);
>>  	if (err)
>>  		goto out_err;
>> -
>> -	mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
>> -
>> -	err = mmc_switch_status(card);
>> -	if (err)
>> -		goto out_err;
>> +       /*In AMD Platform due to hardware ip issue this fails*/
>> +	if (!host->ops->set_hs400_dll) {
>> +		mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
>> +		err = mmc_switch_status(card);
>> +		if (err)
>> +			goto out_err;
>> +	}
>>  
>>  	/* Switch HS DDR to HS */
>>  	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
>> diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
>> index cf66a3d..7702459 100644
>> --- a/drivers/mmc/host/sdhci-acpi.c
>> +++ b/drivers/mmc/host/sdhci-acpi.c
>> @@ -103,6 +103,16 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
>>  	usleep_range(300, 1000);
>>  }
>>  
>> +static void sdhci_acpi_amd_hs400_dll(struct sdhci_host *host)
>> +{
>> +	host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> 
> Why are you re-reading caps1 here?
> 
>> +	if (host->mmc->caps2 == MMC_CAP2_HS400_1_8V) {
> 
> It would be better to avoid setting the callback function when it isn't needed.
> 
>> +		sdhci_writel(host, 0x40003210, 0x908);
>> +		udelay(10);
>> +		sdhci_writel(host, 0x40033210, 0x908);
>> +	}
>> +}
>> +
>>  static const struct sdhci_ops sdhci_acpi_ops_dflt = {
>>  	.set_clock = sdhci_set_clock,
>>  	.set_bus_width = sdhci_set_bus_width,
>> @@ -122,6 +132,17 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
>>  	.ops = &sdhci_acpi_ops_int,
>>  };
>>  
>> +static const struct sdhci_ops sdhci_acpi_ops_amd = {
>> +	.set_clock = sdhci_set_clock,
>> +	.set_bus_width = sdhci_set_bus_width,
>> +	.reset = sdhci_reset,
>> +	.set_uhs_signaling = sdhci_set_uhs_signaling,
>> +	.set_hs400_dll = sdhci_acpi_amd_hs400_dll,
>> +};
>> +
>> +static const struct sdhci_acpi_chip sdhci_acpi_chip_amd = {
>> +	.ops = &sdhci_acpi_ops_amd,
>> +};
>>  #ifdef CONFIG_X86
>>  
>>  static bool sdhci_acpi_byt(void)
>> @@ -282,6 +303,18 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
>>  	.probe_slot	= sdhci_acpi_emmc_probe_slot,
>>  };
>>  
>> +static const struct sdhci_acpi_slot sdhci_acpi_slot_amd_emmc = {
>> +	.chip    = &sdhci_acpi_chip_amd,
>> +	.caps    = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
>> +		   MMC_CAP_HW_RESET,
>> +	.quirks  = SDHCI_QUIRK_32BIT_DMA_ADDR |
>> +		   SDHCI_QUIRK_32BIT_DMA_SIZE |
>> +		   SDHCI_QUIRK_32BIT_ADMA_SIZE,
>> +	.quirks2 = SDHCI_QUIRK2_BROKEN_TUNING_WA,
>> +	.probe_slot     = sdhci_acpi_emmc_probe_slot,
> 
> You should probably implement your own ->probe_slot() function instead of
> sdhci_acpi_emmc_probe_slot() and then put your caps setting there.
> 
>> +
> 
> Unnecessary blank line.
> 
>> +};
>> +
>>  static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
>>  	.quirks  = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
>>  		   SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
>> @@ -335,6 +368,8 @@ struct sdhci_acpi_uid_slot {
>>  	{ "INT344D"  , NULL, &sdhci_acpi_slot_int_sdio },
>>  	{ "PNP0FFF"  , "3" , &sdhci_acpi_slot_int_sd   },
>>  	{ "PNP0D40"  },
>> +	{ "AMDI0040", NULL, &sdhci_acpi_slot_amd_emmc  },
>> +	{ "AMDI0040"},
> 
> The second line here will never match anything, please remove it.
> 
>>  	{ "QCOM8051", NULL, &sdhci_acpi_slot_qcom_sd_3v },
>>  	{ "QCOM8052", NULL, &sdhci_acpi_slot_qcom_sd },
>>  	{ },
>> @@ -353,6 +388,7 @@ struct sdhci_acpi_uid_slot {
>>  	{ "PNP0D40"  },
>>  	{ "QCOM8051" },
>>  	{ "QCOM8052" },
>> +	{ "AMDI0040" },
>>  	{ },
>>  };
>>  MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index ecd0d43..6e0e73d 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -1170,6 +1170,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>>  		flags |= SDHCI_CMD_DATA;
>>  
>>  	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
>> +
>> +	if (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200  &&
>> +	    (host->quirks2 & SDHCI_QUIRK2_BROKEN_TUNING_WA)) {
>> +		mdelay(10);
>> +		sdhci_writel(host, 0x8803040a, 0x8b8);
>> +		mdelay(10);
>> +	}
> 
> That could be better done by making use of CONFIG_MMC_SDHCI_IO_ACCESSORS and
> providing an implementation for host->ops->write_w() that does that check
> for the SDHCI_COMMAND register.
> 
>>  }
>>  EXPORT_SYMBOL_GPL(sdhci_send_command);
>>  
>> @@ -1818,6 +1825,14 @@ static void sdhci_hw_reset(struct mmc_host *mmc)
>>  		host->ops->hw_reset(host);
>>  }
>>  
>> +static void sdhci_set_hs400_dll(struct mmc_host *mmc)
>> +{
>> +	struct sdhci_host *host = mmc_priv(mmc);
>> +
>> +	if (host->ops && host->ops->set_hs400_dll)
>> +		host->ops->set_hs400_dll(host);
>> +}
> 
> This isn't necessary.  The driver can override mmc host operations directly.
> i.e.
> 	host->mmc_host_ops.set_hs400_dll = sdhci_acpi_amd_hs400_dll;
> 
> Although if my suggestion about struct mmc_ios is taken up, then you will
> need to override ->set_ios as well or instead.
> 
>> +
>>  static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
>>  {
>>  	if (!(host->flags & SDHCI_DEVICE_DEAD)) {
>> @@ -2295,6 +2310,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
>>  	.get_cd		= sdhci_get_cd,
>>  	.get_ro		= sdhci_get_ro,
>>  	.hw_reset	= sdhci_hw_reset,
>> +	.set_hs400_dll = sdhci_set_hs400_dll,
> 
> As above, this isn't necessary.
> 
>>  	.enable_sdio_irq = sdhci_enable_sdio_irq,
>>  	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
>>  	.prepare_hs400_tuning		= sdhci_prepare_hs400_tuning,
>> @@ -3202,6 +3218,12 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
>>  		host->caps1 &= ~upper_32_bits(dt_caps_mask);
>>  		host->caps1 |= upper_32_bits(dt_caps);
>>  	}
>> +
>> +	if ((host->caps1 & SDHCI_SUPPORT_SDR104) &&
>> +	    (host->caps1 & SDHCI_SUPPORT_DDR50) &&
>> +	    (host->quirks2 & SDHCI_QUIRK2_BROKEN_TUNING_WA)) {
>> +		host->mmc->caps2 = MMC_CAP2_HS400_1_8V;
>> +	}
> 
> This doesn't belong here.  See further above about ->probe_slot().
> 
>>  }
>>  EXPORT_SYMBOL_GPL(__sdhci_read_caps);
>>  
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index 0469fa1..d3ec57c 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -435,7 +435,8 @@ struct sdhci_host {
>>  #define SDHCI_QUIRK2_ACMD23_BROKEN			(1<<14)
>>  /* Broken Clock divider zero in controller */
>>  #define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN		(1<<15)
>> -
>> +/* Tuning work around */
>> +#define SDHCI_QUIRK2_BROKEN_TUNING_WA		        (1<<16)
>>  	int irq;		/* Device IRQ */
>>  	void __iomem *ioaddr;	/* Mapped address */
>>  
>> @@ -576,6 +577,7 @@ struct sdhci_ops {
>>  	int	(*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
>>  	void	(*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
>>  	void	(*hw_reset)(struct sdhci_host *host);
>> +	void    (*set_hs400_dll)(struct sdhci_host *host);
> 
> As further above, this isn't necessary.
> 
>>  	void    (*adma_workaround)(struct sdhci_host *host, u32 intmask);
>>  	void    (*card_event)(struct sdhci_host *host);
>>  	void	(*voltage_switch)(struct sdhci_host *host);
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index ebd1ceb..1b00c28 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -152,6 +152,7 @@ struct mmc_host_ops {
>>  					 unsigned int max_dtr, int host_drv,
>>  					 int card_drv, int *drv_type);
>>  	void	(*hw_reset)(struct mmc_host *host);
>> +	void	(*set_hs400_dll)(struct mmc_host *host);
>>  	void	(*card_event)(struct mmc_host *host);
>>  
>>  	/*
>>
> 
Hi Adrian

Thanks for the valuable feedback and i will get back with new patch trying incorporating reviw comments. I will also try to minimize the dependency on core changes.

Regards
Nehal Shah

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



[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux