Skip to content

Commit

Permalink
mmc: sdhci-pci: Add support for drive strength selection for SPT
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Adrian Hunter authored and Ulf Hansson committed Jun 1, 2015
1 parent cb84964 commit e1bfad6
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 0 deletions.
3 changes: 3 additions & 0 deletions drivers/mmc/host/sdhci-pci-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
83 changes: 83 additions & 0 deletions drivers/mmc/host/sdhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 |
Expand All @@ -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;
}

Expand Down Expand Up @@ -1203,13 +1271,28 @@ 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,
.set_bus_width = sdhci_pci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.hw_reset = sdhci_pci_hw_reset,
.select_drive_strength = sdhci_pci_select_drive_strength,
};

/*****************************************************************************\
Expand Down
4 changes: 4 additions & 0 deletions drivers/mmc/host/sdhci-pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions include/linux/mmc/sdhci-pci-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit e1bfad6

Please sign in to comment.