forked from mirrors/linux
		
	mmc: sdhci-pci: Add support for drive strength selection for SPT
Implement the select_drive_strength callback to provide drive strength selection for Intel SPT. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
		
							parent
							
								
									cb8496482e
								
							
						
					
					
						commit
						e1bfad6d93
					
				
					 4 changed files with 92 additions and 0 deletions
				
			
		|  | @ -3,3 +3,6 @@ | |||
| 
 | ||||
| struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno); | ||||
| EXPORT_SYMBOL_GPL(sdhci_pci_get_data); | ||||
| 
 | ||||
| int sdhci_pci_spt_drive_strength; | ||||
| EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength); | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
| #include <linux/slab.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/mmc/host.h> | ||||
| #include <linux/mmc/mmc.h> | ||||
| #include <linux/scatterlist.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/gpio.h> | ||||
|  | @ -266,6 +267,69 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host) | |||
| 	usleep_range(300, 1000); | ||||
| } | ||||
| 
 | ||||
| static int spt_select_drive_strength(struct sdhci_host *host, | ||||
| 				     struct mmc_card *card, | ||||
| 				     unsigned int max_dtr, | ||||
| 				     int host_drv, int card_drv, int *drv_type) | ||||
| { | ||||
| 	int drive_strength; | ||||
| 
 | ||||
| 	if (sdhci_pci_spt_drive_strength > 0) | ||||
| 		drive_strength = sdhci_pci_spt_drive_strength & 0xf; | ||||
| 	else | ||||
| 		drive_strength = 1; /* 33-ohm */ | ||||
| 
 | ||||
| 	if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0) | ||||
| 		drive_strength = 0; /* Default 50-ohm */ | ||||
| 
 | ||||
| 	return drive_strength; | ||||
| } | ||||
| 
 | ||||
| /* Try to read the drive strength from the card */ | ||||
| static void spt_read_drive_strength(struct sdhci_host *host) | ||||
| { | ||||
| 	u32 val, i, t; | ||||
| 	u16 m; | ||||
| 
 | ||||
| 	if (sdhci_pci_spt_drive_strength) | ||||
| 		return; | ||||
| 
 | ||||
| 	sdhci_pci_spt_drive_strength = -1; | ||||
| 
 | ||||
| 	m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7; | ||||
| 	if (m != 3 && m != 5) | ||||
| 		return; | ||||
| 	val = sdhci_readl(host, SDHCI_PRESENT_STATE); | ||||
| 	if (val & 0x3) | ||||
| 		return; | ||||
| 	sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE); | ||||
| 	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); | ||||
| 	sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE); | ||||
| 	sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); | ||||
| 	sdhci_writew(host, 512, SDHCI_BLOCK_SIZE); | ||||
| 	sdhci_writew(host, 1, SDHCI_BLOCK_COUNT); | ||||
| 	sdhci_writel(host, 0, SDHCI_ARGUMENT); | ||||
| 	sdhci_writew(host, 0x83b, SDHCI_COMMAND); | ||||
| 	for (i = 0; i < 1000; i++) { | ||||
| 		val = sdhci_readl(host, SDHCI_INT_STATUS); | ||||
| 		if (val & 0xffff8000) | ||||
| 			return; | ||||
| 		if (val & 0x20) | ||||
| 			break; | ||||
| 		udelay(1); | ||||
| 	} | ||||
| 	val = sdhci_readl(host, SDHCI_PRESENT_STATE); | ||||
| 	if (!(val & 0x800)) | ||||
| 		return; | ||||
| 	for (i = 0; i < 47; i++) | ||||
| 		val = sdhci_readl(host, SDHCI_BUFFER); | ||||
| 	t = val & 0xf00; | ||||
| 	if (t != 0x200 && t != 0x300) | ||||
| 		return; | ||||
| 
 | ||||
| 	sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf); | ||||
| } | ||||
| 
 | ||||
| static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) | ||||
| { | ||||
| 	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | | ||||
|  | @ -276,6 +340,10 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) | |||
| 	slot->hw_reset = sdhci_pci_int_hw_reset; | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) | ||||
| 		slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ | ||||
| 	if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) { | ||||
| 		spt_read_drive_strength(slot->host); | ||||
| 		slot->select_drive_strength = spt_select_drive_strength; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -1203,6 +1271,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) | |||
| 		slot->hw_reset(host); | ||||
| } | ||||
| 
 | ||||
| static int sdhci_pci_select_drive_strength(struct sdhci_host *host, | ||||
| 					   struct mmc_card *card, | ||||
| 					   unsigned int max_dtr, int host_drv, | ||||
| 					   int card_drv, int *drv_type) | ||||
| { | ||||
| 	struct sdhci_pci_slot *slot = sdhci_priv(host); | ||||
| 
 | ||||
| 	if (!slot->select_drive_strength) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return slot->select_drive_strength(host, card, max_dtr, host_drv, | ||||
| 					   card_drv, drv_type); | ||||
| } | ||||
| 
 | ||||
| static const struct sdhci_ops sdhci_pci_ops = { | ||||
| 	.set_clock	= sdhci_set_clock, | ||||
| 	.enable_dma	= sdhci_pci_enable_dma, | ||||
|  | @ -1210,6 +1292,7 @@ static const struct sdhci_ops sdhci_pci_ops = { | |||
| 	.reset		= sdhci_reset, | ||||
| 	.set_uhs_signaling = sdhci_set_uhs_signaling, | ||||
| 	.hw_reset		= sdhci_pci_hw_reset, | ||||
| 	.select_drive_strength	= sdhci_pci_select_drive_strength, | ||||
| }; | ||||
| 
 | ||||
| /*****************************************************************************\
 | ||||
|  |  | |||
|  | @ -72,6 +72,10 @@ struct sdhci_pci_slot { | |||
| 	bool			cd_override_level; | ||||
| 
 | ||||
| 	void (*hw_reset)(struct sdhci_host *host); | ||||
| 	int (*select_drive_strength)(struct sdhci_host *host, | ||||
| 				     struct mmc_card *card, | ||||
| 				     unsigned int max_dtr, int host_drv, | ||||
| 				     int card_drv, int *drv_type); | ||||
| }; | ||||
| 
 | ||||
| struct sdhci_pci_chip { | ||||
|  |  | |||
|  | @ -15,4 +15,6 @@ struct sdhci_pci_data { | |||
| extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, | ||||
| 				int slotno); | ||||
| 
 | ||||
| extern int sdhci_pci_spt_drive_strength; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Adrian Hunter
						Adrian Hunter