summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2015-02-06 14:13:00 +0200
committerUlf Hansson <ulf.hansson@linaro.org>2015-06-01 09:07:14 +0200
commite1bfad6d936d7149a83423e2a7244dd5771f27e7 (patch)
tree4ef73a2b9a7b21597679d36959e0db9ebda3e9f5 /drivers/mmc
parentcb8496482e8886bbd6bd79462d8062db572b0473 (diff)
downloadlinux-e1bfad6d936d7149a83423e2a7244dd5771f27e7.tar.bz2
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>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-pci-data.c3
-rw-r--r--drivers/mmc/host/sdhci-pci.c83
-rw-r--r--drivers/mmc/host/sdhci-pci.h4
3 files changed, 90 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c
index a611217769f5..56fddc622a54 100644
--- a/drivers/mmc/host/sdhci-pci-data.c
+++ b/drivers/mmc/host/sdhci-pci-data.c
@@ -3,3 +3,6 @@
struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
+
+int sdhci_pci_spt_drive_strength;
+EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 7a3fc16d0a6c..6ecd158d8084 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
#include <linux/scatterlist.h>
#include <linux/io.h>
#include <linux/gpio.h>
@@ -266,6 +267,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,
};
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 1ec684d06d54..541f1cad5247 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -72,6 +72,10 @@ struct sdhci_pci_slot {
bool cd_override_level;
void (*hw_reset)(struct sdhci_host *host);
+ int (*select_drive_strength)(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int max_dtr, int host_drv,
+ int card_drv, int *drv_type);
};
struct sdhci_pci_chip {