linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V2 0/7] mmc: add support for sdhci 4.0
@ 2018-06-15  2:04 Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode Chunyan Zhang
                   ` (6 more replies)
  0 siblings, 7 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

From: Chunyan Zhang <chunyan.zhang@spreadtrum.com>

From the SD host controller version 4.0 on, SDHCI implementation either
is version 3 compatible or version 4 mode. This patch-set covers those
changes which are common for SDHCI 4.0 version, regardless of whether
they are used with SD or eMMC storage devices.

This patchset also added a new sdhci driver for Spreadtrum's controller
which supports v4.0 mode.

This patchset has been tested on Spreadtrum's mobile phone, emmc can be
initialized, mounted, read and written, with these changes for common
sdhci framework and sdhci-sprd driver.

Changes from v1:
* Addressed comments from Ulf:
 - Add dt-bindings for Spreadtrum sdhci;
 - Use assigned-clocks* DT bindings to set default source of sdio clock;
 - Removed unuseful print;
 - Removed two functions which are not used;
 - Add back the missing pm_runtime_put_autosuspend() after adding sdhci host.

* Changed Spreadtrum sdhci driver name to sdhci-sprd.

Chunyan Zhang (7):
  mmc: sdhci: add sd host v4 mode
  mmc: sdhci: made changes for System Address register of SDMA
  mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode
  mmc: sdhci: add 32-bit block count support for v4 mode
  mmc: sdhci: add CMD23 support for v4 mode
  mmc: sdhci-sprd: added Spreadtrum's initial host controller
  dt-bindings: sdhci-sprd: Add bindings for the sdhci-sprd controller

 .../devicetree/bindings/mmc/sdhci-sprd.txt         |  41 ++
 drivers/mmc/host/Kconfig                           |  13 +
 drivers/mmc/host/Makefile                          |   1 +
 drivers/mmc/host/sdhci-sprd.c                      | 426 +++++++++++++++++++++
 drivers/mmc/host/sdhci.c                           |  85 +++-
 drivers/mmc/host/sdhci.h                           |  31 +-
 6 files changed, 575 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-sprd.txt
 create mode 100644 drivers/mmc/host/sdhci-sprd.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  2018-06-21 10:49   ` Adrian Hunter
  2018-06-15  2:04 ` [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA Chunyan Zhang
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

For SD host controller version 4.00 or later ones, there're two
modes of implementation - Version 3.00 compatible mode or
Version 4 mode.  This patch introduces a flag to record this.

Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
---
 drivers/mmc/host/sdhci.c | 6 ++++++
 drivers/mmc/host/sdhci.h | 6 ++++++
 2 files changed, 12 insertions(+)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2ededa7f..cf5695f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -3302,6 +3302,12 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
 	v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
 	host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
 
+	if (host->version >= SDHCI_SPEC_400) {
+		if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+			SDHCI_CTRL_V4_MODE)
+			host->v4_mode = true;
+	}
+
 	if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
 		return;
 
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c95b0a4..128b0ba 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -184,6 +184,7 @@
 #define   SDHCI_CTRL_DRV_TYPE_D		0x0030
 #define  SDHCI_CTRL_EXEC_TUNING		0x0040
 #define  SDHCI_CTRL_TUNED_CLK		0x0080
+#define  SDHCI_CTRL_V4_MODE		0x1000
 #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
 
 #define SDHCI_CAPABILITIES	0x40
@@ -270,6 +271,8 @@
 #define   SDHCI_SPEC_100	0
 #define   SDHCI_SPEC_200	1
 #define   SDHCI_SPEC_300	2
+#define   SDHCI_SPEC_400	3
+#define   SDHCI_SPEC_410	4
 
 /*
  * End of controller registers.
@@ -551,6 +554,9 @@ struct sdhci_host {
 	u32			sdma_boundary;
 
 	unsigned long private[0] ____cacheline_aligned;
+
+	/* Host Version 4 Enable */
+	bool			v4_mode;
 };
 
 struct sdhci_ops {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  2018-06-21 11:22   ` Adrian Hunter
  2018-06-15  2:04 ` [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode Chunyan Zhang
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

According to the SD host controller specification version 4.10, when
Host Version 4 is enabled, SDMA uses ADMA System Address register
(05Fh-058h) instead of using SDMA System Address register to
support both 32-bit and 64-bit addressing.

Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
---
 drivers/mmc/host/sdhci.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index cf5695f..f57201f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -805,6 +805,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
 static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 {
 	u8 ctrl;
+	u32 reg;
 	struct mmc_data *data = cmd->data;
 
 	if (sdhci_data_line_cmd(cmd))
@@ -894,8 +895,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 					     SDHCI_ADMA_ADDRESS_HI);
 		} else {
 			WARN_ON(sg_cnt != 1);
+			reg = host->v4_mode ? SDHCI_ADMA_ADDRESS :
+				SDHCI_DMA_ADDRESS;
 			sdhci_writel(host, sdhci_sdma_address(host),
-				     SDHCI_DMA_ADDRESS);
+				     reg);
 		}
 	}
 
@@ -2721,6 +2724,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 		 */
 		if (intmask & SDHCI_INT_DMA_END) {
 			u32 dmastart, dmanow;
+			u32 reg;
 
 			dmastart = sdhci_sdma_address(host);
 			dmanow = dmastart + host->data->bytes_xfered;
@@ -2733,7 +2737,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 			host->data->bytes_xfered = dmanow - dmastart;
 			DBG("DMA base 0x%08x, transferred 0x%06x bytes, next 0x%08x\n",
 			    dmastart, host->data->bytes_xfered, dmanow);
-			sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS);
+			reg = host->v4_mode ? SDHCI_ADMA_ADDRESS :
+				SDHCI_DMA_ADDRESS;
+			sdhci_writel(host, dmanow, reg);
 		}
 
 		if (intmask & SDHCI_INT_DATA_END) {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  2018-06-21 13:20   ` Adrian Hunter
  2018-06-15  2:04 ` [PATCH V2 4/7] mmc: sdhci: add 32-bit block count support for v4 mode Chunyan Zhang
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

ADMA2 64-bit addressing support is divided into V3 mode and V4 mode.
So there are two kinds of descriptors for ADMA2 64-bit addressing
i.e. 96-bit Descriptor for V3 mode, and 128-bit Descriptor for V4
mode. 128-bit Descriptor is aligned to 8-byte.

For V4 mode, ADMA2 64-bit addressing is enabled via Host Control 2
register.

Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
---
 drivers/mmc/host/sdhci.c | 50 +++++++++++++++++++++++++++++++++++-------------
 drivers/mmc/host/sdhci.h | 23 +++++++++++++++++-----
 2 files changed, 55 insertions(+), 18 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f57201f..5d3b0d8 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -585,6 +585,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
 	void *desc, *align;
 	char *buffer;
 	int len, offset, i;
+	unsigned int adma2_align = SDHCI_ADMA2_ALIGN(host);
+	unsigned int adma2_mask = SDHCI_ADMA2_MASK(host);
 
 	/*
 	 * The spec does not specify endianness of descriptor table.
@@ -608,8 +610,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
 		 * buffer for the (up to three) bytes that screw up the
 		 * alignment.
 		 */
-		offset = (SDHCI_ADMA2_ALIGN - (addr & SDHCI_ADMA2_MASK)) &
-			 SDHCI_ADMA2_MASK;
+		offset = (adma2_align - (addr & adma2_align)) &
+			 adma2_mask;
 		if (offset) {
 			if (data->flags & MMC_DATA_WRITE) {
 				buffer = sdhci_kmap_atomic(sg, &flags);
@@ -623,8 +625,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
 
 			BUG_ON(offset > 65536);
 
-			align += SDHCI_ADMA2_ALIGN;
-			align_addr += SDHCI_ADMA2_ALIGN;
+			align += adma2_align;
+			align_addr += adma2_align;
 
 			desc += host->desc_sz;
 
@@ -668,13 +670,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
 	void *align;
 	char *buffer;
 	unsigned long flags;
+	unsigned int adma2_align = SDHCI_ADMA2_ALIGN(host);
+	unsigned int adma2_mask = SDHCI_ADMA2_MASK(host);
 
 	if (data->flags & MMC_DATA_READ) {
 		bool has_unaligned = false;
 
 		/* Do a quick scan of the SG list for any unaligned mappings */
 		for_each_sg(data->sg, sg, host->sg_count, i)
-			if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
+			if (sg_dma_address(sg) & adma2_mask) {
 				has_unaligned = true;
 				break;
 			}
@@ -686,15 +690,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
 			align = host->align_buffer;
 
 			for_each_sg(data->sg, sg, host->sg_count, i) {
-				if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
-					size = SDHCI_ADMA2_ALIGN -
-					       (sg_dma_address(sg) & SDHCI_ADMA2_MASK);
+				if (sg_dma_address(sg) & adma2_mask) {
+					size = adma2_align -
+					       (sg_dma_address(sg) & adma2_mask);
 
 					buffer = sdhci_kmap_atomic(sg, &flags);
 					memcpy(buffer, align, size);
 					sdhci_kunmap_atomic(buffer, &flags);
 
-					align += SDHCI_ADMA2_ALIGN;
+					align += adma2_align;
 				}
 			}
 		}
@@ -3400,6 +3404,26 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
 	return 0;
 }
 
+static inline bool sdhci_use_64bit_dma(struct sdhci_host *host)
+{
+	u32 addr64bit_en;
+
+	/*
+	 * According to SD Host Controller spec v4.10, bit[27] added from
+	 * version 4.10 in Capabilities Register is used as 64-bit System
+	 * Address support for V4 mode, 64-bit DMA Addressing for V4 mode
+	 * is enabled only if 64-bit Addressing =1 in the Host Control 2
+	 * register.
+	 */
+	if (host->version == SDHCI_SPEC_410 && host->v4_mode) {
+		addr64bit_en = (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+				SDHCI_CTRL_64BIT_ADDR);
+		return addr64bit_en && (host->caps & SDHCI_CAN_64BIT_V4);
+	}
+
+	return host->caps & SDHCI_CAN_64BIT;
+}
+
 int sdhci_setup_host(struct sdhci_host *host)
 {
 	struct mmc_host *mmc;
@@ -3471,7 +3495,7 @@ int sdhci_setup_host(struct sdhci_host *host)
 	 * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
 	 * implement.
 	 */
-	if (host->caps & SDHCI_CAN_64BIT)
+	if (sdhci_use_64bit_dma(host))
 		host->flags |= SDHCI_USE_64_BIT_DMA;
 
 	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -3505,15 +3529,15 @@ int sdhci_setup_host(struct sdhci_host *host)
 		 */
 		if (host->flags & SDHCI_USE_64_BIT_DMA) {
 			host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
-					      SDHCI_ADMA2_64_DESC_SZ;
-			host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
+					      SDHCI_ADMA2_64_DESC_SZ(host);
+			host->desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
 		} else {
 			host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
 					      SDHCI_ADMA2_32_DESC_SZ;
 			host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
 		}
 
-		host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
+		host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN(host);
 		buf = dma_alloc_coherent(mmc_dev(mmc), host->align_buffer_sz +
 					 host->adma_table_sz, &dma, GFP_KERNEL);
 		if (!buf) {
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 128b0ba..820a863 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -185,6 +185,7 @@
 #define  SDHCI_CTRL_EXEC_TUNING		0x0040
 #define  SDHCI_CTRL_TUNED_CLK		0x0080
 #define  SDHCI_CTRL_V4_MODE		0x1000
+#define  SDHCI_CTRL_64BIT_ADDR		0x2000
 #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
 
 #define SDHCI_CAPABILITIES	0x40
@@ -206,6 +207,7 @@
 #define  SDHCI_CAN_VDD_300	0x02000000
 #define  SDHCI_CAN_VDD_180	0x04000000
 #define  SDHCI_CAN_64BIT	0x10000000
+#define  SDHCI_CAN_64BIT_V4	0x8000000
 
 #define  SDHCI_SUPPORT_SDR50	0x00000001
 #define  SDHCI_SUPPORT_SDR104	0x00000002
@@ -297,9 +299,14 @@ struct sdhci_adma2_32_desc {
 	__le32	addr;
 }  __packed __aligned(4);
 
-/* ADMA2 data alignment */
-#define SDHCI_ADMA2_ALIGN	4
-#define SDHCI_ADMA2_MASK	(SDHCI_ADMA2_ALIGN - 1)
+/*
+ * ADMA2 data alignment
+ * According to SD Host Controller spec v4.10, if Host Version 4 Enable is set
+ * in the Host Control 2 register, 128-bit Descriptor will be selected which
+ * shall be aligned 8-byte address boundary.
+ */
+#define SDHCI_ADMA2_ALIGN(host)	((host)->v4_mode ? 8 : 4)
+#define SDHCI_ADMA2_MASK(host)	(SDHCI_ADMA2_ALIGN(host) - 1)
 
 /*
  * ADMA2 descriptor alignment.  Some controllers (e.g. Intel) require 8 byte
@@ -308,8 +315,14 @@ struct sdhci_adma2_32_desc {
  */
 #define SDHCI_ADMA2_DESC_ALIGN	8
 
-/* ADMA2 64-bit DMA descriptor size */
-#define SDHCI_ADMA2_64_DESC_SZ	12
+/*
+ * ADMA2 64-bit DMA descriptor size
+ * According to SD Host Controller spec v4.10, there are two kinds of
+ * descriptors for 64-bit addressing mode: 96-bit Descriptor and 128-bit
+ * Descriptor, if Host Version 4 Enable is set in the Host Control 2
+ * register, 128-bit Descriptor will be selected.
+ */
+#define SDHCI_ADMA2_64_DESC_SZ(host)	((host)->v4_mode ? 16 : 12)
 
 /*
  * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V2 4/7] mmc: sdhci: add 32-bit block count support for v4 mode
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
                   ` (2 preceding siblings ...)
  2018-06-15  2:04 ` [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 5/7] mmc: sdhci: add CMD23 " Chunyan Zhang
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

When Host Version 4 is enabled, SDMA System Address register is
re-defined as 32-bit Block Count, and SDMA uses ADMA System
Address register (05Fh-058h) instead.

Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
---
 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 5d3b0d8..b8ee124 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -943,7 +943,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 	/* Set the DMA boundary value and block size */
 	sdhci_writew(host, SDHCI_MAKE_BLKSZ(host->sdma_boundary, data->blksz),
 		     SDHCI_BLOCK_SIZE);
-	sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+	reg = host->v4_mode ? SDHCI_32BIT_BLK_CNT : SDHCI_BLOCK_COUNT;
+	sdhci_writew(host, data->blocks, reg);
 }
 
 static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 820a863..1e84539 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -28,6 +28,7 @@
 
 #define SDHCI_DMA_ADDRESS	0x00
 #define SDHCI_ARGUMENT2		SDHCI_DMA_ADDRESS
+#define SDHCI_32BIT_BLK_CNT	SDHCI_DMA_ADDRESS
 
 #define SDHCI_BLOCK_SIZE	0x04
 #define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V2 5/7] mmc: sdhci: add CMD23 support for v4 mode
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
                   ` (3 preceding siblings ...)
  2018-06-15  2:04 ` [PATCH V2 4/7] mmc: sdhci: add 32-bit block count support for v4 mode Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  2018-06-22 19:40   ` Adrian Hunter
  2018-06-15  2:04 ` [PATCH V2 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 7/7] dt-bindings: sdhci-sprd: Add bindings for the sdhci-sprd controller Chunyan Zhang
  6 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

Host Driver Version 4.10 adds a new bit in Host Control 2 Register
for selecting Auto CMD23 or Auto CMD12 for ADMA3 data transfer.

Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
---
 drivers/mmc/host/sdhci.c | 16 +++++++++++++++-
 drivers/mmc/host/sdhci.h |  1 +
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b8ee124..3b2af7e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -954,6 +954,20 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
 	       !mrq->cap_cmd_during_tfr;
 }
 
+static inline void sdhci_set_auto_cmd23(struct sdhci_host *host,
+					struct mmc_command *cmd)
+{
+	u16 ctrl2;
+
+	if (host->v4_mode) {
+		ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		ctrl2 |= SDHCI_CMD23_ENABLE;
+		sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
+	} else {
+		sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
+	}
+}
+
 static void sdhci_set_transfer_mode(struct sdhci_host *host,
 	struct mmc_command *cmd)
 {
@@ -989,7 +1003,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
 			mode |= SDHCI_TRNS_AUTO_CMD12;
 		else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
 			mode |= SDHCI_TRNS_AUTO_CMD23;
-			sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
+			sdhci_set_auto_cmd23(host, cmd);
 		}
 	}
 
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 1e84539..d5e1c10 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -185,6 +185,7 @@
 #define   SDHCI_CTRL_DRV_TYPE_D		0x0030
 #define  SDHCI_CTRL_EXEC_TUNING		0x0040
 #define  SDHCI_CTRL_TUNED_CLK		0x0080
+#define  SDHCI_CMD23_ENABLE		0x0800
 #define  SDHCI_CTRL_V4_MODE		0x1000
 #define  SDHCI_CTRL_64BIT_ADDR		0x2000
 #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V2 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
                   ` (4 preceding siblings ...)
  2018-06-15  2:04 ` [PATCH V2 5/7] mmc: sdhci: add CMD23 " Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  2018-06-15  9:36   ` [PATCH V3 " Chunyan Zhang
  2018-06-15  2:04 ` [PATCH V2 7/7] dt-bindings: sdhci-sprd: Add bindings for the sdhci-sprd controller Chunyan Zhang
  6 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

From: Chunyan Zhang <chunyan.zhang@spreadtrum.com>

This patch adds the initial support of Secure Digital Host Controller
Interface compliant controller found in some latest Spreadtrum chipsets.
This patch has been tested on the version of SPRD-R11 controller.

R11 is a variant based on SD v4.0 specification.

With this driver, R11 mmc can be initialized, can be mounted, read and
written.

Original-by: Billows Wu <billows.wu@spreadtrum.com>
Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
---
 drivers/mmc/host/Kconfig      |  13 ++
 drivers/mmc/host/Makefile     |   1 +
 drivers/mmc/host/sdhci-sprd.c | 426 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 440 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-sprd.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9589f9c..1b0ee11 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -584,6 +584,19 @@ config MMC_SDRICOH_CS
 	  To compile this driver as a module, choose M here: the
 	  module will be called sdricoh_cs.
 
+config MMC_SDHCI_SPRD
+	tristate "Spreadtrum SDIO host Controller"
+	depends on ARCH_SPRD
+	depends on MMC_SDHCI_PLTFM
+	select MMC_SDHCI_IO_ACCESSORS
+	help
+	  This selects the SDIO Host Controller in Spreadtrum
+	  SoCs, this driver supports R11(IP version: R11P0).
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_TMIO_CORE
 	tristate
 
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 6aead24..5835bc4 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_ST)		+= sdhci-st.o
 obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
 obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
 obj-$(CONFIG_MMC_SDHCI_OMAP)		+= sdhci-omap.o
+obj-$(CONFIG_MMC_SDHCI_SPRD)		+= sdhci-sprd.o
 obj-$(CONFIG_MMC_CQHCI)			+= cqhci.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
new file mode 100644
index 0000000..f1b0f2b
--- /dev/null
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Secure Digital Host Controller
+//
+// Copyright (C) 2018 Spreadtrum, Inc.
+// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "sdhci-pltfm.h"
+
+#define SDHCI_SPRD_REG_32_DLL_DLY_OFFSET	0x208
+#define  SDHCIBSPRD_IT_WR_DLY_INV		(1 << 5)
+#define  SDHCI_SPRD_BIT_CMD_DLY_INV		(1 << 13)
+#define  SDHCI_SPRD_BIT_POSRD_DLY_INV		(1 << 21)
+#define  SDHCI_SPRD_BIT_NEGRD_DLY_INV		(1 << 29)
+
+#define SDHCI_SPRD_REG_32_BUSY_POSI		0x250
+#define  SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN	(1 << 25)
+#define  SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN	(1 << 24)
+
+#define SDHCI_SPRD_REG_DEBOUNCE		0x28C
+#define  SDHCI_SPRD_BIT_DLL_BAK		(1 << 0)
+#define  SDHCI_SPRD_BIT_DLL_VAL		(1 << 1)
+
+#define  SDHCI_SPRD_INT_SIGNAL_MASK	0x1B7F410B
+
+/* SDHCI_HOST_CONTROL2 */
+#define  SDHCI_SPRD_CTRL_HS200		0x0005
+#define  SDHCI_SPRD_CTRL_HS400		0x0006
+
+/* SDHCI_SOFTWARE_RESET */
+#define  SDHCI_HW_RESET_CARD		0x8 /* For Spreadtrum's design */
+
+#define SDHCI_SPRD_MAX_CUR		0xFFFFFF
+#define SDHCI_SPRD_CLK_MAX_DIV		0x3FF
+
+#define SDHCI_SPRD_CLK_DEF_RATE		26000000
+
+struct sdhci_sprd_host {
+	u32 version;
+	struct clk *clk_sdio;
+	struct clk *clk_enable;
+	u32 base_rate;
+};
+
+#define TO_SPRD_HOST(host) sdhci_pltfm_priv(sdhci_priv(host))
+
+static void sdhci_sprd_init_config(struct sdhci_host *host)
+{
+	u16 val;
+
+	/* set 64-bit addressing modes */
+	val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	val |= SDHCI_CTRL_64BIT_ADDR;
+	sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+
+	/* set dll backup mode */
+	val = sdhci_readl(host, SDHCI_SPRD_REG_DEBOUNCE);
+	val |= SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL;
+	sdhci_writel(host, val, SDHCI_SPRD_REG_DEBOUNCE);
+}
+
+static inline u32 sdhci_sprd_readl(struct sdhci_host *host, int reg)
+{
+	if (unlikely(reg == SDHCI_MAX_CURRENT))
+		return SDHCI_SPRD_MAX_CUR;
+
+	return readl_relaxed(host->ioaddr + reg);
+}
+
+static inline void sdhci_sprd_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	/* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */
+	if (unlikely(reg == SDHCI_MAX_CURRENT))
+		return;
+
+	if (unlikely(reg == SDHCI_SIGNAL_ENABLE || reg == SDHCI_INT_ENABLE))
+		val = val & SDHCI_SPRD_INT_SIGNAL_MASK;
+
+	return writel_relaxed(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_sprd_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	if (unlikely(reg == SDHCI_SOFTWARE_RESET)) {
+		if (readb_relaxed(host->ioaddr + reg) & SDHCI_HW_RESET_CARD)
+			val |= SDHCI_HW_RESET_CARD;
+	}
+
+	return writeb_relaxed(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_sprd_sd_clk_off(struct sdhci_host *host)
+{
+	u16 ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+	ctrl &= (~SDHCI_CLOCK_CARD_EN);
+	sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
+}
+
+static inline void
+sdhci_sprd_set_dll_invert(struct sdhci_host *host, u32 mask, bool en)
+{
+	u32 dll_dly_offset;
+
+	dll_dly_offset = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
+	if (en)
+		dll_dly_offset |= mask;
+	else
+		dll_dly_offset &= ~mask;
+	sdhci_writel(host, dll_dly_offset, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
+}
+
+static inline u32 sdhci_sprd_calc_div(u32 base_clk, u32 clk)
+{
+	u32 div;
+
+	/* select 2x clock source */
+	if (base_clk <= clk * 2)
+		return 0;
+
+	div = (u32) (base_clk / (clk * 2));
+
+	if ((base_clk / div) > (clk * 2))
+		div++;
+
+	if (div > SDHCI_SPRD_CLK_MAX_DIV)
+		div = SDHCI_SPRD_CLK_MAX_DIV;
+
+	if (div % 2)
+		div = (div + 1) / 2;
+	else
+		div = div / 2;
+
+	return div;
+}
+
+static inline void _sdhci_sprd_set_clock(struct sdhci_host *host,
+					unsigned int clk)
+{
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+	u32 div, val, mask;
+
+	div = sdhci_sprd_calc_div(sprd_host->base_rate, clk);
+
+	clk |= ((div & 0x300) >> 2) | ((div & 0xFF) << 8);
+	sdhci_enable_clk(host, clk);
+
+	/* enable auto gate sdhc_enable_auto_gate */
+	val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
+	mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
+	       SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
+	if (mask != (val & mask)) {
+		val |= mask;
+		sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
+	}
+}
+
+static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	bool en = false;
+
+	if (clock == 0) {
+		sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+	} else if (clock != host->clock) {
+		sdhci_sprd_sd_clk_off(host);
+		_sdhci_sprd_set_clock(host, clock);
+
+		if (clock <= 400000)
+			en = true;
+		sdhci_sprd_set_dll_invert(host, SDHCI_SPRD_BIT_CMD_DLY_INV |
+					  SDHCI_SPRD_BIT_POSRD_DLY_INV, en);
+	} else {
+		_sdhci_sprd_set_clock(host, clock);
+	}
+
+}
+
+static unsigned int sdhci_sprd_get_max_clock(struct sdhci_host *host)
+{
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+
+	return clk_round_rate(sprd_host->clk_sdio, ULONG_MAX);
+}
+
+static unsigned int sdhci_sprd_get_min_clock(struct sdhci_host *host)
+{
+	return 400000;
+}
+
+static void sdhci_sprd_set_uhs_signaling(struct sdhci_host *host,
+					 unsigned int timing)
+{
+	u16 ctrl_2;
+
+	if (timing == host->timing)
+		return;
+
+	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	/* Select Bus Speed Mode for host */
+	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+	switch (timing) {
+	case MMC_TIMING_UHS_SDR12:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR12;
+		break;
+	case MMC_TIMING_MMC_HS:
+	case MMC_TIMING_SD_HS:
+	case MMC_TIMING_UHS_SDR25:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR25;
+		break;
+	case MMC_TIMING_UHS_SDR50:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR50;
+		break;
+	case MMC_TIMING_UHS_SDR104:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR104;
+		break;
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_MMC_DDR52:
+		ctrl_2 = SDHCI_CTRL_UHS_DDR50;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		ctrl_2 = SDHCI_SPRD_CTRL_HS200;
+		break;
+	case MMC_TIMING_MMC_HS400:
+		ctrl_2 = SDHCI_SPRD_CTRL_HS400;
+		break;
+	default:
+		break;
+	}
+
+	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_sprd_hw_reset(struct sdhci_host *host)
+{
+	int val;
+
+	/* Note: don't use sdhci_readb/writeb() API here */
+	val = readb_relaxed(host->ioaddr + SDHCI_SOFTWARE_RESET);
+	val &= ~SDHCI_HW_RESET_CARD;
+	writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
+	udelay(10);
+
+	val |= SDHCI_HW_RESET_CARD;
+	writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
+	udelay(300);
+}
+
+static struct sdhci_ops sdhci_sprd_ops = {
+	.read_l = sdhci_sprd_readl,
+	.write_l = sdhci_sprd_writel,
+	.write_b = sdhci_sprd_writeb,
+	.set_clock = sdhci_sprd_set_clock,
+	.get_max_clock = sdhci_sprd_get_max_clock,
+	.get_min_clock = sdhci_sprd_get_min_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.reset = sdhci_reset,
+	.set_uhs_signaling = sdhci_sprd_set_uhs_signaling,
+	.hw_reset = sdhci_sprd_hw_reset,
+};
+
+static const struct sdhci_pltfm_data sdhci_sprd_pdata = {
+	.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+	.quirks2 = SDHCI_QUIRK2_BROKEN_HS200,
+	.ops = &sdhci_sprd_ops,
+};
+
+static int sdhci_sprd_probe(struct platform_device *pdev)
+{
+	struct sdhci_host *host;
+	struct sdhci_sprd_host *sprd_host;
+	struct clk *clk;
+	int ret = 0;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_sprd_pdata, sizeof(*sprd_host));
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	host->dma_mask = DMA_BIT_MASK(64);
+	pdev->dev.dma_mask = &host->dma_mask;
+
+	host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
+		MMC_CAP_ERASE | MMC_CAP_CMD23;
+	mmc_of_parse(host->mmc);
+	if (ret)
+		goto pltfm_free;
+
+	sprd_host = TO_SPRD_HOST(host);
+
+	clk = devm_clk_get(&pdev->dev, "sdio");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		goto pltfm_free;
+	}
+	sprd_host->clk_sdio = clk;
+	sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio);
+	if (!sprd_host->base_rate)
+		sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE;
+
+	clk = devm_clk_get(&pdev->dev, "enable");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		goto pltfm_free;
+	}
+	sprd_host->clk_enable = clk;
+
+	clk_prepare_enable(sprd_host->clk_sdio);
+	clk_prepare_enable(sprd_host->clk_enable);
+
+	sdhci_sprd_init_config(host);
+
+	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+	sprd_host->version = ((host->version & SDHCI_VENDOR_VER_MASK) >>
+			       SDHCI_VENDOR_VER_SHIFT);
+
+	pm_runtime_get_noresume(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_suspend_ignore_children(&pdev->dev, 1);
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add mmc host: %d\n", ret);
+		goto pm_runtime_disable;
+	}
+
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_put_autosuspend(&pdev->dev);
+
+	return 0;
+
+pm_runtime_disable:
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	clk_disable_unprepare(sprd_host->clk_sdio);
+	clk_disable_unprepare(sprd_host->clk_enable);
+
+pltfm_free:
+	sdhci_pltfm_free(pdev);
+	return ret;
+}
+
+static int sdhci_sprd_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+	struct mmc_host *mmc = host->mmc;
+
+	mmc_remove_host(mmc);
+	clk_disable_unprepare(sprd_host->clk_sdio);
+	clk_disable_unprepare(sprd_host->clk_enable);
+
+	mmc_free_host(mmc);
+
+	return 0;
+}
+
+static const struct of_device_id sdhci_sprd_of_match[] = {
+	{ .compatible = "sprd,sdhci-r11", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match);
+
+#ifdef CONFIG_PM
+static int sdhci_sprd_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+
+	sdhci_runtime_suspend_host(host);
+
+	clk_disable_unprepare(sprd_host->clk_sdio);
+	clk_disable_unprepare(sprd_host->clk_enable);
+
+	return 0;
+}
+
+static int sdhci_sprd_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+
+	clk_prepare_enable(sprd_host->clk_enable);
+	clk_prepare_enable(sprd_host->clk_sdio);
+
+	sdhci_runtime_resume_host(host);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops sdhci_sprd_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend,
+			   sdhci_sprd_runtime_resume, NULL)
+};
+
+static struct platform_driver sdhci_sprd_driver = {
+	.probe = sdhci_sprd_probe,
+	.remove = sdhci_sprd_remove,
+	.driver = {
+		.name = "sdhci_sprd_r11",
+		.of_match_table = of_match_ptr(sdhci_sprd_of_match),
+		.pm = &sdhci_sprd_pm_ops,
+	},
+};
+module_platform_driver(sdhci_sprd_driver);
+
+MODULE_DESCRIPTION("Spreadtrum sdio host controller r11 driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sdhci-sprd-r11");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V2 7/7] dt-bindings: sdhci-sprd: Add bindings for the sdhci-sprd controller
  2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
                   ` (5 preceding siblings ...)
  2018-06-15  2:04 ` [PATCH V2 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller Chunyan Zhang
@ 2018-06-15  2:04 ` Chunyan Zhang
  6 siblings, 0 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  2:04 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

From: Chunyan Zhang <chunyan.zhang@spreadtrum.com>

This patch adds the device-tree binding documentation for Spreadtrum
SDHCI driver.

Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
---
 .../devicetree/bindings/mmc/sdhci-sprd.txt         | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-sprd.txt

diff --git a/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt b/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt
new file mode 100644
index 0000000..45c9978
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt
@@ -0,0 +1,41 @@
+* Spreadtrum SDHCI controller (sdhci-sprd)
+
+The Secure Digital (SD) Host controller on Spreadtrum SoCs provides an interface
+for MMC, SD and SDIO types of cards.
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci-sprd driver.
+
+Required properties:
+- compatible: Should contain "sprd,sdhci-r11".
+- reg: physical base address of the controller and length.
+- interrupts: Interrupts used by the SDHCI controller.
+- clocks: Should contain phandle for the clock feeding the SDHCI controller
+- clock-names: Should contain the following:
+	"sdio" - SDIO source clock (required)
+	"enable" - gate clock which used for enabling/disabling the device (required)
+
+Optional properties:
+- assigned-clocks: the same with "sdio" clock
+- assigned-clock-parents: the default parent of "sdio" clock
+
+Examples:
+
+sdio0: sdio@20600000 {
+	compatible  = "sprd,sdhci-r11";
+	reg = <0 0x20600000 0 0x1000>;
+	interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+
+	clock-names = "sdio", "enable";
+	clocks = <&ap_clk CLK_EMMC_2X>,
+		 <&apahb_gate CLK_EMMC_EB>;
+	assigned-clocks = <&ap_clk CLK_EMMC_2X>;
+	assigned-clock-parents = <&rpll CLK_RPLL_390M>;
+
+	bus-width = <8>;
+	non-removable;
+	no-sdio;
+	no-sd;
+	cap-mmc-hw-reset;
+	status = "okay";
+};
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH V3 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller
  2018-06-15  2:04 ` [PATCH V2 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller Chunyan Zhang
@ 2018-06-15  9:36   ` Chunyan Zhang
  2018-06-18  8:56     ` Ulf Hansson
  0 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-15  9:36 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

From: Chunyan Zhang <chunyan.zhang@spreadtrum.com>

This patch adds the initial support of Secure Digital Host Controller
Interface compliant controller found in some latest Spreadtrum chipsets.
This patch has been tested on the version of SPRD-R11 controller.

R11 is a variant based on SD v4.0 specification.

With this driver, R11 mmc can be initialized, can be mounted, read and
written.

Original-by: Billows Wu <billows.wu@spreadtrum.com>
Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
---

Changes from V2:
- Save return value of mmc_of_parse();
- Add checking for clk_prepare_enable();
- Use BIT() macro instead.

---
 drivers/mmc/host/Kconfig      |  13 ++
 drivers/mmc/host/Makefile     |   1 +
 drivers/mmc/host/sdhci-sprd.c | 432 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 446 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-sprd.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9589f9c..1b0ee11 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -584,6 +584,19 @@ config MMC_SDRICOH_CS
 	  To compile this driver as a module, choose M here: the
 	  module will be called sdricoh_cs.
 
+config MMC_SDHCI_SPRD
+	tristate "Spreadtrum SDIO host Controller"
+	depends on ARCH_SPRD
+	depends on MMC_SDHCI_PLTFM
+	select MMC_SDHCI_IO_ACCESSORS
+	help
+	  This selects the SDIO Host Controller in Spreadtrum
+	  SoCs, this driver supports R11(IP version: R11P0).
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_TMIO_CORE
 	tristate
 
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 6aead24..5835bc4 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_ST)		+= sdhci-st.o
 obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
 obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
 obj-$(CONFIG_MMC_SDHCI_OMAP)		+= sdhci-omap.o
+obj-$(CONFIG_MMC_SDHCI_SPRD)		+= sdhci-sprd.o
 obj-$(CONFIG_MMC_CQHCI)			+= cqhci.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
new file mode 100644
index 0000000..2b551c2
--- /dev/null
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Secure Digital Host Controller
+//
+// Copyright (C) 2018 Spreadtrum, Inc.
+// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "sdhci-pltfm.h"
+
+#define SDHCI_SPRD_REG_32_DLL_DLY_OFFSET	0x208
+#define  SDHCIBSPRD_IT_WR_DLY_INV		BIT(5)
+#define  SDHCI_SPRD_BIT_CMD_DLY_INV		BIT(13)
+#define  SDHCI_SPRD_BIT_POSRD_DLY_INV		BIT(21)
+#define  SDHCI_SPRD_BIT_NEGRD_DLY_INV		BIT(29)
+
+#define SDHCI_SPRD_REG_32_BUSY_POSI		0x250
+#define  SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN	BIT(25)
+#define  SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN	BIT(24)
+
+#define SDHCI_SPRD_REG_DEBOUNCE		0x28C
+#define  SDHCI_SPRD_BIT_DLL_BAK		BIT(0)
+#define  SDHCI_SPRD_BIT_DLL_VAL		BIT(1)
+
+#define  SDHCI_SPRD_INT_SIGNAL_MASK	0x1B7F410B
+
+/* SDHCI_HOST_CONTROL2 */
+#define  SDHCI_SPRD_CTRL_HS200		0x0005
+#define  SDHCI_SPRD_CTRL_HS400		0x0006
+
+/* SDHCI_SOFTWARE_RESET */
+#define  SDHCI_HW_RESET_CARD		0x8 /* For Spreadtrum's design */
+
+#define SDHCI_SPRD_MAX_CUR		0xFFFFFF
+#define SDHCI_SPRD_CLK_MAX_DIV		1023
+
+#define SDHCI_SPRD_CLK_DEF_RATE		26000000
+
+struct sdhci_sprd_host {
+	u32 version;
+	struct clk *clk_sdio;
+	struct clk *clk_enable;
+	u32 base_rate;
+};
+
+#define TO_SPRD_HOST(host) sdhci_pltfm_priv(sdhci_priv(host))
+
+static void sdhci_sprd_init_config(struct sdhci_host *host)
+{
+	u16 val;
+
+	/* set 64-bit addressing modes */
+	val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	val |= SDHCI_CTRL_64BIT_ADDR;
+	sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+
+	/* set dll backup mode */
+	val = sdhci_readl(host, SDHCI_SPRD_REG_DEBOUNCE);
+	val |= SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL;
+	sdhci_writel(host, val, SDHCI_SPRD_REG_DEBOUNCE);
+}
+
+static inline u32 sdhci_sprd_readl(struct sdhci_host *host, int reg)
+{
+	if (unlikely(reg == SDHCI_MAX_CURRENT))
+		return SDHCI_SPRD_MAX_CUR;
+
+	return readl_relaxed(host->ioaddr + reg);
+}
+
+static inline void sdhci_sprd_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	/* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */
+	if (unlikely(reg == SDHCI_MAX_CURRENT))
+		return;
+
+	if (unlikely(reg == SDHCI_SIGNAL_ENABLE || reg == SDHCI_INT_ENABLE))
+		val = val & SDHCI_SPRD_INT_SIGNAL_MASK;
+
+	writel_relaxed(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_sprd_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	if (unlikely(reg == SDHCI_SOFTWARE_RESET)) {
+		if (readb_relaxed(host->ioaddr + reg) & SDHCI_HW_RESET_CARD)
+			val |= SDHCI_HW_RESET_CARD;
+	}
+
+	writeb_relaxed(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_sprd_sd_clk_off(struct sdhci_host *host)
+{
+	u16 ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+	ctrl &= ~SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
+}
+
+static inline void
+sdhci_sprd_set_dll_invert(struct sdhci_host *host, u32 mask, bool en)
+{
+	u32 dll_dly_offset;
+
+	dll_dly_offset = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
+	if (en)
+		dll_dly_offset |= mask;
+	else
+		dll_dly_offset &= ~mask;
+	sdhci_writel(host, dll_dly_offset, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
+}
+
+static inline u32 sdhci_sprd_calc_div(u32 base_clk, u32 clk)
+{
+	u32 div;
+
+	/* select 2x clock source */
+	if (base_clk <= clk * 2)
+		return 0;
+
+	div = (u32) (base_clk / (clk * 2));
+
+	if ((base_clk / div) > (clk * 2))
+		div++;
+
+	if (div > SDHCI_SPRD_CLK_MAX_DIV)
+		div = SDHCI_SPRD_CLK_MAX_DIV;
+
+	if (div % 2)
+		div = (div + 1) / 2;
+	else
+		div = div / 2;
+
+	return div;
+}
+
+static inline void _sdhci_sprd_set_clock(struct sdhci_host *host,
+					unsigned int clk)
+{
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+	u32 div, val, mask;
+
+	div = sdhci_sprd_calc_div(sprd_host->base_rate, clk);
+
+	clk |= ((div & 0x300) >> 2) | ((div & 0xFF) << 8);
+	sdhci_enable_clk(host, clk);
+
+	/* enable auto gate sdhc_enable_auto_gate */
+	val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
+	mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
+	       SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
+	if (mask != (val & mask)) {
+		val |= mask;
+		sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
+	}
+}
+
+static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	bool en = false;
+
+	if (clock == 0) {
+		sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+	} else if (clock != host->clock) {
+		sdhci_sprd_sd_clk_off(host);
+		_sdhci_sprd_set_clock(host, clock);
+
+		if (clock <= 400000)
+			en = true;
+		sdhci_sprd_set_dll_invert(host, SDHCI_SPRD_BIT_CMD_DLY_INV |
+					  SDHCI_SPRD_BIT_POSRD_DLY_INV, en);
+	} else {
+		_sdhci_sprd_set_clock(host, clock);
+	}
+}
+
+static unsigned int sdhci_sprd_get_max_clock(struct sdhci_host *host)
+{
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+
+	return clk_round_rate(sprd_host->clk_sdio, ULONG_MAX);
+}
+
+static unsigned int sdhci_sprd_get_min_clock(struct sdhci_host *host)
+{
+	return 400000;
+}
+
+static void sdhci_sprd_set_uhs_signaling(struct sdhci_host *host,
+					 unsigned int timing)
+{
+	u16 ctrl_2;
+
+	if (timing == host->timing)
+		return;
+
+	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	/* Select Bus Speed Mode for host */
+	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+	switch (timing) {
+	case MMC_TIMING_UHS_SDR12:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR12;
+		break;
+	case MMC_TIMING_MMC_HS:
+	case MMC_TIMING_SD_HS:
+	case MMC_TIMING_UHS_SDR25:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR25;
+		break;
+	case MMC_TIMING_UHS_SDR50:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR50;
+		break;
+	case MMC_TIMING_UHS_SDR104:
+		ctrl_2 = SDHCI_CTRL_UHS_SDR104;
+		break;
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_MMC_DDR52:
+		ctrl_2 = SDHCI_CTRL_UHS_DDR50;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		ctrl_2 = SDHCI_SPRD_CTRL_HS200;
+		break;
+	case MMC_TIMING_MMC_HS400:
+		ctrl_2 = SDHCI_SPRD_CTRL_HS400;
+		break;
+	default:
+		break;
+	}
+
+	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_sprd_hw_reset(struct sdhci_host *host)
+{
+	int val;
+
+	/* Note: don't use sdhci_readb/writeb() API here */
+	val = readb_relaxed(host->ioaddr + SDHCI_SOFTWARE_RESET);
+	val &= ~SDHCI_HW_RESET_CARD;
+	writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
+	udelay(10);
+
+	val |= SDHCI_HW_RESET_CARD;
+	writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
+	udelay(300);
+}
+
+static struct sdhci_ops sdhci_sprd_ops = {
+	.read_l = sdhci_sprd_readl,
+	.write_l = sdhci_sprd_writel,
+	.write_b = sdhci_sprd_writeb,
+	.set_clock = sdhci_sprd_set_clock,
+	.get_max_clock = sdhci_sprd_get_max_clock,
+	.get_min_clock = sdhci_sprd_get_min_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.reset = sdhci_reset,
+	.set_uhs_signaling = sdhci_sprd_set_uhs_signaling,
+	.hw_reset = sdhci_sprd_hw_reset,
+};
+
+static const struct sdhci_pltfm_data sdhci_sprd_pdata = {
+	.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+	.quirks2 = SDHCI_QUIRK2_BROKEN_HS200,
+	.ops = &sdhci_sprd_ops,
+};
+
+static int sdhci_sprd_probe(struct platform_device *pdev)
+{
+	struct sdhci_host *host;
+	struct sdhci_sprd_host *sprd_host;
+	struct clk *clk;
+	int ret = 0;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_sprd_pdata, sizeof(*sprd_host));
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	host->dma_mask = DMA_BIT_MASK(64);
+	pdev->dev.dma_mask = &host->dma_mask;
+
+	host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
+		MMC_CAP_ERASE | MMC_CAP_CMD23;
+	ret = mmc_of_parse(host->mmc);
+	if (ret)
+		goto pltfm_free;
+
+	sprd_host = TO_SPRD_HOST(host);
+
+	clk = devm_clk_get(&pdev->dev, "sdio");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		goto pltfm_free;
+	}
+	sprd_host->clk_sdio = clk;
+	sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio);
+	if (!sprd_host->base_rate)
+		sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE;
+
+	clk = devm_clk_get(&pdev->dev, "enable");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		goto pltfm_free;
+	}
+	sprd_host->clk_enable = clk;
+
+	ret = clk_prepare_enable(sprd_host->clk_sdio);
+	if (ret)
+		goto pltfm_free;
+
+	clk_prepare_enable(sprd_host->clk_enable);
+	if (ret)
+		goto clk_disable;
+
+	sdhci_sprd_init_config(host);
+
+	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+	sprd_host->version = ((host->version & SDHCI_VENDOR_VER_MASK) >>
+			       SDHCI_VENDOR_VER_SHIFT);
+
+	pm_runtime_get_noresume(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_suspend_ignore_children(&pdev->dev, 1);
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add mmc host: %d\n", ret);
+		goto pm_runtime_disable;
+	}
+
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_put_autosuspend(&pdev->dev);
+
+	return 0;
+
+pm_runtime_disable:
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	clk_disable_unprepare(sprd_host->clk_enable);
+
+clk_disable:
+	clk_disable_unprepare(sprd_host->clk_sdio);
+
+pltfm_free:
+	sdhci_pltfm_free(pdev);
+	return ret;
+}
+
+static int sdhci_sprd_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+	struct mmc_host *mmc = host->mmc;
+
+	mmc_remove_host(mmc);
+	clk_disable_unprepare(sprd_host->clk_sdio);
+	clk_disable_unprepare(sprd_host->clk_enable);
+
+	mmc_free_host(mmc);
+
+	return 0;
+}
+
+static const struct of_device_id sdhci_sprd_of_match[] = {
+	{ .compatible = "sprd,sdhci-r11", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match);
+
+#ifdef CONFIG_PM
+static int sdhci_sprd_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+
+	sdhci_runtime_suspend_host(host);
+
+	clk_disable_unprepare(sprd_host->clk_sdio);
+	clk_disable_unprepare(sprd_host->clk_enable);
+
+	return 0;
+}
+
+static int sdhci_sprd_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
+
+	clk_prepare_enable(sprd_host->clk_enable);
+	clk_prepare_enable(sprd_host->clk_sdio);
+
+	sdhci_runtime_resume_host(host);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops sdhci_sprd_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend,
+			   sdhci_sprd_runtime_resume, NULL)
+};
+
+static struct platform_driver sdhci_sprd_driver = {
+	.probe = sdhci_sprd_probe,
+	.remove = sdhci_sprd_remove,
+	.driver = {
+		.name = "sdhci_sprd_r11",
+		.of_match_table = of_match_ptr(sdhci_sprd_of_match),
+		.pm = &sdhci_sprd_pm_ops,
+	},
+};
+module_platform_driver(sdhci_sprd_driver);
+
+MODULE_DESCRIPTION("Spreadtrum sdio host controller r11 driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sdhci-sprd-r11");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH V3 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller
  2018-06-15  9:36   ` [PATCH V3 " Chunyan Zhang
@ 2018-06-18  8:56     ` Ulf Hansson
  0 siblings, 0 replies; 20+ messages in thread
From: Ulf Hansson @ 2018-06-18  8:56 UTC (permalink / raw)
  To: Chunyan Zhang
  Cc: Adrian Hunter, linux-mmc, Linux Kernel Mailing List, Orson Zhai,
	Baolin Wang, Billows Wu, Chunyan Zhang

On 15 June 2018 at 11:36, Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
> From: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
>
> This patch adds the initial support of Secure Digital Host Controller
> Interface compliant controller found in some latest Spreadtrum chipsets.
> This patch has been tested on the version of SPRD-R11 controller.
>
> R11 is a variant based on SD v4.0 specification.
>
> With this driver, R11 mmc can be initialized, can be mounted, read and
> written.
>
> Original-by: Billows Wu <billows.wu@spreadtrum.com>
> Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>

This looks good to me now! However, I am deferring a bit to let Adrian
comment as well.

Kind regards
Uffe

> ---
>
> Changes from V2:
> - Save return value of mmc_of_parse();
> - Add checking for clk_prepare_enable();
> - Use BIT() macro instead.
>
> ---
>  drivers/mmc/host/Kconfig      |  13 ++
>  drivers/mmc/host/Makefile     |   1 +
>  drivers/mmc/host/sdhci-sprd.c | 432 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 446 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-sprd.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 9589f9c..1b0ee11 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -584,6 +584,19 @@ config MMC_SDRICOH_CS
>           To compile this driver as a module, choose M here: the
>           module will be called sdricoh_cs.
>
> +config MMC_SDHCI_SPRD
> +       tristate "Spreadtrum SDIO host Controller"
> +       depends on ARCH_SPRD
> +       depends on MMC_SDHCI_PLTFM
> +       select MMC_SDHCI_IO_ACCESSORS
> +       help
> +         This selects the SDIO Host Controller in Spreadtrum
> +         SoCs, this driver supports R11(IP version: R11P0).
> +
> +         If you have a controller with this interface, say Y or M here.
> +
> +         If unsure, say N.
> +
>  config MMC_TMIO_CORE
>         tristate
>
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 6aead24..5835bc4 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_ST)            += sdhci-st.o
>  obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)        += sdhci-pic32.o
>  obj-$(CONFIG_MMC_SDHCI_BRCMSTB)                += sdhci-brcmstb.o
>  obj-$(CONFIG_MMC_SDHCI_OMAP)           += sdhci-omap.o
> +obj-$(CONFIG_MMC_SDHCI_SPRD)           += sdhci-sprd.o
>  obj-$(CONFIG_MMC_CQHCI)                        += cqhci.o
>
>  ifeq ($(CONFIG_CB710_DEBUG),y)
> diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
> new file mode 100644
> index 0000000..2b551c2
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-sprd.c
> @@ -0,0 +1,432 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Secure Digital Host Controller
> +//
> +// Copyright (C) 2018 Spreadtrum, Inc.
> +// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
> +
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/highmem.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +
> +#include "sdhci-pltfm.h"
> +
> +#define SDHCI_SPRD_REG_32_DLL_DLY_OFFSET       0x208
> +#define  SDHCIBSPRD_IT_WR_DLY_INV              BIT(5)
> +#define  SDHCI_SPRD_BIT_CMD_DLY_INV            BIT(13)
> +#define  SDHCI_SPRD_BIT_POSRD_DLY_INV          BIT(21)
> +#define  SDHCI_SPRD_BIT_NEGRD_DLY_INV          BIT(29)
> +
> +#define SDHCI_SPRD_REG_32_BUSY_POSI            0x250
> +#define  SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN       BIT(25)
> +#define  SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN       BIT(24)
> +
> +#define SDHCI_SPRD_REG_DEBOUNCE                0x28C
> +#define  SDHCI_SPRD_BIT_DLL_BAK                BIT(0)
> +#define  SDHCI_SPRD_BIT_DLL_VAL                BIT(1)
> +
> +#define  SDHCI_SPRD_INT_SIGNAL_MASK    0x1B7F410B
> +
> +/* SDHCI_HOST_CONTROL2 */
> +#define  SDHCI_SPRD_CTRL_HS200         0x0005
> +#define  SDHCI_SPRD_CTRL_HS400         0x0006
> +
> +/* SDHCI_SOFTWARE_RESET */
> +#define  SDHCI_HW_RESET_CARD           0x8 /* For Spreadtrum's design */
> +
> +#define SDHCI_SPRD_MAX_CUR             0xFFFFFF
> +#define SDHCI_SPRD_CLK_MAX_DIV         1023
> +
> +#define SDHCI_SPRD_CLK_DEF_RATE                26000000
> +
> +struct sdhci_sprd_host {
> +       u32 version;
> +       struct clk *clk_sdio;
> +       struct clk *clk_enable;
> +       u32 base_rate;
> +};
> +
> +#define TO_SPRD_HOST(host) sdhci_pltfm_priv(sdhci_priv(host))
> +
> +static void sdhci_sprd_init_config(struct sdhci_host *host)
> +{
> +       u16 val;
> +
> +       /* set 64-bit addressing modes */
> +       val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +       val |= SDHCI_CTRL_64BIT_ADDR;
> +       sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
> +
> +       /* set dll backup mode */
> +       val = sdhci_readl(host, SDHCI_SPRD_REG_DEBOUNCE);
> +       val |= SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL;
> +       sdhci_writel(host, val, SDHCI_SPRD_REG_DEBOUNCE);
> +}
> +
> +static inline u32 sdhci_sprd_readl(struct sdhci_host *host, int reg)
> +{
> +       if (unlikely(reg == SDHCI_MAX_CURRENT))
> +               return SDHCI_SPRD_MAX_CUR;
> +
> +       return readl_relaxed(host->ioaddr + reg);
> +}
> +
> +static inline void sdhci_sprd_writel(struct sdhci_host *host, u32 val, int reg)
> +{
> +       /* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */
> +       if (unlikely(reg == SDHCI_MAX_CURRENT))
> +               return;
> +
> +       if (unlikely(reg == SDHCI_SIGNAL_ENABLE || reg == SDHCI_INT_ENABLE))
> +               val = val & SDHCI_SPRD_INT_SIGNAL_MASK;
> +
> +       writel_relaxed(val, host->ioaddr + reg);
> +}
> +
> +static inline void sdhci_sprd_writeb(struct sdhci_host *host, u8 val, int reg)
> +{
> +       if (unlikely(reg == SDHCI_SOFTWARE_RESET)) {
> +               if (readb_relaxed(host->ioaddr + reg) & SDHCI_HW_RESET_CARD)
> +                       val |= SDHCI_HW_RESET_CARD;
> +       }
> +
> +       writeb_relaxed(val, host->ioaddr + reg);
> +}
> +
> +static inline void sdhci_sprd_sd_clk_off(struct sdhci_host *host)
> +{
> +       u16 ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +
> +       ctrl &= ~SDHCI_CLOCK_CARD_EN;
> +       sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
> +}
> +
> +static inline void
> +sdhci_sprd_set_dll_invert(struct sdhci_host *host, u32 mask, bool en)
> +{
> +       u32 dll_dly_offset;
> +
> +       dll_dly_offset = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
> +       if (en)
> +               dll_dly_offset |= mask;
> +       else
> +               dll_dly_offset &= ~mask;
> +       sdhci_writel(host, dll_dly_offset, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
> +}
> +
> +static inline u32 sdhci_sprd_calc_div(u32 base_clk, u32 clk)
> +{
> +       u32 div;
> +
> +       /* select 2x clock source */
> +       if (base_clk <= clk * 2)
> +               return 0;
> +
> +       div = (u32) (base_clk / (clk * 2));
> +
> +       if ((base_clk / div) > (clk * 2))
> +               div++;
> +
> +       if (div > SDHCI_SPRD_CLK_MAX_DIV)
> +               div = SDHCI_SPRD_CLK_MAX_DIV;
> +
> +       if (div % 2)
> +               div = (div + 1) / 2;
> +       else
> +               div = div / 2;
> +
> +       return div;
> +}
> +
> +static inline void _sdhci_sprd_set_clock(struct sdhci_host *host,
> +                                       unsigned int clk)
> +{
> +       struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
> +       u32 div, val, mask;
> +
> +       div = sdhci_sprd_calc_div(sprd_host->base_rate, clk);
> +
> +       clk |= ((div & 0x300) >> 2) | ((div & 0xFF) << 8);
> +       sdhci_enable_clk(host, clk);
> +
> +       /* enable auto gate sdhc_enable_auto_gate */
> +       val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
> +       mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
> +              SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
> +       if (mask != (val & mask)) {
> +               val |= mask;
> +               sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
> +       }
> +}
> +
> +static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
> +{
> +       bool en = false;
> +
> +       if (clock == 0) {
> +               sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
> +       } else if (clock != host->clock) {
> +               sdhci_sprd_sd_clk_off(host);
> +               _sdhci_sprd_set_clock(host, clock);
> +
> +               if (clock <= 400000)
> +                       en = true;
> +               sdhci_sprd_set_dll_invert(host, SDHCI_SPRD_BIT_CMD_DLY_INV |
> +                                         SDHCI_SPRD_BIT_POSRD_DLY_INV, en);
> +       } else {
> +               _sdhci_sprd_set_clock(host, clock);
> +       }
> +}
> +
> +static unsigned int sdhci_sprd_get_max_clock(struct sdhci_host *host)
> +{
> +       struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
> +
> +       return clk_round_rate(sprd_host->clk_sdio, ULONG_MAX);
> +}
> +
> +static unsigned int sdhci_sprd_get_min_clock(struct sdhci_host *host)
> +{
> +       return 400000;
> +}
> +
> +static void sdhci_sprd_set_uhs_signaling(struct sdhci_host *host,
> +                                        unsigned int timing)
> +{
> +       u16 ctrl_2;
> +
> +       if (timing == host->timing)
> +               return;
> +
> +       ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +       /* Select Bus Speed Mode for host */
> +       ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
> +       switch (timing) {
> +       case MMC_TIMING_UHS_SDR12:
> +               ctrl_2 = SDHCI_CTRL_UHS_SDR12;
> +               break;
> +       case MMC_TIMING_MMC_HS:
> +       case MMC_TIMING_SD_HS:
> +       case MMC_TIMING_UHS_SDR25:
> +               ctrl_2 = SDHCI_CTRL_UHS_SDR25;
> +               break;
> +       case MMC_TIMING_UHS_SDR50:
> +               ctrl_2 = SDHCI_CTRL_UHS_SDR50;
> +               break;
> +       case MMC_TIMING_UHS_SDR104:
> +               ctrl_2 = SDHCI_CTRL_UHS_SDR104;
> +               break;
> +       case MMC_TIMING_UHS_DDR50:
> +       case MMC_TIMING_MMC_DDR52:
> +               ctrl_2 = SDHCI_CTRL_UHS_DDR50;
> +               break;
> +       case MMC_TIMING_MMC_HS200:
> +               ctrl_2 = SDHCI_SPRD_CTRL_HS200;
> +               break;
> +       case MMC_TIMING_MMC_HS400:
> +               ctrl_2 = SDHCI_SPRD_CTRL_HS400;
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +}
> +
> +static void sdhci_sprd_hw_reset(struct sdhci_host *host)
> +{
> +       int val;
> +
> +       /* Note: don't use sdhci_readb/writeb() API here */
> +       val = readb_relaxed(host->ioaddr + SDHCI_SOFTWARE_RESET);
> +       val &= ~SDHCI_HW_RESET_CARD;
> +       writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
> +       udelay(10);
> +
> +       val |= SDHCI_HW_RESET_CARD;
> +       writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
> +       udelay(300);
> +}
> +
> +static struct sdhci_ops sdhci_sprd_ops = {
> +       .read_l = sdhci_sprd_readl,
> +       .write_l = sdhci_sprd_writel,
> +       .write_b = sdhci_sprd_writeb,
> +       .set_clock = sdhci_sprd_set_clock,
> +       .get_max_clock = sdhci_sprd_get_max_clock,
> +       .get_min_clock = sdhci_sprd_get_min_clock,
> +       .set_bus_width = sdhci_set_bus_width,
> +       .reset = sdhci_reset,
> +       .set_uhs_signaling = sdhci_sprd_set_uhs_signaling,
> +       .hw_reset = sdhci_sprd_hw_reset,
> +};
> +
> +static const struct sdhci_pltfm_data sdhci_sprd_pdata = {
> +       .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
> +       .quirks2 = SDHCI_QUIRK2_BROKEN_HS200,
> +       .ops = &sdhci_sprd_ops,
> +};
> +
> +static int sdhci_sprd_probe(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host;
> +       struct sdhci_sprd_host *sprd_host;
> +       struct clk *clk;
> +       int ret = 0;
> +
> +       host = sdhci_pltfm_init(pdev, &sdhci_sprd_pdata, sizeof(*sprd_host));
> +       if (IS_ERR(host))
> +               return PTR_ERR(host);
> +
> +       host->dma_mask = DMA_BIT_MASK(64);
> +       pdev->dev.dma_mask = &host->dma_mask;
> +
> +       host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
> +               MMC_CAP_ERASE | MMC_CAP_CMD23;
> +       ret = mmc_of_parse(host->mmc);
> +       if (ret)
> +               goto pltfm_free;
> +
> +       sprd_host = TO_SPRD_HOST(host);
> +
> +       clk = devm_clk_get(&pdev->dev, "sdio");
> +       if (IS_ERR(clk)) {
> +               ret = PTR_ERR(clk);
> +               goto pltfm_free;
> +       }
> +       sprd_host->clk_sdio = clk;
> +       sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio);
> +       if (!sprd_host->base_rate)
> +               sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE;
> +
> +       clk = devm_clk_get(&pdev->dev, "enable");
> +       if (IS_ERR(clk)) {
> +               ret = PTR_ERR(clk);
> +               goto pltfm_free;
> +       }
> +       sprd_host->clk_enable = clk;
> +
> +       ret = clk_prepare_enable(sprd_host->clk_sdio);
> +       if (ret)
> +               goto pltfm_free;
> +
> +       clk_prepare_enable(sprd_host->clk_enable);
> +       if (ret)
> +               goto clk_disable;
> +
> +       sdhci_sprd_init_config(host);
> +
> +       host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
> +       sprd_host->version = ((host->version & SDHCI_VENDOR_VER_MASK) >>
> +                              SDHCI_VENDOR_VER_SHIFT);
> +
> +       pm_runtime_get_noresume(&pdev->dev);
> +       pm_runtime_set_active(&pdev->dev);
> +       pm_runtime_enable(&pdev->dev);
> +       pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
> +       pm_runtime_use_autosuspend(&pdev->dev);
> +       pm_suspend_ignore_children(&pdev->dev, 1);
> +
> +       ret = sdhci_add_host(host);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to add mmc host: %d\n", ret);
> +               goto pm_runtime_disable;
> +       }
> +
> +       pm_runtime_mark_last_busy(&pdev->dev);
> +       pm_runtime_put_autosuspend(&pdev->dev);
> +
> +       return 0;
> +
> +pm_runtime_disable:
> +       pm_runtime_disable(&pdev->dev);
> +       pm_runtime_set_suspended(&pdev->dev);
> +
> +       clk_disable_unprepare(sprd_host->clk_enable);
> +
> +clk_disable:
> +       clk_disable_unprepare(sprd_host->clk_sdio);
> +
> +pltfm_free:
> +       sdhci_pltfm_free(pdev);
> +       return ret;
> +}
> +
> +static int sdhci_sprd_remove(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host = platform_get_drvdata(pdev);
> +       struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
> +       struct mmc_host *mmc = host->mmc;
> +
> +       mmc_remove_host(mmc);
> +       clk_disable_unprepare(sprd_host->clk_sdio);
> +       clk_disable_unprepare(sprd_host->clk_enable);
> +
> +       mmc_free_host(mmc);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id sdhci_sprd_of_match[] = {
> +       { .compatible = "sprd,sdhci-r11", },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match);
> +
> +#ifdef CONFIG_PM
> +static int sdhci_sprd_runtime_suspend(struct device *dev)
> +{
> +       struct sdhci_host *host = dev_get_drvdata(dev);
> +       struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
> +
> +       sdhci_runtime_suspend_host(host);
> +
> +       clk_disable_unprepare(sprd_host->clk_sdio);
> +       clk_disable_unprepare(sprd_host->clk_enable);
> +
> +       return 0;
> +}
> +
> +static int sdhci_sprd_runtime_resume(struct device *dev)
> +{
> +       struct sdhci_host *host = dev_get_drvdata(dev);
> +       struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
> +
> +       clk_prepare_enable(sprd_host->clk_enable);
> +       clk_prepare_enable(sprd_host->clk_sdio);
> +
> +       sdhci_runtime_resume_host(host);
> +
> +       return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops sdhci_sprd_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +                               pm_runtime_force_resume)
> +       SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend,
> +                          sdhci_sprd_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver sdhci_sprd_driver = {
> +       .probe = sdhci_sprd_probe,
> +       .remove = sdhci_sprd_remove,
> +       .driver = {
> +               .name = "sdhci_sprd_r11",
> +               .of_match_table = of_match_ptr(sdhci_sprd_of_match),
> +               .pm = &sdhci_sprd_pm_ops,
> +       },
> +};
> +module_platform_driver(sdhci_sprd_driver);
> +
> +MODULE_DESCRIPTION("Spreadtrum sdio host controller r11 driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:sdhci-sprd-r11");
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode
  2018-06-15  2:04 ` [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode Chunyan Zhang
@ 2018-06-21 10:49   ` Adrian Hunter
  2018-06-21 11:14     ` Chunyan Zhang
  0 siblings, 1 reply; 20+ messages in thread
From: Adrian Hunter @ 2018-06-21 10:49 UTC (permalink / raw)
  To: Chunyan Zhang, Ulf Hansson
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

On 15/06/18 05:04, Chunyan Zhang wrote:
> For SD host controller version 4.00 or later ones, there're two
> modes of implementation - Version 3.00 compatible mode or
> Version 4 mode.  This patch introduces a flag to record this.
> 
> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
> ---
>  drivers/mmc/host/sdhci.c | 6 ++++++
>  drivers/mmc/host/sdhci.h | 6 ++++++
>  2 files changed, 12 insertions(+)
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 2ededa7f..cf5695f 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -3302,6 +3302,12 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
>  	v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
>  	host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
>  
> +	if (host->version >= SDHCI_SPEC_400) {
> +		if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
> +			SDHCI_CTRL_V4_MODE)
> +			host->v4_mode = true;
> +	}

At this point the host controller has just been reset which would mean it
must be in version 3 compatibility mode, which would mean this code doesn't
do anything.

> +
>  	if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
>  		return;
>  
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index c95b0a4..128b0ba 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -184,6 +184,7 @@
>  #define   SDHCI_CTRL_DRV_TYPE_D		0x0030
>  #define  SDHCI_CTRL_EXEC_TUNING		0x0040
>  #define  SDHCI_CTRL_TUNED_CLK		0x0080
> +#define  SDHCI_CTRL_V4_MODE		0x1000
>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
>  
>  #define SDHCI_CAPABILITIES	0x40
> @@ -270,6 +271,8 @@
>  #define   SDHCI_SPEC_100	0
>  #define   SDHCI_SPEC_200	1
>  #define   SDHCI_SPEC_300	2
> +#define   SDHCI_SPEC_400	3
> +#define   SDHCI_SPEC_410	4
>  
>  /*
>   * End of controller registers.
> @@ -551,6 +554,9 @@ struct sdhci_host {
>  	u32			sdma_boundary;
>  
>  	unsigned long private[0] ____cacheline_aligned;
> +
> +	/* Host Version 4 Enable */
> +	bool			v4_mode;
>  };
>  
>  struct sdhci_ops {
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode
  2018-06-21 10:49   ` Adrian Hunter
@ 2018-06-21 11:14     ` Chunyan Zhang
  2018-06-21 13:15       ` Adrian Hunter
  0 siblings, 1 reply; 20+ messages in thread
From: Chunyan Zhang @ 2018-06-21 11:14 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Chunyan Zhang, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
	Orson Zhai, Baolin Wang, Billows Wu

On 21 June 2018 at 18:49, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 15/06/18 05:04, Chunyan Zhang wrote:
>> For SD host controller version 4.00 or later ones, there're two
>> modes of implementation - Version 3.00 compatible mode or
>> Version 4 mode.  This patch introduces a flag to record this.
>>
>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>> ---
>>  drivers/mmc/host/sdhci.c | 6 ++++++
>>  drivers/mmc/host/sdhci.h | 6 ++++++
>>  2 files changed, 12 insertions(+)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index 2ededa7f..cf5695f 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -3302,6 +3302,12 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
>>       v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
>>       host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
>>
>> +     if (host->version >= SDHCI_SPEC_400) {
>> +             if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
>> +                     SDHCI_CTRL_V4_MODE)
>> +                     host->v4_mode = true;
>> +     }
>
> At this point the host controller has just been reset which would mean it
> must be in version 3 compatibility mode, which would mean this code doesn't
> do anything.

Why is it version 3 mode at this point?

I've tested this code on the sd host controller which was introduced
in 6/7 in this patch-set, the result showed that it was v4_mode.
Moreover without this patch, the Spreadtrum's sdhci driver in patch
6/7 couldn't work.

Am I missing something here?

Best,
Chunyan

>
>> +
>>       if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
>>               return;
>>
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index c95b0a4..128b0ba 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -184,6 +184,7 @@
>>  #define   SDHCI_CTRL_DRV_TYPE_D              0x0030
>>  #define  SDHCI_CTRL_EXEC_TUNING              0x0040
>>  #define  SDHCI_CTRL_TUNED_CLK                0x0080
>> +#define  SDHCI_CTRL_V4_MODE          0x1000
>>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE        0x8000
>>
>>  #define SDHCI_CAPABILITIES   0x40
>> @@ -270,6 +271,8 @@
>>  #define   SDHCI_SPEC_100     0
>>  #define   SDHCI_SPEC_200     1
>>  #define   SDHCI_SPEC_300     2
>> +#define   SDHCI_SPEC_400     3
>> +#define   SDHCI_SPEC_410     4
>>
>>  /*
>>   * End of controller registers.
>> @@ -551,6 +554,9 @@ struct sdhci_host {
>>       u32                     sdma_boundary;
>>
>>       unsigned long private[0] ____cacheline_aligned;
>> +
>> +     /* Host Version 4 Enable */
>> +     bool                    v4_mode;
>>  };
>>
>>  struct sdhci_ops {
>>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA
  2018-06-15  2:04 ` [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA Chunyan Zhang
@ 2018-06-21 11:22   ` Adrian Hunter
  2018-07-04  3:02     ` Chunyan Zhang
  0 siblings, 1 reply; 20+ messages in thread
From: Adrian Hunter @ 2018-06-21 11:22 UTC (permalink / raw)
  To: Chunyan Zhang, Ulf Hansson
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

On 15/06/18 05:04, Chunyan Zhang wrote:
> According to the SD host controller specification version 4.10, when
> Host Version 4 is enabled, SDMA uses ADMA System Address register
> (05Fh-058h) instead of using SDMA System Address register to
> support both 32-bit and 64-bit addressing.
> 
> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
> ---
>  drivers/mmc/host/sdhci.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index cf5695f..f57201f 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -805,6 +805,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
>  static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>  {
>  	u8 ctrl;
> +	u32 reg;

reg could just be an int.

>  	struct mmc_data *data = cmd->data;
>  
>  	if (sdhci_data_line_cmd(cmd))
> @@ -894,8 +895,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>  					     SDHCI_ADMA_ADDRESS_HI);
>  		} else {
>  			WARN_ON(sg_cnt != 1);
> +			reg = host->v4_mode ? SDHCI_ADMA_ADDRESS :
> +				SDHCI_DMA_ADDRESS;
>  			sdhci_writel(host, sdhci_sdma_address(host),
> -				     SDHCI_DMA_ADDRESS);
> +				     reg);

Shouldn't we support 64-bit SDMA in version 4 mode?


>  		}
>  	}
>  
> @@ -2721,6 +2724,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>  		 */
>  		if (intmask & SDHCI_INT_DMA_END) {
>  			u32 dmastart, dmanow;
> +			u32 reg;
>  
>  			dmastart = sdhci_sdma_address(host);
>  			dmanow = dmastart + host->data->bytes_xfered;
> @@ -2733,7 +2737,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>  			host->data->bytes_xfered = dmanow - dmastart;
>  			DBG("DMA base 0x%08x, transferred 0x%06x bytes, next 0x%08x\n",
>  			    dmastart, host->data->bytes_xfered, dmanow);
> -			sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS);
> +			reg = host->v4_mode ? SDHCI_ADMA_ADDRESS :
> +				SDHCI_DMA_ADDRESS;
> +			sdhci_writel(host, dmanow, reg);

Shouldn't we support 64-bit SDMA in version 4 mode?

>  		}
>  
>  		if (intmask & SDHCI_INT_DATA_END) {
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode
  2018-06-21 11:14     ` Chunyan Zhang
@ 2018-06-21 13:15       ` Adrian Hunter
  2018-07-04  3:02         ` Chunyan Zhang
  0 siblings, 1 reply; 20+ messages in thread
From: Adrian Hunter @ 2018-06-21 13:15 UTC (permalink / raw)
  To: Chunyan Zhang
  Cc: Chunyan Zhang, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
	Orson Zhai, Baolin Wang, Billows Wu

On 21/06/18 14:14, Chunyan Zhang wrote:
> On 21 June 2018 at 18:49, Adrian Hunter <adrian.hunter@intel.com> wrote:
>> On 15/06/18 05:04, Chunyan Zhang wrote:
>>> For SD host controller version 4.00 or later ones, there're two
>>> modes of implementation - Version 3.00 compatible mode or
>>> Version 4 mode.  This patch introduces a flag to record this.
>>>
>>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>>> ---
>>>  drivers/mmc/host/sdhci.c | 6 ++++++
>>>  drivers/mmc/host/sdhci.h | 6 ++++++
>>>  2 files changed, 12 insertions(+)
>>>
>>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>>> index 2ededa7f..cf5695f 100644
>>> --- a/drivers/mmc/host/sdhci.c
>>> +++ b/drivers/mmc/host/sdhci.c
>>> @@ -3302,6 +3302,12 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
>>>       v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
>>>       host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
>>>
>>> +     if (host->version >= SDHCI_SPEC_400) {
>>> +             if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
>>> +                     SDHCI_CTRL_V4_MODE)
>>> +                     host->v4_mode = true;
>>> +     }
>>
>> At this point the host controller has just been reset which would mean it
>> must be in version 3 compatibility mode, which would mean this code doesn't
>> do anything.
> 
> Why is it version 3 mode at this point?

According to the specification, reset clears RW fields to zero.

> 
> I've tested this code on the sd host controller which was introduced
> in 6/7 in this patch-set, the result showed that it was v4_mode.
> Moreover without this patch, the Spreadtrum's sdhci driver in patch
> 6/7 couldn't work.
> 
> Am I missing something here?

It seems the Spreadtrum controller doesn't clear the "Host Version 4 Enable"
bit upon software reset for all.

Also this seems the wrong way around.  The driver should decide whether or
not to use V4 mode and then the "Host Version 4 Enable" bit should be set
accordingly.

V4 has been around so long that we can't just enable all supporting hardware
without risking the possibility it will break some platform.  So I suggest
adding a function sdhci_enable_v4_mode() which is called during probe.

> 
> Best,
> Chunyan
> 
>>
>>> +
>>>       if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
>>>               return;
>>>
>>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>>> index c95b0a4..128b0ba 100644
>>> --- a/drivers/mmc/host/sdhci.h
>>> +++ b/drivers/mmc/host/sdhci.h
>>> @@ -184,6 +184,7 @@
>>>  #define   SDHCI_CTRL_DRV_TYPE_D              0x0030
>>>  #define  SDHCI_CTRL_EXEC_TUNING              0x0040
>>>  #define  SDHCI_CTRL_TUNED_CLK                0x0080
>>> +#define  SDHCI_CTRL_V4_MODE          0x1000
>>>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE        0x8000
>>>
>>>  #define SDHCI_CAPABILITIES   0x40
>>> @@ -270,6 +271,8 @@
>>>  #define   SDHCI_SPEC_100     0
>>>  #define   SDHCI_SPEC_200     1
>>>  #define   SDHCI_SPEC_300     2
>>> +#define   SDHCI_SPEC_400     3
>>> +#define   SDHCI_SPEC_410     4
>>>
>>>  /*
>>>   * End of controller registers.
>>> @@ -551,6 +554,9 @@ struct sdhci_host {
>>>       u32                     sdma_boundary;
>>>
>>>       unsigned long private[0] ____cacheline_aligned;
>>> +
>>> +     /* Host Version 4 Enable */
>>> +     bool                    v4_mode;
>>>  };
>>>
>>>  struct sdhci_ops {
>>>
>>
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode
  2018-06-15  2:04 ` [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode Chunyan Zhang
@ 2018-06-21 13:20   ` Adrian Hunter
  2018-07-04  3:03     ` Chunyan Zhang
  0 siblings, 1 reply; 20+ messages in thread
From: Adrian Hunter @ 2018-06-21 13:20 UTC (permalink / raw)
  To: Chunyan Zhang, Ulf Hansson
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

On 15/06/18 05:04, Chunyan Zhang wrote:
> ADMA2 64-bit addressing support is divided into V3 mode and V4 mode.
> So there are two kinds of descriptors for ADMA2 64-bit addressing
> i.e. 96-bit Descriptor for V3 mode, and 128-bit Descriptor for V4
> mode. 128-bit Descriptor is aligned to 8-byte.
> 
> For V4 mode, ADMA2 64-bit addressing is enabled via Host Control 2
> register.
> 
> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
> ---
>  drivers/mmc/host/sdhci.c | 50 +++++++++++++++++++++++++++++++++++-------------
>  drivers/mmc/host/sdhci.h | 23 +++++++++++++++++-----
>  2 files changed, 55 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index f57201f..5d3b0d8 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -585,6 +585,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
>  	void *desc, *align;
>  	char *buffer;
>  	int len, offset, i;
> +	unsigned int adma2_align = SDHCI_ADMA2_ALIGN(host);
> +	unsigned int adma2_mask = SDHCI_ADMA2_MASK(host);
>  
>  	/*
>  	 * The spec does not specify endianness of descriptor table.
> @@ -608,8 +610,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
>  		 * buffer for the (up to three) bytes that screw up the
>  		 * alignment.
>  		 */
> -		offset = (SDHCI_ADMA2_ALIGN - (addr & SDHCI_ADMA2_MASK)) &
> -			 SDHCI_ADMA2_MASK;
> +		offset = (adma2_align - (addr & adma2_align)) &
> +			 adma2_mask;
>  		if (offset) {
>  			if (data->flags & MMC_DATA_WRITE) {
>  				buffer = sdhci_kmap_atomic(sg, &flags);
> @@ -623,8 +625,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
>  
>  			BUG_ON(offset > 65536);
>  
> -			align += SDHCI_ADMA2_ALIGN;
> -			align_addr += SDHCI_ADMA2_ALIGN;
> +			align += adma2_align;
> +			align_addr += adma2_align;
>  
>  			desc += host->desc_sz;
>  
> @@ -668,13 +670,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
>  	void *align;
>  	char *buffer;
>  	unsigned long flags;
> +	unsigned int adma2_align = SDHCI_ADMA2_ALIGN(host);
> +	unsigned int adma2_mask = SDHCI_ADMA2_MASK(host);
>  
>  	if (data->flags & MMC_DATA_READ) {
>  		bool has_unaligned = false;
>  
>  		/* Do a quick scan of the SG list for any unaligned mappings */
>  		for_each_sg(data->sg, sg, host->sg_count, i)
> -			if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
> +			if (sg_dma_address(sg) & adma2_mask) {
>  				has_unaligned = true;
>  				break;
>  			}
> @@ -686,15 +690,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
>  			align = host->align_buffer;
>  
>  			for_each_sg(data->sg, sg, host->sg_count, i) {
> -				if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
> -					size = SDHCI_ADMA2_ALIGN -
> -					       (sg_dma_address(sg) & SDHCI_ADMA2_MASK);
> +				if (sg_dma_address(sg) & adma2_mask) {
> +					size = adma2_align -
> +					       (sg_dma_address(sg) & adma2_mask);
>  
>  					buffer = sdhci_kmap_atomic(sg, &flags);
>  					memcpy(buffer, align, size);
>  					sdhci_kunmap_atomic(buffer, &flags);
>  
> -					align += SDHCI_ADMA2_ALIGN;
> +					align += adma2_align;
>  				}
>  			}
>  		}
> @@ -3400,6 +3404,26 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
>  	return 0;
>  }
>  
> +static inline bool sdhci_use_64bit_dma(struct sdhci_host *host)
> +{
> +	u32 addr64bit_en;
> +
> +	/*
> +	 * According to SD Host Controller spec v4.10, bit[27] added from
> +	 * version 4.10 in Capabilities Register is used as 64-bit System
> +	 * Address support for V4 mode, 64-bit DMA Addressing for V4 mode
> +	 * is enabled only if 64-bit Addressing =1 in the Host Control 2
> +	 * register.
> +	 */
> +	if (host->version == SDHCI_SPEC_410 && host->v4_mode) {
> +		addr64bit_en = (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
> +				SDHCI_CTRL_64BIT_ADDR);

This seems the wrong way around.  SDHCI_CTRL_64BIT_ADDR should be set based
on the driver's requirements, not read to determine what the driver will do.

Can you clarify what SDHCI_CTRL_64BIT_ADDR is for?


> +		return addr64bit_en && (host->caps & SDHCI_CAN_64BIT_V4);
> +	}
> +
> +	return host->caps & SDHCI_CAN_64BIT;
> +}
> +
>  int sdhci_setup_host(struct sdhci_host *host)
>  {
>  	struct mmc_host *mmc;
> @@ -3471,7 +3495,7 @@ int sdhci_setup_host(struct sdhci_host *host)
>  	 * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
>  	 * implement.
>  	 */
> -	if (host->caps & SDHCI_CAN_64BIT)
> +	if (sdhci_use_64bit_dma(host))
>  		host->flags |= SDHCI_USE_64_BIT_DMA;
>  
>  	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> @@ -3505,15 +3529,15 @@ int sdhci_setup_host(struct sdhci_host *host)
>  		 */
>  		if (host->flags & SDHCI_USE_64_BIT_DMA) {
>  			host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
> -					      SDHCI_ADMA2_64_DESC_SZ;
> -			host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
> +					      SDHCI_ADMA2_64_DESC_SZ(host);
> +			host->desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
>  		} else {
>  			host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
>  					      SDHCI_ADMA2_32_DESC_SZ;
>  			host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
>  		}
>  
> -		host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
> +		host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN(host);
>  		buf = dma_alloc_coherent(mmc_dev(mmc), host->align_buffer_sz +
>  					 host->adma_table_sz, &dma, GFP_KERNEL);

This needs to be dma_zalloc_coherent to ensure that the unused 4-bytes in
each 16-byte descriptor is zero.

>  		if (!buf) {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 128b0ba..820a863 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -185,6 +185,7 @@
>  #define  SDHCI_CTRL_EXEC_TUNING		0x0040
>  #define  SDHCI_CTRL_TUNED_CLK		0x0080
>  #define  SDHCI_CTRL_V4_MODE		0x1000
> +#define  SDHCI_CTRL_64BIT_ADDR		0x2000
>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
>  
>  #define SDHCI_CAPABILITIES	0x40
> @@ -206,6 +207,7 @@
>  #define  SDHCI_CAN_VDD_300	0x02000000
>  #define  SDHCI_CAN_VDD_180	0x04000000
>  #define  SDHCI_CAN_64BIT	0x10000000
> +#define  SDHCI_CAN_64BIT_V4	0x8000000

Please make it 8 digits and put it in the right numerical order

>  
>  #define  SDHCI_SUPPORT_SDR50	0x00000001
>  #define  SDHCI_SUPPORT_SDR104	0x00000002
> @@ -297,9 +299,14 @@ struct sdhci_adma2_32_desc {
>  	__le32	addr;
>  }  __packed __aligned(4);
>  
> -/* ADMA2 data alignment */
> -#define SDHCI_ADMA2_ALIGN	4
> -#define SDHCI_ADMA2_MASK	(SDHCI_ADMA2_ALIGN - 1)
> +/*
> + * ADMA2 data alignment
> + * According to SD Host Controller spec v4.10, if Host Version 4 Enable is set
> + * in the Host Control 2 register, 128-bit Descriptor will be selected which
> + * shall be aligned 8-byte address boundary.
> + */
> +#define SDHCI_ADMA2_ALIGN(host)	((host)->v4_mode ? 8 : 4)
> +#define SDHCI_ADMA2_MASK(host)	(SDHCI_ADMA2_ALIGN(host) - 1)

Are you really sure about that, because it reads like it is still 4-byte
alignment for data and 8-byte alignment for descriptors, which is what we
already do.

>  
>  /*
>   * ADMA2 descriptor alignment.  Some controllers (e.g. Intel) require 8 byte
> @@ -308,8 +315,14 @@ struct sdhci_adma2_32_desc {
>   */
>  #define SDHCI_ADMA2_DESC_ALIGN	8
>  
> -/* ADMA2 64-bit DMA descriptor size */
> -#define SDHCI_ADMA2_64_DESC_SZ	12
> +/*
> + * ADMA2 64-bit DMA descriptor size
> + * According to SD Host Controller spec v4.10, there are two kinds of
> + * descriptors for 64-bit addressing mode: 96-bit Descriptor and 128-bit
> + * Descriptor, if Host Version 4 Enable is set in the Host Control 2
> + * register, 128-bit Descriptor will be selected.
> + */
> +#define SDHCI_ADMA2_64_DESC_SZ(host)	((host)->v4_mode ? 16 : 12)
>  
>  /*
>   * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 5/7] mmc: sdhci: add CMD23 support for v4 mode
  2018-06-15  2:04 ` [PATCH V2 5/7] mmc: sdhci: add CMD23 " Chunyan Zhang
@ 2018-06-22 19:40   ` Adrian Hunter
  2018-07-04  3:03     ` Chunyan Zhang
  0 siblings, 1 reply; 20+ messages in thread
From: Adrian Hunter @ 2018-06-22 19:40 UTC (permalink / raw)
  To: Chunyan Zhang, Ulf Hansson
  Cc: linux-mmc, linux-kernel, Orson Zhai, Baolin Wang, Billows Wu, zhang.lyra

On 06/15/2018 05:04 AM, Chunyan Zhang wrote:
> Host Driver Version 4.10 adds a new bit in Host Control 2 Register
> for selecting Auto CMD23 or Auto CMD12 for ADMA3 data transfer.

We don't support ADMA3.  It would require changes to the block driver.
So is this change needed?

> 
> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
> ---
>  drivers/mmc/host/sdhci.c | 16 +++++++++++++++-
>  drivers/mmc/host/sdhci.h |  1 +
>  2 files changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index b8ee124..3b2af7e 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -954,6 +954,20 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
>  	       !mrq->cap_cmd_during_tfr;
>  }
>  
> +static inline void sdhci_set_auto_cmd23(struct sdhci_host *host,
> +					struct mmc_command *cmd)
> +{
> +	u16 ctrl2;
> +
> +	if (host->v4_mode) {

Isn't this only for a V4.1 controller, and doesn't the mode have to be "Auto
Cmd Auto Select"?


> +		ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +		ctrl2 |= SDHCI_CMD23_ENABLE;
> +		sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
> +	} else {
> +		sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
> +	}
> +}
> +
>  static void sdhci_set_transfer_mode(struct sdhci_host *host,
>  	struct mmc_command *cmd)
>  {
> @@ -989,7 +1003,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
>  			mode |= SDHCI_TRNS_AUTO_CMD12;
>  		else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
>  			mode |= SDHCI_TRNS_AUTO_CMD23;
> -			sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
> +			sdhci_set_auto_cmd23(host, cmd);
>  		}
>  	}
>  
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 1e84539..d5e1c10 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -185,6 +185,7 @@
>  #define   SDHCI_CTRL_DRV_TYPE_D		0x0030
>  #define  SDHCI_CTRL_EXEC_TUNING		0x0040
>  #define  SDHCI_CTRL_TUNED_CLK		0x0080
> +#define  SDHCI_CMD23_ENABLE		0x0800
>  #define  SDHCI_CTRL_V4_MODE		0x1000
>  #define  SDHCI_CTRL_64BIT_ADDR		0x2000
>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE	0x8000
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode
  2018-06-21 13:15       ` Adrian Hunter
@ 2018-07-04  3:02         ` Chunyan Zhang
  0 siblings, 0 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-07-04  3:02 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Chunyan Zhang, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
	Orson Zhai, Baolin Wang, Billows Wu

On 21 June 2018 at 21:15, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 21/06/18 14:14, Chunyan Zhang wrote:
>> On 21 June 2018 at 18:49, Adrian Hunter <adrian.hunter@intel.com> wrote:
>>> On 15/06/18 05:04, Chunyan Zhang wrote:
>>>> For SD host controller version 4.00 or later ones, there're two
>>>> modes of implementation - Version 3.00 compatible mode or
>>>> Version 4 mode.  This patch introduces a flag to record this.
>>>>
>>>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>>>> ---
>>>>  drivers/mmc/host/sdhci.c | 6 ++++++
>>>>  drivers/mmc/host/sdhci.h | 6 ++++++
>>>>  2 files changed, 12 insertions(+)
>>>>
>>>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>>>> index 2ededa7f..cf5695f 100644
>>>> --- a/drivers/mmc/host/sdhci.c
>>>> +++ b/drivers/mmc/host/sdhci.c
>>>> @@ -3302,6 +3302,12 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
>>>>       v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
>>>>       host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
>>>>
>>>> +     if (host->version >= SDHCI_SPEC_400) {
>>>> +             if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
>>>> +                     SDHCI_CTRL_V4_MODE)
>>>> +                     host->v4_mode = true;
>>>> +     }
>>>
>>> At this point the host controller has just been reset which would mean it
>>> must be in version 3 compatibility mode, which would mean this code doesn't
>>> do anything.
>>
>> Why is it version 3 mode at this point?
>
> According to the specification, reset clears RW fields to zero.
>
>>
>> I've tested this code on the sd host controller which was introduced
>> in 6/7 in this patch-set, the result showed that it was v4_mode.
>> Moreover without this patch, the Spreadtrum's sdhci driver in patch
>> 6/7 couldn't work.
>>
>> Am I missing something here?
>
> It seems the Spreadtrum controller doesn't clear the "Host Version 4 Enable"
> bit upon software reset for all.
>
> Also this seems the wrong way around.  The driver should decide whether or
> not to use V4 mode and then the "Host Version 4 Enable" bit should be set
> accordingly.
>
> V4 has been around so long that we can't just enable all supporting hardware
> without risking the possibility it will break some platform.  So I suggest

Ok, understand.

> adding a function sdhci_enable_v4_mode() which is called during probe.

Ok, will do, that is more a safe way.

>
>>
>> Best,
>> Chunyan
>>
>>>
>>>> +
>>>>       if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
>>>>               return;
>>>>
>>>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>>>> index c95b0a4..128b0ba 100644
>>>> --- a/drivers/mmc/host/sdhci.h
>>>> +++ b/drivers/mmc/host/sdhci.h
>>>> @@ -184,6 +184,7 @@
>>>>  #define   SDHCI_CTRL_DRV_TYPE_D              0x0030
>>>>  #define  SDHCI_CTRL_EXEC_TUNING              0x0040
>>>>  #define  SDHCI_CTRL_TUNED_CLK                0x0080
>>>> +#define  SDHCI_CTRL_V4_MODE          0x1000
>>>>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE        0x8000
>>>>
>>>>  #define SDHCI_CAPABILITIES   0x40
>>>> @@ -270,6 +271,8 @@
>>>>  #define   SDHCI_SPEC_100     0
>>>>  #define   SDHCI_SPEC_200     1
>>>>  #define   SDHCI_SPEC_300     2
>>>> +#define   SDHCI_SPEC_400     3
>>>> +#define   SDHCI_SPEC_410     4
>>>>
>>>>  /*
>>>>   * End of controller registers.
>>>> @@ -551,6 +554,9 @@ struct sdhci_host {
>>>>       u32                     sdma_boundary;
>>>>
>>>>       unsigned long private[0] ____cacheline_aligned;
>>>> +
>>>> +     /* Host Version 4 Enable */
>>>> +     bool                    v4_mode;
>>>>  };
>>>>
>>>>  struct sdhci_ops {
>>>>
>>>
>>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA
  2018-06-21 11:22   ` Adrian Hunter
@ 2018-07-04  3:02     ` Chunyan Zhang
  0 siblings, 0 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-07-04  3:02 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Chunyan Zhang, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
	Orson Zhai, Baolin Wang, Billows Wu

On 21 June 2018 at 19:22, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 15/06/18 05:04, Chunyan Zhang wrote:
>> According to the SD host controller specification version 4.10, when
>> Host Version 4 is enabled, SDMA uses ADMA System Address register
>> (05Fh-058h) instead of using SDMA System Address register to
>> support both 32-bit and 64-bit addressing.
>>
>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>> ---
>>  drivers/mmc/host/sdhci.c | 10 ++++++++--
>>  1 file changed, 8 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index cf5695f..f57201f 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -805,6 +805,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
>>  static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>>  {
>>       u8 ctrl;
>> +     u32 reg;
>
> reg could just be an int.

Why? Shouldn't addresses be unsigned?

>
>>       struct mmc_data *data = cmd->data;
>>
>>       if (sdhci_data_line_cmd(cmd))
>> @@ -894,8 +895,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
>>                                            SDHCI_ADMA_ADDRESS_HI);
>>               } else {
>>                       WARN_ON(sg_cnt != 1);
>> +                     reg = host->v4_mode ? SDHCI_ADMA_ADDRESS :
>> +                             SDHCI_DMA_ADDRESS;
>>                       sdhci_writel(host, sdhci_sdma_address(host),
>> -                                  SDHCI_DMA_ADDRESS);
>> +                                  reg);
>
> Shouldn't we support 64-bit SDMA in version 4 mode?

I will address.

>
>
>>               }
>>       }
>>
>> @@ -2721,6 +2724,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>>                */
>>               if (intmask & SDHCI_INT_DMA_END) {
>>                       u32 dmastart, dmanow;
>> +                     u32 reg;
>>
>>                       dmastart = sdhci_sdma_address(host);
>>                       dmanow = dmastart + host->data->bytes_xfered;
>> @@ -2733,7 +2737,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>>                       host->data->bytes_xfered = dmanow - dmastart;
>>                       DBG("DMA base 0x%08x, transferred 0x%06x bytes, next 0x%08x\n",
>>                           dmastart, host->data->bytes_xfered, dmanow);
>> -                     sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS);
>> +                     reg = host->v4_mode ? SDHCI_ADMA_ADDRESS :
>> +                             SDHCI_DMA_ADDRESS;
>> +                     sdhci_writel(host, dmanow, reg);
>
> Shouldn't we support 64-bit SDMA in version 4 mode?
>
>>               }
>>
>>               if (intmask & SDHCI_INT_DATA_END) {
>>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode
  2018-06-21 13:20   ` Adrian Hunter
@ 2018-07-04  3:03     ` Chunyan Zhang
  0 siblings, 0 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-07-04  3:03 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Chunyan Zhang, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
	Orson Zhai, Baolin Wang, Billows Wu

On 21 June 2018 at 21:20, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 15/06/18 05:04, Chunyan Zhang wrote:
>> ADMA2 64-bit addressing support is divided into V3 mode and V4 mode.
>> So there are two kinds of descriptors for ADMA2 64-bit addressing
>> i.e. 96-bit Descriptor for V3 mode, and 128-bit Descriptor for V4
>> mode. 128-bit Descriptor is aligned to 8-byte.
>>
>> For V4 mode, ADMA2 64-bit addressing is enabled via Host Control 2
>> register.
>>
>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>> ---
>>  drivers/mmc/host/sdhci.c | 50 +++++++++++++++++++++++++++++++++++-------------
>>  drivers/mmc/host/sdhci.h | 23 +++++++++++++++++-----
>>  2 files changed, 55 insertions(+), 18 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index f57201f..5d3b0d8 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -585,6 +585,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
>>       void *desc, *align;
>>       char *buffer;
>>       int len, offset, i;
>> +     unsigned int adma2_align = SDHCI_ADMA2_ALIGN(host);
>> +     unsigned int adma2_mask = SDHCI_ADMA2_MASK(host);
>>
>>       /*
>>        * The spec does not specify endianness of descriptor table.
>> @@ -608,8 +610,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
>>                * buffer for the (up to three) bytes that screw up the
>>                * alignment.
>>                */
>> -             offset = (SDHCI_ADMA2_ALIGN - (addr & SDHCI_ADMA2_MASK)) &
>> -                      SDHCI_ADMA2_MASK;
>> +             offset = (adma2_align - (addr & adma2_align)) &
>> +                      adma2_mask;
>>               if (offset) {
>>                       if (data->flags & MMC_DATA_WRITE) {
>>                               buffer = sdhci_kmap_atomic(sg, &flags);
>> @@ -623,8 +625,8 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
>>
>>                       BUG_ON(offset > 65536);
>>
>> -                     align += SDHCI_ADMA2_ALIGN;
>> -                     align_addr += SDHCI_ADMA2_ALIGN;
>> +                     align += adma2_align;
>> +                     align_addr += adma2_align;
>>
>>                       desc += host->desc_sz;
>>
>> @@ -668,13 +670,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
>>       void *align;
>>       char *buffer;
>>       unsigned long flags;
>> +     unsigned int adma2_align = SDHCI_ADMA2_ALIGN(host);
>> +     unsigned int adma2_mask = SDHCI_ADMA2_MASK(host);
>>
>>       if (data->flags & MMC_DATA_READ) {
>>               bool has_unaligned = false;
>>
>>               /* Do a quick scan of the SG list for any unaligned mappings */
>>               for_each_sg(data->sg, sg, host->sg_count, i)
>> -                     if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
>> +                     if (sg_dma_address(sg) & adma2_mask) {
>>                               has_unaligned = true;
>>                               break;
>>                       }
>> @@ -686,15 +690,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
>>                       align = host->align_buffer;
>>
>>                       for_each_sg(data->sg, sg, host->sg_count, i) {
>> -                             if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
>> -                                     size = SDHCI_ADMA2_ALIGN -
>> -                                            (sg_dma_address(sg) & SDHCI_ADMA2_MASK);
>> +                             if (sg_dma_address(sg) & adma2_mask) {
>> +                                     size = adma2_align -
>> +                                            (sg_dma_address(sg) & adma2_mask);
>>
>>                                       buffer = sdhci_kmap_atomic(sg, &flags);
>>                                       memcpy(buffer, align, size);
>>                                       sdhci_kunmap_atomic(buffer, &flags);
>>
>> -                                     align += SDHCI_ADMA2_ALIGN;
>> +                                     align += adma2_align;
>>                               }
>>                       }
>>               }
>> @@ -3400,6 +3404,26 @@ static int sdhci_allocate_bounce_buffer(struct sdhci_host *host)
>>       return 0;
>>  }
>>
>> +static inline bool sdhci_use_64bit_dma(struct sdhci_host *host)
>> +{
>> +     u32 addr64bit_en;
>> +
>> +     /*
>> +      * According to SD Host Controller spec v4.10, bit[27] added from
>> +      * version 4.10 in Capabilities Register is used as 64-bit System
>> +      * Address support for V4 mode, 64-bit DMA Addressing for V4 mode
>> +      * is enabled only if 64-bit Addressing =1 in the Host Control 2
>> +      * register.
>> +      */
>> +     if (host->version == SDHCI_SPEC_410 && host->v4_mode) {
>> +             addr64bit_en = (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
>> +                             SDHCI_CTRL_64BIT_ADDR);
>
> This seems the wrong way around.  SDHCI_CTRL_64BIT_ADDR should be set based
> on the driver's requirements, not read to determine what the driver will do.

It was actually set based on the host controller driver's
configuration, the driver can determine by setting bit[13] (i.e.
SDHCI_CTRL_64BIT_ADDR) in the register SDHCI_HOST_CONTROL2 or not.

I probably have got your point, but why don't we leave this register
for vender's controller driver to set?

From what I noticed (maybe missing something), whether use 64bit or
not is determined in the initialization (set SDHCI_USE_64_BIT_DMA to
host->flags) and could not be changed afterward, but every time send
data, the register SDHCI_HOST_CONTROL (in case of v4.10
SDHCI_HOST_CONTROL2 it is) would be set, my question is what was the
reason we chose to do in this way rather than letting drivers to set
this register before sdhci_setup_host(), since in the latter way,
there's no need to write this register every time before sending
command.

>
> Can you clarify what SDHCI_CTRL_64BIT_ADDR is for?

bit[13] in the register SDHCI_HOST_CONTROL2.

>
>
>> +             return addr64bit_en && (host->caps & SDHCI_CAN_64BIT_V4);
>> +     }
>> +
>> +     return host->caps & SDHCI_CAN_64BIT;
>> +}
>> +
>>  int sdhci_setup_host(struct sdhci_host *host)
>>  {
>>       struct mmc_host *mmc;
>> @@ -3471,7 +3495,7 @@ int sdhci_setup_host(struct sdhci_host *host)
>>        * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
>>        * implement.
>>        */
>> -     if (host->caps & SDHCI_CAN_64BIT)
>> +     if (sdhci_use_64bit_dma(host))
>>               host->flags |= SDHCI_USE_64_BIT_DMA;
>>
>>       if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
>> @@ -3505,15 +3529,15 @@ int sdhci_setup_host(struct sdhci_host *host)
>>                */
>>               if (host->flags & SDHCI_USE_64_BIT_DMA) {
>>                       host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
>> -                                           SDHCI_ADMA2_64_DESC_SZ;
>> -                     host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
>> +                                           SDHCI_ADMA2_64_DESC_SZ(host);
>> +                     host->desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
>>               } else {
>>                       host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
>>                                             SDHCI_ADMA2_32_DESC_SZ;
>>                       host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
>>               }
>>
>> -             host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
>> +             host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN(host);
>>               buf = dma_alloc_coherent(mmc_dev(mmc), host->align_buffer_sz +
>>                                        host->adma_table_sz, &dma, GFP_KERNEL);
>
> This needs to be dma_zalloc_coherent to ensure that the unused 4-bytes in
> each 16-byte descriptor is zero.

Oh right, I will address.

>
>>               if (!buf) {
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index 128b0ba..820a863 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -185,6 +185,7 @@
>>  #define  SDHCI_CTRL_EXEC_TUNING              0x0040
>>  #define  SDHCI_CTRL_TUNED_CLK                0x0080
>>  #define  SDHCI_CTRL_V4_MODE          0x1000
>> +#define  SDHCI_CTRL_64BIT_ADDR               0x2000
>>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE        0x8000
>>
>>  #define SDHCI_CAPABILITIES   0x40
>> @@ -206,6 +207,7 @@
>>  #define  SDHCI_CAN_VDD_300   0x02000000
>>  #define  SDHCI_CAN_VDD_180   0x04000000
>>  #define  SDHCI_CAN_64BIT     0x10000000
>> +#define  SDHCI_CAN_64BIT_V4  0x8000000
>
> Please make it 8 digits and put it in the right numerical order

Ok.

>
>>
>>  #define  SDHCI_SUPPORT_SDR50 0x00000001
>>  #define  SDHCI_SUPPORT_SDR104        0x00000002
>> @@ -297,9 +299,14 @@ struct sdhci_adma2_32_desc {
>>       __le32  addr;
>>  }  __packed __aligned(4);
>>
>> -/* ADMA2 data alignment */
>> -#define SDHCI_ADMA2_ALIGN    4
>> -#define SDHCI_ADMA2_MASK     (SDHCI_ADMA2_ALIGN - 1)
>> +/*
>> + * ADMA2 data alignment
>> + * According to SD Host Controller spec v4.10, if Host Version 4 Enable is set
>> + * in the Host Control 2 register, 128-bit Descriptor will be selected which
>> + * shall be aligned 8-byte address boundary.
>> + */
>> +#define SDHCI_ADMA2_ALIGN(host)      ((host)->v4_mode ? 8 : 4)
>> +#define SDHCI_ADMA2_MASK(host)       (SDHCI_ADMA2_ALIGN(host) - 1)
>
> Are you really sure about that, because it reads like it is still 4-byte
> alignment for data and 8-byte alignment for descriptors, which is what we
> already do.

Oh, right, my mistake.

Thanks for your review,
Chunyan

>
>>
>>  /*
>>   * ADMA2 descriptor alignment.  Some controllers (e.g. Intel) require 8 byte
>> @@ -308,8 +315,14 @@ struct sdhci_adma2_32_desc {
>>   */
>>  #define SDHCI_ADMA2_DESC_ALIGN       8
>>
>> -/* ADMA2 64-bit DMA descriptor size */
>> -#define SDHCI_ADMA2_64_DESC_SZ       12
>> +/*
>> + * ADMA2 64-bit DMA descriptor size
>> + * According to SD Host Controller spec v4.10, there are two kinds of
>> + * descriptors for 64-bit addressing mode: 96-bit Descriptor and 128-bit
>> + * Descriptor, if Host Version 4 Enable is set in the Host Control 2
>> + * register, 128-bit Descriptor will be selected.
>> + */
>> +#define SDHCI_ADMA2_64_DESC_SZ(host) ((host)->v4_mode ? 16 : 12)
>>
>>  /*
>>   * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
>>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH V2 5/7] mmc: sdhci: add CMD23 support for v4 mode
  2018-06-22 19:40   ` Adrian Hunter
@ 2018-07-04  3:03     ` Chunyan Zhang
  0 siblings, 0 replies; 20+ messages in thread
From: Chunyan Zhang @ 2018-07-04  3:03 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Chunyan Zhang, Ulf Hansson, linux-mmc, Linux Kernel Mailing List,
	Orson Zhai, Baolin Wang, Billows Wu

On 23 June 2018 at 03:40, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 06/15/2018 05:04 AM, Chunyan Zhang wrote:
>> Host Driver Version 4.10 adds a new bit in Host Control 2 Register
>> for selecting Auto CMD23 or Auto CMD12 for ADMA3 data transfer.
>
> We don't support ADMA3.  It would require changes to the block driver.
> So is this change needed?
>
>>
>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>> ---
>>  drivers/mmc/host/sdhci.c | 16 +++++++++++++++-
>>  drivers/mmc/host/sdhci.h |  1 +
>>  2 files changed, 16 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index b8ee124..3b2af7e 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -954,6 +954,20 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
>>              !mrq->cap_cmd_during_tfr;
>>  }
>>
>> +static inline void sdhci_set_auto_cmd23(struct sdhci_host *host,
>> +                                     struct mmc_command *cmd)
>> +{
>> +     u16 ctrl2;
>> +
>> +     if (host->v4_mode) {
>
> Isn't this only for a V4.1 controller, and doesn't the mode have to be "Auto
> Cmd Auto Select"?

I will send another version of changes for this new mode "Auto Cmd
Auto Select", let's see if the next iteration gets better then.

Thanks for your review,
Chunyan

>
>
>> +             ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>> +             ctrl2 |= SDHCI_CMD23_ENABLE;
>> +             sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
>> +     } else {
>> +             sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
>> +     }
>> +}
>> +
>>  static void sdhci_set_transfer_mode(struct sdhci_host *host,
>>       struct mmc_command *cmd)
>>  {
>> @@ -989,7 +1003,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
>>                       mode |= SDHCI_TRNS_AUTO_CMD12;
>>               else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
>>                       mode |= SDHCI_TRNS_AUTO_CMD23;
>> -                     sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
>> +                     sdhci_set_auto_cmd23(host, cmd);
>>               }
>>       }
>>
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index 1e84539..d5e1c10 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -185,6 +185,7 @@
>>  #define   SDHCI_CTRL_DRV_TYPE_D              0x0030
>>  #define  SDHCI_CTRL_EXEC_TUNING              0x0040
>>  #define  SDHCI_CTRL_TUNED_CLK                0x0080
>> +#define  SDHCI_CMD23_ENABLE          0x0800
>>  #define  SDHCI_CTRL_V4_MODE          0x1000
>>  #define  SDHCI_CTRL_64BIT_ADDR               0x2000
>>  #define  SDHCI_CTRL_PRESET_VAL_ENABLE        0x8000
>>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2018-07-04  3:04 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-15  2:04 [PATCH V2 0/7] mmc: add support for sdhci 4.0 Chunyan Zhang
2018-06-15  2:04 ` [PATCH V2 1/7] mmc: sdhci: add sd host v4 mode Chunyan Zhang
2018-06-21 10:49   ` Adrian Hunter
2018-06-21 11:14     ` Chunyan Zhang
2018-06-21 13:15       ` Adrian Hunter
2018-07-04  3:02         ` Chunyan Zhang
2018-06-15  2:04 ` [PATCH V2 2/7] mmc: sdhci: made changes for System Address register of SDMA Chunyan Zhang
2018-06-21 11:22   ` Adrian Hunter
2018-07-04  3:02     ` Chunyan Zhang
2018-06-15  2:04 ` [PATCH V2 3/7] mmc: sdhci: add ADMA2 64-bit addressing support for V4 mode Chunyan Zhang
2018-06-21 13:20   ` Adrian Hunter
2018-07-04  3:03     ` Chunyan Zhang
2018-06-15  2:04 ` [PATCH V2 4/7] mmc: sdhci: add 32-bit block count support for v4 mode Chunyan Zhang
2018-06-15  2:04 ` [PATCH V2 5/7] mmc: sdhci: add CMD23 " Chunyan Zhang
2018-06-22 19:40   ` Adrian Hunter
2018-07-04  3:03     ` Chunyan Zhang
2018-06-15  2:04 ` [PATCH V2 6/7] mmc: sdhci-sprd: added Spreadtrum's initial host controller Chunyan Zhang
2018-06-15  9:36   ` [PATCH V3 " Chunyan Zhang
2018-06-18  8:56     ` Ulf Hansson
2018-06-15  2:04 ` [PATCH V2 7/7] dt-bindings: sdhci-sprd: Add bindings for the sdhci-sprd controller Chunyan Zhang

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).