mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-22 19:03:28 +07:00
347f6be11d
Add CQHCI initialization and implement CQHCI operations for GL9763E. Use bit19 of the register (0x888) to decide whether to disable command queuing. If the bit is set, the command queuing will be disabled. Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw> Link: https://lore.kernel.org/r/20201005105509.11343-1-ben.chuanggli@gmail.com Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
861 lines
24 KiB
C
861 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2019 Genesys Logic, Inc.
|
|
*
|
|
* Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw>
|
|
*
|
|
* Version: v0.9.0 (2019-08-08)
|
|
*/
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/mmc/mmc.h>
|
|
#include <linux/delay.h>
|
|
#include "sdhci.h"
|
|
#include "sdhci-pci.h"
|
|
#include "cqhci.h"
|
|
|
|
/* Genesys Logic extra registers */
|
|
#define SDHCI_GLI_9750_WT 0x800
|
|
#define SDHCI_GLI_9750_WT_EN BIT(0)
|
|
#define GLI_9750_WT_EN_ON 0x1
|
|
#define GLI_9750_WT_EN_OFF 0x0
|
|
|
|
#define SDHCI_GLI_9750_DRIVING 0x860
|
|
#define SDHCI_GLI_9750_DRIVING_1 GENMASK(11, 0)
|
|
#define SDHCI_GLI_9750_DRIVING_2 GENMASK(27, 26)
|
|
#define GLI_9750_DRIVING_1_VALUE 0xFFF
|
|
#define GLI_9750_DRIVING_2_VALUE 0x3
|
|
#define SDHCI_GLI_9750_SEL_1 BIT(29)
|
|
#define SDHCI_GLI_9750_SEL_2 BIT(31)
|
|
#define SDHCI_GLI_9750_ALL_RST (BIT(24)|BIT(25)|BIT(28)|BIT(30))
|
|
|
|
#define SDHCI_GLI_9750_PLL 0x864
|
|
#define SDHCI_GLI_9750_PLL_LDIV GENMASK(9, 0)
|
|
#define SDHCI_GLI_9750_PLL_PDIV GENMASK(14, 12)
|
|
#define SDHCI_GLI_9750_PLL_DIR BIT(15)
|
|
#define SDHCI_GLI_9750_PLL_TX2_INV BIT(23)
|
|
#define SDHCI_GLI_9750_PLL_TX2_DLY GENMASK(22, 20)
|
|
#define GLI_9750_PLL_TX2_INV_VALUE 0x1
|
|
#define GLI_9750_PLL_TX2_DLY_VALUE 0x0
|
|
#define SDHCI_GLI_9750_PLLSSC_STEP GENMASK(28, 24)
|
|
#define SDHCI_GLI_9750_PLLSSC_EN BIT(31)
|
|
|
|
#define SDHCI_GLI_9750_PLLSSC 0x86C
|
|
#define SDHCI_GLI_9750_PLLSSC_PPM GENMASK(31, 16)
|
|
|
|
#define SDHCI_GLI_9750_SW_CTRL 0x874
|
|
#define SDHCI_GLI_9750_SW_CTRL_4 GENMASK(7, 6)
|
|
#define GLI_9750_SW_CTRL_4_VALUE 0x3
|
|
|
|
#define SDHCI_GLI_9750_MISC 0x878
|
|
#define SDHCI_GLI_9750_MISC_TX1_INV BIT(2)
|
|
#define SDHCI_GLI_9750_MISC_RX_INV BIT(3)
|
|
#define SDHCI_GLI_9750_MISC_TX1_DLY GENMASK(6, 4)
|
|
#define GLI_9750_MISC_TX1_INV_VALUE 0x0
|
|
#define GLI_9750_MISC_RX_INV_ON 0x1
|
|
#define GLI_9750_MISC_RX_INV_OFF 0x0
|
|
#define GLI_9750_MISC_RX_INV_VALUE GLI_9750_MISC_RX_INV_OFF
|
|
#define GLI_9750_MISC_TX1_DLY_VALUE 0x5
|
|
|
|
#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
|
|
#define SDHCI_GLI_9750_TUNING_CONTROL_EN BIT(4)
|
|
#define GLI_9750_TUNING_CONTROL_EN_ON 0x1
|
|
#define GLI_9750_TUNING_CONTROL_EN_OFF 0x0
|
|
#define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1 BIT(16)
|
|
#define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2 GENMASK(20, 19)
|
|
#define GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE 0x1
|
|
#define GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE 0x2
|
|
|
|
#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
|
|
#define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0)
|
|
#define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1
|
|
|
|
#define SDHCI_GLI_9763E_CTRL_HS400 0x7
|
|
|
|
#define SDHCI_GLI_9763E_HS400_ES_REG 0x52C
|
|
#define SDHCI_GLI_9763E_HS400_ES_BIT BIT(8)
|
|
|
|
#define PCIE_GLI_9763E_VHS 0x884
|
|
#define GLI_9763E_VHS_REV GENMASK(19, 16)
|
|
#define GLI_9763E_VHS_REV_R 0x0
|
|
#define GLI_9763E_VHS_REV_M 0x1
|
|
#define GLI_9763E_VHS_REV_W 0x2
|
|
#define PCIE_GLI_9763E_MB 0x888
|
|
#define GLI_9763E_MB_CMDQ_OFF BIT(19)
|
|
#define PCIE_GLI_9763E_SCR 0x8E0
|
|
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
|
|
|
#define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200
|
|
#define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
|
|
SDHCI_TRNS_BLK_CNT_EN | \
|
|
SDHCI_TRNS_DMA)
|
|
|
|
#define PCI_GLI_9755_WT 0x800
|
|
#define PCI_GLI_9755_WT_EN BIT(0)
|
|
#define GLI_9755_WT_EN_ON 0x1
|
|
#define GLI_9755_WT_EN_OFF 0x0
|
|
|
|
#define PCI_GLI_9755_PLL 0x64
|
|
#define PCI_GLI_9755_PLL_LDIV GENMASK(9, 0)
|
|
#define PCI_GLI_9755_PLL_PDIV GENMASK(14, 12)
|
|
#define PCI_GLI_9755_PLL_DIR BIT(15)
|
|
#define PCI_GLI_9755_PLLSSC_STEP GENMASK(28, 24)
|
|
#define PCI_GLI_9755_PLLSSC_EN BIT(31)
|
|
|
|
#define PCI_GLI_9755_PLLSSC 0x68
|
|
#define PCI_GLI_9755_PLLSSC_PPM GENMASK(15, 0)
|
|
|
|
#define GLI_MAX_TUNING_LOOP 40
|
|
|
|
/* Genesys Logic chipset */
|
|
static inline void gl9750_wt_on(struct sdhci_host *host)
|
|
{
|
|
u32 wt_value;
|
|
u32 wt_enable;
|
|
|
|
wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
|
|
wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value);
|
|
|
|
if (wt_enable == GLI_9750_WT_EN_ON)
|
|
return;
|
|
|
|
wt_value &= ~SDHCI_GLI_9750_WT_EN;
|
|
wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_ON);
|
|
|
|
sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
|
|
}
|
|
|
|
static inline void gl9750_wt_off(struct sdhci_host *host)
|
|
{
|
|
u32 wt_value;
|
|
u32 wt_enable;
|
|
|
|
wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
|
|
wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value);
|
|
|
|
if (wt_enable == GLI_9750_WT_EN_OFF)
|
|
return;
|
|
|
|
wt_value &= ~SDHCI_GLI_9750_WT_EN;
|
|
wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_OFF);
|
|
|
|
sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
|
|
}
|
|
|
|
static void gli_set_9750(struct sdhci_host *host)
|
|
{
|
|
u32 driving_value;
|
|
u32 pll_value;
|
|
u32 sw_ctrl_value;
|
|
u32 misc_value;
|
|
u32 parameter_value;
|
|
u32 control_value;
|
|
u16 ctrl2;
|
|
|
|
gl9750_wt_on(host);
|
|
|
|
driving_value = sdhci_readl(host, SDHCI_GLI_9750_DRIVING);
|
|
pll_value = sdhci_readl(host, SDHCI_GLI_9750_PLL);
|
|
sw_ctrl_value = sdhci_readl(host, SDHCI_GLI_9750_SW_CTRL);
|
|
misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
|
|
parameter_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_PARAMETERS);
|
|
control_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_CONTROL);
|
|
|
|
driving_value &= ~(SDHCI_GLI_9750_DRIVING_1);
|
|
driving_value &= ~(SDHCI_GLI_9750_DRIVING_2);
|
|
driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_1,
|
|
GLI_9750_DRIVING_1_VALUE);
|
|
driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_2,
|
|
GLI_9750_DRIVING_2_VALUE);
|
|
driving_value &= ~(SDHCI_GLI_9750_SEL_1|SDHCI_GLI_9750_SEL_2|SDHCI_GLI_9750_ALL_RST);
|
|
driving_value |= SDHCI_GLI_9750_SEL_2;
|
|
sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
|
|
|
|
sw_ctrl_value &= ~SDHCI_GLI_9750_SW_CTRL_4;
|
|
sw_ctrl_value |= FIELD_PREP(SDHCI_GLI_9750_SW_CTRL_4,
|
|
GLI_9750_SW_CTRL_4_VALUE);
|
|
sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
|
|
|
|
/* reset the tuning flow after reinit and before starting tuning */
|
|
pll_value &= ~SDHCI_GLI_9750_PLL_TX2_INV;
|
|
pll_value &= ~SDHCI_GLI_9750_PLL_TX2_DLY;
|
|
pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_INV,
|
|
GLI_9750_PLL_TX2_INV_VALUE);
|
|
pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_DLY,
|
|
GLI_9750_PLL_TX2_DLY_VALUE);
|
|
|
|
misc_value &= ~SDHCI_GLI_9750_MISC_TX1_INV;
|
|
misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
|
|
misc_value &= ~SDHCI_GLI_9750_MISC_TX1_DLY;
|
|
misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_INV,
|
|
GLI_9750_MISC_TX1_INV_VALUE);
|
|
misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV,
|
|
GLI_9750_MISC_RX_INV_VALUE);
|
|
misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_DLY,
|
|
GLI_9750_MISC_TX1_DLY_VALUE);
|
|
|
|
parameter_value &= ~SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY;
|
|
parameter_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY,
|
|
GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE);
|
|
|
|
control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1;
|
|
control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2;
|
|
control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1,
|
|
GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE);
|
|
control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2,
|
|
GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE);
|
|
|
|
sdhci_writel(host, pll_value, SDHCI_GLI_9750_PLL);
|
|
sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
|
|
|
|
/* disable tuned clk */
|
|
ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl2 &= ~SDHCI_CTRL_TUNED_CLK;
|
|
sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
|
|
|
|
/* enable tuning parameters control */
|
|
control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN;
|
|
control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN,
|
|
GLI_9750_TUNING_CONTROL_EN_ON);
|
|
sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
|
|
|
|
/* write tuning parameters */
|
|
sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
|
|
|
|
/* disable tuning parameters control */
|
|
control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN;
|
|
control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN,
|
|
GLI_9750_TUNING_CONTROL_EN_OFF);
|
|
sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
|
|
|
|
/* clear tuned clk */
|
|
ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl2 &= ~SDHCI_CTRL_TUNED_CLK;
|
|
sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
|
|
|
|
gl9750_wt_off(host);
|
|
}
|
|
|
|
static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
|
|
{
|
|
u32 misc_value;
|
|
|
|
gl9750_wt_on(host);
|
|
|
|
misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
|
|
misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
|
|
if (b) {
|
|
misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV,
|
|
GLI_9750_MISC_RX_INV_ON);
|
|
} else {
|
|
misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV,
|
|
GLI_9750_MISC_RX_INV_OFF);
|
|
}
|
|
sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
|
|
|
|
gl9750_wt_off(host);
|
|
}
|
|
|
|
static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
|
|
{
|
|
int i;
|
|
int rx_inv;
|
|
|
|
for (rx_inv = 0; rx_inv < 2; rx_inv++) {
|
|
gli_set_9750_rx_inv(host, !!rx_inv);
|
|
sdhci_start_tuning(host);
|
|
|
|
for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
|
|
u16 ctrl;
|
|
|
|
sdhci_send_tuning(host, opcode);
|
|
|
|
if (!host->tuning_done) {
|
|
sdhci_abort_tuning(host, opcode);
|
|
break;
|
|
}
|
|
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) {
|
|
if (ctrl & SDHCI_CTRL_TUNED_CLK)
|
|
return 0; /* Success! */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!host->tuning_done) {
|
|
pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
|
|
mmc_hostname(host->mmc));
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
pr_info("%s: Tuning failed, falling back to fixed sampling clock\n",
|
|
mmc_hostname(host->mmc));
|
|
sdhci_reset_tuning(host);
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
static int gl9750_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|
{
|
|
host->mmc->retune_period = 0;
|
|
if (host->tuning_mode == SDHCI_TUNING_MODE_1)
|
|
host->mmc->retune_period = host->tuning_count;
|
|
|
|
gli_set_9750(host);
|
|
host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
|
|
sdhci_end_tuning(host);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gl9750_disable_ssc_pll(struct sdhci_host *host)
|
|
{
|
|
u32 pll;
|
|
|
|
gl9750_wt_on(host);
|
|
pll = sdhci_readl(host, SDHCI_GLI_9750_PLL);
|
|
pll &= ~(SDHCI_GLI_9750_PLL_DIR | SDHCI_GLI_9750_PLLSSC_EN);
|
|
sdhci_writel(host, pll, SDHCI_GLI_9750_PLL);
|
|
gl9750_wt_off(host);
|
|
}
|
|
|
|
static void gl9750_set_pll(struct sdhci_host *host, u8 dir, u16 ldiv, u8 pdiv)
|
|
{
|
|
u32 pll;
|
|
|
|
gl9750_wt_on(host);
|
|
pll = sdhci_readl(host, SDHCI_GLI_9750_PLL);
|
|
pll &= ~(SDHCI_GLI_9750_PLL_LDIV |
|
|
SDHCI_GLI_9750_PLL_PDIV |
|
|
SDHCI_GLI_9750_PLL_DIR);
|
|
pll |= FIELD_PREP(SDHCI_GLI_9750_PLL_LDIV, ldiv) |
|
|
FIELD_PREP(SDHCI_GLI_9750_PLL_PDIV, pdiv) |
|
|
FIELD_PREP(SDHCI_GLI_9750_PLL_DIR, dir);
|
|
sdhci_writel(host, pll, SDHCI_GLI_9750_PLL);
|
|
gl9750_wt_off(host);
|
|
|
|
/* wait for pll stable */
|
|
mdelay(1);
|
|
}
|
|
|
|
static void gl9750_set_ssc(struct sdhci_host *host, u8 enable, u8 step, u16 ppm)
|
|
{
|
|
u32 pll;
|
|
u32 ssc;
|
|
|
|
gl9750_wt_on(host);
|
|
pll = sdhci_readl(host, SDHCI_GLI_9750_PLL);
|
|
ssc = sdhci_readl(host, SDHCI_GLI_9750_PLLSSC);
|
|
pll &= ~(SDHCI_GLI_9750_PLLSSC_STEP |
|
|
SDHCI_GLI_9750_PLLSSC_EN);
|
|
ssc &= ~SDHCI_GLI_9750_PLLSSC_PPM;
|
|
pll |= FIELD_PREP(SDHCI_GLI_9750_PLLSSC_STEP, step) |
|
|
FIELD_PREP(SDHCI_GLI_9750_PLLSSC_EN, enable);
|
|
ssc |= FIELD_PREP(SDHCI_GLI_9750_PLLSSC_PPM, ppm);
|
|
sdhci_writel(host, ssc, SDHCI_GLI_9750_PLLSSC);
|
|
sdhci_writel(host, pll, SDHCI_GLI_9750_PLL);
|
|
gl9750_wt_off(host);
|
|
}
|
|
|
|
static void gl9750_set_ssc_pll_205mhz(struct sdhci_host *host)
|
|
{
|
|
/* set pll to 205MHz and enable ssc */
|
|
gl9750_set_ssc(host, 0x1, 0x1F, 0xFFE7);
|
|
gl9750_set_pll(host, 0x1, 0x246, 0x0);
|
|
}
|
|
|
|
static void sdhci_gl9750_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
{
|
|
struct mmc_ios *ios = &host->mmc->ios;
|
|
u16 clk;
|
|
|
|
host->mmc->actual_clock = 0;
|
|
|
|
gl9750_disable_ssc_pll(host);
|
|
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
|
|
|
if (clock == 0)
|
|
return;
|
|
|
|
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
|
|
if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) {
|
|
host->mmc->actual_clock = 205000000;
|
|
gl9750_set_ssc_pll_205mhz(host);
|
|
}
|
|
|
|
sdhci_enable_clk(host, clk);
|
|
}
|
|
|
|
static void gli_pcie_enable_msi(struct sdhci_pci_slot *slot)
|
|
{
|
|
int ret;
|
|
|
|
ret = pci_alloc_irq_vectors(slot->chip->pdev, 1, 1,
|
|
PCI_IRQ_MSI | PCI_IRQ_MSIX);
|
|
if (ret < 0) {
|
|
pr_warn("%s: enable PCI MSI failed, error=%d\n",
|
|
mmc_hostname(slot->host->mmc), ret);
|
|
return;
|
|
}
|
|
|
|
slot->host->irq = pci_irq_vector(slot->chip->pdev, 0);
|
|
}
|
|
|
|
static inline void gl9755_wt_on(struct pci_dev *pdev)
|
|
{
|
|
u32 wt_value;
|
|
u32 wt_enable;
|
|
|
|
pci_read_config_dword(pdev, PCI_GLI_9755_WT, &wt_value);
|
|
wt_enable = FIELD_GET(PCI_GLI_9755_WT_EN, wt_value);
|
|
|
|
if (wt_enable == GLI_9755_WT_EN_ON)
|
|
return;
|
|
|
|
wt_value &= ~PCI_GLI_9755_WT_EN;
|
|
wt_value |= FIELD_PREP(PCI_GLI_9755_WT_EN, GLI_9755_WT_EN_ON);
|
|
|
|
pci_write_config_dword(pdev, PCI_GLI_9755_WT, wt_value);
|
|
}
|
|
|
|
static inline void gl9755_wt_off(struct pci_dev *pdev)
|
|
{
|
|
u32 wt_value;
|
|
u32 wt_enable;
|
|
|
|
pci_read_config_dword(pdev, PCI_GLI_9755_WT, &wt_value);
|
|
wt_enable = FIELD_GET(PCI_GLI_9755_WT_EN, wt_value);
|
|
|
|
if (wt_enable == GLI_9755_WT_EN_OFF)
|
|
return;
|
|
|
|
wt_value &= ~PCI_GLI_9755_WT_EN;
|
|
wt_value |= FIELD_PREP(PCI_GLI_9755_WT_EN, GLI_9755_WT_EN_OFF);
|
|
|
|
pci_write_config_dword(pdev, PCI_GLI_9755_WT, wt_value);
|
|
}
|
|
|
|
static void gl9755_disable_ssc_pll(struct pci_dev *pdev)
|
|
{
|
|
u32 pll;
|
|
|
|
gl9755_wt_on(pdev);
|
|
pci_read_config_dword(pdev, PCI_GLI_9755_PLL, &pll);
|
|
pll &= ~(PCI_GLI_9755_PLL_DIR | PCI_GLI_9755_PLLSSC_EN);
|
|
pci_write_config_dword(pdev, PCI_GLI_9755_PLL, pll);
|
|
gl9755_wt_off(pdev);
|
|
}
|
|
|
|
static void gl9755_set_pll(struct pci_dev *pdev, u8 dir, u16 ldiv, u8 pdiv)
|
|
{
|
|
u32 pll;
|
|
|
|
gl9755_wt_on(pdev);
|
|
pci_read_config_dword(pdev, PCI_GLI_9755_PLL, &pll);
|
|
pll &= ~(PCI_GLI_9755_PLL_LDIV |
|
|
PCI_GLI_9755_PLL_PDIV |
|
|
PCI_GLI_9755_PLL_DIR);
|
|
pll |= FIELD_PREP(PCI_GLI_9755_PLL_LDIV, ldiv) |
|
|
FIELD_PREP(PCI_GLI_9755_PLL_PDIV, pdiv) |
|
|
FIELD_PREP(PCI_GLI_9755_PLL_DIR, dir);
|
|
pci_write_config_dword(pdev, PCI_GLI_9755_PLL, pll);
|
|
gl9755_wt_off(pdev);
|
|
|
|
/* wait for pll stable */
|
|
mdelay(1);
|
|
}
|
|
|
|
static void gl9755_set_ssc(struct pci_dev *pdev, u8 enable, u8 step, u16 ppm)
|
|
{
|
|
u32 pll;
|
|
u32 ssc;
|
|
|
|
gl9755_wt_on(pdev);
|
|
pci_read_config_dword(pdev, PCI_GLI_9755_PLL, &pll);
|
|
pci_read_config_dword(pdev, PCI_GLI_9755_PLLSSC, &ssc);
|
|
pll &= ~(PCI_GLI_9755_PLLSSC_STEP |
|
|
PCI_GLI_9755_PLLSSC_EN);
|
|
ssc &= ~PCI_GLI_9755_PLLSSC_PPM;
|
|
pll |= FIELD_PREP(PCI_GLI_9755_PLLSSC_STEP, step) |
|
|
FIELD_PREP(PCI_GLI_9755_PLLSSC_EN, enable);
|
|
ssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_PPM, ppm);
|
|
pci_write_config_dword(pdev, PCI_GLI_9755_PLLSSC, ssc);
|
|
pci_write_config_dword(pdev, PCI_GLI_9755_PLL, pll);
|
|
gl9755_wt_off(pdev);
|
|
}
|
|
|
|
static void gl9755_set_ssc_pll_205mhz(struct pci_dev *pdev)
|
|
{
|
|
/* set pll to 205MHz and enable ssc */
|
|
gl9755_set_ssc(pdev, 0x1, 0x1F, 0xFFE7);
|
|
gl9755_set_pll(pdev, 0x1, 0x246, 0x0);
|
|
}
|
|
|
|
static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
{
|
|
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
|
struct mmc_ios *ios = &host->mmc->ios;
|
|
struct pci_dev *pdev;
|
|
u16 clk;
|
|
|
|
pdev = slot->chip->pdev;
|
|
host->mmc->actual_clock = 0;
|
|
|
|
gl9755_disable_ssc_pll(pdev);
|
|
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
|
|
|
if (clock == 0)
|
|
return;
|
|
|
|
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
|
|
if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) {
|
|
host->mmc->actual_clock = 205000000;
|
|
gl9755_set_ssc_pll_205mhz(pdev);
|
|
}
|
|
|
|
sdhci_enable_clk(host, clk);
|
|
}
|
|
|
|
static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
|
|
{
|
|
struct sdhci_host *host = slot->host;
|
|
|
|
gli_pcie_enable_msi(slot);
|
|
slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
|
|
sdhci_enable_v4_mode(host);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
|
|
{
|
|
struct sdhci_host *host = slot->host;
|
|
|
|
gli_pcie_enable_msi(slot);
|
|
slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
|
|
sdhci_enable_v4_mode(host);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sdhci_gli_voltage_switch(struct sdhci_host *host)
|
|
{
|
|
/*
|
|
* According to Section 3.6.1 signal voltage switch procedure in
|
|
* SD Host Controller Simplified Spec. 4.20, steps 6~8 are as
|
|
* follows:
|
|
* (6) Set 1.8V Signal Enable in the Host Control 2 register.
|
|
* (7) Wait 5ms. 1.8V voltage regulator shall be stable within this
|
|
* period.
|
|
* (8) If 1.8V Signal Enable is cleared by Host Controller, go to
|
|
* step (12).
|
|
*
|
|
* Wait 5ms after set 1.8V signal enable in Host Control 2 register
|
|
* to ensure 1.8V signal enable bit is set by GL9750/GL9755.
|
|
*/
|
|
usleep_range(5000, 5500);
|
|
}
|
|
|
|
static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask)
|
|
{
|
|
sdhci_reset(host, mask);
|
|
gli_set_9750(host);
|
|
}
|
|
|
|
static u32 sdhci_gl9750_readl(struct sdhci_host *host, int reg)
|
|
{
|
|
u32 value;
|
|
|
|
value = readl(host->ioaddr + reg);
|
|
if (unlikely(reg == SDHCI_MAX_CURRENT && !(value & 0xff)))
|
|
value |= 0xc8;
|
|
|
|
return value;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
|
|
{
|
|
struct sdhci_pci_slot *slot = chip->slots[0];
|
|
|
|
pci_free_irq_vectors(slot->chip->pdev);
|
|
gli_pcie_enable_msi(slot);
|
|
|
|
return sdhci_pci_resume_host(chip);
|
|
}
|
|
|
|
static int sdhci_cqhci_gli_resume(struct sdhci_pci_chip *chip)
|
|
{
|
|
struct sdhci_pci_slot *slot = chip->slots[0];
|
|
int ret;
|
|
|
|
ret = sdhci_pci_gli_resume(chip);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return cqhci_resume(slot->host->mmc);
|
|
}
|
|
|
|
static int sdhci_cqhci_gli_suspend(struct sdhci_pci_chip *chip)
|
|
{
|
|
struct sdhci_pci_slot *slot = chip->slots[0];
|
|
int ret;
|
|
|
|
ret = cqhci_suspend(slot->host->mmc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sdhci_suspend_host(slot->host);
|
|
}
|
|
#endif
|
|
|
|
static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
|
|
struct mmc_ios *ios)
|
|
{
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
u32 val;
|
|
|
|
val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG);
|
|
if (ios->enhanced_strobe)
|
|
val |= SDHCI_GLI_9763E_HS400_ES_BIT;
|
|
else
|
|
val &= ~SDHCI_GLI_9763E_HS400_ES_BIT;
|
|
|
|
sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG);
|
|
}
|
|
|
|
static void sdhci_set_gl9763e_signaling(struct sdhci_host *host,
|
|
unsigned int timing)
|
|
{
|
|
u16 ctrl_2;
|
|
|
|
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
|
if (timing == MMC_TIMING_MMC_HS200)
|
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
|
else if (timing == MMC_TIMING_MMC_HS)
|
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
|
else if (timing == MMC_TIMING_MMC_DDR52)
|
|
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
|
else if (timing == MMC_TIMING_MMC_HS400)
|
|
ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400;
|
|
|
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
|
}
|
|
|
|
static void sdhci_gl9763e_dumpregs(struct mmc_host *mmc)
|
|
{
|
|
sdhci_dumpregs(mmc_priv(mmc));
|
|
}
|
|
|
|
static void sdhci_gl9763e_cqe_pre_enable(struct mmc_host *mmc)
|
|
{
|
|
struct cqhci_host *cq_host = mmc->cqe_private;
|
|
u32 value;
|
|
|
|
value = cqhci_readl(cq_host, CQHCI_CFG);
|
|
value |= CQHCI_ENABLE;
|
|
cqhci_writel(cq_host, value, CQHCI_CFG);
|
|
}
|
|
|
|
static void sdhci_gl9763e_cqe_enable(struct mmc_host *mmc)
|
|
{
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
|
|
sdhci_writew(host, GLI_9763E_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
|
|
sdhci_cqe_enable(mmc);
|
|
}
|
|
|
|
static u32 sdhci_gl9763e_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
|
{
|
|
int cmd_error = 0;
|
|
int data_error = 0;
|
|
|
|
if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
|
|
return intmask;
|
|
|
|
cqhci_irq(host->mmc, intmask, cmd_error, data_error);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sdhci_gl9763e_cqe_post_disable(struct mmc_host *mmc)
|
|
{
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
struct cqhci_host *cq_host = mmc->cqe_private;
|
|
u32 value;
|
|
|
|
value = cqhci_readl(cq_host, CQHCI_CFG);
|
|
value &= ~CQHCI_ENABLE;
|
|
cqhci_writel(cq_host, value, CQHCI_CFG);
|
|
sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE);
|
|
}
|
|
|
|
static const struct cqhci_host_ops sdhci_gl9763e_cqhci_ops = {
|
|
.enable = sdhci_gl9763e_cqe_enable,
|
|
.disable = sdhci_cqe_disable,
|
|
.dumpregs = sdhci_gl9763e_dumpregs,
|
|
.pre_enable = sdhci_gl9763e_cqe_pre_enable,
|
|
.post_disable = sdhci_gl9763e_cqe_post_disable,
|
|
};
|
|
|
|
static int gl9763e_add_host(struct sdhci_pci_slot *slot)
|
|
{
|
|
struct device *dev = &slot->chip->pdev->dev;
|
|
struct sdhci_host *host = slot->host;
|
|
struct cqhci_host *cq_host;
|
|
bool dma64;
|
|
int ret;
|
|
|
|
ret = sdhci_setup_host(host);
|
|
if (ret)
|
|
return ret;
|
|
|
|
cq_host = devm_kzalloc(dev, sizeof(*cq_host), GFP_KERNEL);
|
|
if (!cq_host) {
|
|
ret = -ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
|
|
cq_host->mmio = host->ioaddr + SDHCI_GLI_9763E_CQE_BASE_ADDR;
|
|
cq_host->ops = &sdhci_gl9763e_cqhci_ops;
|
|
|
|
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
|
if (dma64)
|
|
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
|
|
|
|
ret = cqhci_init(cq_host, host->mmc, dma64);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
ret = __sdhci_add_host(host);
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
return 0;
|
|
|
|
cleanup:
|
|
sdhci_cleanup_host(host);
|
|
return ret;
|
|
}
|
|
|
|
static void sdhci_gl9763e_reset(struct sdhci_host *host, u8 mask)
|
|
{
|
|
if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) &&
|
|
host->mmc->cqe_private)
|
|
cqhci_deactivate(host->mmc);
|
|
sdhci_reset(host, mask);
|
|
}
|
|
|
|
static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
|
{
|
|
struct pci_dev *pdev = slot->chip->pdev;
|
|
u32 value;
|
|
|
|
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
|
value &= ~GLI_9763E_VHS_REV;
|
|
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
|
|
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
|
|
|
|
pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value);
|
|
value |= GLI_9763E_SCR_AXI_REQ;
|
|
pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value);
|
|
|
|
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
|
value &= ~GLI_9763E_VHS_REV;
|
|
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
|
|
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
|
|
}
|
|
|
|
static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
|
|
{
|
|
struct pci_dev *pdev = slot->chip->pdev;
|
|
struct sdhci_host *host = slot->host;
|
|
u32 value;
|
|
|
|
host->mmc->caps |= MMC_CAP_8_BIT_DATA |
|
|
MMC_CAP_1_8V_DDR |
|
|
MMC_CAP_NONREMOVABLE;
|
|
host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR |
|
|
MMC_CAP2_HS400_1_8V |
|
|
MMC_CAP2_HS400_ES |
|
|
MMC_CAP2_NO_SDIO |
|
|
MMC_CAP2_NO_SD;
|
|
|
|
pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value);
|
|
if (!(value & GLI_9763E_MB_CMDQ_OFF))
|
|
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
|
|
|
gli_pcie_enable_msi(slot);
|
|
host->mmc_host_ops.hs400_enhanced_strobe =
|
|
gl9763e_hs400_enhanced_strobe;
|
|
gli_set_gl9763e(slot);
|
|
sdhci_enable_v4_mode(host);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct sdhci_ops sdhci_gl9755_ops = {
|
|
.set_clock = sdhci_gl9755_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,
|
|
.voltage_switch = sdhci_gli_voltage_switch,
|
|
};
|
|
|
|
const struct sdhci_pci_fixes sdhci_gl9755 = {
|
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
|
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50,
|
|
.probe_slot = gli_probe_slot_gl9755,
|
|
.ops = &sdhci_gl9755_ops,
|
|
#ifdef CONFIG_PM_SLEEP
|
|
.resume = sdhci_pci_gli_resume,
|
|
#endif
|
|
};
|
|
|
|
static const struct sdhci_ops sdhci_gl9750_ops = {
|
|
.read_l = sdhci_gl9750_readl,
|
|
.set_clock = sdhci_gl9750_set_clock,
|
|
.enable_dma = sdhci_pci_enable_dma,
|
|
.set_bus_width = sdhci_set_bus_width,
|
|
.reset = sdhci_gl9750_reset,
|
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
|
.voltage_switch = sdhci_gli_voltage_switch,
|
|
.platform_execute_tuning = gl9750_execute_tuning,
|
|
};
|
|
|
|
const struct sdhci_pci_fixes sdhci_gl9750 = {
|
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
|
.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50,
|
|
.probe_slot = gli_probe_slot_gl9750,
|
|
.ops = &sdhci_gl9750_ops,
|
|
#ifdef CONFIG_PM_SLEEP
|
|
.resume = sdhci_pci_gli_resume,
|
|
#endif
|
|
};
|
|
|
|
static const struct sdhci_ops sdhci_gl9763e_ops = {
|
|
.set_clock = sdhci_set_clock,
|
|
.enable_dma = sdhci_pci_enable_dma,
|
|
.set_bus_width = sdhci_set_bus_width,
|
|
.reset = sdhci_gl9763e_reset,
|
|
.set_uhs_signaling = sdhci_set_gl9763e_signaling,
|
|
.voltage_switch = sdhci_gli_voltage_switch,
|
|
.irq = sdhci_gl9763e_cqhci_irq,
|
|
};
|
|
|
|
const struct sdhci_pci_fixes sdhci_gl9763e = {
|
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
|
.probe_slot = gli_probe_slot_gl9763e,
|
|
.ops = &sdhci_gl9763e_ops,
|
|
#ifdef CONFIG_PM_SLEEP
|
|
.resume = sdhci_cqhci_gli_resume,
|
|
.suspend = sdhci_cqhci_gli_suspend,
|
|
#endif
|
|
.add_host = gl9763e_add_host,
|
|
};
|