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);
 | 
					struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
 | 
				
			||||||
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
 | 
					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/slab.h>
 | 
				
			||||||
#include <linux/device.h>
 | 
					#include <linux/device.h>
 | 
				
			||||||
#include <linux/mmc/host.h>
 | 
					#include <linux/mmc/host.h>
 | 
				
			||||||
 | 
					#include <linux/mmc/mmc.h>
 | 
				
			||||||
#include <linux/scatterlist.h>
 | 
					#include <linux/scatterlist.h>
 | 
				
			||||||
#include <linux/io.h>
 | 
					#include <linux/io.h>
 | 
				
			||||||
#include <linux/gpio.h>
 | 
					#include <linux/gpio.h>
 | 
				
			||||||
| 
						 | 
					@ -266,6 +267,69 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
 | 
				
			||||||
	usleep_range(300, 1000);
 | 
						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)
 | 
					static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
 | 
						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;
 | 
						slot->hw_reset = sdhci_pci_int_hw_reset;
 | 
				
			||||||
	if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
 | 
						if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
 | 
				
			||||||
		slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
 | 
							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;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1203,6 +1271,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
 | 
				
			||||||
		slot->hw_reset(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 = {
 | 
					static const struct sdhci_ops sdhci_pci_ops = {
 | 
				
			||||||
	.set_clock	= sdhci_set_clock,
 | 
						.set_clock	= sdhci_set_clock,
 | 
				
			||||||
	.enable_dma	= sdhci_pci_enable_dma,
 | 
						.enable_dma	= sdhci_pci_enable_dma,
 | 
				
			||||||
| 
						 | 
					@ -1210,6 +1292,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
 | 
				
			||||||
	.reset		= sdhci_reset,
 | 
						.reset		= sdhci_reset,
 | 
				
			||||||
	.set_uhs_signaling = sdhci_set_uhs_signaling,
 | 
						.set_uhs_signaling = sdhci_set_uhs_signaling,
 | 
				
			||||||
	.hw_reset		= sdhci_pci_hw_reset,
 | 
						.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;
 | 
						bool			cd_override_level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void (*hw_reset)(struct sdhci_host *host);
 | 
						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 {
 | 
					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,
 | 
					extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
 | 
				
			||||||
				int slotno);
 | 
									int slotno);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int sdhci_pci_spt_drive_strength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue