mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps
@ 2023-08-16  9:39 Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 2/7] mci: sdhci: Add registers defines Jules Maselbas
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Set the mci::host_cap MMC_CAP_8_BIT_DATA if supported by the hardware.

Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
 drivers/mci/sdhci.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 635884e2a2..f6deea4020 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -568,6 +568,9 @@ int sdhci_setup_host(struct sdhci *host)
 	if (host->caps & SDHCI_CAN_DO_HISPD)
 		mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
+	if (host->caps & SDHCI_CAN_DO_8BIT)
+		mci->host_caps |= MMC_CAP_8_BIT_DATA;
+
 	host->sdma_boundary = SDHCI_DMA_BOUNDARY_512K;
 
 	return 0;
-- 
2.41.0




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

* [PATCH v2 2/7] mci: sdhci: Add registers defines
  2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
@ 2023-08-16  9:39 ` Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 3/7] mci: sdhci: Actually return the error code instead of 0 Jules Maselbas
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
 drivers/mci/sdhci.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index fe8c25cb9c..50179de0e6 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -117,6 +117,9 @@
 #define SDHCI_INT_ERROR_ENABLE					0x36
 #define SDHCI_SIGNAL_ENABLE					0x38
 #define SDHCI_ACMD12_ERR__HOST_CONTROL2				0x3C
+#define SDHCI_HOST_CONTROL2					0x3E
+#define  SDHCI_CTRL_64BIT_ADDR			BIT(13)
+#define  SDHCI_CTRL_V4_MODE			BIT(12)
 #define SDHCI_CAPABILITIES					0x40
 #define  SDHCI_TIMEOUT_CLK_MASK			GENMASK(5, 0)
 #define  SDHCI_TIMEOUT_CLK_UNIT			0x00000080
@@ -173,6 +176,9 @@
 
 #define  SDHCI_CLOCK_MUL_SHIFT	16
 
+#define SDHCI_ADMA_ADDRESS					0x58
+#define SDHCI_ADMA_ADDRESS_HI					0x5c
+
 #define SDHCI_MMC_BOOT						0xC4
 
 #define SDHCI_MAX_DIV_SPEC_200	256
-- 
2.41.0




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

* [PATCH v2 3/7] mci: sdhci: Actually return the error code instead of 0
  2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 2/7] mci: sdhci: Add registers defines Jules Maselbas
@ 2023-08-16  9:39 ` Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 4/7] mci: sdhci: Add sd host v4 mode Jules Maselbas
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

The sdhci_transfer_data_dma function always returned 0 even in case of
an error, fix this.

Fixes: 60b608b271 ("mci: sdhci: Add DMA transfer helpers")
Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
 drivers/mci/sdhci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index f6deea4020..a980e34314 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -244,7 +244,7 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
 	else
 		dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
 
-	return 0;
+	return ret;
 }
 
 int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
-- 
2.41.0




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

* [PATCH v2 4/7] mci: sdhci: Add sd host v4 mode
  2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 2/7] mci: sdhci: Add registers defines Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 3/7] mci: sdhci: Actually return the error code instead of 0 Jules Maselbas
@ 2023-08-16  9:39 ` Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 5/7] mci: sdhci: Add 64-bit DMA addressing suport for V4 mode Jules Maselbas
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
 drivers/mci/sdhci.c | 21 +++++++++++++++++++++
 drivers/mci/sdhci.h |  2 ++
 2 files changed, 23 insertions(+)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index a980e34314..bce4ff2a14 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -480,6 +480,24 @@ void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_
 	sdhci_enable_clk(host, clk);
 }
 
+static void sdhci_do_enable_v4_mode(struct sdhci *host)
+{
+	u16 ctrl2;
+
+	ctrl2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+	if (ctrl2 & SDHCI_CTRL_V4_MODE)
+		return;
+
+	ctrl2 |= SDHCI_CTRL_V4_MODE;
+	sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl2);
+}
+
+void sdhci_enable_v4_mode(struct sdhci *host)
+{
+	host->v4_mode = true;
+	sdhci_do_enable_v4_mode(host);
+}
+
 void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
 			const u32 *caps, const u32 *caps1)
 {
@@ -497,6 +515,9 @@ void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
 
 	sdhci_reset(host, SDHCI_RESET_ALL);
 
+	if (host->v4_mode)
+		sdhci_do_enable_v4_mode(host);
+
 	of_property_read_u64(np, "sdhci-caps-mask", &dt_caps_mask);
 	of_property_read_u64(np, "sdhci-caps", &dt_caps);
 
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 50179de0e6..1f5d0564fc 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -201,6 +201,7 @@ struct sdhci {
 
 	enum mci_timing timing;
 	bool preset_enabled; /* Preset is enabled */
+	bool v4_mode;		/* Host Version 4 Enable */
 
 	unsigned int quirks;
 #define SDHCI_QUIRK_MISSING_CAPS		BIT(27)
@@ -279,6 +280,7 @@ u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
 		   unsigned int *actual_clock, unsigned int input_clock);
 void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock);
 void sdhci_enable_clk(struct sdhci *host, u16 clk);
+void sdhci_enable_v4_mode(struct sdhci *host);
 int sdhci_setup_host(struct sdhci *host);
 void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
 			const u32 *caps, const u32 *caps1);
-- 
2.41.0




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

* [PATCH v2 5/7] mci: sdhci: Add 64-bit DMA addressing suport for V4 mode
  2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
                   ` (2 preceding siblings ...)
  2023-08-16  9:39 ` [PATCH v2 4/7] mci: sdhci: Add sd host v4 mode Jules Maselbas
@ 2023-08-16  9:39 ` Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 6/7] mci: sdhci: Force DMA update to the next block boundary Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver Jules Maselbas
  5 siblings, 0 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
 drivers/mci/sdhci.c | 64 +++++++++++++++++++++++++++++++++++++++++++--
 drivers/mci/sdhci.h | 15 +++++++++++
 2 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index bce4ff2a14..4aca3af5aa 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -111,6 +111,35 @@ void sdhci_set_bus_width(struct sdhci *host, int width)
 	sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl);
 }
 
+static inline bool sdhci_can_64bit_dma(struct sdhci *host)
+{
+	/*
+	 * 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.
+	 */
+	if (host->version >= SDHCI_SPEC_410 && host->v4_mode)
+		return host->caps & SDHCI_CAN_64BIT_V4;
+
+	return host->caps & SDHCI_CAN_64BIT;
+}
+
+
+static void sdhci_set_adma_addr(struct sdhci *host, dma_addr_t addr)
+{
+	sdhci_write32(host, SDHCI_ADMA_ADDRESS, lower_32_bits(addr));
+	if (host->flags & SDHCI_USE_64_BIT_DMA)
+		sdhci_write32(host, SDHCI_ADMA_ADDRESS_HI, upper_32_bits(addr));
+}
+
+static void sdhci_set_sdma_addr(struct sdhci *host, dma_addr_t addr)
+{
+	if (host->v4_mode)
+		sdhci_set_adma_addr(host, addr);
+	else
+		sdhci_write32(host, SDHCI_DMA_ADDRESS, addr);
+}
+
 #ifdef __PBL__
 /*
  * Stubs to make timeout logic below work in PBL
@@ -160,6 +189,33 @@ void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data)
 		      SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize) | data->blocks << 16);
 }
 
+static void sdhci_config_dma(struct sdhci *host)
+{
+	u8 ctrl;
+	u16 ctrl2;
+
+	if (host->version < SDHCI_SPEC_200)
+		return;
+
+	ctrl = sdhci_read8(host, SDHCI_HOST_CONTROL);
+	/* Note if DMA Select is zero then SDMA is selected */
+	ctrl &= ~SDHCI_CTRL_DMA_MASK;
+	sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl);
+
+	if (host->flags & SDHCI_USE_64_BIT_DMA) {
+		/*
+		 * If v4 mode, all supported DMA can be 64-bit addressing if
+		 * controller supports 64-bit system address, otherwise only
+		 * ADMA can support 64-bit addressing.
+		 */
+		if (host->v4_mode) {
+			ctrl2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+			ctrl2 |= SDHCI_CTRL_64BIT_ADDR;
+			sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl2);
+		}
+	}
+}
+
 void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
 			  dma_addr_t *dma)
 {
@@ -188,7 +244,8 @@ void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
 		return;
 	}
 
-	sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, *dma);
+	sdhci_config_dma(sdhci);
+	sdhci_set_sdma_addr(sdhci, *dma);
 }
 
 int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
@@ -230,7 +287,7 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
 			 * the interrupt and kick the DMA engine again.
 			 */
 			sdhci_write32(sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA);
-			sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, addr);
+			sdhci_set_sdma_addr(sdhci, dma);
 		}
 
 		if (irqstat & SDHCI_INT_XFER_COMPLETE)
@@ -594,5 +651,8 @@ int sdhci_setup_host(struct sdhci *host)
 
 	host->sdma_boundary = SDHCI_DMA_BOUNDARY_512K;
 
+	if (sdhci_can_64bit_dma(host))
+		host->flags |= SDHCI_USE_64_BIT_DMA;
+
 	return 0;
 }
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 1f5d0564fc..f3ffd62dff 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -197,6 +197,21 @@ struct sdhci {
 	int max_clk; /* Max possible freq (Hz) */
 	int clk_mul; /* Clock Muliplier value */
 
+	int flags;		/* Host attributes */
+#define SDHCI_USE_SDMA		(1<<0)	/* Host is SDMA capable */
+#define SDHCI_USE_ADMA		(1<<1)	/* Host is ADMA capable */
+#define SDHCI_REQ_USE_DMA	(1<<2)	/* Use DMA for this req. */
+#define SDHCI_DEVICE_DEAD	(1<<3)	/* Device unresponsive */
+#define SDHCI_SDR50_NEEDS_TUNING (1<<4)	/* SDR50 needs tuning */
+#define SDHCI_AUTO_CMD12	(1<<6)	/* Auto CMD12 support */
+#define SDHCI_AUTO_CMD23	(1<<7)	/* Auto CMD23 support */
+#define SDHCI_PV_ENABLED	(1<<8)	/* Preset value enabled */
+#define SDHCI_USE_64_BIT_DMA	(1<<12)	/* Use 64-bit DMA */
+#define SDHCI_HS400_TUNING	(1<<13)	/* Tuning for HS400 */
+#define SDHCI_SIGNALING_330	(1<<14)	/* Host is capable of 3.3V signaling */
+#define SDHCI_SIGNALING_180	(1<<15)	/* Host is capable of 1.8V signaling */
+#define SDHCI_SIGNALING_120	(1<<16)	/* Host is capable of 1.2V signaling */
+
 	unsigned int version; /* SDHCI spec. version */
 
 	enum mci_timing timing;
-- 
2.41.0




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

* [PATCH v2 6/7] mci: sdhci: Force DMA update to the next block boundary
  2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
                   ` (3 preceding siblings ...)
  2023-08-16  9:39 ` [PATCH v2 5/7] mci: sdhci: Add 64-bit DMA addressing suport for V4 mode Jules Maselbas
@ 2023-08-16  9:39 ` Jules Maselbas
  2023-08-16  9:39 ` [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver Jules Maselbas
  5 siblings, 0 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
change in v2:
 - applied the fixup:
   straight copy from Linux didn't worked because the macro SDHCI_DMA_BOUNDARY_512K
   (default value of sdhci::sdma_boundary) is not a size but the enum value to be
   written in the transfer control register.

 drivers/mci/sdhci.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 4aca3af5aa..b0b83bfaa9 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -279,8 +279,20 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
 			goto out;
 		}
 
+		/*
+		 * We currently don't do anything fancy with DMA
+		 * boundaries, but as we can't disable the feature
+		 * we need to at least restart the transfer.
+		 *
+		 * According to the spec sdhci_readl(host, SDHCI_DMA_ADDRESS)
+		 * should return a valid address to continue from, but as
+		 * some controllers are faulty, don't trust them.
+		 */
 		if (irqstat & SDHCI_INT_DMA) {
-			u32 addr = sdhci_read32(sdhci, SDHCI_DMA_ADDRESS);
+			int boundary_cfg = (sdhci->sdma_boundary >> 12) & 0x7;
+			dma_addr_t boundary_size = 4096 << boundary_cfg;
+			/* Force update to the next DMA block boundary. */
+			dma = (dma & ~(boundary_size - 1)) + boundary_size;
 
 			/*
 			 * DMA engine has stopped on buffer boundary. Acknowledge
-- 
2.41.0




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

* [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver
  2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
                   ` (4 preceding siblings ...)
  2023-08-16  9:39 ` [PATCH v2 6/7] mci: sdhci: Force DMA update to the next block boundary Jules Maselbas
@ 2023-08-16  9:39 ` Jules Maselbas
  2023-08-18  6:27   ` Sascha Hauer
  5 siblings, 1 reply; 9+ messages in thread
From: Jules Maselbas @ 2023-08-16  9:39 UTC (permalink / raw)
  To: barebox; +Cc: Jules Maselbas

The dwcmshc driver started as a modification of the dove-sdhci driver.
This new dwcmshc driver has been heavily reworked to be closer to the
rockchip-dwcmshc-sdhci driver, both drivers are for the same IP from
Synopsys DesignWare Cores.

This new dwcmshc driver use new features introduced in common sdhci code,
mainly to support v4_mode and 64-bit DMA.

Signed-off-by: Jules Maselbas <jmaselbas@kalray.eu>
---
changes in v2:
 - squashed both driver commit into one: the driver is introduced after
   the common sdhci code is modified.

 drivers/mci/Kconfig         |   8 +
 drivers/mci/Makefile        |   1 +
 drivers/mci/dwcmshc-sdhci.c | 360 ++++++++++++++++++++++++++++++++++++
 3 files changed, 369 insertions(+)
 create mode 100644 drivers/mci/dwcmshc-sdhci.c

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index bbdca67e9d..1fde0c8ffe 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -58,6 +58,14 @@ config MCI_MMC_GPP_PARTITIONS
 
 comment "--- MCI host drivers ---"
 
+config MCI_DWC_MSHC
+	bool "Synopsys DesignWare Cores MSHC"
+	depends on HAS_DMA
+	select MCI_SDHCI
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage Host Controller
+	  block (DWC_mshc), this provides host support for SD/eMMC interfaces, in SDMA mode.
+
 config MCI_DW
 	bool "Synopsys DesignWare Memory Card Interface"
 	depends on HAS_DMA
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index e3dc5ad8ae..35d11fb439 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MCI_ROCKCHIP_DWCMSHC)	+= rockchip-dwcmshc-sdhci.o
 obj-$(CONFIG_MCI_TEGRA)		+= tegra-sdmmc.o
 obj-$(CONFIG_MCI_SPI)		+= mci_spi.o
 obj-$(CONFIG_MCI_DW)		+= dw_mmc.o
+obj-$(CONFIG_MCI_DWC_MSHC)	+= dwcmshc-sdhci.o
 obj-$(CONFIG_MCI_MMCI)		+= mmci.o
 obj-$(CONFIG_MCI_STM32_SDMMC2)	+= stm32_sdmmc2.o
 obj-pbl-$(CONFIG_MCI_SDHCI)	+= sdhci.o
diff --git a/drivers/mci/dwcmshc-sdhci.c b/drivers/mci/dwcmshc-sdhci.c
new file mode 100644
index 0000000000..a95e3bd1e9
--- /dev/null
+++ b/drivers/mci/dwcmshc-sdhci.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2019 Yann Sionneau, Kalray Inc.
+// SPDX-FileCopyrightText: 2023 Jules Maselbas, Kalray Inc.
+
+#include <clock.h>
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <dma.h>
+#include <malloc.h>
+#include <mci.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include "sdhci.h"
+
+#define CARD_STATUS_MASK (0x1e00)
+#define CARD_STATUS_TRAN (4 << 9)
+static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+				struct mci_data *data);
+
+struct dwcmshc_host {
+	struct mci_host mci;
+	struct sdhci sdhci;
+	unsigned int in_abort_sequence;
+};
+
+#define priv_from_mci_host(h) container_of(h, struct dwcmshc_host, mci)
+
+static void mci_setup_cmd(struct mci_cmd *p, unsigned int cmd, unsigned int arg,
+			  unsigned int response)
+{
+	p->cmdidx = cmd;
+	p->cmdarg = arg;
+	p->resp_type = response;
+}
+
+static int do_abort_sequence(struct mci_host *mci, struct mci_cmd *current_cmd)
+{
+	int ret = 0;
+	struct dwcmshc_host *host = priv_from_mci_host(mci);
+	struct mci_cmd cmd;
+	u64 start;
+
+	host->in_abort_sequence = 1;
+
+	mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
+	if (ret) {
+		dev_err(host->mci.hw_dev, "Abort failed at first cmd12!\n");
+		goto out;
+	}
+	mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
+		      MMC_RSP_R1);
+	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
+	if (ret) {
+		dev_err(host->mci.hw_dev, "Abort failed at first cmd13!\n");
+		goto out;
+	}
+
+	if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN)
+		goto out; /* All is OK! */
+
+	mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
+	if (ret) {
+		dev_err(host->mci.hw_dev, "Abort failed at second cmd12!\n");
+		goto out;
+	}
+
+	mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
+		      MMC_RSP_R1);
+	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
+	if (ret) {
+		dev_err(host->mci.hw_dev, "Abort failed at second cmd13!\n");
+		goto out;
+	}
+
+	if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN) {
+		goto out; /* All is OK! */
+	} else {
+		dev_err(host->mci.hw_dev,
+			"Abort sequence failed to put card in TRAN state!\n");
+		ret = 1;
+		goto out;
+	}
+
+out:
+	/* Perform SW reset if in abort sequence */
+	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET,
+		       SDHCI_RESET_DATA | SDHCI_RESET_CMD);
+	start = get_time_ns();
+	while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) != 0) {
+		if (is_timeout(start, 50 * MSECOND)) {
+			dev_err(host->mci.hw_dev,
+				"SDHCI data reset timeout\n");
+			break;
+		}
+	}
+	host->in_abort_sequence = 0;
+	return ret;
+}
+
+static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+				struct mci_data *data)
+{
+	struct dwcmshc_host *host = priv_from_mci_host(mci);
+	dma_addr_t dma = SDHCI_NO_DMA;
+	u32 mask, command, xfer;
+	int ret;
+
+	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION
+	    && host->in_abort_sequence == 0)
+		return do_abort_sequence(mci, cmd);
+
+	/* Do not wait for CMD_INHIBIT_DAT on stop commands */
+	mask = SDHCI_CMD_INHIBIT_CMD;
+	if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
+		mask |= SDHCI_CMD_INHIBIT_DATA;
+
+	/* Wait for bus idle */
+	ret = wait_on_timeout(50 * MSECOND,
+			      !(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
+	if (ret) {
+		dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
+		return -ETIMEDOUT;
+	}
+
+	if (data)
+		dev_dbg(host->mci.hw_dev, "cmd %d arg %d bcnt %d bsize %d\n",
+			cmd->cmdidx, cmd->cmdarg, data->blocks, data->blocksize);
+	else
+		dev_dbg(host->mci.hw_dev, "cmd %d arg %d\n", cmd->cmdidx, cmd->cmdarg);
+
+	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+	sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+	sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+	sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+				dma == SDHCI_NO_DMA ? false : true,
+				&command, &xfer);
+
+	sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+
+	sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+	ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+	if (ret)
+		goto error;
+
+	sdhci_read_response(&host->sdhci, cmd);
+
+	ret = sdhci_transfer_data(&host->sdhci, data, dma);
+error:
+	if (ret) {
+		sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+		sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+	}
+
+	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+	return ret;
+}
+
+static u16 dwcmshc_get_clock_divider(struct dwcmshc_host *host, u32 reqclk)
+{
+	u16 div;
+
+	for (div = 1; div < SDHCI_MAX_DIV_SPEC_300; div += 2)
+		if ((host->sdhci.max_clk / div) <= reqclk)
+			break;
+	div /= 2;
+
+	return div;
+}
+
+static void dwcmshc_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+	struct dwcmshc_host *host = priv_from_mci_host(mci);
+	u16 val;
+
+	debug("%s: clock = %u, bus-width = %d, timing = %02x\n", __func__,
+	      ios->clock, ios->bus_width, ios->timing);
+
+	/* stop clock */
+	sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+	if (!ios->clock)
+		return;
+
+	/* enable bus power */
+	val = SDHCI_BUS_VOLTAGE_330;
+	sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL, val | SDHCI_BUS_POWER_EN);
+	udelay(400);
+
+	/* set bus width */
+	sdhci_set_bus_width(&host->sdhci, ios->bus_width);
+
+	val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+	if (ios->clock > 26000000)
+		val |= SDHCI_CTRL_HISPD;
+	else
+		val &= ~SDHCI_CTRL_HISPD;
+	sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
+
+	/* set bus clock */
+	sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+	val = dwcmshc_get_clock_divider(host, ios->clock);
+	val = SDHCI_CLOCK_INT_EN | SDHCI_FREQ_SEL(val) | ((val & 0x300) >> 2);
+	sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+	/* wait for internal clock stable */
+	if (wait_on_timeout(20 * MSECOND,
+			sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL)
+			& SDHCI_CLOCK_INT_STABLE)) {
+		dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n");
+		return;
+	}
+
+	/* enable bus clock */
+	sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_CLOCK_CARD_EN);
+}
+
+static int dwcmshc_mci_init(struct mci_host *mci, struct device *dev)
+{
+	struct dwcmshc_host *host = priv_from_mci_host(mci);
+	u16 ctrl2;
+
+	/* reset mshci controller */
+	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
+
+	/* wait for reset completion */
+	if (wait_on_timeout(100 * MSECOND,
+			    (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET)
+			     & SDHCI_RESET_ALL) == 0)) {
+		dev_err(dev, "SDHCI reset timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	sdhci_write16(&host->sdhci, SDHCI_INT_ERROR_ENABLE, ~0);
+	sdhci_write16(&host->sdhci, SDHCI_INT_ENABLE, ~0);
+	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+	sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0);
+
+	sdhci_enable_v4_mode(&host->sdhci);
+
+	dev_dbg(host->mci.hw_dev, "host version4: %s\n",
+		ctrl2 & SDHCI_CTRL_V4_MODE ? "enabled" : "disabled");
+
+	return 0;
+}
+
+static int dwcmshc_detect(struct device *dev)
+{
+	struct dwcmshc_host *host = dev->priv;
+
+	return mci_detect_card(&host->mci);
+}
+
+static int dwcmshc_mci_card_present(struct mci_host *mci)
+{
+	struct dwcmshc_host *host = priv_from_mci_host(mci);
+	u32 pstate;
+
+	pstate = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
+	return pstate & SDHCI_CARD_PRESENT;
+}
+
+static void dwcmshc_set_dma_mask(struct device *dev)
+{
+	struct dwcmshc_host *host = dev->priv;
+
+	if (host->sdhci.caps & SDHCI_CAN_64BIT_V4)
+		dma_set_mask(dev, DMA_BIT_MASK(64));
+	else
+		dma_set_mask(dev, DMA_BIT_MASK(32));
+}
+
+static int dwcmshc_probe(struct device *dev)
+{
+	struct dwcmshc_host *host;
+	struct resource *iores;
+	struct mci_host *mci;
+	struct clk *clk;
+	int ret;
+
+	host = xzalloc(sizeof(*host));
+	mci = &host->mci;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores)) {
+		ret = PTR_ERR(iores);
+		goto err_mem_req;
+	}
+
+	clk = clk_get(dev, NULL);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		goto err_clk_get;
+	}
+	clk_enable(clk);
+
+	host->sdhci.base = IOMEM(iores->start);
+	host->sdhci.mci = mci;
+	host->sdhci.max_clk = clk_get_rate(clk);
+
+	mci->hw_dev = dev;
+	mci->init = dwcmshc_mci_init;
+	mci->set_ios = dwcmshc_mci_set_ios;
+	mci->send_cmd = dwcmshc_mci_send_cmd;
+	mci->card_present = dwcmshc_mci_card_present;
+
+	mci_of_parse(&host->mci);
+
+	/* Enable host_version4 */
+	sdhci_enable_v4_mode(&host->sdhci);
+	sdhci_setup_host(&host->sdhci);
+
+	mci->max_req_size = 0x8000;
+	mci->f_max = clk_get_rate(clk);
+	mci->f_min = mci->f_max / SDHCI_MAX_DIV_SPEC_300;
+
+	dev->priv = host;
+	dev->detect = dwcmshc_detect;
+
+	dwcmshc_set_dma_mask(dev);
+
+	dev_dbg(host->mci.hw_dev, "host controller version: %u\n",
+		host->sdhci.version);
+
+	ret = mci_register(&host->mci);
+	if (ret)
+		goto err_register;
+
+	return ret;
+
+err_register:
+	clk_disable(clk);
+	clk_put(clk);
+err_clk_get:
+	release_region(iores);
+err_mem_req:
+	free(host);
+
+	return ret;
+}
+
+static struct of_device_id dwcmshc_dt_ids[] = {
+	{ .compatible = "snps,dwcmshc-sdhci", },
+	{ }
+};
+
+static struct driver dwcmshc_driver = {
+	.name = "dwcmshc-sdhci",
+	.probe = dwcmshc_probe,
+	.of_compatible = DRV_OF_COMPAT(dwcmshc_dt_ids),
+};
+device_platform_driver(dwcmshc_driver);
-- 
2.41.0




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

* Re: [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver
  2023-08-16  9:39 ` [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver Jules Maselbas
@ 2023-08-18  6:27   ` Sascha Hauer
  2023-08-18 11:37     ` Jules Maselbas
  0 siblings, 1 reply; 9+ messages in thread
From: Sascha Hauer @ 2023-08-18  6:27 UTC (permalink / raw)
  To: Jules Maselbas; +Cc: barebox

Hi Jules,

Some small things inline.


On Wed, Aug 16, 2023 at 11:39:45AM +0200, Jules Maselbas wrote:
> +#include "sdhci.h"
> +
> +#define CARD_STATUS_MASK (0x1e00)
> +#define CARD_STATUS_TRAN (4 << 9)
> +static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
> +				struct mci_data *data);
> +
> +struct dwcmshc_host {
> +	struct mci_host mci;
> +	struct sdhci sdhci;
> +	unsigned int in_abort_sequence;
> +};
> +
> +#define priv_from_mci_host(h) container_of(h, struct dwcmshc_host, mci)

Writing this as static inline function immediately makes the type of 'h'
clear.

> +
> +static void mci_setup_cmd(struct mci_cmd *p, unsigned int cmd, unsigned int arg,
> +			  unsigned int response)
> +{
> +	p->cmdidx = cmd;
> +	p->cmdarg = arg;
> +	p->resp_type = response;
> +}
> +
> +static int do_abort_sequence(struct mci_host *mci, struct mci_cmd *current_cmd)
> +{
> +	int ret = 0;
> +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> +	struct mci_cmd cmd;
> +	u64 start;
> +
> +	host->in_abort_sequence = 1;
> +
> +	mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
> +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> +	if (ret) {
> +		dev_err(host->mci.hw_dev, "Abort failed at first cmd12!\n");
> +		goto out;
> +	}
> +	mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
> +		      MMC_RSP_R1);
> +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> +	if (ret) {
> +		dev_err(host->mci.hw_dev, "Abort failed at first cmd13!\n");
> +		goto out;
> +	}
> +
> +	if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN)
> +		goto out; /* All is OK! */
> +
> +	mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
> +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> +	if (ret) {
> +		dev_err(host->mci.hw_dev, "Abort failed at second cmd12!\n");
> +		goto out;
> +	}
> +
> +	mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
> +		      MMC_RSP_R1);
> +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> +	if (ret) {
> +		dev_err(host->mci.hw_dev, "Abort failed at second cmd13!\n");
> +		goto out;
> +	}
> +
> +	if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN) {
> +		goto out; /* All is OK! */
> +	} else {
> +		dev_err(host->mci.hw_dev,
> +			"Abort sequence failed to put card in TRAN state!\n");
> +		ret = 1;

This should be an error code.

> +		goto out;
> +	}
> +
> +out:
> +	/* Perform SW reset if in abort sequence */
> +	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET,
> +		       SDHCI_RESET_DATA | SDHCI_RESET_CMD);
> +	start = get_time_ns();
> +	while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) != 0) {
> +		if (is_timeout(start, 50 * MSECOND)) {
> +			dev_err(host->mci.hw_dev,
> +				"SDHCI data reset timeout\n");
> +			break;
> +		}
> +	}
> +	host->in_abort_sequence = 0;
> +	return ret;
> +}
> +
> +static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
> +				struct mci_data *data)
> +{
> +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> +	dma_addr_t dma = SDHCI_NO_DMA;
> +	u32 mask, command, xfer;
> +	int ret;
> +
> +	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION
> +	    && host->in_abort_sequence == 0)
> +		return do_abort_sequence(mci, cmd);

You could factor the rest of this function out to some dwcmshc_mci_do_send_cmd()
and call it from here and do_abort_sequence().

> +
> +	/* Do not wait for CMD_INHIBIT_DAT on stop commands */
> +	mask = SDHCI_CMD_INHIBIT_CMD;
> +	if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
> +		mask |= SDHCI_CMD_INHIBIT_DATA;
> +
> +	/* Wait for bus idle */
> +	ret = wait_on_timeout(50 * MSECOND,
> +			      !(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
> +	if (ret) {
> +		dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (data)
> +		dev_dbg(host->mci.hw_dev, "cmd %d arg %d bcnt %d bsize %d\n",
> +			cmd->cmdidx, cmd->cmdarg, data->blocks, data->blocksize);
> +	else
> +		dev_dbg(host->mci.hw_dev, "cmd %d arg %d\n", cmd->cmdidx, cmd->cmdarg);
> +
> +	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
> +
> +	sdhci_setup_data_dma(&host->sdhci, data, &dma);
> +
> +	sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
> +
> +	sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
> +				dma == SDHCI_NO_DMA ? false : true,
> +				&command, &xfer);
> +
> +	sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
> +
> +	sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
> +	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
> +
> +	ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
> +	if (ret)
> +		goto error;
> +
> +	sdhci_read_response(&host->sdhci, cmd);
> +
> +	ret = sdhci_transfer_data(&host->sdhci, data, dma);
> +error:
> +	if (ret) {
> +		sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
> +		sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
> +	}
> +
> +	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
> +	return ret;
> +}
> +
> +static void dwcmshc_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
> +{
> +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> +	u16 val;
> +
> +	debug("%s: clock = %u, bus-width = %d, timing = %02x\n", __func__,
> +	      ios->clock, ios->bus_width, ios->timing);

dev_dbg()

> +static int dwcmshc_mci_init(struct mci_host *mci, struct device *dev)
> +{
> +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> +	u16 ctrl2;
> +
> +	/* reset mshci controller */
> +	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
> +
> +	/* wait for reset completion */
> +	if (wait_on_timeout(100 * MSECOND,
> +			    (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET)
> +			     & SDHCI_RESET_ALL) == 0)) {
> +		dev_err(dev, "SDHCI reset timeout\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	sdhci_write16(&host->sdhci, SDHCI_INT_ERROR_ENABLE, ~0);
> +	sdhci_write16(&host->sdhci, SDHCI_INT_ENABLE, ~0);
> +	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
> +	sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0);
> +
> +	sdhci_enable_v4_mode(&host->sdhci);
> +
> +	dev_dbg(host->mci.hw_dev, "host version4: %s\n",
> +		ctrl2 & SDHCI_CTRL_V4_MODE ? "enabled" : "disabled");
> +
> +	return 0;
> +}
> +

[...]

> +static int dwcmshc_probe(struct device *dev)
> +{
> +	struct dwcmshc_host *host;
> +	struct resource *iores;
> +	struct mci_host *mci;
> +	struct clk *clk;
> +	int ret;
> +
> +	host = xzalloc(sizeof(*host));
> +	mci = &host->mci;
> +
> +	iores = dev_request_mem_resource(dev, 0);
> +	if (IS_ERR(iores)) {
> +		ret = PTR_ERR(iores);
> +		goto err_mem_req;
> +	}
> +
> +	clk = clk_get(dev, NULL);
> +	if (IS_ERR(clk)) {
> +		ret = PTR_ERR(clk);
> +		goto err_clk_get;
> +	}
> +	clk_enable(clk);
> +
> +	host->sdhci.base = IOMEM(iores->start);
> +	host->sdhci.mci = mci;
> +	host->sdhci.max_clk = clk_get_rate(clk);
> +
> +	mci->hw_dev = dev;
> +	mci->init = dwcmshc_mci_init;
> +	mci->set_ios = dwcmshc_mci_set_ios;
> +	mci->send_cmd = dwcmshc_mci_send_cmd;
> +	mci->card_present = dwcmshc_mci_card_present;
> +
> +	mci_of_parse(&host->mci);
> +
> +	/* Enable host_version4 */
> +	sdhci_enable_v4_mode(&host->sdhci);

This is also called from dwcmshc_mci_init(). Do we need both?

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



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

* Re: [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver
  2023-08-18  6:27   ` Sascha Hauer
@ 2023-08-18 11:37     ` Jules Maselbas
  0 siblings, 0 replies; 9+ messages in thread
From: Jules Maselbas @ 2023-08-18 11:37 UTC (permalink / raw)
  To: barebox, Sascha Hauer, Jules Maselbas



On August 18, 2023 8:27:48 AM GMT+02:00, Sascha Hauer <sha@pengutronix.de> wrote:
> Hi Jules,
> 
> Some small things inline.
> 
> 
> On Wed, Aug 16, 2023 at 11:39:45AM +0200, Jules Maselbas wrote:
> > +#include "sdhci.h"
> > +
> > +#define CARD_STATUS_MASK (0x1e00)
> > +#define CARD_STATUS_TRAN (4 << 9)
> > +static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
> > +				struct mci_data *data);
> > +
> > +struct dwcmshc_host {
> > +	struct mci_host mci;
> > +	struct sdhci sdhci;
> > +	unsigned int in_abort_sequence;
> > +};
> > +
> > +#define priv_from_mci_host(h) container_of(h, struct dwcmshc_host, mci)
> 
> Writing this as static inline function immediately makes the type of 'h'
> clear.
ack

> 
> > +
> > +static void mci_setup_cmd(struct mci_cmd *p, unsigned int cmd, unsigned int arg,
> > +			  unsigned int response)
> > +{
> > +	p->cmdidx = cmd;
> > +	p->cmdarg = arg;
> > +	p->resp_type = response;
> > +}
> > +
> > +static int do_abort_sequence(struct mci_host *mci, struct mci_cmd *current_cmd)
> > +{
> > +	int ret = 0;
> > +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> > +	struct mci_cmd cmd;
> > +	u64 start;
> > +
> > +	host->in_abort_sequence = 1;
> > +
> > +	mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
> > +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> > +	if (ret) {
> > +		dev_err(host->mci.hw_dev, "Abort failed at first cmd12!\n");
> > +		goto out;
> > +	}
> > +	mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
> > +		      MMC_RSP_R1);
> > +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> > +	if (ret) {
> > +		dev_err(host->mci.hw_dev, "Abort failed at first cmd13!\n");
> > +		goto out;
> > +	}
> > +
> > +	if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN)
> > +		goto out; /* All is OK! */
> > +
> > +	mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
> > +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> > +	if (ret) {
> > +		dev_err(host->mci.hw_dev, "Abort failed at second cmd12!\n");
> > +		goto out;
> > +	}
> > +
> > +	mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
> > +		      MMC_RSP_R1);
> > +	ret = dwcmshc_mci_send_cmd(mci, &cmd, NULL);
> > +	if (ret) {
> > +		dev_err(host->mci.hw_dev, "Abort failed at second cmd13!\n");
> > +		goto out;
> > +	}
> > +
> > +	if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN) {
> > +		goto out; /* All is OK! */
> > +	} else {
> > +		dev_err(host->mci.hw_dev,
> > +			"Abort sequence failed to put card in TRAN state!\n");
> > +		ret = 1;
> 
> This should be an error code.
ack, not sure which error code is the most appropriated here, I am going to use EPROTO or EIO
> 
> > +		goto out;
> > +	}
> > +
> > +out:
> > +	/* Perform SW reset if in abort sequence */
> > +	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET,
> > +		       SDHCI_RESET_DATA | SDHCI_RESET_CMD);
> > +	start = get_time_ns();
> > +	while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) != 0) {
> > +		if (is_timeout(start, 50 * MSECOND)) {
> > +			dev_err(host->mci.hw_dev,
> > +				"SDHCI data reset timeout\n");
> > +			break;
> > +		}
> > +	}
> > +	host->in_abort_sequence = 0;
> > +	return ret;
> > +}
> > +
> > +static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
> > +				struct mci_data *data)
> > +{
> > +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> > +	dma_addr_t dma = SDHCI_NO_DMA;
> > +	u32 mask, command, xfer;
> > +	int ret;
> > +
> > +	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION
> > +	    && host->in_abort_sequence == 0)
> > +		return do_abort_sequence(mci, cmd);
> 
> You could factor the rest of this function out to some dwcmshc_mci_do_send_cmd()
> and call it from here and do_abort_sequence().
ack

> 
> > +
> > +	/* Do not wait for CMD_INHIBIT_DAT on stop commands */
> > +	mask = SDHCI_CMD_INHIBIT_CMD;
> > +	if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
> > +		mask |= SDHCI_CMD_INHIBIT_DATA;
> > +
> > +	/* Wait for bus idle */
> > +	ret = wait_on_timeout(50 * MSECOND,
> > +			      !(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
> > +	if (ret) {
> > +		dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	if (data)
> > +		dev_dbg(host->mci.hw_dev, "cmd %d arg %d bcnt %d bsize %d\n",
> > +			cmd->cmdidx, cmd->cmdarg, data->blocks, data->blocksize);
> > +	else
> > +		dev_dbg(host->mci.hw_dev, "cmd %d arg %d\n", cmd->cmdidx, cmd->cmdarg);
> > +
> > +	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
> > +
> > +	sdhci_setup_data_dma(&host->sdhci, data, &dma);
> > +
> > +	sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
> > +
> > +	sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
> > +				dma == SDHCI_NO_DMA ? false : true,
> > +				&command, &xfer);
> > +
> > +	sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
> > +
> > +	sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
> > +	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
> > +
> > +	ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
> > +	if (ret)
> > +		goto error;
> > +
> > +	sdhci_read_response(&host->sdhci, cmd);
> > +
> > +	ret = sdhci_transfer_data(&host->sdhci, data, dma);
> > +error:
> > +	if (ret) {
> > +		sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
> > +		sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
> > +	}
> > +
> > +	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
> > +	return ret;
> > +}
> > +
> > +static void dwcmshc_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
> > +{
> > +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> > +	u16 val;
> > +
> > +	debug("%s: clock = %u, bus-width = %d, timing = %02x\n", __func__,
> > +	      ios->clock, ios->bus_width, ios->timing);
> 
> dev_dbg()
ack

> 
> > +static int dwcmshc_mci_init(struct mci_host *mci, struct device *dev)
> > +{
> > +	struct dwcmshc_host *host = priv_from_mci_host(mci);
> > +	u16 ctrl2;
> > +
> > +	/* reset mshci controller */
> > +	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
> > +
> > +	/* wait for reset completion */
> > +	if (wait_on_timeout(100 * MSECOND,
> > +			    (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET)
> > +			     & SDHCI_RESET_ALL) == 0)) {
> > +		dev_err(dev, "SDHCI reset timeout\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	sdhci_write16(&host->sdhci, SDHCI_INT_ERROR_ENABLE, ~0);
> > +	sdhci_write16(&host->sdhci, SDHCI_INT_ENABLE, ~0);
> > +	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
> > +	sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0);
> > +
> > +	sdhci_enable_v4_mode(&host->sdhci);
> > +
> > +	dev_dbg(host->mci.hw_dev, "host version4: %s\n",
> > +		ctrl2 & SDHCI_CTRL_V4_MODE ? "enabled" : "disabled");
> > +
> > +	return 0;
> > +}
> > +
> 
> [...]
> 
> > +static int dwcmshc_probe(struct device *dev)
> > +{
> > +	struct dwcmshc_host *host;
> > +	struct resource *iores;
> > +	struct mci_host *mci;
> > +	struct clk *clk;
> > +	int ret;
> > +
> > +	host = xzalloc(sizeof(*host));
> > +	mci = &host->mci;
> > +
> > +	iores = dev_request_mem_resource(dev, 0);
> > +	if (IS_ERR(iores)) {
> > +		ret = PTR_ERR(iores);
> > +		goto err_mem_req;
> > +	}
> > +
> > +	clk = clk_get(dev, NULL);
> > +	if (IS_ERR(clk)) {
> > +		ret = PTR_ERR(clk);
> > +		goto err_clk_get;
> > +	}
> > +	clk_enable(clk);
> > +
> > +	host->sdhci.base = IOMEM(iores->start);
> > +	host->sdhci.mci = mci;
> > +	host->sdhci.max_clk = clk_get_rate(clk);
> > +
> > +	mci->hw_dev = dev;
> > +	mci->init = dwcmshc_mci_init;
> > +	mci->set_ios = dwcmshc_mci_set_ios;
> > +	mci->send_cmd = dwcmshc_mci_send_cmd;
> > +	mci->card_present = dwcmshc_mci_card_present;
> > +
> > +	mci_of_parse(&host->mci);
> > +
> > +	/* Enable host_version4 */
> > +	sdhci_enable_v4_mode(&host->sdhci);
> 
> This is also called from dwcmshc_mci_init(). Do we need both?
IIRC sdhci_enable_v4_mode needs to be called before sdhci_read_caps which is called by sdhci_setup_host (called during probe),
and I think it also needs to be called after the controller is reset.

> 
> Sascha
> 



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

end of thread, other threads:[~2023-08-18 11:39 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-16  9:39 [PATCH v2 1/7] mci: sdhci: Set 8-bit host caps Jules Maselbas
2023-08-16  9:39 ` [PATCH v2 2/7] mci: sdhci: Add registers defines Jules Maselbas
2023-08-16  9:39 ` [PATCH v2 3/7] mci: sdhci: Actually return the error code instead of 0 Jules Maselbas
2023-08-16  9:39 ` [PATCH v2 4/7] mci: sdhci: Add sd host v4 mode Jules Maselbas
2023-08-16  9:39 ` [PATCH v2 5/7] mci: sdhci: Add 64-bit DMA addressing suport for V4 mode Jules Maselbas
2023-08-16  9:39 ` [PATCH v2 6/7] mci: sdhci: Force DMA update to the next block boundary Jules Maselbas
2023-08-16  9:39 ` [PATCH v2 7/7] mci: Add dwcmshc-sdhci driver Jules Maselbas
2023-08-18  6:27   ` Sascha Hauer
2023-08-18 11:37     ` Jules Maselbas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox