Skip to content

Commit

Permalink
mmc: sdhci: Fix O2 Host PLL and card detect issue
Browse files Browse the repository at this point in the history
1. O2 Host Controller PLL lock status is not in compliance with
CLOCK_CONTROL register bit 1
2. O2 Host Controller card detect function only work when PLL is
enabled and locked

Signed-off-by: Ernest Zhang <ernest.zhang@bayhubtech.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
  • Loading branch information
Ernest Zhang(WH) authored and Ulf Hansson committed Feb 25, 2019
1 parent 328be8b commit 69d91ed
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 3 deletions.
130 changes: 127 additions & 3 deletions drivers/mmc/host/sdhci-pci-o2micro.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@
#define O2_SD_VENDOR_SETTING2 0x1C8
#define O2_SD_HW_TUNING_DISABLE BIT(4)

#define O2_PLL_WDT_CONTROL1 0x1CC
#define O2_PLL_FORCE_ACTIVE BIT(18)
#define O2_PLL_LOCK_STATUS BIT(14)
#define O2_PLL_SOFT_RESET BIT(12)

#define O2_SD_DETECT_SETTING 0x324

static void sdhci_o2_set_tuning_mode(struct sdhci_host *host)
{
u16 reg;
Expand Down Expand Up @@ -283,6 +290,113 @@ static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip,
host->irq = pci_irq_vector(chip->pdev, 0);
}

static void sdhci_o2_wait_card_detect_stable(struct sdhci_host *host)
{
ktime_t timeout;
u32 scratch32;

/* Wait max 50 ms */
timeout = ktime_add_ms(ktime_get(), 50);
while (1) {
bool timedout = ktime_after(ktime_get(), timeout);

scratch32 = sdhci_readl(host, SDHCI_PRESENT_STATE);
if ((scratch32 & SDHCI_CARD_PRESENT) >> SDHCI_CARD_PRES_SHIFT
== (scratch32 & SDHCI_CD_LVL) >> SDHCI_CD_LVL_SHIFT)
break;

if (timedout) {
pr_err("%s: Card Detect debounce never finished.\n",
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
return;
}
udelay(10);
}
}

static void sdhci_o2_enable_internal_clock(struct sdhci_host *host)
{
ktime_t timeout;
u16 scratch;
u32 scratch32;

/* PLL software reset */
scratch32 = sdhci_readl(host, O2_PLL_WDT_CONTROL1);
scratch32 |= O2_PLL_SOFT_RESET;
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);
udelay(1);
scratch32 &= ~(O2_PLL_SOFT_RESET);
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);

/* PLL force active */
scratch32 |= O2_PLL_FORCE_ACTIVE;
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);

/* Wait max 20 ms */
timeout = ktime_add_ms(ktime_get(), 20);
while (1) {
bool timedout = ktime_after(ktime_get(), timeout);

scratch = sdhci_readw(host, O2_PLL_WDT_CONTROL1);
if (scratch & O2_PLL_LOCK_STATUS)
break;
if (timedout) {
pr_err("%s: Internal clock never stabilised.\n",
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
goto out;
}
udelay(10);
}

/* Wait for card detect finish */
udelay(1);
sdhci_o2_wait_card_detect_stable(host);

out:
/* Cancel PLL force active */
scratch32 = sdhci_readl(host, O2_PLL_WDT_CONTROL1);
scratch32 &= ~O2_PLL_FORCE_ACTIVE;
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);
}

static int sdhci_o2_get_cd(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);

sdhci_o2_enable_internal_clock(host);

return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
}

static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk)
{
/* Enable internal clock */
clk |= SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

if (sdhci_o2_get_cd(host->mmc)) {
clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}
}

void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk;

host->mmc->actual_clock = 0;

sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);

if (clock == 0)
return;

clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
sdhci_o2_enable_clk(host, clk);
}

int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
{
struct sdhci_pci_chip *chip;
Expand Down Expand Up @@ -316,7 +430,11 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
host->flags |= SDHCI_SIGNALING_180;
host->mmc->caps2 |= MMC_CAP2_NO_SD;
host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
pci_write_config_dword(chip->pdev,
O2_SD_DETECT_SETTING, 3);
}

slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd;
}

host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning;
Expand Down Expand Up @@ -490,9 +608,6 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
break;
case PCI_DEVICE_ID_O2_SEABIRD0:
if (chip->pdev->revision == 0x01)
chip->quirks |= SDHCI_QUIRK_DELAY_AFTER_POWER;
/* fall through */
case PCI_DEVICE_ID_O2_SEABIRD1:
/* UnLock WP */
ret = pci_read_config_byte(chip->pdev,
Expand Down Expand Up @@ -551,6 +666,14 @@ int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
}
#endif

static const struct sdhci_ops sdhci_pci_o2_ops = {
.set_clock = sdhci_pci_o2_set_clock,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};

const struct sdhci_pci_fixes sdhci_o2 = {
.probe = sdhci_pci_o2_probe,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
Expand All @@ -559,4 +682,5 @@ const struct sdhci_pci_fixes sdhci_o2 = {
#ifdef CONFIG_PM_SLEEP
.resume = sdhci_pci_o2_resume,
#endif
.ops = &sdhci_pci_o2_ops,
};
4 changes: 4 additions & 0 deletions drivers/mmc/host/sdhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
#define SDHCI_SPACE_AVAILABLE 0x00000400
#define SDHCI_DATA_AVAILABLE 0x00000800
#define SDHCI_CARD_PRESENT 0x00010000
#define SDHCI_CARD_PRES_SHIFT 16
#define SDHCI_CD_STABLE 0x00020000
#define SDHCI_CD_LVL 0x00040000
#define SDHCI_CD_LVL_SHIFT 18
#define SDHCI_WRITE_PROTECT 0x00080000
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
Expand Down

0 comments on commit 69d91ed

Please sign in to comment.