* [PATCH V8 0/5] Add Genesys Logic GL975x support
@ 2019-09-06 2:29 Ben Chuang
2019-09-06 2:30 ` [PATCH V8 1/5] mmc: sdhci: Change timeout of loop for checking internal clock stable Ben Chuang
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Ben Chuang @ 2019-09-06 2:29 UTC (permalink / raw)
To: adrian.hunter, ulf.hansson
Cc: linux-mmc, linux-kernel, johnsonm, ben.chuang, Ben Chuang
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
The patches modify internal clock setup to match SD Host Controller
Simplified Specifications 4.20 and support Genesys Logic GL9750/GL9755
chipsets.
v8:
refine codes in sdhci-gli-pci.c
- remove duplicate assignment
- remove redundant delay
- use '!!'(not not) logical operator to refine the true/false condition
- check end condition after outter loop
- add comments for delay 5ms in sdhci_gli_voltage_switch()
- merge two logical conditions to one line
v7:
- remove condition define CONFIG_MMC_SDHCI_IO_ACCESSORS from sdhci-pci-gli.c
- making the accessors(MMC_SDHCI_IO_ACCESSORS) always available when selecting
MMC_SDHCI_PCI in Kconfig
V6:
- export sdhci_abot_tuning() function symbol
- use C-style comments
- use BIT, FIELD_{GET,PREP} and GENMASK to define bit fields of register
- use host->ops->platform_execute_tuning instead of mmc->ops->execute_tuning
- call sdhci_reset() instead of duplicating the code in sdhci_gl9750_reset
- remove .hw_reset
- use condition define CONFIG_MMC_SDHCI_IO_ACCESSORS for read_l
V5:
- add "change timeout of loop .." to a patch
- fix typo "verndor" to "vendor"
V4:
- change name from sdhci_gli_reset to sdhci_gl9750_reset
- fix sdhci_reset to sdhci_gl9750_reset in sdhci_gl9750_ops
- fix sdhci_gli_reset to sdhci_reset in sdhci_gl9755_ops
V3:
- change usleep_range to udelay
- add Genesys Logic PCI Vendor ID to a patch
- separate the Genesys Logic specific part to a patch
V2:
- change udelay to usleep_range
Ben Chuang (5):
mmc: sdhci: Change timeout of loop for checking internal clock stable
mmc: sdhci: Add PLL Enable support to internal clock setup
PCI: Add Genesys Logic, Inc. Vendor ID
mmc: sdhci: Export sdhci_abort_tuning function symbol
mmc: host: sdhci-pci: Add Genesys Logic GL975x support
drivers/mmc/host/Kconfig | 1 +
drivers/mmc/host/Makefile | 2 +-
drivers/mmc/host/sdhci-pci-core.c | 2 +
drivers/mmc/host/sdhci-pci-gli.c | 355 ++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-pci.h | 5 +
drivers/mmc/host/sdhci.c | 30 ++-
drivers/mmc/host/sdhci.h | 2 +
include/linux/pci_ids.h | 2 +
8 files changed, 395 insertions(+), 4 deletions(-)
create mode 100644 drivers/mmc/host/sdhci-pci-gli.c
--
2.23.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH V8 1/5] mmc: sdhci: Change timeout of loop for checking internal clock stable
2019-09-06 2:29 [PATCH V8 0/5] Add Genesys Logic GL975x support Ben Chuang
@ 2019-09-06 2:30 ` Ben Chuang
2019-09-06 2:31 ` [PATCH V8 2/5] mmc: sdhci: Add PLL Enable support to internal clock setup Ben Chuang
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Ben Chuang @ 2019-09-06 2:30 UTC (permalink / raw)
To: adrian.hunter, ulf.hansson
Cc: linux-mmc, linux-kernel, johnsonm, ben.chuang, Ben Chuang
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
According to section 3.2.1 internal clock setup in SD Host Controller
Simplified Specifications 4.20, the timeout of loop for checking
internal clock stable is defined as 150ms.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 59acf8e3331e..bed0760a6c2a 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1636,8 +1636,8 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
clk |= SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
- /* Wait max 20 ms */
- timeout = ktime_add_ms(ktime_get(), 20);
+ /* Wait max 150 ms */
+ timeout = ktime_add_ms(ktime_get(), 150);
while (1) {
bool timedout = ktime_after(ktime_get(), timeout);
--
2.23.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V8 2/5] mmc: sdhci: Add PLL Enable support to internal clock setup
2019-09-06 2:29 [PATCH V8 0/5] Add Genesys Logic GL975x support Ben Chuang
2019-09-06 2:30 ` [PATCH V8 1/5] mmc: sdhci: Change timeout of loop for checking internal clock stable Ben Chuang
@ 2019-09-06 2:31 ` Ben Chuang
2019-09-06 2:32 ` [PATCH V8 3/5] PCI: Add Genesys Logic, Inc. Vendor ID Ben Chuang
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Ben Chuang @ 2019-09-06 2:31 UTC (permalink / raw)
To: adrian.hunter, ulf.hansson
Cc: linux-mmc, linux-kernel, johnsonm, ben.chuang, Ben Chuang
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
The GL9750 and GL9755 chipsets, and possibly others, require PLL Enable
setup as part of the internal clock setup as described in 3.2.1 Internal
Clock Setup Sequence of SD Host Controller Simplified Specification
Version 4.20.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 23 +++++++++++++++++++++++
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 24 insertions(+)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index bed0760a6c2a..9106ebc7a422 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1653,6 +1653,29 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
udelay(10);
}
+ if (host->version >= SDHCI_SPEC_410 && host->v4_mode) {
+ clk |= SDHCI_CLOCK_PLL_EN;
+ clk &= ~SDHCI_CLOCK_INT_STABLE;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 150 ms */
+ timeout = ktime_add_ms(ktime_get(), 150);
+ while (1) {
+ bool timedout = ktime_after(ktime_get(), timeout);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ if (clk & SDHCI_CLOCK_INT_STABLE)
+ break;
+ if (timedout) {
+ pr_err("%s: PLL clock never stabilised.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return;
+ }
+ udelay(10);
+ }
+ }
+
clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 199712e7adbb..72601a4d2e95 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -114,6 +114,7 @@
#define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
#define SDHCI_CLOCK_CARD_EN 0x0004
+#define SDHCI_CLOCK_PLL_EN 0x0008
#define SDHCI_CLOCK_INT_STABLE 0x0002
#define SDHCI_CLOCK_INT_EN 0x0001
--
2.23.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V8 3/5] PCI: Add Genesys Logic, Inc. Vendor ID
2019-09-06 2:29 [PATCH V8 0/5] Add Genesys Logic GL975x support Ben Chuang
2019-09-06 2:30 ` [PATCH V8 1/5] mmc: sdhci: Change timeout of loop for checking internal clock stable Ben Chuang
2019-09-06 2:31 ` [PATCH V8 2/5] mmc: sdhci: Add PLL Enable support to internal clock setup Ben Chuang
@ 2019-09-06 2:32 ` Ben Chuang
2019-09-06 2:32 ` [PATCH V8 4/5] mmc: sdhci: Export sdhci_abort_tuning function symbol Ben Chuang
2019-09-06 2:33 ` [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support Ben Chuang
4 siblings, 0 replies; 8+ messages in thread
From: Ben Chuang @ 2019-09-06 2:32 UTC (permalink / raw)
To: adrian.hunter, ulf.hansson
Cc: linux-mmc, linux-kernel, johnsonm, ben.chuang, Ben Chuang
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Add the Genesys Logic, Inc. vendor ID to pci_ids.h.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
include/linux/pci_ids.h | 2 ++
1 file changed, 2 insertions(+)
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
--
2.23.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V8 4/5] mmc: sdhci: Export sdhci_abort_tuning function symbol
2019-09-06 2:29 [PATCH V8 0/5] Add Genesys Logic GL975x support Ben Chuang
` (2 preceding siblings ...)
2019-09-06 2:32 ` [PATCH V8 3/5] PCI: Add Genesys Logic, Inc. Vendor ID Ben Chuang
@ 2019-09-06 2:32 ` Ben Chuang
2019-09-06 2:33 ` [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support Ben Chuang
4 siblings, 0 replies; 8+ messages in thread
From: Ben Chuang @ 2019-09-06 2:32 UTC (permalink / raw)
To: adrian.hunter, ulf.hansson
Cc: linux-mmc, linux-kernel, johnsonm, ben.chuang, Ben Chuang
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Export sdhci_abort_tuning() function symbols which are used by other SD Host
controller driver modules.
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci.c | 3 ++-
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9106ebc7a422..0f2f110534db 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2328,7 +2328,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);
@@ -2339,6 +2339,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
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 */
--
2.23.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support
2019-09-06 2:29 [PATCH V8 0/5] Add Genesys Logic GL975x support Ben Chuang
` (3 preceding siblings ...)
2019-09-06 2:32 ` [PATCH V8 4/5] mmc: sdhci: Export sdhci_abort_tuning function symbol Ben Chuang
@ 2019-09-06 2:33 ` Ben Chuang
2019-09-11 4:42 ` Guenter Roeck
4 siblings, 1 reply; 8+ messages in thread
From: Ben Chuang @ 2019-09-06 2:33 UTC (permalink / raw)
To: adrian.hunter, ulf.hansson
Cc: linux-mmc, linux-kernel, johnsonm, ben.chuang, Ben Chuang
From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Add support for the GL9750 and GL9755 chipsets.
Enable v4 mode and wait 5ms after set 1.8V signal enable for GL9750/
GL9755. Fix the value of SDHCI_MAX_CURRENT register and use the vendor
tuning flow for GL9750.
Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
---
drivers/mmc/host/Kconfig | 1 +
drivers/mmc/host/Makefile | 2 +-
drivers/mmc/host/sdhci-pci-core.c | 2 +
drivers/mmc/host/sdhci-pci-gli.c | 355 ++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-pci.h | 5 +
5 files changed, 364 insertions(+), 1 deletion(-)
create mode 100644 drivers/mmc/host/sdhci-pci-gli.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 931770f17087..9fbfff514d6c 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -94,6 +94,7 @@ config MMC_SDHCI_PCI
depends on MMC_SDHCI && PCI
select MMC_CQHCI
select IOSF_MBI if X86
+ select MMC_SDHCI_IO_ACCESSORS
help
This selects the PCI Secure Digital Host Controller Interface.
Most controllers found today are PCI devices.
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..94462b94abec
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -0,0 +1,355 @@
+// 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"
+
+/* 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_PLL 0x864
+#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_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 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);
+ 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);
+ if (b) {
+ misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
+ misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV,
+ GLI_9750_MISC_RX_INV_ON);
+ sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
+ } else {
+ misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
+ 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 int gli_probe_slot_gl9750(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 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)
+{
+ /*
+ * Accroding to Section 3.6.1 signal voltage switch procedure in
+ * SD Host Controller Simplified Spec. 4.20, the step 6~8 are the
+ * following:
+ * (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;
+}
+
+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,
+ .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,
+ .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,
+};
+
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 */
--
2.23.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support
2019-09-06 2:33 ` [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support Ben Chuang
@ 2019-09-11 4:42 ` Guenter Roeck
2019-09-11 5:52 ` Ben Chuang
0 siblings, 1 reply; 8+ messages in thread
From: Guenter Roeck @ 2019-09-11 4:42 UTC (permalink / raw)
To: Ben Chuang
Cc: adrian.hunter, ulf.hansson, linux-mmc, linux-kernel, johnsonm,
ben.chuang
On Fri, Sep 06, 2019 at 10:33:26AM +0800, Ben Chuang wrote:
> From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
>
> Add support for the GL9750 and GL9755 chipsets.
>
> Enable v4 mode and wait 5ms after set 1.8V signal enable for GL9750/
> GL9755. Fix the value of SDHCI_MAX_CURRENT register and use the vendor
> tuning flow for GL9750.
>
> Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
> Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
> Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> ---
> drivers/mmc/host/Kconfig | 1 +
> drivers/mmc/host/Makefile | 2 +-
> drivers/mmc/host/sdhci-pci-core.c | 2 +
> drivers/mmc/host/sdhci-pci-gli.c | 355 ++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci-pci.h | 5 +
> 5 files changed, 364 insertions(+), 1 deletion(-)
> create mode 100644 drivers/mmc/host/sdhci-pci-gli.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 931770f17087..9fbfff514d6c 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -94,6 +94,7 @@ config MMC_SDHCI_PCI
> depends on MMC_SDHCI && PCI
> select MMC_CQHCI
> select IOSF_MBI if X86
> + select MMC_SDHCI_IO_ACCESSORS
> help
> This selects the PCI Secure Digital Host Controller Interface.
> Most controllers found today are PCI devices.
> 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..94462b94abec
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-pci-gli.c
> @@ -0,0 +1,355 @@
> +// 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"
> +
> +/* 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_PLL 0x864
> +#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_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 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);
> + 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);
> + if (b) {
> + misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
The above statement can be moved outside the if.
> + misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV,
> + GLI_9750_MISC_RX_INV_ON);
> + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
Can be moved outside the if.
[ maybe that was discussed; if so please ignore ]
> + } else {
> + misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
> + 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 int gli_probe_slot_gl9750(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 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)
> +{
> + /*
> + * Accroding to Section 3.6.1 signal voltage switch procedure in
According
> + * SD Host Controller Simplified Spec. 4.20, the step 6~8 are the
s/the step/steps/
"as follows", maybe, instead of "the following" ?
> + * following:
> + * (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;
> +}
> +
> +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,
> + .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,
> + .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,
> +};
> +
> 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 */
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support
2019-09-11 4:42 ` Guenter Roeck
@ 2019-09-11 5:52 ` Ben Chuang
0 siblings, 0 replies; 8+ messages in thread
From: Ben Chuang @ 2019-09-11 5:52 UTC (permalink / raw)
To: Guenter Roeck
Cc: Adrian Hunter, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
Michael K. Johnson, Ben Chuang
On Wed, Sep 11, 2019 at 12:42 PM Guenter Roeck <linux@roeck-us.net> wrote:
>
> On Fri, Sep 06, 2019 at 10:33:26AM +0800, Ben Chuang wrote:
> > From: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> >
> > Add support for the GL9750 and GL9755 chipsets.
> >
> > Enable v4 mode and wait 5ms after set 1.8V signal enable for GL9750/
> > GL9755. Fix the value of SDHCI_MAX_CURRENT register and use the vendor
> > tuning flow for GL9750.
> >
> > Co-developed-by: Michael K Johnson <johnsonm@danlj.org>
> > Signed-off-by: Michael K Johnson <johnsonm@danlj.org>
> > Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
> > ---
> > drivers/mmc/host/Kconfig | 1 +
> > drivers/mmc/host/Makefile | 2 +-
> > drivers/mmc/host/sdhci-pci-core.c | 2 +
> > drivers/mmc/host/sdhci-pci-gli.c | 355 ++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci-pci.h | 5 +
> > 5 files changed, 364 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/mmc/host/sdhci-pci-gli.c
> >
> > diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> > index 931770f17087..9fbfff514d6c 100644
> > --- a/drivers/mmc/host/Kconfig
> > +++ b/drivers/mmc/host/Kconfig
> > @@ -94,6 +94,7 @@ config MMC_SDHCI_PCI
> > depends on MMC_SDHCI && PCI
> > select MMC_CQHCI
> > select IOSF_MBI if X86
> > + select MMC_SDHCI_IO_ACCESSORS
> > help
> > This selects the PCI Secure Digital Host Controller Interface.
> > Most controllers found today are PCI devices.
> > 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..94462b94abec
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-pci-gli.c
> > @@ -0,0 +1,355 @@
> > +// 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"
> > +
> > +/* 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_PLL 0x864
> > +#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_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 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);
> > + 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);
> > + if (b) {
> > + misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
>
> The above statement can be moved outside the if.
>
You are right. I will do it.
> > + misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV,
> > + GLI_9750_MISC_RX_INV_ON);
> > + sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC);
>
> Can be moved outside the if.
>
> [ maybe that was discussed; if so please ignore ]
>
I will do it, it make code short.
> > + } else {
> > + misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV;
> > + 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 int gli_probe_slot_gl9750(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 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)
> > +{
> > + /*
> > + * Accroding to Section 3.6.1 signal voltage switch procedure in
>
> According
>
> > + * SD Host Controller Simplified Spec. 4.20, the step 6~8 are the
>
> s/the step/steps/
>
> "as follows", maybe, instead of "the following" ?
I will modify it. Thank you for your comments.
>
> > + * following:
> > + * (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;
> > +}
> > +
> > +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,
> > + .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,
> > + .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,
> > +};
> > +
> > 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 */
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2019-09-11 5:53 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-06 2:29 [PATCH V8 0/5] Add Genesys Logic GL975x support Ben Chuang
2019-09-06 2:30 ` [PATCH V8 1/5] mmc: sdhci: Change timeout of loop for checking internal clock stable Ben Chuang
2019-09-06 2:31 ` [PATCH V8 2/5] mmc: sdhci: Add PLL Enable support to internal clock setup Ben Chuang
2019-09-06 2:32 ` [PATCH V8 3/5] PCI: Add Genesys Logic, Inc. Vendor ID Ben Chuang
2019-09-06 2:32 ` [PATCH V8 4/5] mmc: sdhci: Export sdhci_abort_tuning function symbol Ben Chuang
2019-09-06 2:33 ` [PATCH V8 5/5] mmc: host: sdhci-pci: Add Genesys Logic GL975x support Ben Chuang
2019-09-11 4:42 ` Guenter Roeck
2019-09-11 5:52 ` Ben Chuang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).