* [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