2019-07-26 02:10:24

by Michael K. Johnson

[permalink] [raw]
Subject: [PATCH v2 2/2] mmc: sdhci: sdhci-pci-core: Add Genesis Logic GL975x support

Add support for the GL9750 and GL9755 chipsets.

Signed-off-by: Ben Chuang <[email protected]>
Co-developed-by: Michael K Johnson <[email protected]>
Signed-off-by: Michael K Johnson <[email protected]>

diff --git a/drivers/mmc/host/sdhci-gli.h b/drivers/mmc/host/sdhci-gli.h
new file mode 100644
index 000000000000..0acd35b6d3e2
--- /dev/null
+++ b/drivers/mmc/host/sdhci-gli.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __SDHCI_GLI_H
+#define __SDHCI_GLI_H
+
+/* the define PCI_VENDOR_ID_GLI may put in kernel/include/linux/pci_ids.h */
+#ifndef PCI_VENDOR_ID_GLI
+#define PCI_VENDOR_ID_GLI 0x17a0
+#endif
+
+/* Genesys Logic extra registers */
+#define SDHCI_GLI_9750_WT 0x800
+#define SDHCI_GLI_9750_DRIVING 0x860
+#define SDHCI_GLI_9750_PLL 0x864
+#define SDHCI_GLI_9750_SW_CTRL 0x874
+#define SDHCI_GLI_9750_MISC 0x878
+
+#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
+#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
+
+#define GLI_9755_DRIVER_VER "Genesys Logic (GL9755 v0.9.0-y190703)"
+#define GLI_9750_DRIVER_VER "Genesys Logic (GL9750 v0.9.0-y190703)"
+
+#define GLI_MAX_TUNING_LOOP 40
+
+void gli_set_9750(struct sdhci_host *host);
+
+#endif /* __SDHCI_GLI_H */
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 4154ee11b47d..b5c28df39de1 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -35,6 +35,7 @@

#include "sdhci.h"
#include "sdhci-pci.h"
+#include "sdhci-gli.h"

static void sdhci_pci_hw_reset(struct sdhci_host *host);

@@ -1453,6 +1454,223 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
.probe_slot = rtsx_probe_slot,
};

+/* Genesys Logic chipset */
+static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
+{
+ struct sdhci_host *host = slot->host;
+
+ slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
+ dev_info(&slot->chip->pdev->dev, "%s\n", GLI_9755_DRIVER_VER);
+ sdhci_enable_v4_mode(host);
+
+ return 0;
+}
+
+static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
+{
+ u32 wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ u32 misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
+
+ if ((wt_value & 0x1) == 0) {
+ wt_value |= 0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+ }
+
+ misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
+ if (b) {
+ misc_value |= 0x8;
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ } else {
+ misc_value &= ~0x8;
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ }
+
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ wt_value &= ~0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+}
+
+static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
+{
+ int i;
+ int rx_inv = 0;
+
+ for (rx_inv = 0; rx_inv < 2; rx_inv++) {
+ if (rx_inv & 0x1)
+ gli_set_9750_rx_inv(host, true);
+ else
+ gli_set_9750_rx_inv(host, false);
+
+ sdhci_start_tuning(host);
+
+ for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
+ u16 ctrl;
+
+ sdhci_send_tuning(host, opcode);
+
+ if (!host->tuning_done) {
+ if (rx_inv == 1) {
+ pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
+ mmc_hostname(host->mmc));
+ sdhci_abort_tuning(host, opcode);
+ return -ETIMEDOUT;
+ }
+ pr_info("%s: Tuning timeout, try next tuning\n",
+ mmc_hostname(host->mmc));
+ 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) {
+ pr_info("%s: Tuning successful\n",
+ mmc_hostname(host->mmc));
+ return 0; /* Success! */
+ }
+ break;
+ }
+ }
+ }
+
+ 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 mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int err = 0;
+ unsigned int tuning_count = 0;
+ bool hs400_tuning;
+
+ hs400_tuning = host->flags & SDHCI_HS400_TUNING;
+
+ if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+ tuning_count = host->tuning_count;
+
+ /*
+ * The Host Controller needs tuning in case of SDR104 and DDR50
+ * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
+ * the Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
+ */
+ switch (host->timing) {
+ /* HS400 tuning is done in HS200 mode */
+ case MMC_TIMING_MMC_HS400:
+ err = -EINVAL;
+ goto out;
+
+ case MMC_TIMING_MMC_HS200:
+ /*
+ * Periodic re-tuning for HS400 is not expected to be needed, so
+ * disable it here.
+ */
+ if (hs400_tuning)
+ tuning_count = 0;
+ break;
+
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_DDR50:
+ break;
+
+ case MMC_TIMING_UHS_SDR50:
+ if (host->flags & SDHCI_SDR50_NEEDS_TUNING)
+ break;
+ /* FALLTHROUGH */
+
+ default:
+ goto out;
+ }
+
+ if (host->ops->platform_execute_tuning) {
+ err = host->ops->platform_execute_tuning(host, opcode);
+ goto out;
+ }
+
+ host->mmc->retune_period = tuning_count;
+
+ if (host->tuning_delay < 0)
+ host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
+
+ gli_set_9750(host);
+ host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
+
+ sdhci_end_tuning(host);
+out:
+ host->flags &= ~SDHCI_HS400_TUNING;
+
+ return err;
+}
+
+static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
+{
+ struct sdhci_host *host = slot->host;
+ struct mmc_host_ops *ops = &host->mmc_host_ops;
+
+ u32 gli_wt = sdhci_readl(host, SDHCI_GLI_9750_WT);
+
+ dev_info(&slot->chip->pdev->dev, "%s\n", GLI_9750_DRIVER_VER);
+ slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
+
+ if ((gli_wt & 0x1) == 0) {
+ gli_wt |= 0x1;
+ sdhci_writel(host, gli_wt, SDHCI_GLI_9750_WT);
+ }
+
+ gli_wt = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ gli_wt &= ~0x1;
+ sdhci_writel(host, gli_wt, SDHCI_GLI_9750_WT);
+
+ sdhci_enable_v4_mode(host);
+
+ ops->execute_tuning = gl9750_execute_tuning;
+
+ return 0;
+}
+
+static void sdhci_gli_voltage_switch(struct sdhci_host *host)
+{
+ usleep_range(5000, 5500);
+}
+
+static const struct sdhci_ops sdhci_gl9755_ops = {
+ .set_clock = sdhci_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,
+ .hw_reset = sdhci_pci_hw_reset,
+ .voltage_switch = sdhci_gli_voltage_switch,
+};
+
+static 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,
+};
+
+static const struct sdhci_ops sdhci_gl9750_ops = {
+ .set_clock = sdhci_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,
+ .hw_reset = sdhci_pci_hw_reset,
+ .voltage_switch = sdhci_gli_voltage_switch,
+};
+
+static 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,
+};
+
/*AMD chipset generation*/
enum amd_chipset_gen {
AMD_CHIPSET_BEFORE_ML,
@@ -1682,6 +1900,8 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
+ SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
+ SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
/* Generic SD host controller */
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index e5dc6e44c7a4..78e6b631e252 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -65,6 +65,9 @@

#define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202

+#define PCI_DEVICE_ID_GLI_9755 0x9755
+#define PCI_DEVICE_ID_GLI_9750 0x9750
+
/*
* PCI device class and mask
*/
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 14957578bf2e..1ccf1b692d04 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -25,6 +25,8 @@

#include <linux/leds.h>

+#include <linux/pci.h>
+
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@@ -32,6 +34,8 @@
#include <linux/mmc/slot-gpio.h>

#include "sdhci.h"
+#include "sdhci-pci.h"
+#include "sdhci-gli.h"

#define DRIVER_NAME "sdhci"

@@ -50,6 +54,8 @@ static void sdhci_finish_data(struct sdhci_host *);

static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);

+static bool gli_is_9750(struct sdhci_host *);
+
void sdhci_dumpregs(struct sdhci_host *host)
{
SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
@@ -328,6 +334,8 @@ static void sdhci_init(struct sdhci_host *host, int soft)

host->cqe_on = false;

+ gli_set_9750(host);
+
if (soft) {
/* force clock reconfiguration */
host->clock = 0;
@@ -2326,7 +2334,7 @@ void sdhci_reset_tuning(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_reset_tuning);

-static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
+void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
{
sdhci_reset_tuning(host);

@@ -2337,6 +2345,7 @@ static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)

mmc_abort_tuning(host->mmc, opcode);
}
+EXPORT_SYMBOL_GPL(sdhci_abort_tuning);

/*
* We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI
@@ -2493,9 +2502,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;

sdhci_start_tuning(host);
-
host->tuning_err = __sdhci_execute_tuning(host, opcode);
-
sdhci_end_tuning(host);
out:
host->flags &= ~SDHCI_HS400_TUNING;
@@ -4090,6 +4097,12 @@ int sdhci_setup_host(struct sdhci_host *host)
* value.
*/
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
+
+ if (gli_is_9750(host)) {
+ if (!(max_current_caps & 0xff))
+ max_current_caps |= 0xc8;
+ }
+
if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
int curr = regulator_get_current_limit(mmc->supply.vmmc);
if (curr > 0) {
@@ -4269,6 +4282,106 @@ void sdhci_cleanup_host(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_cleanup_host);

+// for Genesys Logic
+void gli_set_9750(struct sdhci_host *host)
+{
+ u32 wt_value = 0;
+ u32 driving_value = 0;
+ u32 pll_value = 0;
+ u32 sw_ctrl_value = 0;
+ u32 misc_value = 0;
+ u32 parameter_value = 0;
+ u32 control_value = 0;
+
+ u16 ctrl2 = 0;
+
+ if (!gli_is_9750(host))
+ return;
+
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ if ((wt_value & 0x1) == 0) {
+ wt_value |= 0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+ }
+
+ 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 &= ~(0x0C000FFF);
+ driving_value |= 0x0C000FFF;
+ sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
+
+ sw_ctrl_value |= 0xc0;
+ sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
+
+ // reset the tuning flow after reinit and before starting tuning
+ pll_value |= 0x800000; // bit23-1
+ pll_value &= ~(0x00700000); // bit22:20-0
+
+ misc_value &= ~(0x8); // bit3-0
+ misc_value &= ~(0x4); // bit2-0
+
+ misc_value &= ~(0x70); // bit6:4-0
+ misc_value |= 0x50; // bit6:4-5
+
+ parameter_value &= ~(0x7); // bit2:0-0
+ parameter_value |= 0x1; // bit2:0-1
+
+ control_value &= ~(0x190000); // bit20:19-0, bit16-0
+ control_value |= 0x110000; // bit20:19-b10, bit16-1
+
+ 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);
+
+ // 540 enable tuning parameters control
+ control_value |= 0x10;
+ sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
+
+ // write 544 tuning parameters
+ sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
+
+ // 540 disable tuning parameters control
+ control_value &= ~0x10;
+ 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);
+
+ udelay(1);
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ wt_value &= ~0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+}
+EXPORT_SYMBOL_GPL(gli_set_9750);
+
+bool gli_is_9750(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct sdhci_pci_chip *chip;
+ struct pci_dev *pdev;
+
+ chip = slot->chip;
+ pdev = chip->pdev;
+
+ if (pdev->vendor == PCI_VENDOR_ID_GLI &&
+ pdev->device == PCI_DEVICE_ID_GLI_9750) {
+ return true;
+ }
+
+ return false;
+}
+
int __sdhci_add_host(struct sdhci_host *host)
{
unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 72601a4d2e95..437bab3af195 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -797,5 +797,6 @@ void sdhci_start_tuning(struct sdhci_host *host);
void sdhci_end_tuning(struct sdhci_host *host);
void sdhci_reset_tuning(struct sdhci_host *host);
void sdhci_send_tuning(struct sdhci_host *host, u32 opcode);
+void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode);

#endif /* __SDHCI_HW_H */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 70e86148cb1e..e668da5c8df5 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2403,6 +2403,7 @@
#define PCI_DEVICE_ID_RDC_R6061 0x6061
#define PCI_DEVICE_ID_RDC_D1010 0x1010

+#define PCI_VENDOR_ID_GLI 0x17a0
#define PCI_VENDOR_ID_LENOVO 0x17aa

#define PCI_VENDOR_ID_QCOM 0x17cb


2019-08-07 12:27:27

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] mmc: sdhci: sdhci-pci-core: Add Genesis Logic GL975x support

On 26/07/19 5:07 AM, Michael K. Johnson wrote:
> Add support for the GL9750 and GL9755 chipsets.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Co-developed-by: Michael K Johnson <[email protected]>
> Signed-off-by: Michael K Johnson <[email protected]>
>
> diff --git a/drivers/mmc/host/sdhci-gli.h b/drivers/mmc/host/sdhci-gli.h

I suggest creating sdhci-pci-gli.c and putting definitions there. Have a
look at sdhci-pci-arasan.c, sdhci-pci-dwc-mshc.c or sdhci-pci-o2micro.c

> new file mode 100644
> index 000000000000..0acd35b6d3e2
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-gli.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +#ifndef __SDHCI_GLI_H
> +#define __SDHCI_GLI_H
> +
> +/* the define PCI_VENDOR_ID_GLI may put in kernel/include/linux/pci_ids.h */
> +#ifndef PCI_VENDOR_ID_GLI
> +#define PCI_VENDOR_ID_GLI 0x17a0
> +#endif

If it is in include/linux/pci_ids.h then this code should be removed

> +
> +/* Genesys Logic extra registers */
> +#define SDHCI_GLI_9750_WT 0x800
> +#define SDHCI_GLI_9750_DRIVING 0x860
> +#define SDHCI_GLI_9750_PLL 0x864
> +#define SDHCI_GLI_9750_SW_CTRL 0x874
> +#define SDHCI_GLI_9750_MISC 0x878
> +
> +#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
> +#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
> +
> +#define GLI_9755_DRIVER_VER "Genesys Logic (GL9755 v0.9.0-y190703)"
> +#define GLI_9750_DRIVER_VER "Genesys Logic (GL9750 v0.9.0-y190703)"
> +
> +#define GLI_MAX_TUNING_LOOP 40
> +
> +void gli_set_9750(struct sdhci_host *host);
> +
> +#endif /* __SDHCI_GLI_H */
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 4154ee11b47d..b5c28df39de1 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -35,6 +35,7 @@
>
> #include "sdhci.h"
> #include "sdhci-pci.h"
> +#include "sdhci-gli.h"
>
> static void sdhci_pci_hw_reset(struct sdhci_host *host);
>
> @@ -1453,6 +1454,223 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
> .probe_slot = rtsx_probe_slot,
> };
>
> +/* Genesys Logic chipset */
> +static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
> +{
> + struct sdhci_host *host = slot->host;
> +
> + slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
> + dev_info(&slot->chip->pdev->dev, "%s\n", GLI_9755_DRIVER_VER);
> + sdhci_enable_v4_mode(host);
> +
> + return 0;
> +}
> +
> +static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
> +{
> + u32 wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + u32 misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
> +
> + if ((wt_value & 0x1) == 0) {
> + wt_value |= 0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> + }
> +
> + misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
> + if (b) {
> + misc_value |= 0x8;
> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
> + } else {
> + misc_value &= ~0x8;
> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
> + }
> +
> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + wt_value &= ~0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> +}
> +
> +static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
> +{
> + int i;
> + int rx_inv = 0;
> +
> + for (rx_inv = 0; rx_inv < 2; rx_inv++) {
> + if (rx_inv & 0x1)
> + gli_set_9750_rx_inv(host, true);
> + else
> + gli_set_9750_rx_inv(host, false);
> +
> + sdhci_start_tuning(host);
> +
> + for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
> + u16 ctrl;
> +
> + sdhci_send_tuning(host, opcode);
> +
> + if (!host->tuning_done) {
> + if (rx_inv == 1) {
> + pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
> + mmc_hostname(host->mmc));
> + sdhci_abort_tuning(host, opcode);
> + return -ETIMEDOUT;
> + }
> + pr_info("%s: Tuning timeout, try next tuning\n",
> + mmc_hostname(host->mmc));
> + 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) {
> + pr_info("%s: Tuning successful\n",
> + mmc_hostname(host->mmc));
> + return 0; /* Success! */
> + }
> + break;
> + }
> + }
> + }
> +
> + 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 mmc_host *mmc, u32 opcode)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + int err = 0;
> + unsigned int tuning_count = 0;
> + bool hs400_tuning;
> +
> + hs400_tuning = host->flags & SDHCI_HS400_TUNING;
> +
> + if (host->tuning_mode == SDHCI_TUNING_MODE_1)
> + tuning_count = host->tuning_count;
> +
> + /*
> + * The Host Controller needs tuning in case of SDR104 and DDR50
> + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
> + * the Capabilities register.
> + * If the Host Controller supports the HS200 mode then the
> + * tuning function has to be executed.
> + */
> + switch (host->timing) {
> + /* HS400 tuning is done in HS200 mode */
> + case MMC_TIMING_MMC_HS400:
> + err = -EINVAL;
> + goto out;
> +
> + case MMC_TIMING_MMC_HS200:
> + /*
> + * Periodic re-tuning for HS400 is not expected to be needed, so
> + * disable it here.
> + */
> + if (hs400_tuning)
> + tuning_count = 0;
> + break;
> +
> + case MMC_TIMING_UHS_SDR104:
> + case MMC_TIMING_UHS_DDR50:
> + break;
> +
> + case MMC_TIMING_UHS_SDR50:
> + if (host->flags & SDHCI_SDR50_NEEDS_TUNING)
> + break;
> + /* FALLTHROUGH */
> +
> + default:
> + goto out;
> + }
> +
> + if (host->ops->platform_execute_tuning) {
> + err = host->ops->platform_execute_tuning(host, opcode);
> + goto out;
> + }
> +
> + host->mmc->retune_period = tuning_count;
> +
> + if (host->tuning_delay < 0)
> + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
> +
> + gli_set_9750(host);
> + host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
> +
> + sdhci_end_tuning(host);
> +out:
> + host->flags &= ~SDHCI_HS400_TUNING;
> +
> + return err;
> +}
> +
> +static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
> +{
> + struct sdhci_host *host = slot->host;
> + struct mmc_host_ops *ops = &host->mmc_host_ops;
> +
> + u32 gli_wt = sdhci_readl(host, SDHCI_GLI_9750_WT);
> +
> + dev_info(&slot->chip->pdev->dev, "%s\n", GLI_9750_DRIVER_VER);
> + slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
> +
> + if ((gli_wt & 0x1) == 0) {
> + gli_wt |= 0x1;
> + sdhci_writel(host, gli_wt, SDHCI_GLI_9750_WT);
> + }
> +
> + gli_wt = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + gli_wt &= ~0x1;
> + sdhci_writel(host, gli_wt, SDHCI_GLI_9750_WT);
> +
> + sdhci_enable_v4_mode(host);
> +
> + ops->execute_tuning = gl9750_execute_tuning;
> +
> + return 0;
> +}
> +
> +static void sdhci_gli_voltage_switch(struct sdhci_host *host)
> +{
> + usleep_range(5000, 5500);
> +}
> +
> +static const struct sdhci_ops sdhci_gl9755_ops = {
> + .set_clock = sdhci_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,
> + .hw_reset = sdhci_pci_hw_reset,
> + .voltage_switch = sdhci_gli_voltage_switch,
> +};
> +
> +static 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,
> +};
> +
> +static const struct sdhci_ops sdhci_gl9750_ops = {
> + .set_clock = sdhci_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,
> + .hw_reset = sdhci_pci_hw_reset,
> + .voltage_switch = sdhci_gli_voltage_switch,
> +};
> +
> +static 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,
> +};
> +
> /*AMD chipset generation*/
> enum amd_chipset_gen {
> AMD_CHIPSET_BEFORE_ML,
> @@ -1682,6 +1900,8 @@ static const struct pci_device_id pci_ids[] = {
> SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
> SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
> SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
> + SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
> + SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
> SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
> /* Generic SD host controller */
> {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
> index e5dc6e44c7a4..78e6b631e252 100644
> --- a/drivers/mmc/host/sdhci-pci.h
> +++ b/drivers/mmc/host/sdhci-pci.h
> @@ -65,6 +65,9 @@
>
> #define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202
>
> +#define PCI_DEVICE_ID_GLI_9755 0x9755
> +#define PCI_DEVICE_ID_GLI_9750 0x9750
> +
> /*
> * PCI device class and mask
> */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 14957578bf2e..1ccf1b692d04 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -25,6 +25,8 @@
>
> #include <linux/leds.h>
>
> +#include <linux/pci.h>
> +
> #include <linux/mmc/mmc.h>
> #include <linux/mmc/host.h>
> #include <linux/mmc/card.h>
> @@ -32,6 +34,8 @@
> #include <linux/mmc/slot-gpio.h>
>
> #include "sdhci.h"
> +#include "sdhci-pci.h"
> +#include "sdhci-gli.h"

Vendor specific changes do not belong in sdhci.c

>
> #define DRIVER_NAME "sdhci"
>
> @@ -50,6 +54,8 @@ static void sdhci_finish_data(struct sdhci_host *);
>
> static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
>
> +static bool gli_is_9750(struct sdhci_host *);
> +
> void sdhci_dumpregs(struct sdhci_host *host)
> {
> SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
> @@ -328,6 +334,8 @@ static void sdhci_init(struct sdhci_host *host, int soft)
>
> host->cqe_on = false;
>
> + gli_set_9750(host);

Maybe look at using the sdhci_ops ->reset() callback.

> +
> if (soft) {
> /* force clock reconfiguration */
> host->clock = 0;
> @@ -2326,7 +2334,7 @@ void sdhci_reset_tuning(struct sdhci_host *host)
> }
> EXPORT_SYMBOL_GPL(sdhci_reset_tuning);
>
> -static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
> +void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
> {
> sdhci_reset_tuning(host);
>
> @@ -2337,6 +2345,7 @@ static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
>
> mmc_abort_tuning(host->mmc, opcode);
> }
> +EXPORT_SYMBOL_GPL(sdhci_abort_tuning);

If you need to export sdhci_abort_tuning, then please make it a separate patch.

>
> /*
> * We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI
> @@ -2493,9 +2502,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
> host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
>
> sdhci_start_tuning(host);
> -
> host->tuning_err = __sdhci_execute_tuning(host, opcode);
> -
> sdhci_end_tuning(host);
> out:
> host->flags &= ~SDHCI_HS400_TUNING;
> @@ -4090,6 +4097,12 @@ int sdhci_setup_host(struct sdhci_host *host)
> * value.
> */
> max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> + if (gli_is_9750(host)) {
> + if (!(max_current_caps & 0xff))
> + max_current_caps |= 0xc8;
> + }

Vendor specific changes do not belong in sdhci.c

Look at using sdhci I/O accessors (refer CONFIG_MMC_SDHCI_IO_ACCESSORS)
instead

> +
> if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
> int curr = regulator_get_current_limit(mmc->supply.vmmc);
> if (curr > 0) {
> @@ -4269,6 +4282,106 @@ void sdhci_cleanup_host(struct sdhci_host *host)
> }
> EXPORT_SYMBOL_GPL(sdhci_cleanup_host);
>
> +// for Genesys Logic
> +void gli_set_9750(struct sdhci_host *host)
> +{
> + u32 wt_value = 0;
> + u32 driving_value = 0;
> + u32 pll_value = 0;
> + u32 sw_ctrl_value = 0;
> + u32 misc_value = 0;
> + u32 parameter_value = 0;
> + u32 control_value = 0;
> +
> + u16 ctrl2 = 0;
> +
> + if (!gli_is_9750(host))
> + return;
> +
> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + if ((wt_value & 0x1) == 0) {
> + wt_value |= 0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> + }
> +
> + 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 &= ~(0x0C000FFF);
> + driving_value |= 0x0C000FFF;
> + sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
> +
> + sw_ctrl_value |= 0xc0;
> + sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
> +
> + // reset the tuning flow after reinit and before starting tuning
> + pll_value |= 0x800000; // bit23-1
> + pll_value &= ~(0x00700000); // bit22:20-0
> +
> + misc_value &= ~(0x8); // bit3-0
> + misc_value &= ~(0x4); // bit2-0
> +
> + misc_value &= ~(0x70); // bit6:4-0
> + misc_value |= 0x50; // bit6:4-5
> +
> + parameter_value &= ~(0x7); // bit2:0-0
> + parameter_value |= 0x1; // bit2:0-1
> +
> + control_value &= ~(0x190000); // bit20:19-0, bit16-0
> + control_value |= 0x110000; // bit20:19-b10, bit16-1
> +
> + 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);
> +
> + // 540 enable tuning parameters control
> + control_value |= 0x10;
> + sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
> +
> + // write 544 tuning parameters
> + sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
> +
> + // 540 disable tuning parameters control
> + control_value &= ~0x10;
> + 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);
> +
> + udelay(1);
> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + wt_value &= ~0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> +}
> +EXPORT_SYMBOL_GPL(gli_set_9750);
> +
> +bool gli_is_9750(struct sdhci_host *host)
> +{
> + struct sdhci_pci_slot *slot = sdhci_priv(host);
> + struct sdhci_pci_chip *chip;
> + struct pci_dev *pdev;
> +
> + chip = slot->chip;
> + pdev = chip->pdev;
> +
> + if (pdev->vendor == PCI_VENDOR_ID_GLI &&
> + pdev->device == PCI_DEVICE_ID_GLI_9750) {
> + return true;
> + }
> +
> + return false;
> +}
> +

Vendor specific changes do not belong in sdhci.c

> int __sdhci_add_host(struct sdhci_host *host)
> {
> unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 72601a4d2e95..437bab3af195 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -797,5 +797,6 @@ void sdhci_start_tuning(struct sdhci_host *host);
> void sdhci_end_tuning(struct sdhci_host *host);
> void sdhci_reset_tuning(struct sdhci_host *host);
> void sdhci_send_tuning(struct sdhci_host *host, u32 opcode);
> +void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode);
>
> #endif /* __SDHCI_HW_H */
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 70e86148cb1e..e668da5c8df5 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2403,6 +2403,7 @@
> #define PCI_DEVICE_ID_RDC_R6061 0x6061
> #define PCI_DEVICE_ID_RDC_D1010 0x1010
>
> +#define PCI_VENDOR_ID_GLI 0x17a0

Better as a separate patch.

> #define PCI_VENDOR_ID_LENOVO 0x17aa
>
> #define PCI_VENDOR_ID_QCOM 0x17cb
>

2019-08-12 02:28:27

by Ben Chuang

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] mmc: sdhci: sdhci-pci-core: Add Genesis Logic GL975x support

On 8/7/19 8:25 PM, Adrian Hunter wrote:
> On 26/07/19 5:07 AM, Michael K. Johnson wrote:
>> Add support for the GL9750 and GL9755 chipsets.
>>
>> Signed-off-by: Ben Chuang <[email protected]>
>> Co-developed-by: Michael K Johnson <[email protected]>
>> Signed-off-by: Michael K Johnson <[email protected]>
>>
>> diff --git a/drivers/mmc/host/sdhci-gli.h b/drivers/mmc/host/sdhci-gli.h
> I suggest creating sdhci-pci-gli.c and putting definitions there. Have a
> look at sdhci-pci-arasan.c, sdhci-pci-dwc-mshc.c or sdhci-pci-o2micro.c

Hi, Adrian,

Thanks for the review.

I will break the patch apart as you suggested and create sdhci-pci-gli.c.

Then move vendor specific changes to sdhci-pci-gli.c.

>> new file mode 100644
>> index 000000000000..0acd35b6d3e2
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci-gli.h
>> @@ -0,0 +1,27 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +#ifndef __SDHCI_GLI_H
>> +#define __SDHCI_GLI_H
>> +
>> +/* the define PCI_VENDOR_ID_GLI may put in kernel/include/linux/pci_ids.h */
>> +#ifndef PCI_VENDOR_ID_GLI
>> +#define PCI_VENDOR_ID_GLI 0x17a0
>> +#endif
> If it is in include/linux/pci_ids.h then this code should be removed
>
>> +
>> +/* Genesys Logic extra registers */
>> +#define SDHCI_GLI_9750_WT 0x800
>> +#define SDHCI_GLI_9750_DRIVING 0x860
>> +#define SDHCI_GLI_9750_PLL 0x864
>> +#define SDHCI_GLI_9750_SW_CTRL 0x874
>> +#define SDHCI_GLI_9750_MISC 0x878
>> +
>> +#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
>> +#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
>> +
>> +#define GLI_9755_DRIVER_VER "Genesys Logic (GL9755 v0.9.0-y190703)"
>> +#define GLI_9750_DRIVER_VER "Genesys Logic (GL9750 v0.9.0-y190703)"
>> +
>> +#define GLI_MAX_TUNING_LOOP 40
>> +
>> +void gli_set_9750(struct sdhci_host *host);
>> +
>> +#endif /* __SDHCI_GLI_H */
>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>> index 4154ee11b47d..b5c28df39de1 100644
>> --- a/drivers/mmc/host/sdhci-pci-core.c
>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>> @@ -35,6 +35,7 @@
>>
>> #include "sdhci.h"
>> #include "sdhci-pci.h"
>> +#include "sdhci-gli.h"
>>
>> static void sdhci_pci_hw_reset(struct sdhci_host *host);
>>
>> @@ -1453,6 +1454,223 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
>> .probe_slot = rtsx_probe_slot,
>> };
>>
>> +/* Genesys Logic chipset */
>> +static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
>> +{
>> + struct sdhci_host *host = slot->host;
>> +
>> + slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
>> + dev_info(&slot->chip->pdev->dev, "%s\n", GLI_9755_DRIVER_VER);
>> + sdhci_enable_v4_mode(host);
>> +
>> + return 0;
>> +}
>> +
>> +static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
>> +{
>> + u32 wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
>> + u32 misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
>> +
>> + if ((wt_value & 0x1) == 0) {
>> + wt_value |= 0x1;
>> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
>> + }
>> +
>> + misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
>> + if (b) {
>> + misc_value |= 0x8;
>> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
>> + } else {
>> + misc_value &= ~0x8;
>> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
>> + }
>> +
>> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
>> + wt_value &= ~0x1;
>> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
>> +}
>> +
>> +static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
>> +{
>> + int i;
>> + int rx_inv = 0;
>> +
>> + for (rx_inv = 0; rx_inv < 2; rx_inv++) {
>> + if (rx_inv & 0x1)
>> + gli_set_9750_rx_inv(host, true);
>> + else
>> + gli_set_9750_rx_inv(host, false);
>> +
>> + sdhci_start_tuning(host);
>> +
>> + for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
>> + u16 ctrl;
>> +
>> + sdhci_send_tuning(host, opcode);
>> +
>> + if (!host->tuning_done) {
>> + if (rx_inv == 1) {
>> + pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
>> + mmc_hostname(host->mmc));
>> + sdhci_abort_tuning(host, opcode);
>> + return -ETIMEDOUT;
>> + }
>> + pr_info("%s: Tuning timeout, try next tuning\n",
>> + mmc_hostname(host->mmc));
>> + 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) {
>> + pr_info("%s: Tuning successful\n",
>> + mmc_hostname(host->mmc));
>> + return 0; /* Success! */
>> + }
>> + break;
>> + }
>> + }
>> + }
>> +
>> + 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 mmc_host *mmc, u32 opcode)
>> +{
>> + struct sdhci_host *host = mmc_priv(mmc);
>> + int err = 0;
>> + unsigned int tuning_count = 0;
>> + bool hs400_tuning;
>> +
>> + hs400_tuning = host->flags & SDHCI_HS400_TUNING;
>> +
>> + if (host->tuning_mode == SDHCI_TUNING_MODE_1)
>> + tuning_count = host->tuning_count;
>> +
>> + /*
>> + * The Host Controller needs tuning in case of SDR104 and DDR50
>> + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
>> + * the Capabilities register.
>> + * If the Host Controller supports the HS200 mode then the
>> + * tuning function has to be executed.
>> + */
>> + switch (host->timing) {
>> + /* HS400 tuning is done in HS200 mode */
>> + case MMC_TIMING_MMC_HS400:
>> + err = -EINVAL;
>> + goto out;
>> +
>> + case MMC_TIMING_MMC_HS200:
>> + /*
>> + * Periodic re-tuning for HS400 is not expected to be needed, so
>> + * disable it here.
>> + */
>> + if (hs400_tuning)
>> + tuning_count = 0;
>> + break;
>> +
>> + case MMC_TIMING_UHS_SDR104:
>> + case MMC_TIMING_UHS_DDR50:
>> + break;
>> +
>> + case MMC_TIMING_UHS_SDR50:
>> + if (host->flags & SDHCI_SDR50_NEEDS_TUNING)
>> + break;
>> + /* FALLTHROUGH */
>> +
>> + default:
>> + goto out;
>> + }
>> +
>> + if (host->ops->platform_execute_tuning) {
>> + err = host->ops->platform_execute_tuning(host, opcode);
>> + goto out;
>> + }
>> +
>> + host->mmc->retune_period = tuning_count;
>> +
>> + if (host->tuning_delay < 0)
>> + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
>> +
>> + gli_set_9750(host);
>> + host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
>> +
>> + sdhci_end_tuning(host);
>> +out:
>> + host->flags &= ~SDHCI_HS400_TUNING;
>> +
>> + return err;
>> +}
>> +
>> +static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
>> +{
>> + struct sdhci_host *host = slot->host;
>> + struct mmc_host_ops *ops = &host->mmc_host_ops;
>> +
>> + u32 gli_wt = sdhci_readl(host, SDHCI_GLI_9750_WT);
>> +
>> + dev_info(&slot->chip->pdev->dev, "%s\n", GLI_9750_DRIVER_VER);
>> + slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
>> +
>> + if ((gli_wt & 0x1) == 0) {
>> + gli_wt |= 0x1;
>> + sdhci_writel(host, gli_wt, SDHCI_GLI_9750_WT);
>> + }
>> +
>> + gli_wt = sdhci_readl(host, SDHCI_GLI_9750_WT);
>> + gli_wt &= ~0x1;
>> + sdhci_writel(host, gli_wt, SDHCI_GLI_9750_WT);
>> +
>> + sdhci_enable_v4_mode(host);
>> +
>> + ops->execute_tuning = gl9750_execute_tuning;
>> +
>> + return 0;
>> +}
>> +
>> +static void sdhci_gli_voltage_switch(struct sdhci_host *host)
>> +{
>> + usleep_range(5000, 5500);
>> +}
>> +
>> +static const struct sdhci_ops sdhci_gl9755_ops = {
>> + .set_clock = sdhci_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,
>> + .hw_reset = sdhci_pci_hw_reset,
>> + .voltage_switch = sdhci_gli_voltage_switch,
>> +};
>> +
>> +static 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,
>> +};
>> +
>> +static const struct sdhci_ops sdhci_gl9750_ops = {
>> + .set_clock = sdhci_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,
>> + .hw_reset = sdhci_pci_hw_reset,
>> + .voltage_switch = sdhci_gli_voltage_switch,
>> +};
>> +
>> +static 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,
>> +};
>> +
>> /*AMD chipset generation*/
>> enum amd_chipset_gen {
>> AMD_CHIPSET_BEFORE_ML,
>> @@ -1682,6 +1900,8 @@ static const struct pci_device_id pci_ids[] = {
>> SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
>> SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
>> SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
>> + SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
>> + SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
>> SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
>> /* Generic SD host controller */
>> {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
>> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
>> index e5dc6e44c7a4..78e6b631e252 100644
>> --- a/drivers/mmc/host/sdhci-pci.h
>> +++ b/drivers/mmc/host/sdhci-pci.h
>> @@ -65,6 +65,9 @@
>>
>> #define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202
>>
>> +#define PCI_DEVICE_ID_GLI_9755 0x9755
>> +#define PCI_DEVICE_ID_GLI_9750 0x9750
>> +
>> /*
>> * PCI device class and mask
>> */
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index 14957578bf2e..1ccf1b692d04 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -25,6 +25,8 @@
>>
>> #include <linux/leds.h>
>>
>> +#include <linux/pci.h>
>> +
>> #include <linux/mmc/mmc.h>
>> #include <linux/mmc/host.h>
>> #include <linux/mmc/card.h>
>> @@ -32,6 +34,8 @@
>> #include <linux/mmc/slot-gpio.h>
>>
>> #include "sdhci.h"
>> +#include "sdhci-pci.h"
>> +#include "sdhci-gli.h"
> Vendor specific changes do not belong in sdhci.c
>
>>
>> #define DRIVER_NAME "sdhci"
>>
>> @@ -50,6 +54,8 @@ static void sdhci_finish_data(struct sdhci_host *);
>>
>> static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
>>
>> +static bool gli_is_9750(struct sdhci_host *);
>> +
>> void sdhci_dumpregs(struct sdhci_host *host)
>> {
>> SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
>> @@ -328,6 +334,8 @@ static void sdhci_init(struct sdhci_host *host, int soft)
>>
>> host->cqe_on = false;
>>
>> + gli_set_9750(host);
> Maybe look at using the sdhci_ops ->reset() callback.
>
>> +
>> if (soft) {
>> /* force clock reconfiguration */
>> host->clock = 0;
>> @@ -2326,7 +2334,7 @@ void sdhci_reset_tuning(struct sdhci_host *host)
>> }
>> EXPORT_SYMBOL_GPL(sdhci_reset_tuning);
>>
>> -static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
>> +void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
>> {
>> sdhci_reset_tuning(host);
>>
>> @@ -2337,6 +2345,7 @@ static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode)
>>
>> mmc_abort_tuning(host->mmc, opcode);
>> }
>> +EXPORT_SYMBOL_GPL(sdhci_abort_tuning);
> If you need to export sdhci_abort_tuning, then please make it a separate patch.
>
>>
>> /*
>> * We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI
>> @@ -2493,9 +2502,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
>> host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
>>
>> sdhci_start_tuning(host);
>> -
>> host->tuning_err = __sdhci_execute_tuning(host, opcode);
>> -
>> sdhci_end_tuning(host);
>> out:
>> host->flags &= ~SDHCI_HS400_TUNING;
>> @@ -4090,6 +4097,12 @@ int sdhci_setup_host(struct sdhci_host *host)
>> * value.
>> */
>> max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
>> +
>> + if (gli_is_9750(host)) {
>> + if (!(max_current_caps & 0xff))
>> + max_current_caps |= 0xc8;
>> + }
> Vendor specific changes do not belong in sdhci.c
>
> Look at using sdhci I/O accessors (refer CONFIG_MMC_SDHCI_IO_ACCESSORS)
> instead
>
>> +
>> if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
>> int curr = regulator_get_current_limit(mmc->supply.vmmc);
>> if (curr > 0) {
>> @@ -4269,6 +4282,106 @@ void sdhci_cleanup_host(struct sdhci_host *host)
>> }
>> EXPORT_SYMBOL_GPL(sdhci_cleanup_host);
>>
>> +// for Genesys Logic
>> +void gli_set_9750(struct sdhci_host *host)
>> +{
>> + u32 wt_value = 0;
>> + u32 driving_value = 0;
>> + u32 pll_value = 0;
>> + u32 sw_ctrl_value = 0;
>> + u32 misc_value = 0;
>> + u32 parameter_value = 0;
>> + u32 control_value = 0;
>> +
>> + u16 ctrl2 = 0;
>> +
>> + if (!gli_is_9750(host))
>> + return;
>> +
>> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
>> + if ((wt_value & 0x1) == 0) {
>> + wt_value |= 0x1;
>> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
>> + }
>> +
>> + 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 &= ~(0x0C000FFF);
>> + driving_value |= 0x0C000FFF;
>> + sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
>> +
>> + sw_ctrl_value |= 0xc0;
>> + sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
>> +
>> + // reset the tuning flow after reinit and before starting tuning
>> + pll_value |= 0x800000; // bit23-1
>> + pll_value &= ~(0x00700000); // bit22:20-0
>> +
>> + misc_value &= ~(0x8); // bit3-0
>> + misc_value &= ~(0x4); // bit2-0
>> +
>> + misc_value &= ~(0x70); // bit6:4-0
>> + misc_value |= 0x50; // bit6:4-5
>> +
>> + parameter_value &= ~(0x7); // bit2:0-0
>> + parameter_value |= 0x1; // bit2:0-1
>> +
>> + control_value &= ~(0x190000); // bit20:19-0, bit16-0
>> + control_value |= 0x110000; // bit20:19-b10, bit16-1
>> +
>> + 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);
>> +
>> + // 540 enable tuning parameters control
>> + control_value |= 0x10;
>> + sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
>> +
>> + // write 544 tuning parameters
>> + sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
>> +
>> + // 540 disable tuning parameters control
>> + control_value &= ~0x10;
>> + 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);
>> +
>> + udelay(1);
>> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
>> + wt_value &= ~0x1;
>> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
>> +}
>> +EXPORT_SYMBOL_GPL(gli_set_9750);
>> +
>> +bool gli_is_9750(struct sdhci_host *host)
>> +{
>> + struct sdhci_pci_slot *slot = sdhci_priv(host);
>> + struct sdhci_pci_chip *chip;
>> + struct pci_dev *pdev;
>> +
>> + chip = slot->chip;
>> + pdev = chip->pdev;
>> +
>> + if (pdev->vendor == PCI_VENDOR_ID_GLI &&
>> + pdev->device == PCI_DEVICE_ID_GLI_9750) {
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +
> Vendor specific changes do not belong in sdhci.c
>
>> int __sdhci_add_host(struct sdhci_host *host)
>> {
>> unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index 72601a4d2e95..437bab3af195 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -797,5 +797,6 @@ void sdhci_start_tuning(struct sdhci_host *host);
>> void sdhci_end_tuning(struct sdhci_host *host);
>> void sdhci_reset_tuning(struct sdhci_host *host);
>> void sdhci_send_tuning(struct sdhci_host *host, u32 opcode);
>> +void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode);
>>
>> #endif /* __SDHCI_HW_H */
>> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
>> index 70e86148cb1e..e668da5c8df5 100644
>> --- a/include/linux/pci_ids.h
>> +++ b/include/linux/pci_ids.h
>> @@ -2403,6 +2403,7 @@
>> #define PCI_DEVICE_ID_RDC_R6061 0x6061
>> #define PCI_DEVICE_ID_RDC_D1010 0x1010
>>
>> +#define PCI_VENDOR_ID_GLI 0x17a0
> Better as a separate patch.
I will create a pci_ids.h patch for GLI's pci vendor id.
>> #define PCI_VENDOR_ID_LENOVO 0x17aa
>>
>> #define PCI_VENDOR_ID_QCOM 0x17cb
>>
________________________________

Genesys Logic Email Confidentiality Notice:
This mail and any attachments may contain information that is confidential, proprietary, privileged or otherwise protected by law. The mail is intended solely for the named addressee (or a person responsible for delivering it to the addressee). If you are not the intended recipient of this mail, you are not authorized to read, print, copy or disseminate this mail.

If you have received this email in error, please notify us immediately by reply email and immediately delete this message and any attachments from your system. Please be noted that any unauthorized use, dissemination, distribution or copying of this email is strictly prohibited.
________________________________

2019-08-12 23:03:02

by Michael K. Johnson

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] PCI: Add Genesys Logic, Inc. Vendor ID

Add the Genesys Logic, Inc. verndor ID to pci_ids.h.

Signed-off-by: Ben Chuang <[email protected]>
Co-developed-by: Michael K Johnson <[email protected]>
Signed-off-by: Michael K Johnson <[email protected]>

diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 70e86148cb1e..4f7e12772a14 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2403,6 +2403,8 @@
#define PCI_DEVICE_ID_RDC_R6061 0x6061
#define PCI_DEVICE_ID_RDC_D1010 0x1010

+#define PCI_VENDOR_ID_GLI 0x17a0
+
#define PCI_VENDOR_ID_LENOVO 0x17aa

#define PCI_VENDOR_ID_QCOM 0x17cb

2019-08-12 23:21:05

by Michael K. Johnson

[permalink] [raw]
Subject: Re: [PATCH v3 3/2] mmc: host: sdhci-pci: add Genesys Logic GL975x support

Add support for the GL9750 and GL9755 chipsets.

Signed-off-by: Ben Chuang <[email protected]>
Co-developed-by: Michael K Johnson <[email protected]>
Signed-off-by: Michael K Johnson <[email protected]>

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 73578718f119..661445415090 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
- sdhci-pci-dwc-mshc.o
+ sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 4154ee11b47d..e5835fbf73bc 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -1682,6 +1682,8 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
+ SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
+ SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
/* Generic SD host controller */
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
new file mode 100644
index 000000000000..3eed4c9eb7ca
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Genesys Logic, Inc.
+ *
+ * Authors: Ben Chuang <[email protected]>
+ *
+ * Version: v0.9.0 (2019-08-08)
+ */
+
+#include <linux/pci.h>
+#include <linux/mmc/mmc.h>
+#include <linux/delay.h>
+#include "sdhci.h"
+#include "sdhci-pci.h"
+
+/* Genesys Logic extra registers */
+#define SDHCI_GLI_9750_WT 0x800
+#define SDHCI_GLI_9750_DRIVING 0x860
+#define SDHCI_GLI_9750_PLL 0x864
+#define SDHCI_GLI_9750_SW_CTRL 0x874
+#define SDHCI_GLI_9750_MISC 0x878
+
+#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
+#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
+
+#define GLI_MAX_TUNING_LOOP 40
+
+/* Genesys Logic chipset */
+static void gli_set_9750(struct sdhci_host *host)
+{
+ u32 wt_value = 0;
+ u32 driving_value = 0;
+ u32 pll_value = 0;
+ u32 sw_ctrl_value = 0;
+ u32 misc_value = 0;
+ u32 parameter_value = 0;
+ u32 control_value = 0;
+
+ u16 ctrl2 = 0;
+
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ if ((wt_value & 0x1) == 0) {
+ wt_value |= 0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+ }
+
+ 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 &= ~(0x0C000FFF);
+ driving_value |= 0x0C000FFF;
+ sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
+
+ sw_ctrl_value |= 0xc0;
+ sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
+
+ // reset the tuning flow after reinit and before starting tuning
+ pll_value |= 0x800000; // bit23-1
+ pll_value &= ~(0x00700000); // bit22:20-0
+
+ misc_value &= ~(0x8); // bit3-0
+ misc_value &= ~(0x4); // bit2-0
+
+ misc_value &= ~(0x70); // bit6:4-0
+ misc_value |= 0x50; // bit6:4-5
+
+ parameter_value &= ~(0x7); // bit2:0-0
+ parameter_value |= 0x1; // bit2:0-1
+
+ control_value &= ~(0x190000); // bit20:19-0, bit16-0
+ control_value |= 0x110000; // bit20:19-b10, bit16-1
+
+ 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);
+
+ // 540 enable tuning parameters control
+ control_value |= 0x10;
+ sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
+
+ // write 544 tuning parameters
+ sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
+
+ // 540 disable tuning parameters control
+ control_value &= ~0x10;
+ 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);
+
+ udelay(1);
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ wt_value &= ~0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+}
+
+static void sdhci_gli_do_reset(struct sdhci_host *host, u8 mask)
+{
+ host->ops->reset(host, mask);
+
+ if (mask & SDHCI_RESET_ALL) {
+ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+ }
+
+ /* Resetting the controller clears many */
+ host->preset_enabled = false;
+ }
+}
+
+static void sdhci_gli_abort_tuning(struct sdhci_host *host, u32 opcode)
+{
+ sdhci_reset_tuning(host);
+
+ sdhci_gli_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_gli_do_reset(host, SDHCI_RESET_DATA);
+
+ sdhci_end_tuning(host);
+
+ mmc_abort_tuning(host->mmc, opcode);
+}
+
+static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
+{
+ u32 wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ u32 misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
+
+ if ((wt_value & 0x1) == 0) {
+ wt_value |= 0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+ }
+
+ misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
+ if (b) {
+ misc_value |= 0x8;
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ } else {
+ misc_value &= ~0x8;
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ }
+
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ wt_value &= ~0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+}
+
+static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
+{
+ int i;
+ int rx_inv = 0;
+
+ for (rx_inv = 0; rx_inv < 2; rx_inv++) {
+ if (rx_inv & 0x1)
+ gli_set_9750_rx_inv(host, true);
+ else
+ gli_set_9750_rx_inv(host, false);
+
+ sdhci_start_tuning(host);
+
+ for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
+ u16 ctrl;
+
+ sdhci_send_tuning(host, opcode);
+
+ if (!host->tuning_done) {
+ if (rx_inv == 1) {
+ pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
+ mmc_hostname(host->mmc));
+ sdhci_gli_abort_tuning(host, opcode);
+ return -ETIMEDOUT;
+ }
+ pr_info("%s: Tuning timeout, try next tuning\n",
+ mmc_hostname(host->mmc));
+ sdhci_gli_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;
+ }
+ }
+ }
+
+ 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 mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int err = 0;
+ unsigned int tuning_count = 0;
+ bool hs400_tuning;
+
+ hs400_tuning = host->flags & SDHCI_HS400_TUNING;
+
+ if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+ tuning_count = host->tuning_count;
+
+ /*
+ * The Host Controller needs tuning in case of SDR104 and DDR50
+ * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
+ * the Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
+ */
+ switch (host->timing) {
+ /* HS400 tuning is done in HS200 mode */
+ case MMC_TIMING_MMC_HS400:
+ err = -EINVAL;
+ goto out;
+
+ case MMC_TIMING_MMC_HS200:
+ /*
+ * Periodic re-tuning for HS400 is not expected to be needed, so
+ * disable it here.
+ */
+ if (hs400_tuning)
+ tuning_count = 0;
+ break;
+
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_UHS_SDR50:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (host->ops->platform_execute_tuning) {
+ err = host->ops->platform_execute_tuning(host, opcode);
+ goto out;
+ }
+
+ host->mmc->retune_period = tuning_count;
+
+ if (host->tuning_delay < 0)
+ host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
+
+ gli_set_9750(host);
+ host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
+
+ sdhci_end_tuning(host);
+out:
+ host->flags &= ~SDHCI_HS400_TUNING;
+
+ return err;
+}
+
+static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
+{
+ struct sdhci_host *host = slot->host;
+ struct mmc_host_ops *ops = &host->mmc_host_ops;
+
+ slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
+ sdhci_enable_v4_mode(host);
+
+ ops->execute_tuning = gl9750_execute_tuning;
+
+ return 0;
+}
+
+static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
+{
+ struct sdhci_host *host = slot->host;
+
+ 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)
+{
+ usleep_range(5000, 5500);
+}
+
+static void sdhci_gli_pci_hw_reset(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+
+ if (slot->hw_reset)
+ slot->hw_reset(host);
+}
+
+static void sdhci_gli_reset(struct sdhci_host *host, u8 mask)
+{
+ ktime_t timeout;
+
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = ktime_add_ms(ktime_get(), 100);
+
+ /* hw clears the bit when it's done */
+ while (1) {
+ bool timedout = ktime_after(ktime_get(), timeout);
+
+ if (!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask))
+ break;
+ if (timedout) {
+ pr_err("%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ udelay(10);
+ }
+ 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)) {
+ if (!(value & 0xff))
+ value |= 0xc8;
+ }
+ return value;
+}
+
+static const struct sdhci_ops sdhci_gl9755_ops = {
+ .set_clock = sdhci_set_clock,
+ .enable_dma = sdhci_pci_enable_dma,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_gli_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .hw_reset = sdhci_gli_pci_hw_reset,
+ .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,
+};
+
+static const struct sdhci_ops sdhci_gl9750_ops = {
+ .read_l = sdhci_gl9750_readl,
+ .set_clock = sdhci_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,
+ .hw_reset = sdhci_gli_pci_hw_reset,
+ .voltage_switch = sdhci_gli_voltage_switch,
+};
+
+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,
+};
+
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index e5dc6e44c7a4..738ba5afcc20 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -65,6 +65,9 @@

#define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202

+#define PCI_DEVICE_ID_GLI_9755 0x9755
+#define PCI_DEVICE_ID_GLI_9750 0x9750
+
/*
* PCI device class and mask
*/
@@ -185,5 +188,7 @@ int sdhci_pci_enable_dma(struct sdhci_host *host);
extern const struct sdhci_pci_fixes sdhci_arasan;
extern const struct sdhci_pci_fixes sdhci_snps;
extern const struct sdhci_pci_fixes sdhci_o2;
+extern const struct sdhci_pci_fixes sdhci_gl9750;
+extern const struct sdhci_pci_fixes sdhci_gl9755;

#endif /* __SDHCI_PCI_H */

2019-08-13 14:43:56

by Michael K. Johnson

[permalink] [raw]
Subject: [PATCH v4 3/3] mmc: host: sdhci-pci: add Genesys Logic GL975x support

Add support for the GL9750 and GL9755 chipsets.

Signed-off-by: Ben Chuang <[email protected]>
Co-developed-by: Michael K Johnson <[email protected]>
Signed-off-by: Michael K Johnson <[email protected]>

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 73578718f119..661445415090 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
- sdhci-pci-dwc-mshc.o
+ sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 4154ee11b47d..e5835fbf73bc 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -1682,6 +1682,8 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
+ SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
+ SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
/* Generic SD host controller */
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
new file mode 100644
index 000000000000..28bdc2066580
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Genesys Logic, Inc.
+ *
+ * Authors: Ben Chuang <[email protected]>
+ *
+ * Version: v0.9.0 (2019-08-08)
+ */
+
+#include <linux/pci.h>
+#include <linux/mmc/mmc.h>
+#include <linux/delay.h>
+#include "sdhci.h"
+#include "sdhci-pci.h"
+
+/* Genesys Logic extra registers */
+#define SDHCI_GLI_9750_WT 0x800
+#define SDHCI_GLI_9750_DRIVING 0x860
+#define SDHCI_GLI_9750_PLL 0x864
+#define SDHCI_GLI_9750_SW_CTRL 0x874
+#define SDHCI_GLI_9750_MISC 0x878
+
+#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
+#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
+
+#define GLI_MAX_TUNING_LOOP 40
+
+/* Genesys Logic chipset */
+static void gli_set_9750(struct sdhci_host *host)
+{
+ u32 wt_value = 0;
+ u32 driving_value = 0;
+ u32 pll_value = 0;
+ u32 sw_ctrl_value = 0;
+ u32 misc_value = 0;
+ u32 parameter_value = 0;
+ u32 control_value = 0;
+
+ u16 ctrl2 = 0;
+
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ if ((wt_value & 0x1) == 0) {
+ wt_value |= 0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+ }
+
+ 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 &= ~(0x0C000FFF);
+ driving_value |= 0x0C000FFF;
+ sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
+
+ sw_ctrl_value |= 0xc0;
+ sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
+
+ // reset the tuning flow after reinit and before starting tuning
+ pll_value |= 0x800000; // bit23-1
+ pll_value &= ~(0x00700000); // bit22:20-0
+
+ misc_value &= ~(0x8); // bit3-0
+ misc_value &= ~(0x4); // bit2-0
+
+ misc_value &= ~(0x70); // bit6:4-0
+ misc_value |= 0x50; // bit6:4-5
+
+ parameter_value &= ~(0x7); // bit2:0-0
+ parameter_value |= 0x1; // bit2:0-1
+
+ control_value &= ~(0x190000); // bit20:19-0, bit16-0
+ control_value |= 0x110000; // bit20:19-b10, bit16-1
+
+ 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);
+
+ // 540 enable tuning parameters control
+ control_value |= 0x10;
+ sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
+
+ // write 544 tuning parameters
+ sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
+
+ // 540 disable tuning parameters control
+ control_value &= ~0x10;
+ 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);
+
+ udelay(1);
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ wt_value &= ~0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+}
+
+static void sdhci_gli_do_reset(struct sdhci_host *host, u8 mask)
+{
+ host->ops->reset(host, mask);
+
+ if (mask & SDHCI_RESET_ALL) {
+ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+ }
+
+ /* Resetting the controller clears many */
+ host->preset_enabled = false;
+ }
+}
+
+static void sdhci_gli_abort_tuning(struct sdhci_host *host, u32 opcode)
+{
+ sdhci_reset_tuning(host);
+
+ sdhci_gli_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_gli_do_reset(host, SDHCI_RESET_DATA);
+
+ sdhci_end_tuning(host);
+
+ mmc_abort_tuning(host->mmc, opcode);
+}
+
+static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
+{
+ u32 wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ u32 misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
+
+ if ((wt_value & 0x1) == 0) {
+ wt_value |= 0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+ }
+
+ misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
+ if (b) {
+ misc_value |= 0x8;
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ } else {
+ misc_value &= ~0x8;
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ }
+
+ wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
+ wt_value &= ~0x1;
+ sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
+}
+
+static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
+{
+ int i;
+ int rx_inv = 0;
+
+ for (rx_inv = 0; rx_inv < 2; rx_inv++) {
+ if (rx_inv & 0x1)
+ gli_set_9750_rx_inv(host, true);
+ else
+ gli_set_9750_rx_inv(host, false);
+
+ sdhci_start_tuning(host);
+
+ for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
+ u16 ctrl;
+
+ sdhci_send_tuning(host, opcode);
+
+ if (!host->tuning_done) {
+ if (rx_inv == 1) {
+ pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
+ mmc_hostname(host->mmc));
+ sdhci_gli_abort_tuning(host, opcode);
+ return -ETIMEDOUT;
+ }
+ pr_info("%s: Tuning timeout, try next tuning\n",
+ mmc_hostname(host->mmc));
+ sdhci_gli_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;
+ }
+ }
+ }
+
+ 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 mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int err = 0;
+ unsigned int tuning_count = 0;
+ bool hs400_tuning;
+
+ hs400_tuning = host->flags & SDHCI_HS400_TUNING;
+
+ if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+ tuning_count = host->tuning_count;
+
+ /*
+ * The Host Controller needs tuning in case of SDR104 and DDR50
+ * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
+ * the Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
+ */
+ switch (host->timing) {
+ /* HS400 tuning is done in HS200 mode */
+ case MMC_TIMING_MMC_HS400:
+ err = -EINVAL;
+ goto out;
+
+ case MMC_TIMING_MMC_HS200:
+ /*
+ * Periodic re-tuning for HS400 is not expected to be needed, so
+ * disable it here.
+ */
+ if (hs400_tuning)
+ tuning_count = 0;
+ break;
+
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_UHS_SDR50:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (host->ops->platform_execute_tuning) {
+ err = host->ops->platform_execute_tuning(host, opcode);
+ goto out;
+ }
+
+ host->mmc->retune_period = tuning_count;
+
+ if (host->tuning_delay < 0)
+ host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
+
+ gli_set_9750(host);
+ host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
+
+ sdhci_end_tuning(host);
+out:
+ host->flags &= ~SDHCI_HS400_TUNING;
+
+ return err;
+}
+
+static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
+{
+ struct sdhci_host *host = slot->host;
+ struct mmc_host_ops *ops = &host->mmc_host_ops;
+
+ slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
+ sdhci_enable_v4_mode(host);
+
+ ops->execute_tuning = gl9750_execute_tuning;
+
+ return 0;
+}
+
+static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
+{
+ struct sdhci_host *host = slot->host;
+
+ 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)
+{
+ usleep_range(5000, 5500);
+}
+
+static void sdhci_gli_pci_hw_reset(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+
+ if (slot->hw_reset)
+ slot->hw_reset(host);
+}
+
+static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask)
+{
+ ktime_t timeout;
+
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = ktime_add_ms(ktime_get(), 100);
+
+ /* hw clears the bit when it's done */
+ while (1) {
+ bool timedout = ktime_after(ktime_get(), timeout);
+
+ if (!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask))
+ break;
+ if (timedout) {
+ pr_err("%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ udelay(10);
+ }
+ 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)) {
+ if (!(value & 0xff))
+ value |= 0xc8;
+ }
+ return value;
+}
+
+static const struct sdhci_ops sdhci_gl9755_ops = {
+ .set_clock = sdhci_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,
+ .hw_reset = sdhci_gli_pci_hw_reset,
+ .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,
+};
+
+static const struct sdhci_ops sdhci_gl9750_ops = {
+ .read_l = sdhci_gl9750_readl,
+ .set_clock = sdhci_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,
+ .hw_reset = sdhci_gli_pci_hw_reset,
+ .voltage_switch = sdhci_gli_voltage_switch,
+};
+
+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,
+};
+
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index e5dc6e44c7a4..738ba5afcc20 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -65,6 +65,9 @@

#define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202

+#define PCI_DEVICE_ID_GLI_9755 0x9755
+#define PCI_DEVICE_ID_GLI_9750 0x9750
+
/*
* PCI device class and mask
*/
@@ -185,5 +188,7 @@ int sdhci_pci_enable_dma(struct sdhci_host *host);
extern const struct sdhci_pci_fixes sdhci_arasan;
extern const struct sdhci_pci_fixes sdhci_snps;
extern const struct sdhci_pci_fixes sdhci_o2;
+extern const struct sdhci_pci_fixes sdhci_gl9750;
+extern const struct sdhci_pci_fixes sdhci_gl9755;

#endif /* __SDHCI_PCI_H */

2019-08-13 15:34:03

by Michael K. Johnson

[permalink] [raw]
Subject: Re: [PATCH v3 3/2] mmc: host: sdhci-pci: add Genesys Logic GL975x support

On Mon, Aug 12, 2019 at 06:58:26PM -0400, Michael K. Johnson wrote:
> Add support for the GL9750 and GL9755 chipsets.

This patch has a bug introduced in responding to feedback; it invokes
the wrong reset functions. New v4 patch coming next will fix that,
is based on the first two (unchanged) v3 patches to add pll enable
support and add the GLI PCI identifier.

2019-08-15 11:48:13

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] PCI: Add Genesys Logic, Inc. Vendor ID

On 13/08/19 1:57 AM, Michael K. Johnson wrote:
> Add the Genesys Logic, Inc. verndor ID to pci_ids.h.

verndor -> vendor

>
> Signed-off-by: Ben Chuang <[email protected]>
> Co-developed-by: Michael K Johnson <[email protected]>

Did you mean for this patch to be "From:" Ben Chuang because otherwise
"Co-developed-by" the author is redundant.

> Signed-off-by: Michael K Johnson <[email protected]>
>
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 70e86148cb1e..4f7e12772a14 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2403,6 +2403,8 @@
> #define PCI_DEVICE_ID_RDC_R6061 0x6061
> #define PCI_DEVICE_ID_RDC_D1010 0x1010
>
> +#define PCI_VENDOR_ID_GLI 0x17a0
> +
> #define PCI_VENDOR_ID_LENOVO 0x17aa
>
> #define PCI_VENDOR_ID_QCOM 0x17cb
>

2019-08-21 10:16:42

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] mmc: host: sdhci-pci: add Genesys Logic GL975x support

On Tue, 13 Aug 2019 at 16:41, Michael K. Johnson <[email protected]> wrote:
>
> Add support for the GL9750 and GL9755 chipsets.
>
> Signed-off-by: Ben Chuang <[email protected]>
> Co-developed-by: Michael K Johnson <[email protected]>
> Signed-off-by: Michael K Johnson <[email protected]>

I am having a hard time to follow the different versions of the
patches in the series. Would you mind reposting the entire series and
then make sure all patches shares the same version.

Kind regards
Uffe

>
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 73578718f119..661445415090 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -13,7 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
> obj-$(CONFIG_MMC_SDHCI) += sdhci.o
> obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
> sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
> - sdhci-pci-dwc-mshc.o
> + sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
> obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
> obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
> obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 4154ee11b47d..e5835fbf73bc 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -1682,6 +1682,8 @@ static const struct pci_device_id pci_ids[] = {
> SDHCI_PCI_DEVICE(O2, SEABIRD1, o2),
> SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan),
> SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
> + SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
> + SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
> SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
> /* Generic SD host controller */
> {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
> diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
> new file mode 100644
> index 000000000000..28bdc2066580
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pci-gli.c
> @@ -0,0 +1,378 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 Genesys Logic, Inc.
> + *
> + * Authors: Ben Chuang <[email protected]>
> + *
> + * Version: v0.9.0 (2019-08-08)
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/delay.h>
> +#include "sdhci.h"
> +#include "sdhci-pci.h"
> +
> +/* Genesys Logic extra registers */
> +#define SDHCI_GLI_9750_WT 0x800
> +#define SDHCI_GLI_9750_DRIVING 0x860
> +#define SDHCI_GLI_9750_PLL 0x864
> +#define SDHCI_GLI_9750_SW_CTRL 0x874
> +#define SDHCI_GLI_9750_MISC 0x878
> +
> +#define SDHCI_GLI_9750_TUNING_CONTROL 0x540
> +#define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544
> +
> +#define GLI_MAX_TUNING_LOOP 40
> +
> +/* Genesys Logic chipset */
> +static void gli_set_9750(struct sdhci_host *host)
> +{
> + u32 wt_value = 0;
> + u32 driving_value = 0;
> + u32 pll_value = 0;
> + u32 sw_ctrl_value = 0;
> + u32 misc_value = 0;
> + u32 parameter_value = 0;
> + u32 control_value = 0;
> +
> + u16 ctrl2 = 0;
> +
> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + if ((wt_value & 0x1) == 0) {
> + wt_value |= 0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> + }
> +
> + 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 &= ~(0x0C000FFF);
> + driving_value |= 0x0C000FFF;
> + sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING);
> +
> + sw_ctrl_value |= 0xc0;
> + sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL);
> +
> + // reset the tuning flow after reinit and before starting tuning
> + pll_value |= 0x800000; // bit23-1
> + pll_value &= ~(0x00700000); // bit22:20-0
> +
> + misc_value &= ~(0x8); // bit3-0
> + misc_value &= ~(0x4); // bit2-0
> +
> + misc_value &= ~(0x70); // bit6:4-0
> + misc_value |= 0x50; // bit6:4-5
> +
> + parameter_value &= ~(0x7); // bit2:0-0
> + parameter_value |= 0x1; // bit2:0-1
> +
> + control_value &= ~(0x190000); // bit20:19-0, bit16-0
> + control_value |= 0x110000; // bit20:19-b10, bit16-1
> +
> + 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);
> +
> + // 540 enable tuning parameters control
> + control_value |= 0x10;
> + sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL);
> +
> + // write 544 tuning parameters
> + sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS);
> +
> + // 540 disable tuning parameters control
> + control_value &= ~0x10;
> + 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);
> +
> + udelay(1);
> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + wt_value &= ~0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> +}
> +
> +static void sdhci_gli_do_reset(struct sdhci_host *host, u8 mask)
> +{
> + host->ops->reset(host, mask);
> +
> + if (mask & SDHCI_RESET_ALL) {
> + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> + if (host->ops->enable_dma)
> + host->ops->enable_dma(host);
> + }
> +
> + /* Resetting the controller clears many */
> + host->preset_enabled = false;
> + }
> +}
> +
> +static void sdhci_gli_abort_tuning(struct sdhci_host *host, u32 opcode)
> +{
> + sdhci_reset_tuning(host);
> +
> + sdhci_gli_do_reset(host, SDHCI_RESET_CMD);
> + sdhci_gli_do_reset(host, SDHCI_RESET_DATA);
> +
> + sdhci_end_tuning(host);
> +
> + mmc_abort_tuning(host->mmc, opcode);
> +}
> +
> +static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b)
> +{
> + u32 wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + u32 misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
> +
> + if ((wt_value & 0x1) == 0) {
> + wt_value |= 0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> + }
> +
> + misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC);
> + if (b) {
> + misc_value |= 0x8;
> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
> + } else {
> + misc_value &= ~0x8;
> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
> + }
> +
> + wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT);
> + wt_value &= ~0x1;
> + sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT);
> +}
> +
> +static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode)
> +{
> + int i;
> + int rx_inv = 0;
> +
> + for (rx_inv = 0; rx_inv < 2; rx_inv++) {
> + if (rx_inv & 0x1)
> + gli_set_9750_rx_inv(host, true);
> + else
> + gli_set_9750_rx_inv(host, false);
> +
> + sdhci_start_tuning(host);
> +
> + for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) {
> + u16 ctrl;
> +
> + sdhci_send_tuning(host, opcode);
> +
> + if (!host->tuning_done) {
> + if (rx_inv == 1) {
> + pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
> + mmc_hostname(host->mmc));
> + sdhci_gli_abort_tuning(host, opcode);
> + return -ETIMEDOUT;
> + }
> + pr_info("%s: Tuning timeout, try next tuning\n",
> + mmc_hostname(host->mmc));
> + sdhci_gli_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;
> + }
> + }
> + }
> +
> + 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 mmc_host *mmc, u32 opcode)
> +{
> + struct sdhci_host *host = mmc_priv(mmc);
> + int err = 0;
> + unsigned int tuning_count = 0;
> + bool hs400_tuning;
> +
> + hs400_tuning = host->flags & SDHCI_HS400_TUNING;
> +
> + if (host->tuning_mode == SDHCI_TUNING_MODE_1)
> + tuning_count = host->tuning_count;
> +
> + /*
> + * The Host Controller needs tuning in case of SDR104 and DDR50
> + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
> + * the Capabilities register.
> + * If the Host Controller supports the HS200 mode then the
> + * tuning function has to be executed.
> + */
> + switch (host->timing) {
> + /* HS400 tuning is done in HS200 mode */
> + case MMC_TIMING_MMC_HS400:
> + err = -EINVAL;
> + goto out;
> +
> + case MMC_TIMING_MMC_HS200:
> + /*
> + * Periodic re-tuning for HS400 is not expected to be needed, so
> + * disable it here.
> + */
> + if (hs400_tuning)
> + tuning_count = 0;
> + break;
> +
> + case MMC_TIMING_UHS_SDR104:
> + case MMC_TIMING_UHS_DDR50:
> + case MMC_TIMING_UHS_SDR50:
> + break;
> +
> + default:
> + goto out;
> + }
> +
> + if (host->ops->platform_execute_tuning) {
> + err = host->ops->platform_execute_tuning(host, opcode);
> + goto out;
> + }
> +
> + host->mmc->retune_period = tuning_count;
> +
> + if (host->tuning_delay < 0)
> + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
> +
> + gli_set_9750(host);
> + host->tuning_err = __sdhci_execute_tuning_9750(host, opcode);
> +
> + sdhci_end_tuning(host);
> +out:
> + host->flags &= ~SDHCI_HS400_TUNING;
> +
> + return err;
> +}
> +
> +static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot)
> +{
> + struct sdhci_host *host = slot->host;
> + struct mmc_host_ops *ops = &host->mmc_host_ops;
> +
> + slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
> + sdhci_enable_v4_mode(host);
> +
> + ops->execute_tuning = gl9750_execute_tuning;
> +
> + return 0;
> +}
> +
> +static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot)
> +{
> + struct sdhci_host *host = slot->host;
> +
> + 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)
> +{
> + usleep_range(5000, 5500);
> +}
> +
> +static void sdhci_gli_pci_hw_reset(struct sdhci_host *host)
> +{
> + struct sdhci_pci_slot *slot = sdhci_priv(host);
> +
> + if (slot->hw_reset)
> + slot->hw_reset(host);
> +}
> +
> +static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask)
> +{
> + ktime_t timeout;
> +
> + sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
> +
> + if (mask & SDHCI_RESET_ALL)
> + host->clock = 0;
> +
> + /* Wait max 100 ms */
> + timeout = ktime_add_ms(ktime_get(), 100);
> +
> + /* hw clears the bit when it's done */
> + while (1) {
> + bool timedout = ktime_after(ktime_get(), timeout);
> +
> + if (!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask))
> + break;
> + if (timedout) {
> + pr_err("%s: Reset 0x%x never completed.\n",
> + mmc_hostname(host->mmc), (int)mask);
> + sdhci_dumpregs(host);
> + return;
> + }
> + udelay(10);
> + }
> + 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)) {
> + if (!(value & 0xff))
> + value |= 0xc8;
> + }
> + return value;
> +}
> +
> +static const struct sdhci_ops sdhci_gl9755_ops = {
> + .set_clock = sdhci_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,
> + .hw_reset = sdhci_gli_pci_hw_reset,
> + .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,
> +};
> +
> +static const struct sdhci_ops sdhci_gl9750_ops = {
> + .read_l = sdhci_gl9750_readl,
> + .set_clock = sdhci_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,
> + .hw_reset = sdhci_gli_pci_hw_reset,
> + .voltage_switch = sdhci_gli_voltage_switch,
> +};
> +
> +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,
> +};
> +
> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
> index e5dc6e44c7a4..738ba5afcc20 100644
> --- a/drivers/mmc/host/sdhci-pci.h
> +++ b/drivers/mmc/host/sdhci-pci.h
> @@ -65,6 +65,9 @@
>
> #define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202
>
> +#define PCI_DEVICE_ID_GLI_9755 0x9755
> +#define PCI_DEVICE_ID_GLI_9750 0x9750
> +
> /*
> * PCI device class and mask
> */
> @@ -185,5 +188,7 @@ int sdhci_pci_enable_dma(struct sdhci_host *host);
> extern const struct sdhci_pci_fixes sdhci_arasan;
> extern const struct sdhci_pci_fixes sdhci_snps;
> extern const struct sdhci_pci_fixes sdhci_o2;
> +extern const struct sdhci_pci_fixes sdhci_gl9750;
> +extern const struct sdhci_pci_fixes sdhci_gl9755;
>
> #endif /* __SDHCI_PCI_H */