[PATCH 00/42] mmc: dw_mmc: Enable eMMC on E850-96 board

Bring 64-bit support to dw_mmc core and Exynos dw_mmc drivers, and enable it on E850-96 board. Additionally do some related cleanups and device tree updates.
64-bit version of DesignWare MMC can be often found on modern ARM64 chips. It's different from its older 32-bit version (which is already implemented in U-Boot): some new registers were added, existing register addresses are changed, DMA descriptor table is different, etc.
Exynos DW MMC driver was updated too: ARM64 Exynos chips implement their clock drivers using CCF framework, pinmux configuration is done at startup in DM capable pinctrl drivers, device tree properties were changed w.r.t. their upstream (Linux kernel) counterparts. CLKSEL register address is also different on 64-bit Exynos platforms.
The patch series was tested on E850-96 board by running mainline Linux kernel with Debian rootfs from eMMC ("boot" partition) with these commands:
8<-------------------------------------------------------------------->8 env set fdtaddr 0x8a000000 env set bootaddr 0x94000000 env set boot_kerneladdr 0x94000800 part start mmc 0 boot boot_start part size mmc 0 boot boot_size mmc read $bootaddr $boot_start $boot_size abootimg addr $bootaddr abootimg get dtb --index=0 dtb_start dtb_size cp.b $dtb_start $fdtaddr $dtb_size fdt addr $fdtaddr 0x100000 cp.b $boot_kerneladdr $loadaddr 2aaaa00 env set bootargs console=ttySAC0,115200n8 printk.devkmsg=on \ root=/dev/mmcblk0p12 rootwait rw booti $loadaddr - $fdtaddr 8<-------------------------------------------------------------------->8
For E850-96 eMMC to function properly in DDR mode, the pending patch [1] has to be applied. Otherwise it won't be possible for exynos_dw_mmc driver to change CIU clock rate from 200 MHz up to 400 MHz, and MMC will fall back to SDR mode, which makes eMMC throughput twice as slower. With patch [1] applied, 'mmc info' reports this:
Bus Speed: 52000000 Mode: MMC DDR52 (52MHz)
and 'clk dump' shows CIU clock rate to be ~400 MHz (after first mmc operation, e.g. 'part start' or 'mmc read'):
399750000 gout_mmc_embd_sdclkin
Which makes sense, because dw_mmc requests to set CCLKIN = 52 MHz (with DDR/8-bit mode), and exynos_dw_mmc tries to set CIU clock rate to:
SDCLKIN = 2 * ciu_div * CCLKIN = 2 * 4 * 52 MHz = 416 MHz,
and the closest possible value the clock driver can set is 399.75 MHz, which works just fine.
For Exynos4 and Exynos5 (ARM32) boards, this patch series was only build tested (manually and with buildman). The build is clean (no errors or warnings), but I don't have any Exynos4/Exynos5 boards at my disposal, so I can't actually verify MMC operation there.
[1] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html
Sam Protsenko (42): mmc: dw_mmc: Remove common.h mmc: dw_mmc: Remove unused version field from struct dwmci_host mmc: dw_mmc: Move struct idmac to dw_mmc.c mmc: dw_mmc: Extract waiting for data busy into a separate routine mmc: dw_mmc: Extract FIFO init into a separate routine mmc: dw_mmc: Extract clock on/off code into a separate routine mmc: dw_mmc: Extract FIFO data transfer into a separate routine mmc: dw_mmc: Extract DMA transfer handling code into a separate routine mmc: dw_mmc: Extract setting the DMA descriptor into a separate routine mmc: dw_mmc: Improve 32-bit IDMAC descriptor namings mmc: dw_mmc: Add support for 64-bit IDMAC mmc: dw_mmc: Replace fifoth_val property with fifo-depth mmc: dw_mmc: Fix kernel-doc comments in dwmmc.h mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options mmc: dw_mmc: Improve coding style arm: dts: exynos: Add upstream DW MMC properties to all Exynos dts dt-bindings: exynos: Update bindings doc for DW MMC controller arm: exynos: Add header guard for dwmmc.h mmc: exynos_dw_mmc: Fix obtaining the base address of controller mmc: exynos_dw_mmc: Fix getting private data in exynos_dwmci_board_init() mmc: exynos_dw_mmc: Don't call pinmux functions on ARM64 chips mmc: exynos_dw_mmc: Obtain and use CIU clock via CCF API mmc: exynos_dw_mmc: Use .of_to_plat for device tree parsing mmc: exynos_dw_mmc: Convert to use livetree API mmc: exynos_dw_mmc: Read upstream SDR timing properties mmc: exynos_dw_mmc: Abstract CLKSEL register mmc: exynos_dw_mmc: Refactor fixed CIU clock divider mmc: exynos_dw_mmc: Read common bus-width property mmc: exynos_dw_mmc: Read common clock-frequency property mmc: exynos_dw_mmc: Move quirks from struct dwmci_host to chip data mmc: exynos_dw_mmc: Read and use DDR timing when available mmc: exynos_dw_mmc: Set requested freq in get_mmc_clk() callback mmc: exynos_dw_mmc: Add support for ARM64 Exynos chips mmc: exynos_dw_mmc: Remove common.h mmc: exynos_dw_mmc: Use CONFIG_IS_ENABLED() to check config options mmc: exynos_dw_mmc: Pull all init code into probe function mmc: exynos_dw_mmc: Don't call dwmci_setup_cfg() after add_dwmci() mmc: exynos_dw_mmc: Use dev->name as driver's displayed name mmc: exynos_dw_mmc: Improve coding style arm: dts: exynos: Remove outdated DW MMC properties in all Exynos dts configs: e850-96: Enable MMC doc: samsung: Mention enabled eMMC in E850-96 board doc
arch/arm/dts/exynos4210-origen.dts | 3 +- arch/arm/dts/exynos4210-trats.dts | 6 +- arch/arm/dts/exynos4210-universal_c210.dts | 6 +- arch/arm/dts/exynos4412-odroid.dts | 14 +- arch/arm/dts/exynos4412-trats2.dts | 20 +- arch/arm/dts/exynos5250-arndale.dts | 10 +- arch/arm/dts/exynos5250-smdk5250.dts | 13 +- arch/arm/dts/exynos5250-snow.dts | 13 +- arch/arm/dts/exynos5250-spring.dts | 7 +- arch/arm/dts/exynos5420-smdk5420.dts | 13 +- arch/arm/dts/exynos5422-odroidxu3.dts | 4 +- arch/arm/dts/exynos54xx.dtsi | 13 +- arch/arm/mach-exynos/include/mach/dwmmc.h | 40 +- configs/e850-96_defconfig | 10 +- doc/board/samsung/e850-96.rst | 5 +- doc/device-tree-bindings/exynos/dwmmc.txt | 46 +- drivers/mmc/ca_dw_mmc.c | 2 +- drivers/mmc/dw_mmc.c | 535 +++++++++++++-------- drivers/mmc/exynos_dw_mmc.c | 362 +++++++++----- drivers/mmc/ftsdc010_mci.h | 1 - drivers/mmc/hi6220_dw_mmc.c | 7 +- drivers/mmc/nexell_dw_mmc.c | 5 +- drivers/mmc/rockchip_dw_mmc.c | 5 +- drivers/mmc/s5p_sdhci.c | 2 +- drivers/mmc/snps_dw_mmc.c | 6 +- drivers/mmc/socfpga_dw_mmc.c | 4 +- include/dwmmc.h | 246 +++++----- 27 files changed, 858 insertions(+), 540 deletions(-)

common.h header is marked for removal treewide and shouldn't be used. Remove it from DW MMC driver.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index e1036641452a..e6107c770fe3 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -6,7 +6,6 @@ */
#include <bouncebuf.h> -#include <common.h> #include <cpu_func.h> #include <errno.h> #include <log.h>

Hi Sam,
On 5/23/24 1:30 AM, Sam Protsenko wrote:
common.h header is marked for removal treewide and shouldn't be used. Remove it from DW MMC driver.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Nobody seems to use it, so just remove it.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- include/dwmmc.h | 1 - 1 file changed, 1 deletion(-)
diff --git a/include/dwmmc.h b/include/dwmmc.h index 136a95b8cdb6..39024fb38aaa 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -163,7 +163,6 @@ struct dwmci_host { void *ioaddr; unsigned int quirks; unsigned int caps; - unsigned int version; unsigned int clock; unsigned int bus_hz; unsigned int div;

Hi Sam,
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Nobody seems to use it, so just remove it.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

struct idmac is only used in dw_mmc.c, so move it there from dwmmc.h to avoid cluttering the interface in the header.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 7 +++++++ include/dwmmc.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index e6107c770fe3..9d668b3d8813 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -20,6 +20,13 @@
#define PAGE_SIZE 4096
+struct dwmci_idmac { + u32 flags; + u32 cnt; + u32 addr; + u32 next_addr; +} __aligned(ARCH_DMA_MINALIGN); + static int dwmci_wait_reset(struct dwmci_host *host, u32 value) { unsigned long timeout = 1000; diff --git a/include/dwmmc.h b/include/dwmmc.h index 39024fb38aaa..7e4acf096dce 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -198,13 +198,6 @@ struct dwmci_host { bool fifo_mode; };
-struct dwmci_idmac { - u32 flags; - u32 cnt; - u32 addr; - u32 next_addr; -} __aligned(ARCH_DMA_MINALIGN); - static inline void dwmci_writel(struct dwmci_host *host, int reg, u32 val) { writel(val, host->ioaddr + reg);

Hi Sam,
On 5/23/24 1:30 AM, Sam Protsenko wrote:
struct idmac is only used in dw_mmc.c, so move it there from dwmmc.h to avoid cluttering the interface in the header.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Waiting for data busy is a logically separate operation and should be implemented as a separate routine. Follow Linux kernel example and extract it from dwmci_send_cmd(). This way it doesn't clutter dwmci_send_cmd() function, and can be reused later in other cases.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 9d668b3d8813..a82f6a96db39 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -246,6 +246,21 @@ static int dwmci_set_transfer_mode(struct dwmci_host *host, return mode; }
+static void dwmci_wait_while_busy(struct dwmci_host *host, struct mmc_cmd *cmd) +{ + unsigned int timeout = 500; /* msec */ + ulong start; + + start = get_timer(0); + while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { + if (get_timer(start) > timeout) { + debug("%s: Timeout on data busy, continue anyway\n", + __func__); + break; + } + } +} + #ifdef CONFIG_DM_MMC static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) @@ -260,19 +275,11 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, data ? DIV_ROUND_UP(data->blocks, 8) : 0); int ret = 0, flags = 0, i; - unsigned int timeout = 500; u32 retry = 100000; u32 mask, ctrl; - ulong start = get_timer(0); struct bounce_buffer bbstate;
- while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { - if (get_timer(start) > timeout) { - debug("%s: Timeout on data busy, continue anyway\n", __func__); - break; - } - } - + dwmci_wait_while_busy(host, cmd); dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
if (data) {

Hi Sam,
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Waiting for data busy is a logically separate operation and should be implemented as a separate routine. Follow Linux kernel example and extract it from dwmci_send_cmd(). This way it doesn't clutter dwmci_send_cmd() function, and can be reused later in other cases.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Move FIFO threshold initialization into a separate function to make dwmci_init() more readable.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a82f6a96db39..cbfb8d3b8683 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -540,6 +540,20 @@ static int dwmci_set_ios(struct mmc *mmc) return 0; }
+static void dwmci_init_fifo(struct dwmci_host *host) +{ + if (!host->fifoth_val) { + u32 fifo_size; + + fifo_size = dwmci_readl(host, DWMCI_FIFOTH); + fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; + host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | + TX_WMARK(fifo_size / 2); + } + + dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); +} + static int dwmci_init(struct mmc *mmc) { struct dwmci_host *host = mmc->priv; @@ -564,16 +578,7 @@ static int dwmci_init(struct mmc *mmc)
dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); - - if (!host->fifoth_val) { - uint32_t fifo_size; - - fifo_size = dwmci_readl(host, DWMCI_FIFOTH); - fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; - host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | - TX_WMARK(fifo_size / 2); - } - dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); + dwmci_init_fifo(host);
dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0);

Hi Sam,
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Move FIFO threshold initialization into a separate function to make dwmci_init() more readable.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Extract clock control code into a separate routine to avoid code duplication in dwmci_setup_bus().
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 60 ++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 27 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index cbfb8d3b8683..40b9b034f793 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -412,11 +412,33 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; }
-static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) +static int dwmci_control_clken(struct dwmci_host *host, bool on) { - u32 div, status; + const u32 val = on ? DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR : 0; + const u32 cmd_only_clk = DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK; int timeout = 10000; + u32 status; + + dwmci_writel(host, DWMCI_CLKENA, val); + + /* Inform CIU */ + dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_START | cmd_only_clk); + do { + status = dwmci_readl(host, DWMCI_CMD); + if (timeout-- < 0) { + debug("%s: Timeout!\n", __func__); + return -ETIMEDOUT; + } + } while (status & DWMCI_CMD_START); + + return 0; +} + +static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) +{ + u32 div; unsigned long sclk; + int ret;
if ((freq == host->clock) || (freq == 0)) return 0; @@ -439,35 +461,19 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else div = DIV_ROUND_UP(sclk, 2 * freq);
- dwmci_writel(host, DWMCI_CLKENA, 0); + /* Disable clock */ dwmci_writel(host, DWMCI_CLKSRC, 0); + ret = dwmci_control_clken(host, false); + if (ret) + return ret;
+ /* Set clock to desired speed */ dwmci_writel(host, DWMCI_CLKDIV, div); - dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | - DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
- do { - status = dwmci_readl(host, DWMCI_CMD); - if (timeout-- < 0) { - debug("%s: Timeout!\n", __func__); - return -ETIMEDOUT; - } - } while (status & DWMCI_CMD_START); - - dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | - DWMCI_CLKEN_LOW_PWR); - - dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | - DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); - - timeout = 10000; - do { - status = dwmci_readl(host, DWMCI_CMD); - if (timeout-- < 0) { - debug("%s: Timeout!\n", __func__); - return -ETIMEDOUT; - } - } while (status & DWMCI_CMD_START); + /* Enable clock */ + ret = dwmci_control_clken(host, true); + if (ret) + return ret;
host->clock = freq;

Hi Sam,
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Extract clock control code into a separate routine to avoid code duplication in dwmci_setup_bus().
No functional change.
There are some differences though.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
drivers/mmc/dw_mmc.c | 60 ++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 27 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index cbfb8d3b8683..40b9b034f793 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -412,11 +412,33 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; }
-static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) +static int dwmci_control_clken(struct dwmci_host *host, bool on) {
- u32 div, status;
- const u32 val = on ? DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR : 0;
- const u32 cmd_only_clk = DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK; int timeout = 10000;
- u32 status;
- dwmci_writel(host, DWMCI_CLKENA, val);
- /* Inform CIU */
- dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_START | cmd_only_clk);
- do {
status = dwmci_readl(host, DWMCI_CMD);
if (timeout-- < 0) {
debug("%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
- } while (status & DWMCI_CMD_START);
- return 0;
+}
+static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) +{
u32 div; unsigned long sclk;
int ret;
if ((freq == host->clock) || (freq == 0)) return 0;
@@ -439,35 +461,19 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else div = DIV_ROUND_UP(sclk, 2 * freq);
- dwmci_writel(host, DWMCI_CLKENA, 0);
- /* Disable clock */ dwmci_writel(host, DWMCI_CLKSRC, 0);
- ret = dwmci_control_clken(host, false);
Here, CLKENA and CLKSRC are swapped. The kernel still calls CLKENA before CLKSRC, is this really safe? (I have no documentation so cannot tell). E.g., we could have CLKSRC register be a way to select the clock parent/source, 0 could be 24MHz crystal for example, so switching to this new parent before disabling the clock output would likely result in glitches?
if (ret)
return ret;
/* Set clock to desired speed */ dwmci_writel(host, DWMCI_CLKDIV, div);
Same here, CLKDIV is set now after the CIU is informed, is this an issue? We may want to set the clock speed before we enable the clock again. Right now it's setting the desired speed while disabled, inform the CIU, enable the clock, inform the CIU. This now does, disable the clock, inform the CIU, set the desired speed, enable the clock, inform the CIU. We may need to wait for the clock to stabilize before enabling it? Again, just making guesses, no documentation for me here :/
dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
do {
status = dwmci_readl(host, DWMCI_CMD);
if (timeout-- < 0) {
debug("%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
} while (status & DWMCI_CMD_START);
dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE |
DWMCI_CLKEN_LOW_PWR);
dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
timeout = 10000;
do {
status = dwmci_readl(host, DWMCI_CMD);
if (timeout-- < 0) {
debug("%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
} while (status & DWMCI_CMD_START);
/* Enable clock */
ret = dwmci_control_clken(host, true);
if (ret)
return ret;
host->clock = freq;
Cheers, Quentin

On Thu, May 23, 2024 at 9:36 AM Quentin Schulz quentin.schulz@cherry.de wrote:
Hi Sam,
Hi Quentin,
Thanks for reviewing this series! My answers are below (inline).
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Extract clock control code into a separate routine to avoid code duplication in dwmci_setup_bus().
No functional change.
There are some differences though.
With everything discussed below, I guess it still can be argued that this patch doesn't change the functionality of the driver? Depends on definition though. Please let me know if you don't like this bit and I'll remove it in v2.
[snip]
@@ -439,35 +461,19 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else div = DIV_ROUND_UP(sclk, 2 * freq);
dwmci_writel(host, DWMCI_CLKENA, 0);
/* Disable clock */ dwmci_writel(host, DWMCI_CLKSRC, 0);
ret = dwmci_control_clken(host, false);
Here, CLKENA and CLKSRC are swapped. The kernel still calls CLKENA before CLKSRC, is this really safe? (I have no documentation so cannot tell). E.g., we could have CLKSRC register be a way to select the clock parent/source, 0 could be 24MHz crystal for example, so switching to this new parent before disabling the clock output would likely result in glitches?
Yes, you are right. In fact, Exynos850 TRM specifically mentions this. Here is how TRM describes the whole procedure for "CIU Update":
8<------------------------------------------------------------->8 To prevent a clock glitch, ensure that software stops the clock when it updates the clock divider and clock source registers.
Software uses the following sequence to update the clock divider settings: 1. Disables the clock. 2. Updates the clock divider and/or the clock source registers. 3, Enables the clock.
Code Sample to Update CIU:
// Disable clock while (rSTATUS & (1 << 9)); rCLKENA = 0x0; rCMD = 0x80202000; // inform CIU while (rCMD & 0x80000000);
// Update divider and source rCLKDIV = clock_divider; rCLKSRC = clock_source;
// Enable clock while (rSTATUS & (1 << 9)); rCLKENA= 0x1; rCMD = 0x80202000; // inform CIU while (rCMD & 0x80000000); 8<------------------------------------------------------------->8
It's an overlook on my part. And although CLKSRC reset value is 0x0 and it's only being written with 0x0 in the driver, it still has to be fixed. I'll move CLKSRC modification below CLKDIV to make it look like TRM suggests.
Linux kernel implementation also doesn't follow the sequence recommended in TRM as you can see, but it works fine because CLKSRC is always written to 0 there (and it's also 0 at reset time).
if (ret)
return ret;
/* Set clock to desired speed */ dwmci_writel(host, DWMCI_CLKDIV, div);
Same here, CLKDIV is set now after the CIU is informed, is this an issue? We may want to set the clock speed before we enable the clock again. Right now it's setting the desired speed while disabled, inform the CIU, enable the clock, inform the CIU. This now does, disable the clock, inform the CIU, set the desired speed, enable the clock, inform the CIU. We may need to wait for the clock to stabilize before enabling it? Again, just making guesses, no documentation for me here :/
In this case, my implementation is following TRM (as described above). After CLKDIV modification my code informs CIU again, inside of the dwmci_control_clken() call.
Just to be thorough, here is what TRM says about DWMCI_CMD_UPD_CLK (bit 21):
8<------------------------------------------------------------->8 ...Following are the register values transferred into the card clock domain: * CLKDIV * CLKSRC * CLKENA 8<------------------------------------------------------------->8
So you are right that each time this bit is set all 3 values are being transferred to CIU. But because that bit is set two times (before and after updating CLKDIV/CLKSRC) -- it's ok. And also this way is recommended in TRM, as stated above.
[snip]
Thanks!

Hi Sam,
On 5/30/24 2:06 AM, Sam Protsenko wrote:
On Thu, May 23, 2024 at 9:36 AM Quentin Schulz quentin.schulz@cherry.de wrote:
Hi Sam,
Hi Quentin,
Thanks for reviewing this series! My answers are below (inline).
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Extract clock control code into a separate routine to avoid code duplication in dwmci_setup_bus().
No functional change.
There are some differences though.
With everything discussed below, I guess it still can be argued that this patch doesn't change the functionality of the driver? Depends on definition though. Please let me know if you don't like this bit and I'll remove it in v2.
[snip]
@@ -439,35 +461,19 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else div = DIV_ROUND_UP(sclk, 2 * freq);
dwmci_writel(host, DWMCI_CLKENA, 0);
/* Disable clock */ dwmci_writel(host, DWMCI_CLKSRC, 0);
ret = dwmci_control_clken(host, false);
Here, CLKENA and CLKSRC are swapped. The kernel still calls CLKENA before CLKSRC, is this really safe? (I have no documentation so cannot tell). E.g., we could have CLKSRC register be a way to select the clock parent/source, 0 could be 24MHz crystal for example, so switching to this new parent before disabling the clock output would likely result in glitches?
Yes, you are right. In fact, Exynos850 TRM specifically mentions this. Here is how TRM describes the whole procedure for "CIU Update":
8<------------------------------------------------------------->8 To prevent a clock glitch, ensure that software stops the clock when it updates the clock divider and clock source registers.
Software uses the following sequence to update the clock divider settings:
- Disables the clock.
- Updates the clock divider and/or the clock source registers.
3, Enables the clock.
Code Sample to Update CIU:
// Disable clock while (rSTATUS & (1 << 9)); rCLKENA = 0x0; rCMD = 0x80202000; // inform CIU while (rCMD & 0x80000000);
// Update divider and source rCLKDIV = clock_divider; rCLKSRC = clock_source;
// Enable clock while (rSTATUS & (1 << 9)); rCLKENA= 0x1; rCMD = 0x80202000; // inform CIU while (rCMD & 0x80000000); 8<------------------------------------------------------------->8
It's an overlook on my part. And although CLKSRC reset value is 0x0 and it's only being written with 0x0 in the driver, it still has to be fixed. I'll move CLKSRC modification below CLKDIV to make it look like TRM suggests.
Did you mean "move CLKENA before CLKSRC"? Because if the clock is disabled, I don't think the order in which the parent clock and its divider are set matters?
Linux kernel implementation also doesn't follow the sequence recommended in TRM as you can see, but it works fine because CLKSRC is always written to 0 there (and it's also 0 at reset time).
OK, so wrong logic but it works because we essentially never change CLKSRC so it's virtually a no-op and thus there are no change to the parent clock when the clock is enabled, resulting in no glitch. Not sure we should ignore the improper logic though? What are you suggesting we do here?
if (ret)
return ret;
/* Set clock to desired speed */ dwmci_writel(host, DWMCI_CLKDIV, div);
Same here, CLKDIV is set now after the CIU is informed, is this an issue? We may want to set the clock speed before we enable the clock again. Right now it's setting the desired speed while disabled, inform the CIU, enable the clock, inform the CIU. This now does, disable the clock, inform the CIU, set the desired speed, enable the clock, inform the CIU. We may need to wait for the clock to stabilize before enabling it? Again, just making guesses, no documentation for me here :/
In this case, my implementation is following TRM (as described above). After CLKDIV modification my code informs CIU again, inside of the dwmci_control_clken() call.
Just to be thorough, here is what TRM says about DWMCI_CMD_UPD_CLK (bit 21):
8<------------------------------------------------------------->8 ...Following are the register values transferred into the card clock domain:
- CLKDIV
- CLKSRC
- CLKENA
8<------------------------------------------------------------->8
So you are right that each time this bit is set all 3 values are being transferred to CIU. But because that bit is set two times (before and after updating CLKDIV/CLKSRC) -- it's ok. And also this way is recommended in TRM, as stated above.
Hopefully there aren't other implementations than Exynos 850's that need to do something else :)
Aside from the improper logic, looks good to me, can you please add this information to the commit log so it's not lost in the mailing list :) ?
Thanks, Quentin

On Mon, Jun 3, 2024 at 3:52 AM Quentin Schulz quentin.schulz@cherry.de wrote:
Hi Sam,
On 5/30/24 2:06 AM, Sam Protsenko wrote:
On Thu, May 23, 2024 at 9:36 AM Quentin Schulz quentin.schulz@cherry.de wrote:
Hi Sam,
Hi Quentin,
Thanks for reviewing this series! My answers are below (inline).
On 5/23/24 1:30 AM, Sam Protsenko wrote:
Extract clock control code into a separate routine to avoid code duplication in dwmci_setup_bus().
No functional change.
There are some differences though.
With everything discussed below, I guess it still can be argued that this patch doesn't change the functionality of the driver? Depends on definition though. Please let me know if you don't like this bit and I'll remove it in v2.
[snip]
@@ -439,35 +461,19 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else div = DIV_ROUND_UP(sclk, 2 * freq);
dwmci_writel(host, DWMCI_CLKENA, 0);
/* Disable clock */ dwmci_writel(host, DWMCI_CLKSRC, 0);
ret = dwmci_control_clken(host, false);
Here, CLKENA and CLKSRC are swapped. The kernel still calls CLKENA before CLKSRC, is this really safe? (I have no documentation so cannot tell). E.g., we could have CLKSRC register be a way to select the clock parent/source, 0 could be 24MHz crystal for example, so switching to this new parent before disabling the clock output would likely result in glitches?
Yes, you are right. In fact, Exynos850 TRM specifically mentions this. Here is how TRM describes the whole procedure for "CIU Update":
8<------------------------------------------------------------->8 To prevent a clock glitch, ensure that software stops the clock when it updates the clock divider and clock source registers.
Software uses the following sequence to update the clock divider settings:
- Disables the clock.
- Updates the clock divider and/or the clock source registers.
3, Enables the clock.
Code Sample to Update CIU:
// Disable clock while (rSTATUS & (1 << 9)); rCLKENA = 0x0; rCMD = 0x80202000; // inform CIU while (rCMD & 0x80000000);
// Update divider and source rCLKDIV = clock_divider; rCLKSRC = clock_source;
// Enable clock while (rSTATUS & (1 << 9)); rCLKENA= 0x1; rCMD = 0x80202000; // inform CIU while (rCMD & 0x80000000); 8<------------------------------------------------------------->8
It's an overlook on my part. And although CLKSRC reset value is 0x0 and it's only being written with 0x0 in the driver, it still has to be fixed. I'll move CLKSRC modification below CLKDIV to make it look like TRM suggests.
Did you mean "move CLKENA before CLKSRC"? Because if the clock is disabled, I don't think the order in which the parent clock and its divider are set matters?
Yes, in v2 I'll just pull CLKSRC modification down, so it's executed after disabling the clock, as recommended in TRM. Something like this:
8<------------------------------------------------------------->8 /* Disable clock */ - dwmci_writel(host, DWMCI_CLKSRC, 0); ret = dwmci_control_clken(host, false); if (ret) return ret;
/* Set clock to desired speed */ dwmci_writel(host, DWMCI_CLKDIV, div); + dwmci_writel(host, DWMCI_CLKSRC, 0);
/* Enable clock */ ret = dwmci_control_clken(host, true); 8<------------------------------------------------------------->8
Linux kernel implementation also doesn't follow the sequence recommended in TRM as you can see, but it works fine because CLKSRC is always written to 0 there (and it's also 0 at reset time).
OK, so wrong logic but it works because we essentially never change CLKSRC so it's virtually a no-op and thus there are no change to the parent clock when the clock is enabled, resulting in no glitch. Not sure we should ignore the improper logic though? What are you suggesting we do here?
Yes, exactly. As for the possible actions, I'd say it'd nice to fix it in kernel too, though I kinda see it as a very minor issue right now. Not sure if I can find the time to do that soon though.
On a philosophical note: I'm sure a lot of drivers don't follow the sequences intended by the hardware exactly, but they still work because IP core design is permissive enough. Over the years I found peace with that somehow :)
if (ret)
return ret;
/* Set clock to desired speed */ dwmci_writel(host, DWMCI_CLKDIV, div);
Same here, CLKDIV is set now after the CIU is informed, is this an issue? We may want to set the clock speed before we enable the clock again. Right now it's setting the desired speed while disabled, inform the CIU, enable the clock, inform the CIU. This now does, disable the clock, inform the CIU, set the desired speed, enable the clock, inform the CIU. We may need to wait for the clock to stabilize before enabling it? Again, just making guesses, no documentation for me here :/
In this case, my implementation is following TRM (as described above). After CLKDIV modification my code informs CIU again, inside of the dwmci_control_clken() call.
Just to be thorough, here is what TRM says about DWMCI_CMD_UPD_CLK (bit 21):
8<------------------------------------------------------------->8 ...Following are the register values transferred into the card clock domain:
- CLKDIV
- CLKSRC
- CLKENA
8<------------------------------------------------------------->8
So you are right that each time this bit is set all 3 values are being transferred to CIU. But because that bit is set two times (before and after updating CLKDIV/CLKSRC) -- it's ok. And also this way is recommended in TRM, as stated above.
Hopefully there aren't other implementations than Exynos 850's that need to do something else :)
Aside from the improper logic, looks good to me, can you please add this information to the commit log so it's not lost in the mailing list :) ?
Yes, will do in v2 (going to submit it today). And will also add some related comment in the code. If you don't mind, please add your "Reviewed-by" tags where possible in v2, to keep the ball rolling :)
Thanks for reviewing!
Thanks, Quentin

FIFO data transfer is implemented as quite a massive chunk of code. Extract it into a dedicated function to make dwmci_data_transfer() easier to read and reduce the indentation level of the code.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 107 +++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 54 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 40b9b034f793..144f29f09240 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -140,25 +140,67 @@ static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size) return timeout; }
-static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) +static int dwmci_data_transfer_fifo(struct dwmci_host *host, + struct mmc_data *data, u32 mask) { - struct mmc *mmc = host->mmc; + const u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> + RX_WMARK_SHIFT) + 1) * 2; + const u32 int_rx = mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO); + const u32 int_tx = mask & DWMCI_INTMSK_TXDR; int ret = 0; - u32 timeout, mask, size, i, len = 0; - u32 *buf = NULL; - ulong start = get_timer(0); - u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> - RX_WMARK_SHIFT) + 1) * 2; + u32 len = 0, size, i; + u32 *buf; + + size = (data->blocksize * data->blocks) / 4; + if (!host->fifo_mode || !size) + return 0;
- size = data->blocksize * data->blocks; if (data->flags == MMC_DATA_READ) buf = (unsigned int *)data->dest; else buf = (unsigned int *)data->src;
- timeout = dwmci_get_timeout(mmc, size); + if (data->flags == MMC_DATA_READ && int_rx) { + dwmci_writel(host, DWMCI_RINTSTS, int_rx); + while (size) { + ret = dwmci_fifo_ready(host, DWMCI_FIFO_EMPTY, &len); + if (ret < 0) + break; + + len = (len >> DWMCI_FIFO_SHIFT) & DWMCI_FIFO_MASK; + len = min(size, len); + for (i = 0; i < len; i++) + *buf++ = dwmci_readl(host, DWMCI_DATA); + size = size > len ? (size - len) : 0; + } + } else if (data->flags == MMC_DATA_WRITE && int_tx) { + while (size) { + ret = dwmci_fifo_ready(host, DWMCI_FIFO_FULL, &len); + if (ret < 0) + break; + + len = fifo_depth - ((len >> DWMCI_FIFO_SHIFT) & + DWMCI_FIFO_MASK); + len = min(size, len); + for (i = 0; i < len; i++) + dwmci_writel(host, DWMCI_DATA, *buf++); + size = size > len ? (size - len) : 0; + } + dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR); + }
- size /= 4; + return ret; +} + +static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) +{ + struct mmc *mmc = host->mmc; + int ret = 0; + u32 timeout, mask, size; + ulong start = get_timer(0); + + size = data->blocksize * data->blocks; + timeout = dwmci_get_timeout(mmc, size);
for (;;) { mask = dwmci_readl(host, DWMCI_RINTSTS); @@ -169,50 +211,7 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) break; }
- if (host->fifo_mode && size) { - len = 0; - if (data->flags == MMC_DATA_READ && - (mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO))) { - dwmci_writel(host, DWMCI_RINTSTS, - mask & (DWMCI_INTMSK_RXDR | - DWMCI_INTMSK_DTO)); - while (size) { - ret = dwmci_fifo_ready(host, - DWMCI_FIFO_EMPTY, - &len); - if (ret < 0) - break; - - len = (len >> DWMCI_FIFO_SHIFT) & - DWMCI_FIFO_MASK; - len = min(size, len); - for (i = 0; i < len; i++) - *buf++ = - dwmci_readl(host, DWMCI_DATA); - size = size > len ? (size - len) : 0; - } - } else if (data->flags == MMC_DATA_WRITE && - (mask & DWMCI_INTMSK_TXDR)) { - while (size) { - ret = dwmci_fifo_ready(host, - DWMCI_FIFO_FULL, - &len); - if (ret < 0) - break; - - len = fifo_depth - ((len >> - DWMCI_FIFO_SHIFT) & - DWMCI_FIFO_MASK); - len = min(size, len); - for (i = 0; i < len; i++) - dwmci_writel(host, DWMCI_DATA, - *buf++); - size = size > len ? (size - len) : 0; - } - dwmci_writel(host, DWMCI_RINTSTS, - DWMCI_INTMSK_TXDR); - } - } + ret = dwmci_data_transfer_fifo(host, data, mask);
/* Data arrived correctly. */ if (mask & DWMCI_INTMSK_DTO) {

Make dwmci_send_cmd() easier to read by moving the DMA transfer handling code into a dedicated function.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 51 ++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 21 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 144f29f09240..439f8eb3fb4c 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -233,6 +233,33 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) return ret; }
+static int dwmci_dma_transfer(struct dwmci_host *host, uint flags, + struct bounce_buffer *bbstate) +{ + int ret; + u32 mask, ctrl; + + if (flags == MMC_DATA_READ) + mask = DWMCI_IDINTEN_RI; + else + mask = DWMCI_IDINTEN_TI; + + ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS, + mask, true, 1000, false); + if (ret) + debug("%s: DWMCI_IDINTEN mask 0x%x timeout\n", __func__, mask); + + /* Clear interrupts */ + dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK); + + ctrl = dwmci_readl(host, DWMCI_CTRL); + ctrl &= ~DWMCI_DMA_EN; + dwmci_writel(host, DWMCI_CTRL, ctrl); + + bounce_buffer_stop(bbstate); + return ret; +} + static int dwmci_set_transfer_mode(struct dwmci_host *host, struct mmc_data *data) { @@ -275,7 +302,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, data ? DIV_ROUND_UP(data->blocks, 8) : 0); int ret = 0, flags = 0, i; u32 retry = 100000; - u32 mask, ctrl; + u32 mask; struct bounce_buffer bbstate;
dwmci_wait_while_busy(host, cmd); @@ -384,26 +411,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
if (data) { ret = dwmci_data_transfer(host, data); - - /* only dma mode need it */ - if (!host->fifo_mode) { - if (data->flags == MMC_DATA_READ) - mask = DWMCI_IDINTEN_RI; - else - mask = DWMCI_IDINTEN_TI; - ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS, - mask, true, 1000, false); - if (ret) - debug("%s: DWMCI_IDINTEN mask 0x%x timeout.\n", - __func__, mask); - /* clear interrupts */ - dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK); - - ctrl = dwmci_readl(host, DWMCI_CTRL); - ctrl &= ~(DWMCI_DMA_EN); - dwmci_writel(host, DWMCI_CTRL, ctrl); - bounce_buffer_stop(&bbstate); - } + if (!host->fifo_mode) + ret = dwmci_dma_transfer(host, data->flags, &bbstate); }
udelay(100);

Hi Sam,
On 5/23/24 1:31 AM, Sam Protsenko wrote:
Make dwmci_send_cmd() easier to read by moving the DMA transfer handling code into a dedicated function.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Make dwmci_prepare_data() function easier to read by extracting the preparation of IDMAC descriptor into a dedicated function.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 52 ++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 439f8eb3fb4c..16601cacfc00 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -53,47 +53,57 @@ static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac); }
-static void dwmci_prepare_data(struct dwmci_host *host, - struct mmc_data *data, +static void dwmci_prepare_desc(struct mmc_data *data, struct dwmci_idmac *cur_idmac, void *bounce_buffer) { - unsigned long ctrl; - unsigned int i = 0, flags, cnt, blk_cnt; + struct dwmci_idmac *desc = cur_idmac; ulong data_start, data_end; + unsigned int blk_cnt, i;
- + data_start = (ulong)cur_idmac; blk_cnt = data->blocks;
- dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); + for (i = 0;; i++) { + unsigned int flags, cnt;
- /* Clear IDMAC interrupt */ - dwmci_writel(host, DWMCI_IDSTS, 0xFFFFFFFF); - - data_start = (ulong)cur_idmac; - dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac); - - do { - flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ; - flags |= (i == 0) ? DWMCI_IDMAC_FS : 0; + flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH; + if (i == 0) + flags |= DWMCI_IDMAC_FS; if (blk_cnt <= 8) { flags |= DWMCI_IDMAC_LD; cnt = data->blocksize * blk_cnt; } else cnt = data->blocksize * 8;
- dwmci_set_idma_desc(cur_idmac, flags, cnt, - (ulong)bounce_buffer + (i * PAGE_SIZE)); + dwmci_set_idma_desc(desc, flags, cnt, + (ulong)bounce_buffer + i * PAGE_SIZE); + desc++;
- cur_idmac++; if (blk_cnt <= 8) break; blk_cnt -= 8; - i++; - } while(1); + }
- data_end = (ulong)cur_idmac; + data_end = (ulong)desc; flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); +} + +static void dwmci_prepare_data(struct dwmci_host *host, + struct mmc_data *data, + struct dwmci_idmac *cur_idmac, + void *bounce_buffer) +{ + unsigned long ctrl; + + dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); + + /* Clear IDMAC interrupt */ + dwmci_writel(host, DWMCI_IDSTS, 0xFFFFFFFF); + + dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac); + + dwmci_prepare_desc(data, cur_idmac, bounce_buffer);
ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN;

Hi Sam,
On 5/23/24 1:31 AM, Sam Protsenko wrote:
Make dwmci_prepare_data() function easier to read by extracting the preparation of IDMAC descriptor into a dedicated function.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Prepare for adding 64-bit IDMAC descriptors by renaming current 32-bit descriptor and its fields accordingly. While at it, make use of virt_to_phys() to make it more obvious in which places the physical addresses have to be used.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 16601cacfc00..8e04ab39c2c5 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -20,11 +20,12 @@
#define PAGE_SIZE 4096
-struct dwmci_idmac { - u32 flags; - u32 cnt; - u32 addr; - u32 next_addr; +/* Internal DMA Controller (IDMAC) descriptor for 32-bit addressing mode */ +struct dwmci_idmac32 { + u32 des0; /* Control descriptor */ + u32 des1; /* Buffer size */ + u32 des2; /* Buffer physical address */ + u32 des3; /* Next descriptor physical address */ } __aligned(ARCH_DMA_MINALIGN);
static int dwmci_wait_reset(struct dwmci_host *host, u32 value) @@ -42,22 +43,23 @@ static int dwmci_wait_reset(struct dwmci_host *host, u32 value) return 0; }
-static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, - u32 desc0, u32 desc1, u32 desc2) +static void dwmci_set_idma_desc32(struct dwmci_idmac32 *desc, u32 control, + u32 buf_size, u32 buf_addr) { - struct dwmci_idmac *desc = idmac; + phys_addr_t desc_phys = virt_to_phys(desc); + u32 next_desc_phys = desc_phys + sizeof(struct dwmci_idmac32);
- desc->flags = desc0; - desc->cnt = desc1; - desc->addr = desc2; - desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac); + desc->des0 = control; + desc->des1 = buf_size; + desc->des2 = buf_addr; + desc->des3 = next_desc_phys; }
static void dwmci_prepare_desc(struct mmc_data *data, - struct dwmci_idmac *cur_idmac, + struct dwmci_idmac32 *cur_idmac, void *bounce_buffer) { - struct dwmci_idmac *desc = cur_idmac; + struct dwmci_idmac32 *desc32 = cur_idmac; ulong data_start, data_end; unsigned int blk_cnt, i;
@@ -65,6 +67,7 @@ static void dwmci_prepare_desc(struct mmc_data *data, blk_cnt = data->blocks;
for (i = 0;; i++) { + phys_addr_t buf_phys = virt_to_phys(bounce_buffer); unsigned int flags, cnt;
flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH; @@ -76,22 +79,22 @@ static void dwmci_prepare_desc(struct mmc_data *data, } else cnt = data->blocksize * 8;
- dwmci_set_idma_desc(desc, flags, cnt, - (ulong)bounce_buffer + i * PAGE_SIZE); - desc++; + dwmci_set_idma_desc32(desc32, flags, cnt, + buf_phys + i * PAGE_SIZE); + desc32++;
if (blk_cnt <= 8) break; blk_cnt -= 8; }
- data_end = (ulong)desc; + data_end = (ulong)desc32; flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); }
static void dwmci_prepare_data(struct dwmci_host *host, struct mmc_data *data, - struct dwmci_idmac *cur_idmac, + struct dwmci_idmac32 *cur_idmac, void *bounce_buffer) { unsigned long ctrl; @@ -308,7 +311,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, { #endif struct dwmci_host *host = mmc->priv; - ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac32, cur_idmac, data ? DIV_ROUND_UP(data->blocks, 8) : 0); int ret = 0, flags = 0, i; u32 retry = 100000;

Some DW MMC blocks (e.g. those on modern Exynos chips) support 64-bit DMA addressing mode. 64-bit DW MMC variants differ from their 32-bit counterparts: - the register layout is a bit different (because there are additional IDMAC registers present for storing upper part of 64-bit addresses) - DMA descriptor structure is bigger and different from 32-bit one
Introduce all necessary changes to enable support for 64-bit DMA capable DW MMC blocks. Next changes were made:
1. Check which DMA address mode is supported in current IP-core version. HCON register (bit 27) indicates whether it's 32-bit or 64-bit addressing. Add boolean .dma_64bit_address field to struct dwmci_host and store the result there. dwmci_init_dma() function is introduced for doing so, which is called on driver's init.
2. Add 64-bit DMA descriptor (struct dwmci_idmac64) and use it in dwmci_prepare_desc() in case if .dma_64bit_address field is true. A new dwmci_set_idma_desc64() function was added for populating that descriptor.
3. Add registers for 64-bit DMA capable blocks. To make the access to IDMAC registers universal between 32-bit / 64-bit cases, a new struct dwmci_idmac_regs (and corresponding host->regs field) was introduced, which abstracts the hardware by being set to appropriate offset constants on init. All direct calls to IDMAC registers were correspondingly replaced by accessing host->regs.
4. Allocate and use 64-bit DMA descriptors buffer in case when IDMAC is 64-bit capable. Extract all the code (except for the IDMAC descriptors buffer allocation) from dwmci_send_cmd() to dwmci_send_cmd_common(), so that it's possible to keep IDMAC buffer (either 32-bit or 64-bit) on stack during send_cmd routine.
The insights for this implementation were taken from Linux kernel DW MMC driver.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 152 ++++++++++++++++++++++++++++++++++--------- include/dwmmc.h | 39 ++++++++++- 2 files changed, 160 insertions(+), 31 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 8e04ab39c2c5..32e0e730c77b 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -28,6 +28,39 @@ struct dwmci_idmac32 { u32 des3; /* Next descriptor physical address */ } __aligned(ARCH_DMA_MINALIGN);
+/* Internal DMA Controller (IDMAC) descriptor for 64-bit addressing mode */ +struct dwmci_idmac64 { + u32 des0; /* Control descriptor */ + u32 des1; /* Reserved */ + u32 des2; /* Buffer sizes */ + u32 des3; /* Reserved */ + u32 des4; /* Lower 32-bits of Buffer Address Pointer 1 */ + u32 des5; /* Upper 32-bits of Buffer Address Pointer 1 */ + u32 des6; /* Lower 32-bits of Next Descriptor Address */ + u32 des7; /* Upper 32-bits of Next Descriptor Address */ +} __aligned(ARCH_DMA_MINALIGN); + +/* Register offsets for DW MMC blocks with 32-bit IDMAC */ +static const struct dwmci_idmac_regs dwmci_idmac_regs32 = { + .dbaddrl = DWMCI_DBADDR, + .idsts = DWMCI_IDSTS, + .idinten = DWMCI_IDINTEN, + .dscaddrl = DWMCI_DSCADDR, + .bufaddrl = DWMCI_BUFADDR, +}; + +/* Register offsets for DW MMC blocks with 64-bit IDMAC */ +static const struct dwmci_idmac_regs dwmci_idmac_regs64 = { + .dbaddrl = DWMCI_DBADDRL, + .dbaddru = DWMCI_DBADDRU, + .idsts = DWMCI_IDSTS64, + .idinten = DWMCI_IDINTEN64, + .dscaddrl = DWMCI_DSCADDRL, + .dscaddru = DWMCI_DSCADDRU, + .bufaddrl = DWMCI_BUFADDRL, + .bufaddru = DWMCI_BUFADDRU, +}; + static int dwmci_wait_reset(struct dwmci_host *host, u32 value) { unsigned long timeout = 1000; @@ -55,11 +88,27 @@ static void dwmci_set_idma_desc32(struct dwmci_idmac32 *desc, u32 control, desc->des3 = next_desc_phys; }
-static void dwmci_prepare_desc(struct mmc_data *data, - struct dwmci_idmac32 *cur_idmac, - void *bounce_buffer) +static void dwmci_set_idma_desc64(struct dwmci_idmac64 *desc, u32 control, + u32 buf_size, u64 buf_addr) +{ + phys_addr_t desc_phys = virt_to_phys(desc); + u64 next_desc_phys = desc_phys + sizeof(struct dwmci_idmac64); + + desc->des0 = control; + desc->des1 = 0; + desc->des2 = buf_size; + desc->des3 = 0; + desc->des4 = buf_addr & 0xffffffff; + desc->des5 = buf_addr >> 32; + desc->des6 = next_desc_phys & 0xffffffff; + desc->des7 = next_desc_phys >> 32; +} + +static void dwmci_prepare_desc(struct dwmci_host *host, struct mmc_data *data, + void *cur_idmac, void *bounce_buffer) { struct dwmci_idmac32 *desc32 = cur_idmac; + struct dwmci_idmac64 *desc64 = cur_idmac; ulong data_start, data_end; unsigned int blk_cnt, i;
@@ -79,34 +128,47 @@ static void dwmci_prepare_desc(struct mmc_data *data, } else cnt = data->blocksize * 8;
- dwmci_set_idma_desc32(desc32, flags, cnt, - buf_phys + i * PAGE_SIZE); - desc32++; + if (host->dma_64bit_address) { + dwmci_set_idma_desc64(desc64, flags, cnt, + buf_phys + i * PAGE_SIZE); + desc64++; + } else { + dwmci_set_idma_desc32(desc32, flags, cnt, + buf_phys + i * PAGE_SIZE); + desc32++; + }
if (blk_cnt <= 8) break; blk_cnt -= 8; }
- data_end = (ulong)desc32; + if (host->dma_64bit_address) + data_end = (ulong)desc64; + else + data_end = (ulong)desc32; flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); }
static void dwmci_prepare_data(struct dwmci_host *host, struct mmc_data *data, - struct dwmci_idmac32 *cur_idmac, + void *cur_idmac, void *bounce_buffer) { + const u32 idmacl = virt_to_phys(cur_idmac) & 0xffffffff; + const u32 idmacu = (u64)virt_to_phys(cur_idmac) >> 32; unsigned long ctrl;
dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
/* Clear IDMAC interrupt */ - dwmci_writel(host, DWMCI_IDSTS, 0xFFFFFFFF); + dwmci_writel(host, host->regs->idsts, 0xffffffff);
- dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac); + dwmci_writel(host, host->regs->dbaddrl, idmacl); + if (host->dma_64bit_address) + dwmci_writel(host, host->regs->dbaddru, idmacu);
- dwmci_prepare_desc(data, cur_idmac, bounce_buffer); + dwmci_prepare_desc(host, data, cur_idmac, bounce_buffer);
ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; @@ -257,13 +319,13 @@ static int dwmci_dma_transfer(struct dwmci_host *host, uint flags, else mask = DWMCI_IDINTEN_TI;
- ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS, + ret = wait_for_bit_le32(host->ioaddr + host->regs->idsts, mask, true, 1000, false); if (ret) debug("%s: DWMCI_IDINTEN mask 0x%x timeout\n", __func__, mask);
/* Clear interrupts */ - dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK); + dwmci_writel(host, host->regs->idsts, DWMCI_IDINTEN_MASK);
ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl &= ~DWMCI_DMA_EN; @@ -300,20 +362,10 @@ static void dwmci_wait_while_busy(struct dwmci_host *host, struct mmc_cmd *cmd) } }
-#ifdef CONFIG_DM_MMC -static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct mmc *mmc = mmc_get_mmc_dev(dev); -#else -static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +static int dwmci_send_cmd_common(struct dwmci_host *host, struct mmc_cmd *cmd, + struct mmc_data *data, void *cur_idmac) { -#endif - struct dwmci_host *host = mmc->priv; - ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac32, cur_idmac, - data ? DIV_ROUND_UP(data->blocks, 8) : 0); - int ret = 0, flags = 0, i; + int ret, flags = 0, i; u32 retry = 100000; u32 mask; struct bounce_buffer bbstate; @@ -433,6 +485,28 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; }
+#if CONFIG_IS_ENABLED(DM_MMC) +static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ +#endif + struct dwmci_host *host = mmc->priv; + const size_t buf_size = data ? DIV_ROUND_UP(data->blocks, 8) : 0; + + if (host->dma_64bit_address) { + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac64, idmac, buf_size); + return dwmci_send_cmd_common(host, cmd, data, idmac); + } else { + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac32, idmac, buf_size); + return dwmci_send_cmd_common(host, cmd, data, idmac); + } +} + static int dwmci_control_clken(struct dwmci_host *host, bool on) { const u32 val = on ? DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR : 0; @@ -581,6 +655,27 @@ static void dwmci_init_fifo(struct dwmci_host *host) dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); }
+static void dwmci_init_dma(struct dwmci_host *host) +{ + int addr_config; + + if (host->fifo_mode) + return; + + addr_config = (dwmci_readl(host, DWMCI_HCON) >> 27) & 0x1; + if (addr_config == 1) { + host->dma_64bit_address = true; + host->regs = &dwmci_idmac_regs64; + debug("%s: IDMAC supports 64-bit address mode\n", __func__); + } else { + host->dma_64bit_address = false; + host->regs = &dwmci_idmac_regs32; + debug("%s: IDMAC supports 32-bit address mode\n", __func__); + } + + dwmci_writel(host, host->regs->idinten, DWMCI_IDINTEN_MASK); +} + static int dwmci_init(struct mmc *mmc) { struct dwmci_host *host = mmc->priv; @@ -603,16 +698,13 @@ static int dwmci_init(struct mmc *mmc)
dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF);
- dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); dwmci_init_fifo(host); + dwmci_init_dma(host);
dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0);
- if (!host->fifo_mode) - dwmci_writel(host, DWMCI_IDINTEN, DWMCI_IDINTEN_MASK); - return 0; }
diff --git a/include/dwmmc.h b/include/dwmmc.h index 7e4acf096dce..de18fda68ac8 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -44,12 +44,22 @@ #define DWMCI_UHS_REG 0x074 #define DWMCI_BMOD 0x080 #define DWMCI_PLDMND 0x084 +#define DWMCI_DATA 0x200 +/* Registers to support IDMAC 32-bit address mode */ #define DWMCI_DBADDR 0x088 #define DWMCI_IDSTS 0x08C #define DWMCI_IDINTEN 0x090 #define DWMCI_DSCADDR 0x094 #define DWMCI_BUFADDR 0x098 -#define DWMCI_DATA 0x200 +/* Registers to support IDMAC 64-bit address mode */ +#define DWMCI_DBADDRL 0x088 +#define DWMCI_DBADDRU 0x08c +#define DWMCI_IDSTS64 0x090 +#define DWMCI_IDINTEN64 0x094 +#define DWMCI_DSCADDRL 0x098 +#define DWMCI_DSCADDRU 0x09c +#define DWMCI_BUFADDRL 0x0a0 +#define DWMCI_BUFADDRU 0x0a4
/* Interrupt Mask register */ #define DWMCI_INTMSK_ALL 0xffffffff @@ -142,6 +152,29 @@ /* quirks */ #define DWMCI_QUIRK_DISABLE_SMU (1 << 0)
+/** + * struct dwmci_idmac_regs - Offsets of IDMAC registers + * + * @dbaddrl: Descriptor base address, lower 32 bits + * @dbaddru: Descriptor base address, upper 32 bits + * @idsts: Internal DMA status + * @idinten: Internal DMA interrupt enable + * @dscaddrl: IDMAC descriptor address, lower 32 bits + * @dscaddru: IDMAC descriptor address, upper 32 bits + * @bufaddrl: Current data buffer address, lower 32 bits + * @bufaddru: Current data buffer address, upper 32 bits + */ +struct dwmci_idmac_regs { + u32 dbaddrl; + u32 dbaddru; + u32 idsts; + u32 idinten; + u32 dscaddrl; + u32 dscaddru; + u32 bufaddrl; + u32 bufaddru; +}; + /** * struct dwmci_host - Information about a designware MMC host * @@ -157,6 +190,8 @@ * @fifoth_val: Value for FIFOTH register (or 0 to leave unset) * @mmc: Pointer to generic MMC structure for this device * @priv: Private pointer for use by controller + * @dma_64bit_address: Whether DMA supports 64-bit address mode or not + * @regs: Registers that can vary for different DW MMC block versions */ struct dwmci_host { const char *name; @@ -196,6 +231,8 @@ struct dwmci_host {
/* use fifo mode to read and write data */ bool fifo_mode; + bool dma_64bit_address; + const struct dwmci_idmac_regs *regs; };
static inline void dwmci_writel(struct dwmci_host *host, int reg, u32 val)

Replace fifoth_val property with its fifo-depth counterpart in all DW MMC drivers. fifo-depth is a common property used in upstream Linux kernel. The FIFOTH register value will be calculated using fifo-depth value in DW MMC core (dw_mmc.c). This change reduces code duplication in platform drivers, and pulls common FIFOTH register value calculation into core dw_mmc driver where it belongs.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 21 +++++++++++++-------- drivers/mmc/exynos_dw_mmc.c | 10 +++++----- drivers/mmc/ftsdc010_mci.h | 1 - drivers/mmc/hi6220_dw_mmc.c | 7 +++---- drivers/mmc/nexell_dw_mmc.c | 5 +---- drivers/mmc/rockchip_dw_mmc.c | 5 +---- drivers/mmc/snps_dw_mmc.c | 6 ++---- drivers/mmc/socfpga_dw_mmc.c | 4 ++-- include/dwmmc.h | 4 ++-- 9 files changed, 29 insertions(+), 34 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 32e0e730c77b..a1f06931b7ac 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -218,8 +218,6 @@ static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size) static int dwmci_data_transfer_fifo(struct dwmci_host *host, struct mmc_data *data, u32 mask) { - const u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> - RX_WMARK_SHIFT) + 1) * 2; const u32 int_rx = mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO); const u32 int_tx = mask & DWMCI_INTMSK_TXDR; int ret = 0; @@ -254,8 +252,8 @@ static int dwmci_data_transfer_fifo(struct dwmci_host *host, if (ret < 0) break;
- len = fifo_depth - ((len >> DWMCI_FIFO_SHIFT) & - DWMCI_FIFO_MASK); + len = host->fifo_depth - ((len >> DWMCI_FIFO_SHIFT) & + DWMCI_FIFO_MASK); len = min(size, len); for (i = 0; i < len; i++) dwmci_writel(host, DWMCI_DATA, *buf++); @@ -643,16 +641,23 @@ static int dwmci_set_ios(struct mmc *mmc)
static void dwmci_init_fifo(struct dwmci_host *host) { - if (!host->fifoth_val) { + u32 fifo_thr, fifoth_val; + + if (!host->fifo_depth) { u32 fifo_size;
+ /* + * Automatically detect FIFO depth from FIFOTH register. + * Power-on value of RX_WMark is FIFO_DEPTH-1. + */ fifo_size = dwmci_readl(host, DWMCI_FIFOTH); fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; - host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | - TX_WMARK(fifo_size / 2); + host->fifo_depth = fifo_size; }
- dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); + fifo_thr = host->fifo_depth / 2; + fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_thr - 1) | TX_WMARK(fifo_thr); + dwmci_writel(host, DWMCI_FIFOTH, fifoth_val); }
static void dwmci_init_dma(struct dwmci_host *host) diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 2f849c43b129..14cb0c05cb55 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -151,8 +151,8 @@ static int do_dwmci_init(struct dwmci_host *host) return exynos_dwmci_core_init(host); }
-static int exynos_dwmci_get_config(const void *blob, int node, - struct dwmci_host *host, +static int exynos_dwmci_get_config(struct udevice *dev, const void *blob, + int node, struct dwmci_host *host, struct dwmci_exynos_priv_data *priv) { int err = 0; @@ -201,7 +201,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL; }
- host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0); + host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0); host->div = fdtdec_get_int(blob, node, "div", 0);
@@ -217,8 +217,8 @@ static int exynos_dwmmc_probe(struct udevice *dev) struct dwmci_host *host = &priv->host; int err;
- err = exynos_dwmci_get_config(gd->fdt_blob, dev_of_offset(dev), host, - priv); + err = exynos_dwmci_get_config(dev, gd->fdt_blob, dev_of_offset(dev), + host, priv); if (err) return err; err = do_dwmci_init(host); diff --git a/drivers/mmc/ftsdc010_mci.h b/drivers/mmc/ftsdc010_mci.h index 782d92be2f5f..36187cfa04f6 100644 --- a/drivers/mmc/ftsdc010_mci.h +++ b/drivers/mmc/ftsdc010_mci.h @@ -28,7 +28,6 @@ struct ftsdc010_chip { int dev_index; int dev_id; int buswidth; - u32 fifoth_val; struct mmc *mmc; void *priv; bool fifo_mode; diff --git a/drivers/mmc/hi6220_dw_mmc.c b/drivers/mmc/hi6220_dw_mmc.c index dc0210402bd2..e0b473f3f55c 100644 --- a/drivers/mmc/hi6220_dw_mmc.c +++ b/drivers/mmc/hi6220_dw_mmc.c @@ -37,7 +37,7 @@ struct hi6220_dwmmc_priv_data { struct hisi_mmc_data { unsigned int clock; bool use_fifo; - u32 fifoth_val; + u32 fifo_depth; };
static int hi6220_dwmmc_of_to_plat(struct udevice *dev) @@ -126,7 +126,7 @@ static int hi6220_dwmmc_probe(struct udevice *dev) host->mmc = &plat->mmc;
host->fifo_mode = mmc_data->use_fifo; - host->fifoth_val = mmc_data->fifoth_val; + host->fifo_depth = mmc_data->fifo_depth; host->mmc->priv = &priv->host; upriv->mmc = host->mmc; host->mmc->dev = dev; @@ -159,8 +159,7 @@ static const struct hisi_mmc_data hi6220_mmc_data = { static const struct hisi_mmc_data hi3798mv2x_mmc_data = { .clock = 50000000, .use_fifo = false, - // FIFO depth is 256 - .fifoth_val = MSIZE(4) | RX_WMARK(0x7f) | TX_WMARK(0x80), + .fifo_depth = 256, };
static const struct udevice_id hi6220_dwmmc_ids[] = { diff --git a/drivers/mmc/nexell_dw_mmc.c b/drivers/mmc/nexell_dw_mmc.c index 2723e4887cf7..aad848ca2825 100644 --- a/drivers/mmc/nexell_dw_mmc.c +++ b/drivers/mmc/nexell_dw_mmc.c @@ -187,10 +187,7 @@ static int nexell_dwmmc_probe(struct udevice *dev) struct dwmci_host *host = &priv->host; struct udevice *pwr_dev __maybe_unused;
- host->fifoth_val = MSIZE(0x2) | - RX_WMARK(priv->fifo_size / 2 - 1) | - TX_WMARK(priv->fifo_size / 2); - + host->fifo_depth = priv->fifo_size; host->fifo_mode = priv->fifo_mode;
dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq); diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index ad4529d6afa8..d52ae9cb6fd8 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -139,10 +139,7 @@ static int rockchip_dwmmc_probe(struct udevice *dev) if (ret < 0) return ret; #endif - host->fifoth_val = MSIZE(0x2) | - RX_WMARK(priv->fifo_depth / 2 - 1) | - TX_WMARK(priv->fifo_depth / 2); - + host->fifo_depth = priv->fifo_depth; host->fifo_mode = priv->fifo_mode;
#if CONFIG_IS_ENABLED(MMC_PWRSEQ) diff --git a/drivers/mmc/snps_dw_mmc.c b/drivers/mmc/snps_dw_mmc.c index 0134399e3934..4a72f41ef16f 100644 --- a/drivers/mmc/snps_dw_mmc.c +++ b/drivers/mmc/snps_dw_mmc.c @@ -82,7 +82,7 @@ static int snps_dwmmc_of_to_plat(struct udevice *dev) host->ioaddr = dev_read_addr_ptr(dev);
/* - * If fifo-depth is unset don't set fifoth_val - we will try to + * If fifo-depth is unset don't set fifo_depth - we will try to * auto detect it. */ ret = dev_read_u32(dev, "fifo-depth", &fifo_depth); @@ -90,9 +90,7 @@ static int snps_dwmmc_of_to_plat(struct udevice *dev) if (fifo_depth < FIFO_MIN || fifo_depth > FIFO_MAX) return -EINVAL;
- host->fifoth_val = MSIZE(0x2) | - RX_WMARK(fifo_depth / 2 - 1) | - TX_WMARK(fifo_depth / 2); + host->fifo_depth = fifo_depth; }
host->buswidth = dev_read_u32_default(dev, "bus-width", 4); diff --git a/drivers/mmc/socfpga_dw_mmc.c b/drivers/mmc/socfpga_dw_mmc.c index 387cb8b6b50a..f795472d10f7 100644 --- a/drivers/mmc/socfpga_dw_mmc.c +++ b/drivers/mmc/socfpga_dw_mmc.c @@ -135,8 +135,8 @@ static int socfpga_dwmmc_of_to_plat(struct udevice *dev) * We only have one dwmmc block on gen5 SoCFPGA. */ host->dev_index = 0; - host->fifoth_val = MSIZE(0x2) | - RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); + + host->fifo_depth = fifo_depth; priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "drvsel", 3); priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), diff --git a/include/dwmmc.h b/include/dwmmc.h index de18fda68ac8..7bb456e792b4 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -187,7 +187,7 @@ struct dwmci_idmac_regs { * @dev_index: Arbitrary device index for use by controller * @dev_id: Arbitrary device ID for use by controller * @buswidth: Bus width in bits (8 or 4) - * @fifoth_val: Value for FIFOTH register (or 0 to leave unset) + * @fifo_depth: Depth of FIFO, bytes (or 0 for automatic detection) * @mmc: Pointer to generic MMC structure for this device * @priv: Private pointer for use by controller * @dma_64bit_address: Whether DMA supports 64-bit address mode or not @@ -204,7 +204,7 @@ struct dwmci_host { int dev_index; int dev_id; int buswidth; - u32 fifoth_val; + u32 fifo_depth; struct mmc *mmc; void *priv;

Rework kernel-doc comments in dwmmc.h header so it's actually possible to generate a proper documentation from it usin scripts/kernel-doc script, with no errors.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- include/dwmmc.h | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/include/dwmmc.h b/include/dwmmc.h index 7bb456e792b4..77c8989148a1 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -182,6 +182,7 @@ struct dwmci_idmac_regs { * @ioaddr: Base I/O address of controller * @quirks: Quick flags - see DWMCI_QUIRK_... * @caps: Capabilities - see MMC_MODE_... + * @clock: Current clock frequency (after internal divider), Hz * @bus_hz: Bus speed in Hz, if @get_mmc_clk() is NULL * @div: Arbitrary clock divider value for use by controller * @dev_index: Arbitrary device index for use by controller @@ -190,6 +191,10 @@ struct dwmci_idmac_regs { * @fifo_depth: Depth of FIFO, bytes (or 0 for automatic detection) * @mmc: Pointer to generic MMC structure for this device * @priv: Private pointer for use by controller + * @clksel: (Optional) Platform function to run when speed/width is changed + * @board_init: (Optional) Platform function to run on init + * @cfg: Internal MMC configuration, for !CONFIG_BLK cases + * @fifo_mode: Use FIFO mode (not DMA) to read and write data * @dma_64bit_address: Whether DMA supports 64-bit address mode or not * @regs: Registers that can vary for different DW MMC block versions */ @@ -210,9 +215,12 @@ struct dwmci_host {
int (*clksel)(struct dwmci_host *host); void (*board_init)(struct dwmci_host *host); - /** - * Get / set a particular MMC clock frequency + * @get_mmc_clk: (Optional) Platform function to get/set a particular + * MMC clock frequency + * + * @host: DWMMC host + * @freq: Frequency the host is trying to achieve * * This is used to request the current clock frequency of the clock * that drives the DWMMC peripheral. The caller will then use this @@ -220,16 +228,12 @@ struct dwmci_host { * required MMC bus clock frequency. If you want to handle the * clock external to DWMMC, use @freq to select the frequency and * return that value too. Then DWMMC will put itself in bypass mode. - * - * @host: DWMMC host - * @freq: Frequency the host is trying to achieve */ unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq); #ifndef CONFIG_BLK struct mmc_config cfg; #endif
- /* use fifo mode to read and write data */ bool fifo_mode; bool dma_64bit_address; const struct dwmci_idmac_regs *regs; @@ -267,6 +271,10 @@ static inline u8 dwmci_readb(struct dwmci_host *host, int reg) #ifdef CONFIG_BLK /** * dwmci_setup_cfg() - Set up the configuration for DWMMC + * @cfg: Configuration structure to fill in (generally &plat->mmc) + * @host: DWMMC host + * @max_clk: Maximum supported clock speed in Hz (e.g. 150000000) + * @min_clk: Minimum supported clock speed in Hz (e.g. 400000) * * This is used to set up a DWMMC device when you are using CONFIG_BLK. * @@ -291,28 +299,23 @@ static inline u8 dwmci_readb(struct dwmci_host *host, int reg) * struct rockchip_mmc_plat *plat = dev_get_plat(dev); * * See rockchip_dw_mmc.c for an example. - * - * @cfg: Configuration structure to fill in (generally &plat->mmc) - * @host: DWMMC host - * @max_clk: Maximum supported clock speed in HZ (e.g. 150000000) - * @min_clk: Minimum supported clock speed in HZ (e.g. 400000) */ void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, u32 max_clk, u32 min_clk);
/** * dwmci_bind() - Set up a new MMC block device + * @dev: Device to set up + * @mmc: Pointer to mmc structure (normally &plat->mmc) + * @cfg: Empty configuration structure (generally &plat->cfg). This is + * normally all zeroes at this point. The only purpose of passing + * this in is to set mmc->cfg to it. * * This is used to set up a DWMMC block device when you are using CONFIG_BLK. * It should be called from your driver's bind() method. * * See rockchip_dw_mmc.c for an example. * - * @dev: Device to set up - * @mmc: Pointer to mmc structure (normally &plat->mmc) - * @cfg: Empty configuration structure (generally &plat->cfg). This is - * normally all zeroes at this point. The only purpose of passing - * this in is to set mmc->cfg to it. * Return: 0 if OK, -ve if the block device could not be created */ int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg); @@ -320,12 +323,12 @@ int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg); #else /** * add_dwmci() - Add a new DWMMC interface + * @host: DWMMC host structure + * @max_clk: Maximum supported clock speed in Hz (e.g. 150000000) + * @min_clk: Minimum supported clock speed in Hz (e.g. 400000) * * This is used when you are not using CONFIG_BLK. Convert your driver over! * - * @host: DWMMC host structure - * @max_clk: Maximum supported clock speed in HZ (e.g. 150000000) - * @min_clk: Minimum supported clock speed in HZ (e.g. 400000) * Return: 0 if OK, -ve on error */ int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk);

Use CONFIG_IS_ENABLED() macro to check config options as recommended by checkpatch, instead of checking those with just #ifdef CONFIG_...
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 6 +++--- include/dwmmc.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a1f06931b7ac..ebe239547a7d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -573,7 +573,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) return 0; }
-#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) static int dwmci_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); @@ -713,7 +713,7 @@ static int dwmci_init(struct mmc *mmc) return 0; }
-#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) int dwmci_probe(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); @@ -738,7 +738,7 @@ void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, u32 max_clk, u32 min_clk) { cfg->name = host->name; -#ifndef CONFIG_DM_MMC +#if !CONFIG_IS_ENABLED(DM_MMC) cfg->ops = &dwmci_ops; #endif cfg->f_min = min_clk; diff --git a/include/dwmmc.h b/include/dwmmc.h index 77c8989148a1..9252bef24329 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -268,7 +268,7 @@ static inline u8 dwmci_readb(struct dwmci_host *host, int reg) return readb(host->ioaddr + reg); }
-#ifdef CONFIG_BLK +#if CONFIG_IS_ENABLED(BLK) /** * dwmci_setup_cfg() - Set up the configuration for DWMMC * @cfg: Configuration structure to fill in (generally &plat->mmc) @@ -334,7 +334,7 @@ int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg); int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk); #endif /* !CONFIG_BLK */
-#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) /* Export the operations to drivers */ int dwmci_probe(struct udevice *dev); extern const struct dm_mmc_ops dm_dwmci_ops;

Hi Sam,
On 5/23/24 1:31 AM, Sam Protsenko wrote:
Use CONFIG_IS_ENABLED() macro to check config options as recommended by checkpatch, instead of checking those with just #ifdef CONFIG_...
No functional change.
There are actual functional changes in here.
defined(CONFIG_DM_MMC) != CONFIG_IS_ENABLED(DM_MMC)
Currently, if one has SPL_MMC and MMC_DW_ROCKCHIP defined, it'll build the SPL variant of MMC_DW_ROCKCHIP but guarding only with the U-Boot proper symbols, meaning, essentially the SPL and proper variant of rockchip_dw_mmc.o are more or less identical.
I think we can argue this isn't proper and should be fixed. I think we need to migrate all the MMC_DW drivers to use $(SPL_TPL_) in there and create the symbols in Kconfig with the appropriate dependencies. We'll likely also need to modify a bunch of defconfigs or make SPL_MMC_DW_ROCKCHIP default y if MMC_DW_ROCKCHIP for example, so that we don't break current support (it's pretty much expected to have MMC support in SPL).
I may have misinterpreted this, so please let me know where I am wrong in my assumptions here, but this does look like more work than just this.
Cheers, Quentin

On Thu, May 23, 2024 at 10:03 AM Quentin Schulz quentin.schulz@cherry.de wrote:
Hi Sam,
On 5/23/24 1:31 AM, Sam Protsenko wrote:
Use CONFIG_IS_ENABLED() macro to check config options as recommended by checkpatch, instead of checking those with just #ifdef CONFIG_...
No functional change.
There are actual functional changes in here.
defined(CONFIG_DM_MMC) != CONFIG_IS_ENABLED(DM_MMC)
Currently, if one has SPL_MMC and MMC_DW_ROCKCHIP defined, it'll build the SPL variant of MMC_DW_ROCKCHIP but guarding only with the U-Boot proper symbols, meaning, essentially the SPL and proper variant of rockchip_dw_mmc.o are more or less identical.
I think we can argue this isn't proper and should be fixed. I think we need to migrate all the MMC_DW drivers to use $(SPL_TPL_) in there and create the symbols in Kconfig with the appropriate dependencies. We'll likely also need to modify a bunch of defconfigs or make SPL_MMC_DW_ROCKCHIP default y if MMC_DW_ROCKCHIP for example, so that we don't break current support (it's pretty much expected to have MMC support in SPL).
I may have misinterpreted this, so please let me know where I am wrong in my assumptions here, but this does look like more work than just this.
Thanks for noticing this! I completely forgot about SPL as it's not built for my board. I tried to check with buildman and it seems like replacing #ifdefs with CONFIG_IS_ENABLED() doesn't change anything, but I'm not sure if buildman actually checks SPL at all (it probably only checks the U-Boot proper binary?).
In my v2 I'll stick to #ifdef and avoid using CONFIG_IS_ENABLED(). As you said, correct rework probably takes much more than just replacing #ifdefs. It can be done later in a separate series.
Cheers, Quentin

Fix most of checkpatch warnings and other obvious style issues.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/dw_mmc.c | 69 ++++++++++---------- include/dwmmc.h | 149 ++++++++++++++++++++++--------------------- 2 files changed, 110 insertions(+), 108 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index ebe239547a7d..d0331550ef6b 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -125,8 +125,9 @@ static void dwmci_prepare_desc(struct dwmci_host *host, struct mmc_data *data, if (blk_cnt <= 8) { flags |= DWMCI_IDMAC_LD; cnt = data->blocksize * blk_cnt; - } else + } else { cnt = data->blocksize * 8; + }
if (host->dma_64bit_address) { dwmci_set_idma_desc64(desc64, flags, cnt, @@ -150,10 +151,8 @@ static void dwmci_prepare_desc(struct dwmci_host *host, struct mmc_data *data, flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); }
-static void dwmci_prepare_data(struct dwmci_host *host, - struct mmc_data *data, - void *cur_idmac, - void *bounce_buffer) +static void dwmci_prepare_data(struct dwmci_host *host, struct mmc_data *data, + void *cur_idmac, void *bounce_buffer) { const u32 idmacl = virt_to_phys(cur_idmac) & 0xffffffff; const u32 idmacu = (u64)virt_to_phys(cur_idmac) >> 32; @@ -277,7 +276,7 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
for (;;) { mask = dwmci_readl(host, DWMCI_RINTSTS); - /* Error during data transfer. */ + /* Error during data transfer */ if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { debug("%s: DATA ERROR!\n", __func__); ret = -EINVAL; @@ -286,16 +285,15 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
ret = dwmci_data_transfer_fifo(host, data, mask);
- /* Data arrived correctly. */ + /* Data arrived correctly */ if (mask & DWMCI_INTMSK_DTO) { ret = 0; break; }
- /* Check for timeout. */ + /* Check for timeout */ if (get_timer(start) > timeout) { - debug("%s: Timeout waiting for data!\n", - __func__); + debug("%s: Timeout waiting for data!\n", __func__); ret = -ETIMEDOUT; break; } @@ -317,8 +315,8 @@ static int dwmci_dma_transfer(struct dwmci_host *host, uint flags, else mask = DWMCI_IDINTEN_TI;
- ret = wait_for_bit_le32(host->ioaddr + host->regs->idsts, - mask, true, 1000, false); + ret = wait_for_bit_le32(host->ioaddr + host->regs->idsts, mask, true, + 1000, false); if (ret) debug("%s: DWMCI_IDINTEN mask 0x%x timeout\n", __func__, mask);
@@ -334,7 +332,7 @@ static int dwmci_dma_transfer(struct dwmci_host *host, uint flags, }
static int dwmci_set_transfer_mode(struct dwmci_host *host, - struct mmc_data *data) + struct mmc_data *data) { unsigned long mode;
@@ -380,12 +378,12 @@ static int dwmci_send_cmd_common(struct dwmci_host *host, struct mmc_cmd *cmd, } else { if (data->flags == MMC_DATA_READ) { ret = bounce_buffer_start(&bbstate, - (void*)data->dest, + (void *)data->dest, data->blocksize * data->blocks, GEN_BB_WRITE); } else { ret = bounce_buffer_start(&bbstate, - (void*)data->src, + (void *)data->src, data->blocksize * data->blocks, GEN_BB_READ); } @@ -420,9 +418,9 @@ static int dwmci_send_cmd_common(struct dwmci_host *host, struct mmc_cmd *cmd, if (cmd->resp_type & MMC_RSP_CRC) flags |= DWMCI_CMD_CHECK_CRC;
- flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); + flags |= cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG;
- debug("Sending CMD%d\n",cmd->cmdidx); + debug("Sending CMD%d\n", cmd->cmdidx);
dwmci_writel(host, DWMCI_CMD, flags);
@@ -436,7 +434,7 @@ static int dwmci_send_cmd_common(struct dwmci_host *host, struct mmc_cmd *cmd, }
if (i == retry) { - debug("%s: Timeout.\n", __func__); + debug("%s: Timeout\n", __func__); return -ETIMEDOUT; }
@@ -449,18 +447,17 @@ static int dwmci_send_cmd_common(struct dwmci_host *host, struct mmc_cmd *cmd, * below shall be debug(). eMMC cards also do not favor * CMD8, please keep that in mind. */ - debug("%s: Response Timeout.\n", __func__); + debug("%s: Response Timeout\n", __func__); return -ETIMEDOUT; } else if (mask & DWMCI_INTMSK_RE) { - debug("%s: Response Error.\n", __func__); + debug("%s: Response Error\n", __func__); return -EIO; } else if ((cmd->resp_type & MMC_RSP_CRC) && (mask & DWMCI_INTMSK_RCRC)) { - debug("%s: Response CRC Error.\n", __func__); + debug("%s: Response CRC Error\n", __func__); return -EIO; }
- if (cmd->resp_type & MMC_RSP_PRESENT) { if (cmd->resp_type & MMC_RSP_136) { cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); @@ -533,24 +530,24 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) unsigned long sclk; int ret;
- if ((freq == host->clock) || (freq == 0)) + if (freq == host->clock || freq == 0) return 0; + /* - * If host->get_mmc_clk isn't defined, - * then assume that host->bus_hz is source clock value. - * host->bus_hz should be set by user. + * If host->get_mmc_clk isn't defined, then assume that host->bus_hz is + * source clock value. host->bus_hz should be set by user. */ - if (host->get_mmc_clk) + if (host->get_mmc_clk) { sclk = host->get_mmc_clk(host, freq); - else if (host->bus_hz) + } else if (host->bus_hz) { sclk = host->bus_hz; - else { - debug("%s: Didn't get source clock value.\n", __func__); + } else { + debug("%s: Didn't get source clock value\n", __func__); return -EINVAL; }
if (sclk == freq) - div = 0; /* bypass mode */ + div = 0; /* bypass mode */ else div = DIV_ROUND_UP(sclk, 2 * freq);
@@ -584,7 +581,7 @@ static int dwmci_set_ios(struct mmc *mmc) struct dwmci_host *host = (struct dwmci_host *)mmc->priv; u32 ctype, regs;
- debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock); + debug("Bus width = %d, clock: %d\n", mmc->bus_width, mmc->clock);
dwmci_setup_bus(host, mmc->clock); switch (mmc->bus_width) { @@ -698,10 +695,10 @@ static int dwmci_init(struct mmc *mmc) /* Enumerate at 400KHz */ dwmci_setup_bus(host, mmc->cfg->f_min);
- dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); + dwmci_writel(host, DWMCI_RINTSTS, 0xffffffff); dwmci_writel(host, DWMCI_INTMASK, 0);
- dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); + dwmci_writel(host, DWMCI_TMOUT, 0xffffffff);
dwmci_writel(host, DWMCI_BMOD, 1); dwmci_init_fifo(host); @@ -735,7 +732,7 @@ static const struct mmc_ops dwmci_ops = { #endif
void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, - u32 max_clk, u32 min_clk) + u32 max_clk, u32 min_clk) { cfg->name = host->name; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -771,7 +768,7 @@ int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk) dwmci_setup_cfg(&host->cfg, host, max_clk, min_clk);
host->mmc = mmc_create(&host->cfg, host); - if (host->mmc == NULL) + if (!host->mmc) return -1;
return 0; diff --git a/include/dwmmc.h b/include/dwmmc.h index 9252bef24329..e17884ade7e6 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -15,31 +15,31 @@ #define DWMCI_CTRL 0x000 #define DWMCI_PWREN 0x004 #define DWMCI_CLKDIV 0x008 -#define DWMCI_CLKSRC 0x00C +#define DWMCI_CLKSRC 0x00c #define DWMCI_CLKENA 0x010 #define DWMCI_TMOUT 0x014 #define DWMCI_CTYPE 0x018 -#define DWMCI_BLKSIZ 0x01C +#define DWMCI_BLKSIZ 0x01c #define DWMCI_BYTCNT 0x020 #define DWMCI_INTMASK 0x024 #define DWMCI_CMDARG 0x028 -#define DWMCI_CMD 0x02C +#define DWMCI_CMD 0x02c #define DWMCI_RESP0 0x030 #define DWMCI_RESP1 0x034 #define DWMCI_RESP2 0x038 -#define DWMCI_RESP3 0x03C +#define DWMCI_RESP3 0x03c #define DWMCI_MINTSTS 0x040 #define DWMCI_RINTSTS 0x044 #define DWMCI_STATUS 0x048 -#define DWMCI_FIFOTH 0x04C +#define DWMCI_FIFOTH 0x04c #define DWMCI_CDETECT 0x050 #define DWMCI_WRTPRT 0x054 #define DWMCI_GPIO 0x058 -#define DWMCI_TCMCNT 0x05C +#define DWMCI_TCMCNT 0x05c #define DWMCI_TBBCNT 0x060 #define DWMCI_DEBNCE 0x064 #define DWMCI_USRID 0x068 -#define DWMCI_VERID 0x06C +#define DWMCI_VERID 0x06c #define DWMCI_HCON 0x070 #define DWMCI_UHS_REG 0x074 #define DWMCI_BMOD 0x080 @@ -47,7 +47,7 @@ #define DWMCI_DATA 0x200 /* Registers to support IDMAC 32-bit address mode */ #define DWMCI_DBADDR 0x088 -#define DWMCI_IDSTS 0x08C +#define DWMCI_IDSTS 0x08c #define DWMCI_IDINTEN 0x090 #define DWMCI_DSCADDR 0x094 #define DWMCI_BUFADDR 0x098 @@ -63,94 +63,94 @@
/* Interrupt Mask register */ #define DWMCI_INTMSK_ALL 0xffffffff -#define DWMCI_INTMSK_RE (1 << 1) -#define DWMCI_INTMSK_CDONE (1 << 2) -#define DWMCI_INTMSK_DTO (1 << 3) -#define DWMCI_INTMSK_TXDR (1 << 4) -#define DWMCI_INTMSK_RXDR (1 << 5) -#define DWMCI_INTMSK_RCRC (1 << 6) -#define DWMCI_INTMSK_DCRC (1 << 7) -#define DWMCI_INTMSK_RTO (1 << 8) -#define DWMCI_INTMSK_DRTO (1 << 9) -#define DWMCI_INTMSK_HTO (1 << 10) -#define DWMCI_INTMSK_FRUN (1 << 11) -#define DWMCI_INTMSK_HLE (1 << 12) -#define DWMCI_INTMSK_SBE (1 << 13) -#define DWMCI_INTMSK_ACD (1 << 14) -#define DWMCI_INTMSK_EBE (1 << 15) - -/* Raw interrupt Regsiter */ -#define DWMCI_DATA_ERR (DWMCI_INTMSK_EBE | DWMCI_INTMSK_SBE | DWMCI_INTMSK_HLE |\ - DWMCI_INTMSK_FRUN | DWMCI_INTMSK_EBE | DWMCI_INTMSK_DCRC) -#define DWMCI_DATA_TOUT (DWMCI_INTMSK_HTO | DWMCI_INTMSK_DRTO) +#define DWMCI_INTMSK_RE BIT(1) +#define DWMCI_INTMSK_CDONE BIT(2) +#define DWMCI_INTMSK_DTO BIT(3) +#define DWMCI_INTMSK_TXDR BIT(4) +#define DWMCI_INTMSK_RXDR BIT(5) +#define DWMCI_INTMSK_RCRC BIT(6) +#define DWMCI_INTMSK_DCRC BIT(7) +#define DWMCI_INTMSK_RTO BIT(8) +#define DWMCI_INTMSK_DRTO BIT(9) +#define DWMCI_INTMSK_HTO BIT(10) +#define DWMCI_INTMSK_FRUN BIT(11) +#define DWMCI_INTMSK_HLE BIT(12) +#define DWMCI_INTMSK_SBE BIT(13) +#define DWMCI_INTMSK_ACD BIT(14) +#define DWMCI_INTMSK_EBE BIT(15) + +/* Raw interrupt register */ +#define DWMCI_DATA_ERR (DWMCI_INTMSK_EBE | DWMCI_INTMSK_SBE | \ + DWMCI_INTMSK_HLE | DWMCI_INTMSK_FRUN | \ + DWMCI_INTMSK_EBE | DWMCI_INTMSK_DCRC) +#define DWMCI_DATA_TOUT (DWMCI_INTMSK_HTO | DWMCI_INTMSK_DRTO) + /* CTRL register */ -#define DWMCI_CTRL_RESET (1 << 0) -#define DWMCI_CTRL_FIFO_RESET (1 << 1) -#define DWMCI_CTRL_DMA_RESET (1 << 2) -#define DWMCI_DMA_EN (1 << 5) -#define DWMCI_CTRL_SEND_AS_CCSD (1 << 10) -#define DWMCI_IDMAC_EN (1 << 25) +#define DWMCI_CTRL_RESET BIT(0) +#define DWMCI_CTRL_FIFO_RESET BIT(1) +#define DWMCI_CTRL_DMA_RESET BIT(2) +#define DWMCI_DMA_EN BIT(5) +#define DWMCI_CTRL_SEND_AS_CCSD BIT(10) +#define DWMCI_IDMAC_EN BIT(25) #define DWMCI_RESET_ALL (DWMCI_CTRL_RESET | DWMCI_CTRL_FIFO_RESET |\ DWMCI_CTRL_DMA_RESET)
/* CMD register */ -#define DWMCI_CMD_RESP_EXP (1 << 6) -#define DWMCI_CMD_RESP_LENGTH (1 << 7) -#define DWMCI_CMD_CHECK_CRC (1 << 8) -#define DWMCI_CMD_DATA_EXP (1 << 9) -#define DWMCI_CMD_RW (1 << 10) -#define DWMCI_CMD_SEND_STOP (1 << 12) -#define DWMCI_CMD_ABORT_STOP (1 << 14) -#define DWMCI_CMD_PRV_DAT_WAIT (1 << 13) -#define DWMCI_CMD_UPD_CLK (1 << 21) -#define DWMCI_CMD_USE_HOLD_REG (1 << 29) -#define DWMCI_CMD_START (1 << 31) +#define DWMCI_CMD_RESP_EXP BIT(6) +#define DWMCI_CMD_RESP_LENGTH BIT(7) +#define DWMCI_CMD_CHECK_CRC BIT(8) +#define DWMCI_CMD_DATA_EXP BIT(9) +#define DWMCI_CMD_RW BIT(10) +#define DWMCI_CMD_SEND_STOP BIT(12) +#define DWMCI_CMD_ABORT_STOP BIT(14) +#define DWMCI_CMD_PRV_DAT_WAIT BIT(13) +#define DWMCI_CMD_UPD_CLK BIT(21) +#define DWMCI_CMD_USE_HOLD_REG BIT(29) +#define DWMCI_CMD_START BIT(31)
/* CLKENA register */ -#define DWMCI_CLKEN_ENABLE (1 << 0) -#define DWMCI_CLKEN_LOW_PWR (1 << 16) +#define DWMCI_CLKEN_ENABLE BIT(0) +#define DWMCI_CLKEN_LOW_PWR BIT(16)
-/* Card-type registe */ +/* Card type register */ #define DWMCI_CTYPE_1BIT 0 -#define DWMCI_CTYPE_4BIT (1 << 0) -#define DWMCI_CTYPE_8BIT (1 << 16) +#define DWMCI_CTYPE_4BIT BIT(0) +#define DWMCI_CTYPE_8BIT BIT(16)
-/* Status Register */ -#define DWMCI_FIFO_EMPTY (1 << 2) -#define DWMCI_FIFO_FULL (1 << 3) -#define DWMCI_BUSY (1 << 9) +/* Status register */ +#define DWMCI_FIFO_EMPTY BIT(2) +#define DWMCI_FIFO_FULL BIT(3) +#define DWMCI_BUSY BIT(9) #define DWMCI_FIFO_MASK 0x1fff #define DWMCI_FIFO_SHIFT 17
-/* FIFOTH Register */ +/* FIFOTH register */ #define MSIZE(x) ((x) << 28) #define RX_WMARK(x) ((x) << 16) #define TX_WMARK(x) (x) #define RX_WMARK_SHIFT 16 #define RX_WMARK_MASK (0xfff << RX_WMARK_SHIFT)
-#define DWMCI_IDMAC_OWN (1 << 31) -#define DWMCI_IDMAC_CH (1 << 4) -#define DWMCI_IDMAC_FS (1 << 3) -#define DWMCI_IDMAC_LD (1 << 2) +#define DWMCI_IDMAC_OWN BIT(31) +#define DWMCI_IDMAC_CH BIT(4) +#define DWMCI_IDMAC_FS BIT(3) +#define DWMCI_IDMAC_LD BIT(2)
-/* Bus Mode Register */ -#define DWMCI_BMOD_IDMAC_RESET (1 << 0) -#define DWMCI_BMOD_IDMAC_FB (1 << 1) -#define DWMCI_BMOD_IDMAC_EN (1 << 7) +/* Bus Mode register */ +#define DWMCI_BMOD_IDMAC_RESET BIT(0) +#define DWMCI_BMOD_IDMAC_FB BIT(1) +#define DWMCI_BMOD_IDMAC_EN BIT(7)
/* UHS register */ -#define DWMCI_DDR_MODE (1 << 16) +#define DWMCI_DDR_MODE BIT(16)
/* Internal IDMAC interrupt defines */ -#define DWMCI_IDINTEN_RI BIT(1) -#define DWMCI_IDINTEN_TI BIT(0) +#define DWMCI_IDINTEN_RI BIT(1) +#define DWMCI_IDINTEN_TI BIT(0) +#define DWMCI_IDINTEN_MASK (DWMCI_IDINTEN_TI | DWMCI_IDINTEN_RI)
-#define DWMCI_IDINTEN_MASK (DWMCI_IDINTEN_TI | \ - DWMCI_IDINTEN_RI) - -/* quirks */ -#define DWMCI_QUIRK_DISABLE_SMU (1 << 0) +/* Quirks */ +#define DWMCI_QUIRK_DISABLE_SMU BIT(0)
/** * struct dwmci_idmac_regs - Offsets of IDMAC registers @@ -230,6 +230,7 @@ struct dwmci_host { * return that value too. Then DWMMC will put itself in bypass mode. */ unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq); + #ifndef CONFIG_BLK struct mmc_config cfg; #endif @@ -253,6 +254,7 @@ static inline void dwmci_writeb(struct dwmci_host *host, int reg, u8 val) { writeb(val, host->ioaddr + reg); } + static inline u32 dwmci_readl(struct dwmci_host *host, int reg) { return readl(host->ioaddr + reg); @@ -269,6 +271,7 @@ static inline u8 dwmci_readb(struct dwmci_host *host, int reg) }
#if CONFIG_IS_ENABLED(BLK) + /** * dwmci_setup_cfg() - Set up the configuration for DWMMC * @cfg: Configuration structure to fill in (generally &plat->mmc) @@ -301,7 +304,7 @@ static inline u8 dwmci_readb(struct dwmci_host *host, int reg) * See rockchip_dw_mmc.c for an example. */ void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, - u32 max_clk, u32 min_clk); + u32 max_clk, u32 min_clk);
/** * dwmci_bind() - Set up a new MMC block device @@ -321,6 +324,7 @@ void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg);
#else + /** * add_dwmci() - Add a new DWMMC interface * @host: DWMMC host structure @@ -332,6 +336,7 @@ int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg); * Return: 0 if OK, -ve on error */ int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk); + #endif /* !CONFIG_BLK */
#if CONFIG_IS_ENABLED(DM_MMC)

Hi Sam,
On 5/23/24 1:31 AM, Sam Protsenko wrote:
Fix most of checkpatch warnings and other obvious style issues.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org
Reviewed-by: Quentin Schulz quentin.schulz@cherry.de
Thanks, Quentin

Some device tree properties for DW MMC block were updated in Linux kernel. Let's follow its example and rework corresponding properties in all Exynos device trees. Don't remove outdated properties yet, it'll be done later once DW MMC driver is updated accordingly to read the updated properties instead of outdated ones.
Next properties are added:
* samsung,dw-mshc-ciu-div and samsung,dw-mshc-sdr-timing:
They were derived from outdated samsung,timing property.
* fifo-depth (generic replacement for fifoth_val):
FIFO depth was calculated from fifoth_val (using expressions from FIFOTH register description in TRM):
fifo-depth = ((fifoth_val >> 16) + 1) * 2
* bus-width: generic replacement for samsung,bus-width * clock-frequency: generic replacement for bus_hz * non-removable: generic replacement for samsung,removable = <0>
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- arch/arm/dts/exynos4210-origen.dts | 1 + arch/arm/dts/exynos4210-trats.dts | 2 ++ arch/arm/dts/exynos4210-universal_c210.dts | 2 ++ arch/arm/dts/exynos4412-odroid.dts | 6 ++++++ arch/arm/dts/exynos4412-trats2.dts | 8 ++++++++ arch/arm/dts/exynos5250-arndale.dts | 6 ++++++ arch/arm/dts/exynos5250-smdk5250.dts | 7 +++++++ arch/arm/dts/exynos5250-snow.dts | 7 +++++++ arch/arm/dts/exynos5250-spring.dts | 4 ++++ arch/arm/dts/exynos5420-smdk5420.dts | 7 +++++++ arch/arm/dts/exynos5422-odroidxu3.dts | 2 ++ arch/arm/dts/exynos54xx.dtsi | 7 +++++++ 12 files changed, 59 insertions(+)
diff --git a/arch/arm/dts/exynos4210-origen.dts b/arch/arm/dts/exynos4210-origen.dts index 65a5fcd67eff..a4915de2c49c 100644 --- a/arch/arm/dts/exynos4210-origen.dts +++ b/arch/arm/dts/exynos4210-origen.dts @@ -26,6 +26,7 @@
&sdhci2 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; cd-gpios = <&gpk2 2 0>; status = "okay"; diff --git a/arch/arm/dts/exynos4210-trats.dts b/arch/arm/dts/exynos4210-trats.dts index 05989ee97e5b..4fbdf4730994 100644 --- a/arch/arm/dts/exynos4210-trats.dts +++ b/arch/arm/dts/exynos4210-trats.dts @@ -241,6 +241,7 @@
&sdhci0 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 2 0>; status = "okay"; @@ -248,6 +249,7 @@
&sdhci2 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; cd-gpios = <&gpx3 4 0>; status = "okay"; diff --git a/arch/arm/dts/exynos4210-universal_c210.dts b/arch/arm/dts/exynos4210-universal_c210.dts index 610a8ad2e71e..1b3ac1fee15f 100644 --- a/arch/arm/dts/exynos4210-universal_c210.dts +++ b/arch/arm/dts/exynos4210-universal_c210.dts @@ -236,6 +236,7 @@
&sdhci0 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 2 0>; status = "okay"; @@ -243,6 +244,7 @@
&sdhci2 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; cd-gpios = <&gpx3 4 0>; status = "okay"; diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts index ce08e8dc1ebb..24e96ed05868 100644 --- a/arch/arm/dts/exynos4412-odroid.dts +++ b/arch/arm/dts/exynos4412-odroid.dts @@ -243,9 +243,15 @@
&mshc_0 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <2 1 0>; + samsung,dw-mshc-ciu-div = <0>; + samsung,dw-mshc-sdr-timing = <2 1>; samsung,removable = <0>; + non-removable; fifoth_val = <0x203f0040>; + fifo-depth = <0x80>; + clock-frequency = <400000000>; bus_hz = <400000000>; div = <0x3>; index = <4>; diff --git a/arch/arm/dts/exynos4412-trats2.dts b/arch/arm/dts/exynos4412-trats2.dts index c4db137e01f6..30758ffa1ef9 100644 --- a/arch/arm/dts/exynos4412-trats2.dts +++ b/arch/arm/dts/exynos4412-trats2.dts @@ -109,6 +109,7 @@
sdhci@12510000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 4 0>; status = "disabled"; @@ -432,6 +433,7 @@
&sdhci0 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 4 0>; status = "disabled"; @@ -439,6 +441,7 @@
&sdhci2 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; cd-gpios = <&gpk2 2 0>; status = "okay"; @@ -446,9 +449,14 @@
&mshc_0 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <2 1 0>; + samsung,dw-mshc-ciu-div = <0>; + samsung,dw-mshc-sdr-timing = <2 1>; samsung,removable = <0>; + non-removable; fifoth_val = <0x203f0040>; + clock-frequency = <400000000>; bus_hz = <400000000>; div = <0x3>; index = <4>; diff --git a/arch/arm/dts/exynos5250-arndale.dts b/arch/arm/dts/exynos5250-arndale.dts index 60309c61f302..7f84589c97a2 100644 --- a/arch/arm/dts/exynos5250-arndale.dts +++ b/arch/arm/dts/exynos5250-arndale.dts @@ -28,7 +28,10 @@
mmc@12200000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; };
mmc@12210000 { @@ -37,7 +40,10 @@
mmc@12220000 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 2>; };
mmc@12230000 { diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts index afe0cca48a93..882db2d1f608 100644 --- a/arch/arm/dts/exynos5250-smdk5250.dts +++ b/arch/arm/dts/exynos5250-smdk5250.dts @@ -146,8 +146,12 @@
mmc@12200000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; samsung,removable = <0>; + non-removable; };
mmc@12210000 { @@ -156,7 +160,10 @@
mmc@12220000 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 2>; samsung,removable = <1>; };
diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index e41f2d3041e2..bcf04d5c07c0 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -302,8 +302,12 @@
mmc@12200000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; samsung,removable = <0>; + non-removable; };
mmc@12210000 { @@ -312,7 +316,10 @@
mmc@12220000 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 2>; samsung,removable = <1>; };
diff --git a/arch/arm/dts/exynos5250-spring.dts b/arch/arm/dts/exynos5250-spring.dts index 77e7a6b9e45a..7270a546a795 100644 --- a/arch/arm/dts/exynos5250-spring.dts +++ b/arch/arm/dts/exynos5250-spring.dts @@ -104,8 +104,12 @@
mmc@12200000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; samsung,removable = <0>; + non-removable; };
mmc@12210000 { diff --git a/arch/arm/dts/exynos5420-smdk5420.dts b/arch/arm/dts/exynos5420-smdk5420.dts index 7a5da674fbed..1f27baafebaf 100644 --- a/arch/arm/dts/exynos5420-smdk5420.dts +++ b/arch/arm/dts/exynos5420-smdk5420.dts @@ -107,8 +107,12 @@
mmc@12200000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; samsung,removable = <0>; + non-removable; samsung,pre-init; };
@@ -118,7 +122,10 @@
mmc@12220000 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 2>; samsung,removable = <1>; };
diff --git a/arch/arm/dts/exynos5422-odroidxu3.dts b/arch/arm/dts/exynos5422-odroidxu3.dts index 9d055d066fd3..767b3e415d58 100644 --- a/arch/arm/dts/exynos5422-odroidxu3.dts +++ b/arch/arm/dts/exynos5422-odroidxu3.dts @@ -281,10 +281,12 @@
mmc@12200000 { fifoth_val = <0x201f0020>; + fifo-depth = <0x40>; };
mmc@12220000 { fifoth_val = <0x201f0020>; + fifo-depth = <0x40>; };
emmc-reset { diff --git a/arch/arm/dts/exynos54xx.dtsi b/arch/arm/dts/exynos54xx.dtsi index 221da8b4850b..fd74166c7e48 100644 --- a/arch/arm/dts/exynos54xx.dtsi +++ b/arch/arm/dts/exynos54xx.dtsi @@ -120,8 +120,12 @@
mmc@12200000 { samsung,bus-width = <8>; + bus-width = <8>; samsung,timing = <1 3 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; samsung,removable = <0>; + non-removable; samsung,pre-init; };
@@ -131,7 +135,10 @@
mmc@12220000 { samsung,bus-width = <4>; + bus-width = <4>; samsung,timing = <1 2 3>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 2>; samsung,removable = <1>; };

Update the bindings doc for Exynos DW MMC block to follow the upstream example and reflect the latest changes made in corresponding Linux kernel bindings.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- doc/device-tree-bindings/exynos/dwmmc.txt | 46 +++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-)
diff --git a/doc/device-tree-bindings/exynos/dwmmc.txt b/doc/device-tree-bindings/exynos/dwmmc.txt index 694d1959162c..d90792be8599 100644 --- a/doc/device-tree-bindings/exynos/dwmmc.txt +++ b/doc/device-tree-bindings/exynos/dwmmc.txt @@ -12,7 +12,9 @@ SOC specific and Board specific properties are channel specific. Required SoC Specific Properties:
- compatible: should be - - samsung,exynos-dwmmc: for exynos platforms + - samsung,exynos4412-dw-mshc: for Exynos4 platforms + - samsung,exynos-dwmmc: for Exynos5 platforms + - samsung,exynos7-dw-mshc-smu: for Exynos7 platforms (with SMU block)
- reg: physical base address of the controller and length of memory mapped region. @@ -23,32 +25,38 @@ Required Board Specific Properties:
- #address-cells: should be 1. - #size-cells: should be 0. -- samsung,bus-width: The width of the bus used to interface the devices +- bus-width: The width of the bus used to interface the devices supported by DWC_mobile_storage (SD-MMC/EMMC/SDIO). . Typically the bus width is 4 or 8. -- samsung,timing: The timing values to be written into the - Drv/sample clock selection register of corresponding channel. - . It is comprised of 3 values corresponding to the 3 fileds - 'SelClk_sample', 'SelClk_drv' and 'DIVRATIO' of CLKSEL register. - . SelClk_sample: Select sample clock among 8 shifted clocks. - . SelClk_drv: Select drv clock among 8 shifted clocks. - . DIVRATIO: Clock Divide ratio select. - . The above 3 values are used by the clock phase shifter. +- samsung,dw-mshc-ciu-div: The divider value for the card interface unit (ciu) + clock (0..7). +- samsung,dw-mshc-sdr-timing: The timing values for single data rate (SDR) mode + operation. + . First value is CIU clock phase shift value for TX mode (0..7). + . Second value is CIU clock phase shift value for RX mode (0..7). +- samsung,dw-mshc-ddr-timing: The timing values for double data rate (DDR) mode + operation. If missing, values from samsung,dw-mshc-sdr-timing are used. + . First value is CIU clock phase shift value for TX mode (0..7). + . Second value is CIU clock phase shift value for RX mode (0..7).
Example:
mmc@12200000 { - samsung,bus-width = <8>; - samsung,timing = <1 3 3>; - samsung,removable = <1>; -} + bus-width = <8>; + non-removable; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 3>; + samsung,dw-mshc-ddr-timing = <0 2>; +}; + In the above example, . The bus width is 8 - . Timing is comprised of 3 values as explained below + . Divider value for CLKSEL register is 3. The CIU clock rate will be + calculated as SDCLKIN / (3 + 1). + . SDR and DDR timings are comprised of 2 values as explained below 1 - SelClk_sample 3 - SelClk_drv - 3 - DIVRATIO - . The 'removable' flag indicates whether the the particilar device + . The 'non-removable' flag indicates whether the particular device cannot be removed (always present) or it is a removable device. - 1 - Indicates that the device is removable. - 0 - Indicates that the device cannot be removed. + Flag is present - Indicates that the device cannot be removed. + Flag is not present - Indicates that the device is removable.

Add missing header guard to prevent possible build errors.
Fixes: 77b55e8cfcee ("ARM: exynos: move SoC sources to mach-exynos") Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- arch/arm/mach-exynos/include/mach/dwmmc.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h index 59c28ed54c58..811e9a04c6e3 100644 --- a/arch/arm/mach-exynos/include/mach/dwmmc.h +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h @@ -4,6 +4,9 @@ * Jaehoon Chung jh80.chung@samsung.com */
+#ifndef __ASM_ARM_ARCH_DWMMC_H +#define __ASM_ARM_ARCH_DWMMC_H + #define DWMCI_CLKSEL 0x09C #define DWMCI_SET_SAMPLE_CLK(x) (x) #define DWMCI_SET_DRV_CLK(x) ((x) << 16) @@ -25,3 +28,5 @@ /* CLKSEL Register */ #define DWMCI_DIVRATIO_BIT 24 #define DWMCI_DIVRATIO_MASK 0x7 + +#endif /* __ASM_ARM_ARCH_DWMMC_H */

Getting the base address with outdated fdtdec_get_addr() API and further casting it to (void *) leads to next build warning on ARM64 platforms:
In function 'exynos_dwmci_get_config': warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] host->ioaddr = (void *)base;
Use livetree API instead (dev_read_addr_ptr()), which handles this correctly.
Fixes: a082a2dde061 ("EXYNOS5: DWMMC: Added FDT support for DWMMC") Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 14cb0c05cb55..1f69e193f601 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -156,7 +156,7 @@ static int exynos_dwmci_get_config(struct udevice *dev, const void *blob, struct dwmci_exynos_priv_data *priv) { int err = 0; - u32 base, timing[3]; + u32 timing[3];
/* Extract device id for each mmc channel */ host->dev_id = pinmux_decode_periph_id(blob, node); @@ -174,12 +174,11 @@ static int exynos_dwmci_get_config(struct udevice *dev, const void *blob, host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4);
/* Set the base address from the device node */ - base = fdtdec_get_addr(blob, node, "reg"); - if (!base) { + host->ioaddr = dev_read_addr_ptr(dev); + if (!host->ioaddr) { printf("DWMMC%d: Can't get base address\n", host->dev_index); return -EINVAL; } - host->ioaddr = (void *)base;
/* Extract the timing info from the node */ err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3);

In case of CONFIG_DM_MMC, host->priv actually holds (struct udevice *), and not (struct dwmci_exynos_priv_data *). This makes *priv pointer invalid and may lead to Synchronous Abort during its dereference later in exynos_dwmci_board_init(). Fix it by extracting exynos_dwmmc_get_priv() helper from exynos_dwmci_clksel() and using it for getting the private data in exynos_dwmci_board_init()
Fixes: 3537ee879e04 ("mmc: exynos_dw_mmc: support the Driver mode for Exynos") Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 1f69e193f601..8e80bd6a059f 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -42,18 +42,24 @@ struct dwmci_exynos_priv_data { u32 sdr_timing; };
+static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv( + struct dwmci_host *host) +{ +#ifdef CONFIG_DM_MMC + return container_of(host, struct dwmci_exynos_priv_data, host); +#else + return host->priv; +#endif +} + /* * Function used as callback function to initialise the * CLKSEL register for every mmc channel. */ static int exynos_dwmci_clksel(struct dwmci_host *host) { -#ifdef CONFIG_DM_MMC - struct dwmci_exynos_priv_data *priv = - container_of(host, struct dwmci_exynos_priv_data, host); -#else - struct dwmci_exynos_priv_data *priv = host->priv; -#endif + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); + dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing);
return 0; @@ -83,7 +89,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
static void exynos_dwmci_board_init(struct dwmci_host *host) { - struct dwmci_exynos_priv_data *priv = host->priv; + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) { dwmci_writel(host, EMMCP_MPSBEGIN0, 0);

Pinmux configuration on ARM64 platforms must be performed during startup in pinctrl driver using info from device tree. exynos_pinmux_config() and pinmux_decode_periph_id() are only available on ARM32 platforms, so don't call those functions on ARM64 platforms. Instead of the latter function, use "non-removable" property from device tree to derive the dev_index value.
This fixes next linking errors on ARM64 platforms:
ld: drivers/mmc/exynos_dw_mmc.o: in function `exynos_dwmci_get_config': undefined reference to `pinmux_decode_periph_id' ld: drivers/mmc/exynos_dw_mmc.o: in function `do_dwmci_init': undefined reference to `exynos_pinmux_config'
Fixes: a082a2dde061 ("EXYNOS5: DWMMC: Added FDT support for DWMMC") Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 8e80bd6a059f..6d0872e0df50 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -145,6 +145,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host)
static int do_dwmci_init(struct dwmci_host *host) { +#if CONFIG_IS_ENABLED(CPU_V7A) int flag, err;
flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; @@ -153,6 +154,7 @@ static int do_dwmci_init(struct dwmci_host *host) printf("DWMMC%d not configure\n", host->dev_index); return err; } +#endif
return exynos_dwmci_core_init(host); } @@ -164,6 +166,7 @@ static int exynos_dwmci_get_config(struct udevice *dev, const void *blob, int err = 0; u32 timing[3];
+#if CONFIG_IS_ENABLED(CPU_V7A) /* Extract device id for each mmc channel */ host->dev_id = pinmux_decode_periph_id(blob, node);
@@ -175,6 +178,12 @@ static int exynos_dwmci_get_config(struct udevice *dev, const void *blob, printf("DWMMC%d: Can't get the dev index\n", host->dev_index); return -EINVAL; } +#else + if (dev_read_bool(dev, "non-removable")) + host->dev_index = 0; /* eMMC */ + else + host->dev_index = 2; /* SD card */ +#endif
/* Get the bus width from the device node (Default is 4bit buswidth) */ host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4);

New Exynos chips should implement clock drivers using CCF framework. In that case corresponding CCF functions can be used to get/set the clock rates. Moreover, already existing get_mmc_clk() and set_mmc_clk() calls are only implemented for CONFIG_CPU_V7A (i.e. ARM32 chips). In case of ARM64 chips that config option is not defined, so build will crash on linking stage, with errors like these:
ld: drivers/mmc/exynos_dw_mmc.o: in function `exynos_dwmci_get_sclk': undefined reference to `get_mmc_clk' ld: drivers/mmc/exynos_dw_mmc.o: in function `exynos_dwmci_set_sclk': undefined reference to `set_mmc_clk'
Fix that issue by using CCF clocks API on ARM64 platforms for getting and setting the source clock (sclk = SDCLKIN = CIU) rate. To implement this, first extract the existing ARM32 clock control code into helper functions with more generic signatures to abstract getting/setting the sclk rate. Then add CCF clock support to those functions for ARM64 platforms.
Fixes: a082a2dde061 ("EXYNOS5: DWMMC: Added FDT support for DWMMC") Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 87 +++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 8 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 6d0872e0df50..8a3f73e3739c 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -4,6 +4,7 @@ * Jaehoon Chung jh80.chung@samsung.com */
+#include <clk.h> #include <common.h> #include <dwmmc.h> #include <fdtdec.h> @@ -16,6 +17,7 @@ #include <asm/arch/pinmux.h> #include <asm/arch/power.h> #include <asm/gpio.h> +#include <linux/err.h> #include <linux/printk.h>
#define DWMMC_MAX_CH_NUM 4 @@ -39,6 +41,7 @@ struct dwmci_exynos_priv_data { #ifdef CONFIG_DM_MMC struct dwmci_host host; #endif + struct clk clk; u32 sdr_timing; };
@@ -52,6 +55,61 @@ static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv( #endif }
+/** + * exynos_dwmmc_get_sclk - Get source clock (SDCLKIN) rate + * @host: MMC controller object + * @rate: Will contain clock rate, Hz + * + * Return: 0 on success or negative value on error + */ +static int exynos_dwmmc_get_sclk(struct dwmci_host *host, unsigned long *rate) +{ +#if CONFIG_IS_ENABLED(CPU_V7A) + *rate = get_mmc_clk(host->dev_index); +#else + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); + + *rate = clk_get_rate(&priv->clk); +#endif + + if (IS_ERR_VALUE(*rate)) + return *rate; + + return 0; +} + +/** + * exynos_dwmmc_set_sclk - Set source clock (SDCLKIN) rate + * @host: MMC controller object + * @rate: Desired clock rate, Hz + * + * Return: 0 on success or negative value on error + */ +static int exynos_dwmmc_set_sclk(struct dwmci_host *host, unsigned long rate) +{ + int err; + +#if CONFIG_IS_ENABLED(CPU_V7A) + unsigned long sclk; + unsigned int div; + + err = exynos_dwmmc_get_sclk(host, &sclk); + if (err) + return err; + + div = DIV_ROUND_UP(sclk, rate); + set_mmc_clk(host->dev_index, div); +#else + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); + + err = clk_set_rate(&priv->clk, rate); + if (err < 0) + return err; +#endif + + return 0; +} + /* * Function used as callback function to initialise the * CLKSEL register for every mmc channel. @@ -69,6 +127,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { unsigned long sclk; int8_t clk_div; + int err;
/* * Since SDCLKIN is divided inside controller by the DIVRATIO @@ -78,7 +137,13 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) */ clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT) & DWMCI_DIVRATIO_MASK) + 1; - sclk = get_mmc_clk(host->dev_index); + + err = exynos_dwmmc_get_sclk(host, &sclk); + if (err) { + printf("DWMMC%d: failed to get clock rate (%d)\n", + host->dev_index, err); + return 0; + }
/* * Assume to know divider value. @@ -108,19 +173,19 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
static int exynos_dwmci_core_init(struct dwmci_host *host) { - unsigned int div; - unsigned long freq, sclk; + unsigned long freq; + int err;
if (host->bus_hz) freq = host->bus_hz; else freq = DWMMC_MAX_FREQ;
- /* request mmc clock vlaue of 52MHz. */ - sclk = get_mmc_clk(host->dev_index); - div = DIV_ROUND_UP(sclk, freq); - /* set the clock divisor for mmc */ - set_mmc_clk(host->dev_index, div); + err = exynos_dwmmc_set_sclk(host, freq); + if (err) { + printf("DWMMC%d: failed to set clock rate on probe (%d); " + "continue anyway\n", host->dev_index, err); + }
host->name = "EXYNOS DWMMC"; #ifdef CONFIG_EXYNOS5420 @@ -231,6 +296,12 @@ static int exynos_dwmmc_probe(struct udevice *dev) struct dwmci_host *host = &priv->host; int err;
+#if !CONFIG_IS_ENABLED(CPU_V7A) + err = clk_get_by_index(dev, 1, &priv->clk); /* ciu */ + if (err) + return err; +#endif + err = exynos_dwmci_get_config(dev, gd->fdt_blob, dev_of_offset(dev), host, priv); if (err)

exynos_dwmci_get_config() is called from the probe function and used to read data from device tree. Make use of .of_to_plat driver callback instead, and convert exynos_dwmci_get_config() to match its signature.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 8a3f73e3739c..34485311c107 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -224,10 +224,12 @@ static int do_dwmci_init(struct dwmci_host *host) return exynos_dwmci_core_init(host); }
-static int exynos_dwmci_get_config(struct udevice *dev, const void *blob, - int node, struct dwmci_host *host, - struct dwmci_exynos_priv_data *priv) +static int exynos_dwmmc_of_to_plat(struct udevice *dev) { + const void *blob = gd->fdt_blob; + struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + int node = dev_of_offset(dev); int err = 0; u32 timing[3];
@@ -302,10 +304,6 @@ static int exynos_dwmmc_probe(struct udevice *dev) return err; #endif
- err = exynos_dwmci_get_config(dev, gd->fdt_blob, dev_of_offset(dev), - host, priv); - if (err) - return err; err = do_dwmci_init(host); if (err) return err; @@ -336,6 +334,7 @@ U_BOOT_DRIVER(exynos_dwmmc_drv) = { .name = "exynos_dwmmc", .id = UCLASS_MMC, .of_match = exynos_dwmmc_ids, + .of_to_plat = exynos_dwmmc_of_to_plat, .bind = exynos_dwmmc_bind, .ops = &dm_dwmci_ops, .probe = exynos_dwmmc_probe,

Update the driver to use livetree API instead of FDT one.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 34485311c107..84382c606a1f 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -7,9 +7,7 @@ #include <clk.h> #include <common.h> #include <dwmmc.h> -#include <fdtdec.h> #include <asm/global_data.h> -#include <linux/libfdt.h> #include <malloc.h> #include <errno.h> #include <asm/arch/dwmmc.h> @@ -226,18 +224,19 @@ static int do_dwmci_init(struct dwmci_host *host)
static int exynos_dwmmc_of_to_plat(struct udevice *dev) { - const void *blob = gd->fdt_blob; struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; - int node = dev_of_offset(dev); int err = 0; u32 timing[3];
#if CONFIG_IS_ENABLED(CPU_V7A) + const void *blob = gd->fdt_blob; + int node = dev_of_offset(dev); + /* Extract device id for each mmc channel */ host->dev_id = pinmux_decode_periph_id(blob, node);
- host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id); + host->dev_index = dev_read_u32_default(dev, "index", host->dev_id); if (host->dev_index == host->dev_id) host->dev_index = host->dev_id - PERIPH_ID_SDMMC0;
@@ -253,7 +252,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) #endif
/* Get the bus width from the device node (Default is 4bit buswidth) */ - host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4); + host->buswidth = dev_read_u32_default(dev, "samsung,bus-width", 4);
/* Set the base address from the device node */ host->ioaddr = dev_read_addr_ptr(dev); @@ -263,7 +262,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) }
/* Extract the timing info from the node */ - err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3); + err = dev_read_u32_array(dev, "samsung,timing", timing, 3); if (err) { printf("DWMMC%d: Can't get sdr-timings for devider\n", host->dev_index); @@ -283,8 +282,8 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) }
host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); - host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0); - host->div = fdtdec_get_int(blob, node, "div", 0); + host->bus_hz = dev_read_u32_default(dev, "bus_hz", 0); + host->div = dev_read_u32_default(dev, "div", 0);
return 0; }

The obsolete "samsung,timing" dts property is now split into "samsung,dw-mshc-ciu-div" (for holding the internal DW MMC divider value) and "samsung,dw-mshc-sdr-timing" (for actual timing values) in upstream Linux kernel. Rework the driver to make use of new properties instead of the old one. All affected dts files were already updated accordingly.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 84382c606a1f..788587b622ca 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -227,7 +227,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; int err = 0; - u32 timing[3]; + u32 div, timing[2];
#if CONFIG_IS_ENABLED(CPU_V7A) const void *blob = gd->fdt_blob; @@ -262,16 +262,16 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) }
/* Extract the timing info from the node */ - err = dev_read_u32_array(dev, "samsung,timing", timing, 3); + div = dev_read_u32_default(dev, "samsung,dw-mshc-ciu-div", 0); + err = dev_read_u32_array(dev, "samsung,dw-mshc-sdr-timing", timing, 2); if (err) { - printf("DWMMC%d: Can't get sdr-timings for devider\n", - host->dev_index); + printf("DWMMC%d: Can't get sdr-timings\n", host->dev_index); return -EINVAL; }
- priv->sdr_timing = (DWMCI_SET_SAMPLE_CLK(timing[0]) | - DWMCI_SET_DRV_CLK(timing[1]) | - DWMCI_SET_DIV_RATIO(timing[2])); + priv->sdr_timing = DWMCI_SET_SAMPLE_CLK(timing[0]) | + DWMCI_SET_DRV_CLK(timing[1]) | + DWMCI_SET_DIV_RATIO(div);
/* sdr_timing didn't assigned anything, use the default value */ if (!priv->sdr_timing) {

CLKSEL register offset may vary between different Exynos chips, e.g. on ARM64 vs ARM32 chips. Provide a way to specify its offset value for each compatible instead of hard-coding its value in read/write calls.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 788587b622ca..edf8fbc7d734 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -34,6 +34,11 @@ struct exynos_mmc_plat { }; #endif
+/* Chip specific data */ +struct exynos_dwmmc_variant { + u32 clksel; /* CLKSEL register offset */ +}; + /* Exynos implmentation specific drver private data */ struct dwmci_exynos_priv_data { #ifdef CONFIG_DM_MMC @@ -41,6 +46,7 @@ struct dwmci_exynos_priv_data { #endif struct clk clk; u32 sdr_timing; + const struct exynos_dwmmc_variant *chip; };
static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv( @@ -116,13 +122,14 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) { struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
- dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); + dwmci_writel(host, priv->chip->clksel, priv->sdr_timing);
return 0; }
unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); unsigned long sclk; int8_t clk_div; int err; @@ -133,7 +140,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) * clock value to calculate the CLKDIV value. * as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1) */ - clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT) + clk_div = ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT) & DWMCI_DIVRATIO_MASK) + 1;
err = exynos_dwmmc_get_sclk(host, &sclk); @@ -229,6 +236,8 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) int err = 0; u32 div, timing[2];
+ priv->chip = (struct exynos_dwmmc_variant *)dev_get_driver_data(dev); + #if CONFIG_IS_ENABLED(CPU_V7A) const void *blob = gd->fdt_blob; int node = dev_of_offset(dev); @@ -323,9 +332,22 @@ static int exynos_dwmmc_bind(struct udevice *dev) return dwmci_bind(dev, &plat->mmc, &plat->cfg); }
+static const struct exynos_dwmmc_variant exynos4_drv_data = { + .clksel = DWMCI_CLKSEL, +}; + +static const struct exynos_dwmmc_variant exynos5_drv_data = { + .clksel = DWMCI_CLKSEL, +}; + static const struct udevice_id exynos_dwmmc_ids[] = { - { .compatible = "samsung,exynos4412-dw-mshc" }, - { .compatible = "samsung,exynos-dwmmc" }, + { + .compatible = "samsung,exynos4412-dw-mshc", + .data = (ulong)&exynos4_drv_data, + }, { + .compatible = "samsung,exynos-dwmmc", + .data = (ulong)&exynos5_drv_data, + }, { } };

Some chips like Exynos4412 have fixed internal CIU clock divider. Instead of reading it from non-standard "div" dts property, store its value in the driver internally, in static chip data associated with corresponding compatible. This makes it possible to avoid using host->div for storing it, so the latter can be removed safely. Also create a helper function called exynos_dwmmc_get_ciu_div() for getting the current div value: in case the fixed div is provided in the chip data it will be used, otherwise the current div value is being read from CLKSEL register.
The insights for this change were taken from dw_mmc-exynos.c driver in Linux kernel.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/ca_dw_mmc.c | 2 +- drivers/mmc/exynos_dw_mmc.c | 43 +++++++++++++++++++++++++------------ include/dwmmc.h | 2 -- 3 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c index a17ed8c11cbe..342e09d02306 100644 --- a/drivers/mmc/ca_dw_mmc.c +++ b/drivers/mmc/ca_dw_mmc.c @@ -87,7 +87,7 @@ unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, uint freq) clk_div = 1; }
- return SD_SCLK_MAX / clk_div / (host->div + 1); + return SD_SCLK_MAX / clk_div; }
static int ca_dwmmc_of_to_plat(struct udevice *dev) diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index edf8fbc7d734..646302f54ae6 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -24,6 +24,8 @@ #define DWMMC_MMC0_SDR_TIMING_VAL 0x03030001 #define DWMMC_MMC2_SDR_TIMING_VAL 0x03020001
+#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 + #ifdef CONFIG_DM_MMC #include <dm.h> DECLARE_GLOBAL_DATA_PTR; @@ -37,6 +39,7 @@ struct exynos_mmc_plat { /* Chip specific data */ struct exynos_dwmmc_variant { u32 clksel; /* CLKSEL register offset */ + u8 div; /* (optional) fixed clock divider value: 0..7 */ };
/* Exynos implmentation specific drver private data */ @@ -127,12 +130,18 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) return 0; }
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) +/** + * exynos_dwmmc_get_ciu_div - Get internal clock divider value + * @host: MMC controller object + * + * Returns: Divider value, in range of 1..8 + */ +static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host) { struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); - unsigned long sclk; - int8_t clk_div; - int err; + + if (priv->chip->div) + return priv->chip->div + 1;
/* * Since SDCLKIN is divided inside controller by the DIVRATIO @@ -140,9 +149,17 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) * clock value to calculate the CLKDIV value. * as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1) */ - clk_div = ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT) - & DWMCI_DIVRATIO_MASK) + 1; + return ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT) + & DWMCI_DIVRATIO_MASK) + 1; +}
+unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) +{ + unsigned long sclk; + u8 clk_div; + int err; + + clk_div = exynos_dwmmc_get_ciu_div(host); err = exynos_dwmmc_get_sclk(host, &sclk); if (err) { printf("DWMMC%d: failed to get clock rate (%d)\n", @@ -150,11 +167,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) return 0; }
- /* - * Assume to know divider value. - * When clock unit is broken, need to set "host->div" - */ - return sclk / clk_div / (host->div + 1); + return sclk / clk_div; }
static void exynos_dwmci_board_init(struct dwmci_host *host) @@ -270,8 +283,10 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) return -EINVAL; }
- /* Extract the timing info from the node */ - div = dev_read_u32_default(dev, "samsung,dw-mshc-ciu-div", 0); + if (priv->chip->div) + div = priv->chip->div; + else + div = dev_read_u32_default(dev, "samsung,dw-mshc-ciu-div", 0); err = dev_read_u32_array(dev, "samsung,dw-mshc-sdr-timing", timing, 2); if (err) { printf("DWMMC%d: Can't get sdr-timings\n", host->dev_index); @@ -292,7 +307,6 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev)
host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); host->bus_hz = dev_read_u32_default(dev, "bus_hz", 0); - host->div = dev_read_u32_default(dev, "div", 0);
return 0; } @@ -334,6 +348,7 @@ static int exynos_dwmmc_bind(struct udevice *dev)
static const struct exynos_dwmmc_variant exynos4_drv_data = { .clksel = DWMCI_CLKSEL, + .div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1, };
static const struct exynos_dwmmc_variant exynos5_drv_data = { diff --git a/include/dwmmc.h b/include/dwmmc.h index e17884ade7e6..c69c7952b930 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -184,7 +184,6 @@ struct dwmci_idmac_regs { * @caps: Capabilities - see MMC_MODE_... * @clock: Current clock frequency (after internal divider), Hz * @bus_hz: Bus speed in Hz, if @get_mmc_clk() is NULL - * @div: Arbitrary clock divider value for use by controller * @dev_index: Arbitrary device index for use by controller * @dev_id: Arbitrary device ID for use by controller * @buswidth: Bus width in bits (8 or 4) @@ -205,7 +204,6 @@ struct dwmci_host { unsigned int caps; unsigned int clock; unsigned int bus_hz; - unsigned int div; int dev_index; int dev_id; int buswidth;

Instead of using non-standard "samsung,bus-width" dts property, read common "bus-width" property used in upstream Linux kernel. It's safe to do so, as "bus-width" property was already added to corresponding nodes in all affected Exynos device tree files.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 2 +- drivers/mmc/s5p_sdhci.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 646302f54ae6..b046ac6c55ea 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -274,7 +274,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) #endif
/* Get the bus width from the device node (Default is 4bit buswidth) */ - host->buswidth = dev_read_u32_default(dev, "samsung,bus-width", 4); + host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
/* Set the base address from the device node */ host->ioaddr = dev_read_addr_ptr(dev); diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 3b74feae68c7..0b49a9c5f2a9 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -167,7 +167,7 @@ static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) host->index = dev_id - PERIPH_ID_SDMMC0;
/* Get bus width */ - bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); + bus_width = fdtdec_get_int(blob, node, "bus-width", 0); if (bus_width <= 0) { debug("MMC: Can't get bus-width\n"); return -EINVAL;

Instead of using non-standard "bus_hz" dts property, read common "clock-frequency" property used in upstream Linux kernel. It's safe to do so, as "clock-frequency" property was already added to corresponding nodes in all affected Exynos device tree files.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index b046ac6c55ea..8a307c9c123b 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -306,7 +306,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) }
host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); - host->bus_hz = dev_read_u32_default(dev, "bus_hz", 0); + host->bus_hz = dev_read_u32_default(dev, "clock-frequency", 0);
return 0; }

host->quirks field is only used internally in exynos_dw_mmc.c driver. To avoid cluttering the scope of struct dwmci_host, move quirks field into Exynos driver's chip data, where it can be statically defined.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 13 ++++++++----- include/dwmmc.h | 5 ----- 2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 8a307c9c123b..91300f67b9fe 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -26,6 +26,9 @@
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
+/* Quirks */ +#define DWMCI_QUIRK_DISABLE_SMU BIT(0) + #ifdef CONFIG_DM_MMC #include <dm.h> DECLARE_GLOBAL_DATA_PTR; @@ -40,6 +43,7 @@ struct exynos_mmc_plat { struct exynos_dwmmc_variant { u32 clksel; /* CLKSEL register offset */ u8 div; /* (optional) fixed clock divider value: 0..7 */ + u32 quirks; /* quirk flags - see DWMCI_QUIRK_... */ };
/* Exynos implmentation specific drver private data */ @@ -174,7 +178,7 @@ static void exynos_dwmci_board_init(struct dwmci_host *host) { struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
- if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) { + if (priv->chip->quirks & DWMCI_QUIRK_DISABLE_SMU) { dwmci_writel(host, EMMCP_MPSBEGIN0, 0); dwmci_writel(host, EMMCP_SEND0, 0); dwmci_writel(host, EMMCP_CTRL0, @@ -206,11 +210,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host) }
host->name = "EXYNOS DWMMC"; -#ifdef CONFIG_EXYNOS5420 - host->quirks = DWMCI_QUIRK_DISABLE_SMU; -#endif host->board_init = exynos_dwmci_board_init; - host->caps = MMC_MODE_DDR_52MHz; host->clksel = exynos_dwmci_clksel; host->get_mmc_clk = exynos_dwmci_get_clk; @@ -353,6 +353,9 @@ static const struct exynos_dwmmc_variant exynos4_drv_data = {
static const struct exynos_dwmmc_variant exynos5_drv_data = { .clksel = DWMCI_CLKSEL, +#if CONFIG_IS_ENABLED(EXYNOS5420) + .quirks = DWMCI_QUIRK_DISABLE_SMU, +#endif };
static const struct udevice_id exynos_dwmmc_ids[] = { diff --git a/include/dwmmc.h b/include/dwmmc.h index c69c7952b930..a7e8709b7b05 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -149,9 +149,6 @@ #define DWMCI_IDINTEN_TI BIT(0) #define DWMCI_IDINTEN_MASK (DWMCI_IDINTEN_TI | DWMCI_IDINTEN_RI)
-/* Quirks */ -#define DWMCI_QUIRK_DISABLE_SMU BIT(0) - /** * struct dwmci_idmac_regs - Offsets of IDMAC registers * @@ -180,7 +177,6 @@ struct dwmci_idmac_regs { * * @name: Device name * @ioaddr: Base I/O address of controller - * @quirks: Quick flags - see DWMCI_QUIRK_... * @caps: Capabilities - see MMC_MODE_... * @clock: Current clock frequency (after internal divider), Hz * @bus_hz: Bus speed in Hz, if @get_mmc_clk() is NULL @@ -200,7 +196,6 @@ struct dwmci_idmac_regs { struct dwmci_host { const char *name; void *ioaddr; - unsigned int quirks; unsigned int caps; unsigned int clock; unsigned int bus_hz;

DDR timing values should be defined in "samsung,dw-mshc-ddr-timing" dts property, and used when DDR MMC mode is selected. Read that value from dts and use it. If it's not available, use SDR timing values instead. This change is following upstream Linux kernel implementation.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 91300f67b9fe..d3708ecb6407 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -53,6 +53,7 @@ struct dwmci_exynos_priv_data { #endif struct clk clk; u32 sdr_timing; + u32 ddr_timing; const struct exynos_dwmmc_variant *chip; };
@@ -128,8 +129,14 @@ static int exynos_dwmmc_set_sclk(struct dwmci_host *host, unsigned long rate) static int exynos_dwmci_clksel(struct dwmci_host *host) { struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); + u32 timing;
- dwmci_writel(host, priv->chip->clksel, priv->sdr_timing); + if (host->mmc->selected_mode == MMC_DDR_52) + timing = priv->ddr_timing; + else + timing = priv->sdr_timing; + + dwmci_writel(host, priv->chip->clksel, timing);
return 0; } @@ -305,6 +312,17 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL; }
+ err = dev_read_u32_array(dev, "samsung,dw-mshc-ddr-timing", timing, 2); + if (err) { + debug("DWMMC%d: Can't get ddr-timings, using sdr-timings\n", + host->dev_index); + priv->ddr_timing = priv->sdr_timing; + } else { + priv->ddr_timing = DWMCI_SET_SAMPLE_CLK(timing[0]) | + DWMCI_SET_DRV_CLK(timing[1]) | + DWMCI_SET_DIV_RATIO(div); + } + host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); host->bus_hz = dev_read_u32_default(dev, "clock-frequency", 0);

By now exynos_dw_mmc driver was relying on the correct CIU clock frequency being set on driver init. But dw_mmc core is actually trying to change CIU clock rate dynamically, on init and in set_ios() callback, which it's requesting via host->get_mmc_clk() callback (the name is misleading: although it's called "get_mmc_clk()", it can actually request both get and set operations). Implement setting the requested rate for CIU clock in Exynos driver to achieve the correct dw_mmc core driver operation at all times. DDR mode requires the clock to be twice as fast (when 8 bit bus is used), so handle this too, to make DDR function properly.
This change makes the eMMC throughput on E850-96 board twice as fast. That's because "clock-frequency" is set to 800 MHz in E850-96 device tree, but for DDR52 mode it should be 416 MHz (and TRM states it should be 400 MHz for DDR50/8bit mode). The dw_mmc core is requesting 52 MHz bus_hz for DDR52 mode, and DDR+8bit mode means it should be x2 fast, so:
f_ciu = 2 * ciu_div * f_bus = 2 * 4 * 52e6 = 416 MHz,
where f_ciu - freq of clock fed to DW MMC block from CMU (SDCLKIN), Hz f_bus - freq of clock fed to the card (CCLKIN), Hz ciu_div - value of internal divider (in DW MMC block).
Another way to work that around would be overriding the "clock-frequency" property in corresponding dts. But setting the clock frequency dynamically as it's done here looks much neater.
This implementation follows what's done in Linux kernel dw_mmc-exynos driver in .set_ios() callback for MMC_TIMING_MMC_DDR52 case.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index d3708ecb6407..938fd51a2ac6 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -170,7 +170,17 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) u8 clk_div; int err;
+ /* Should be double rate for DDR mode */ + if (host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8) + freq *= 2; + clk_div = exynos_dwmmc_get_ciu_div(host); + err = exynos_dwmmc_set_sclk(host, freq * clk_div); + if (err) { + printf("DWMMC%d: failed to set clock rate (%d); " + "continue anyway\n", host->dev_index, err); + } + err = exynos_dwmmc_get_sclk(host, &sclk); if (err) { printf("DWMMC%d: failed to get clock rate (%d)\n",

Add the compatible entry and corresponding chip data for Exynos7 compatible chips, which covers modern ARM64 based Exynos chips. They have some differences w.r.t. old ARM32 Exynos chips: - CLKSEL register offset is different - 64-bit IDMAC descriptor and 64-bit IDMAC registers are used (implemented in dw_mmc core driver)
In terms of the driver implementation, the CIU clock is obtained via CCF framework (as opposed to ad-hoc clock driver implementation for ARM32 chips).
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- arch/arm/mach-exynos/include/mach/dwmmc.h | 1 + drivers/mmc/exynos_dw_mmc.c | 8 ++++++++ 2 files changed, 9 insertions(+)
diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h index 811e9a04c6e3..7cb71be0d9fd 100644 --- a/arch/arm/mach-exynos/include/mach/dwmmc.h +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h @@ -8,6 +8,7 @@ #define __ASM_ARM_ARCH_DWMMC_H
#define DWMCI_CLKSEL 0x09C +#define DWMCI_CLKSEL64 0x0a8 #define DWMCI_SET_SAMPLE_CLK(x) (x) #define DWMCI_SET_DRV_CLK(x) ((x) << 16) #define DWMCI_SET_DIV_RATIO(x) ((x) << 24) diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 938fd51a2ac6..ac1a1784b440 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -386,6 +386,11 @@ static const struct exynos_dwmmc_variant exynos5_drv_data = { #endif };
+static const struct exynos_dwmmc_variant exynos7_smu_drv_data = { + .clksel = DWMCI_CLKSEL64, + .quirks = DWMCI_QUIRK_DISABLE_SMU, +}; + static const struct udevice_id exynos_dwmmc_ids[] = { { .compatible = "samsung,exynos4412-dw-mshc", @@ -393,6 +398,9 @@ static const struct udevice_id exynos_dwmmc_ids[] = { }, { .compatible = "samsung,exynos-dwmmc", .data = (ulong)&exynos5_drv_data, + }, { + .compatible = "samsung,exynos7-dw-mshc-smu", + .data = (ulong)&exynos7_smu_drv_data, }, { } };

common.h header is marked for removal treewide and shouldn't be used. Remove it from Exynos DW MMC driver.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index ac1a1784b440..e416fbe5397a 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -5,7 +5,6 @@ */
#include <clk.h> -#include <common.h> #include <dwmmc.h> #include <asm/global_data.h> #include <malloc.h>

Use CONFIG_IS_ENABLED() macro to check config options as recommended by checkpatch, instead of checking those with just #ifdef CONFIG_...
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index e416fbe5397a..b7c3b356f0ac 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -28,7 +28,7 @@ /* Quirks */ #define DWMCI_QUIRK_DISABLE_SMU BIT(0)
-#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) #include <dm.h> DECLARE_GLOBAL_DATA_PTR;
@@ -47,7 +47,7 @@ struct exynos_dwmmc_variant {
/* Exynos implmentation specific drver private data */ struct dwmci_exynos_priv_data { -#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) struct dwmci_host host; #endif struct clk clk; @@ -59,7 +59,7 @@ struct dwmci_exynos_priv_data { static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv( struct dwmci_host *host) { -#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) return container_of(host, struct dwmci_exynos_priv_data, host); #else return host->priv; @@ -231,7 +231,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host) host->clksel = exynos_dwmci_clksel; host->get_mmc_clk = exynos_dwmci_get_clk;
-#ifndef CONFIG_DM_MMC +#if !CONFIG_IS_ENABLED(DM_MMC) /* Add the mmc channel to be registered with mmc core */ if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { printf("DWMMC%d registration failed\n", host->dev_index); @@ -338,7 +338,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) return 0; }
-#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) static int exynos_dwmmc_probe(struct udevice *dev) { struct exynos_mmc_plat *plat = dev_get_plat(dev);

There is no logical sense to split the initialization code between multiple functions. Pull both do_dwmci_init() and exynos_dwmci_core_init() into exynos_dwmmc_probe() to make the code more simple and obvious.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 86 +++++++++++++++---------------------- 1 file changed, 35 insertions(+), 51 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index b7c3b356f0ac..19793e7ad460 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -209,55 +209,6 @@ static void exynos_dwmci_board_init(struct dwmci_host *host) exynos_dwmci_clksel(host); }
-static int exynos_dwmci_core_init(struct dwmci_host *host) -{ - unsigned long freq; - int err; - - if (host->bus_hz) - freq = host->bus_hz; - else - freq = DWMMC_MAX_FREQ; - - err = exynos_dwmmc_set_sclk(host, freq); - if (err) { - printf("DWMMC%d: failed to set clock rate on probe (%d); " - "continue anyway\n", host->dev_index, err); - } - - host->name = "EXYNOS DWMMC"; - host->board_init = exynos_dwmci_board_init; - host->caps = MMC_MODE_DDR_52MHz; - host->clksel = exynos_dwmci_clksel; - host->get_mmc_clk = exynos_dwmci_get_clk; - -#if !CONFIG_IS_ENABLED(DM_MMC) - /* Add the mmc channel to be registered with mmc core */ - if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { - printf("DWMMC%d registration failed\n", host->dev_index); - return -1; - } -#endif - - return 0; -} - -static int do_dwmci_init(struct dwmci_host *host) -{ -#if CONFIG_IS_ENABLED(CPU_V7A) - int flag, err; - - flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; - err = exynos_pinmux_config(host->dev_id, flag); - if (err) { - printf("DWMMC%d not configure\n", host->dev_index); - return err; - } -#endif - - return exynos_dwmci_core_init(host); -} - static int exynos_dwmmc_of_to_plat(struct udevice *dev) { struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); @@ -345,6 +296,7 @@ static int exynos_dwmmc_probe(struct udevice *dev) struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; + unsigned long freq; int err;
#if !CONFIG_IS_ENABLED(CPU_V7A) @@ -353,9 +305,41 @@ static int exynos_dwmmc_probe(struct udevice *dev) return err; #endif
- err = do_dwmci_init(host); - if (err) +#if CONFIG_IS_ENABLED(CPU_V7A) + int flag; + + flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; + err = exynos_pinmux_config(host->dev_id, flag); + if (err) { + printf("DWMMC%d not configure\n", host->dev_index); return err; + } +#endif + + if (host->bus_hz) + freq = host->bus_hz; + else + freq = DWMMC_MAX_FREQ; + + err = exynos_dwmmc_set_sclk(host, freq); + if (err) { + printf("DWMMC%d: failed to set clock rate on probe (%d); " + "continue anyway\n", host->dev_index, err); + } + + host->name = "EXYNOS DWMMC"; + host->board_init = exynos_dwmci_board_init; + host->caps = MMC_MODE_DDR_52MHz; + host->clksel = exynos_dwmci_clksel; + host->get_mmc_clk = exynos_dwmci_get_clk; + +#if !CONFIG_IS_ENABLED(DM_MMC) + /* Add the mmc channel to be registered with mmc core */ + if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { + printf("DWMMC%d registration failed\n", host->dev_index); + return -1; + } +#endif
dwmci_setup_cfg(&plat->cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ); host->mmc = &plat->mmc;

add_dwmci() is already calling dwmci_setup_cfg() internally, there is no needed to call dwmci_setup_cfg() again in case when add_dwmci() is used (for non-DM cases). Fix it by calling dwmci_setup_cfg() only in DM cases, when add_dwmci() wasn't called. Also, this assignment:
host->mmc = &plat->mmc;
is wrong in non-DM case when add_dwmci() was called, as it's creating mmc object internally. Fix that by pulling that assignment into DM case, when add_dwmci() isn't called.
While at it, add also this missing assignment:
host->mmc->dev = dev;
Fixes: 3537ee879e04 ("mmc: exynos_dw_mmc: support the Driver mode for Exynos") Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 19793e7ad460..373dd2df206e 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -333,19 +333,21 @@ static int exynos_dwmmc_probe(struct udevice *dev) host->clksel = exynos_dwmci_clksel; host->get_mmc_clk = exynos_dwmci_get_clk;
-#if !CONFIG_IS_ENABLED(DM_MMC) - /* Add the mmc channel to be registered with mmc core */ - if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { +#if CONFIG_IS_ENABLED(BLK) + dwmci_setup_cfg(&plat->cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ); + host->mmc = &plat->mmc; +#else + err = add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ); + if (err) { printf("DWMMC%d registration failed\n", host->dev_index); - return -1; + return err; } #endif
- dwmci_setup_cfg(&plat->cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ); - host->mmc = &plat->mmc; host->mmc->priv = &priv->host; - host->priv = dev; upriv->mmc = host->mmc; + host->mmc->dev = dev; + host->priv = dev;
return dwmci_probe(dev); }

Reduce U-Boot footprint by reusing dev->name as a driver's displayed name. This changes boot device name (and "mmc info" output) from "EXYNOS DWMMC" to something like "mmc@12100000".
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- drivers/mmc/exynos_dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 373dd2df206e..84a079256bca 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -327,7 +327,7 @@ static int exynos_dwmmc_probe(struct udevice *dev) "continue anyway\n", host->dev_index, err); }
- host->name = "EXYNOS DWMMC"; + host->name = dev->name; host->board_init = exynos_dwmci_board_init; host->caps = MMC_MODE_DDR_52MHz; host->clksel = exynos_dwmci_clksel;

Fix most of checkpatch warnings and other obvious style issues.
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- arch/arm/mach-exynos/include/mach/dwmmc.h | 36 +++++++++++++---------- drivers/mmc/exynos_dw_mmc.c | 26 ++++++---------- 2 files changed, 29 insertions(+), 33 deletions(-)
diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h index 7cb71be0d9fd..75d84988b7d6 100644 --- a/arch/arm/mach-exynos/include/mach/dwmmc.h +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h @@ -7,24 +7,28 @@ #ifndef __ASM_ARM_ARCH_DWMMC_H #define __ASM_ARM_ARCH_DWMMC_H
-#define DWMCI_CLKSEL 0x09C -#define DWMCI_CLKSEL64 0x0a8 -#define DWMCI_SET_SAMPLE_CLK(x) (x) -#define DWMCI_SET_DRV_CLK(x) ((x) << 16) -#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#include <linux/bitops.h>
-#define EMMCP_MPSBEGIN0 0x1200 -#define EMMCP_SEND0 0x1204 -#define EMMCP_CTRL0 0x120C +#define DWMCI_CLKSEL 0x09c +#define DWMCI_CLKSEL64 0x0a8 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
-#define MPSCTRL_SECURE_READ_BIT (0x1<<7) -#define MPSCTRL_SECURE_WRITE_BIT (0x1<<6) -#define MPSCTRL_NON_SECURE_READ_BIT (0x1<<5) -#define MPSCTRL_NON_SECURE_WRITE_BIT (0x1<<4) -#define MPSCTRL_USE_FUSE_KEY (0x1<<3) -#define MPSCTRL_ECB_MODE (0x1<<2) -#define MPSCTRL_ENCRYPTION (0x1<<1) -#define MPSCTRL_VALID (0x1<<0) +/* Protector Register */ +#define DWMCI_EMMCP_BASE 0x1000 +#define EMMCP_MPSBEGIN0 (DWMCI_EMMCP_BASE + 0x0200) +#define EMMCP_SEND0 (DWMCI_EMMCP_BASE + 0x0204) +#define EMMCP_CTRL0 (DWMCI_EMMCP_BASE + 0x020c) + +#define MPSCTRL_SECURE_READ_BIT BIT(7) +#define MPSCTRL_SECURE_WRITE_BIT BIT(6) +#define MPSCTRL_NON_SECURE_READ_BIT BIT(5) +#define MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) +#define MPSCTRL_USE_FUSE_KEY BIT(3) +#define MPSCTRL_ECB_MODE BIT(2) +#define MPSCTRL_ENCRYPTION BIT(1) +#define MPSCTRL_VALID BIT(0)
/* CLKSEL Register */ #define DWMCI_DIVRATIO_BIT 24 diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 84a079256bca..9ac0c21216a1 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -45,7 +45,7 @@ struct exynos_dwmmc_variant { u32 quirks; /* quirk flags - see DWMCI_QUIRK_... */ };
-/* Exynos implmentation specific drver private data */ +/* Exynos implementation specific driver private data */ struct dwmci_exynos_priv_data { #if CONFIG_IS_ENABLED(DM_MMC) struct dwmci_host host; @@ -121,10 +121,7 @@ static int exynos_dwmmc_set_sclk(struct dwmci_host *host, unsigned long rate) return 0; }
-/* - * Function used as callback function to initialise the - * CLKSEL register for every mmc channel. - */ +/* Configure CLKSEL register with chosen timing values */ static int exynos_dwmci_clksel(struct dwmci_host *host) { struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host); @@ -163,7 +160,7 @@ static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host) & DWMCI_DIVRATIO_MASK) + 1; }
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) +static unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { unsigned long sclk; u8 clk_div; @@ -204,7 +201,6 @@ static void exynos_dwmci_board_init(struct dwmci_host *host) MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID); }
- /* Set to timing value at initial time */ if (priv->sdr_timing) exynos_dwmci_clksel(host); } @@ -213,8 +209,8 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) { struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); struct dwmci_host *host = &priv->host; - int err = 0; u32 div, timing[2]; + int err;
priv->chip = (struct exynos_dwmmc_variant *)dev_get_driver_data(dev);
@@ -222,9 +218,8 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) const void *blob = gd->fdt_blob; int node = dev_of_offset(dev);
- /* Extract device id for each mmc channel */ + /* Obtain device ID for current MMC channel */ host->dev_id = pinmux_decode_periph_id(blob, node); - host->dev_index = dev_read_u32_default(dev, "index", host->dev_id); if (host->dev_index == host->dev_id) host->dev_index = host->dev_id - PERIPH_ID_SDMMC0; @@ -240,10 +235,6 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) host->dev_index = 2; /* SD card */ #endif
- /* Get the bus width from the device node (Default is 4bit buswidth) */ - host->buswidth = dev_read_u32_default(dev, "bus-width", 4); - - /* Set the base address from the device node */ host->ioaddr = dev_read_addr_ptr(dev); if (!host->ioaddr) { printf("DWMMC%d: Can't get base address\n", host->dev_index); @@ -254,17 +245,17 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) div = priv->chip->div; else div = dev_read_u32_default(dev, "samsung,dw-mshc-ciu-div", 0); + err = dev_read_u32_array(dev, "samsung,dw-mshc-sdr-timing", timing, 2); if (err) { printf("DWMMC%d: Can't get sdr-timings\n", host->dev_index); return -EINVAL; } - priv->sdr_timing = DWMCI_SET_SAMPLE_CLK(timing[0]) | DWMCI_SET_DRV_CLK(timing[1]) | DWMCI_SET_DIV_RATIO(div);
- /* sdr_timing didn't assigned anything, use the default value */ + /* sdr_timing wasn't set, use the default value */ if (!priv->sdr_timing) { if (host->dev_index == 0) priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL; @@ -283,6 +274,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev) DWMCI_SET_DIV_RATIO(div); }
+ host->buswidth = dev_read_u32_default(dev, "bus-width", 4); host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); host->bus_hz = dev_read_u32_default(dev, "clock-frequency", 0);
@@ -396,8 +388,8 @@ U_BOOT_DRIVER(exynos_dwmmc_drv) = { .of_match = exynos_dwmmc_ids, .of_to_plat = exynos_dwmmc_of_to_plat, .bind = exynos_dwmmc_bind, - .ops = &dm_dwmci_ops, .probe = exynos_dwmmc_probe, + .ops = &dm_dwmci_ops, .priv_auto = sizeof(struct dwmci_exynos_priv_data), .plat_auto = sizeof(struct exynos_mmc_plat), };

Upstream properties were added to device trees to follow current Linux kernel. DW MMC driver was updated accordingly. Safely remove outdated MMC properties. Details on removed properties are as follows:
* samsung,removable: replaced by non-removable * samsung,bus-width: replaced by bus-width * samsung,timing: - replaced by samsung,dw-mshc-ciu-div and samsung,dw-mshc-sdr-timing in dw_mmc nodes - removed from sdhci nodes (it's neither described in bindings, nor it's used in s5p_sdhci.c driver) * fifoth_val: replaced by fifo-depth * bus_hz: replaced by clock-frequency * div: the fixed CIU clock divider value was moved to the chip data in exynos_dw_mmc.c driver
No functional change.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- arch/arm/dts/exynos4210-origen.dts | 2 -- arch/arm/dts/exynos4210-trats.dts | 4 ---- arch/arm/dts/exynos4210-universal_c210.dts | 4 ---- arch/arm/dts/exynos4412-odroid.dts | 8 -------- arch/arm/dts/exynos4412-trats2.dts | 12 ------------ arch/arm/dts/exynos5250-arndale.dts | 4 ---- arch/arm/dts/exynos5250-smdk5250.dts | 6 ------ arch/arm/dts/exynos5250-snow.dts | 6 ------ arch/arm/dts/exynos5250-spring.dts | 3 --- arch/arm/dts/exynos5420-smdk5420.dts | 6 ------ arch/arm/dts/exynos5422-odroidxu3.dts | 2 -- arch/arm/dts/exynos54xx.dtsi | 6 ------ 12 files changed, 63 deletions(-)
diff --git a/arch/arm/dts/exynos4210-origen.dts b/arch/arm/dts/exynos4210-origen.dts index a4915de2c49c..40289c8c4aaf 100644 --- a/arch/arm/dts/exynos4210-origen.dts +++ b/arch/arm/dts/exynos4210-origen.dts @@ -25,9 +25,7 @@ };
&sdhci2 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; cd-gpios = <&gpk2 2 0>; status = "okay"; }; diff --git a/arch/arm/dts/exynos4210-trats.dts b/arch/arm/dts/exynos4210-trats.dts index 4fbdf4730994..88e9c0ed2bb0 100644 --- a/arch/arm/dts/exynos4210-trats.dts +++ b/arch/arm/dts/exynos4210-trats.dts @@ -240,17 +240,13 @@ };
&sdhci0 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 2 0>; status = "okay"; };
&sdhci2 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; cd-gpios = <&gpx3 4 0>; status = "okay"; }; diff --git a/arch/arm/dts/exynos4210-universal_c210.dts b/arch/arm/dts/exynos4210-universal_c210.dts index 1b3ac1fee15f..c87b92be609d 100644 --- a/arch/arm/dts/exynos4210-universal_c210.dts +++ b/arch/arm/dts/exynos4210-universal_c210.dts @@ -235,17 +235,13 @@ };
&sdhci0 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 2 0>; status = "okay"; };
&sdhci2 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; cd-gpios = <&gpx3 4 0>; status = "okay"; }; diff --git a/arch/arm/dts/exynos4412-odroid.dts b/arch/arm/dts/exynos4412-odroid.dts index 24e96ed05868..6822c6aaaa8a 100644 --- a/arch/arm/dts/exynos4412-odroid.dts +++ b/arch/arm/dts/exynos4412-odroid.dts @@ -234,26 +234,18 @@ };
&sdhci2 { - samsung,bus-width = <4>; - samsung,timing = <1 2 3>; cd-inverted; cd-gpios = <&gpk2 2 0>; status = "okay"; };
&mshc_0 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <2 1 0>; samsung,dw-mshc-ciu-div = <0>; samsung,dw-mshc-sdr-timing = <2 1>; - samsung,removable = <0>; non-removable; - fifoth_val = <0x203f0040>; fifo-depth = <0x80>; clock-frequency = <400000000>; - bus_hz = <400000000>; - div = <0x3>; index = <4>; status = "okay"; }; diff --git a/arch/arm/dts/exynos4412-trats2.dts b/arch/arm/dts/exynos4412-trats2.dts index 30758ffa1ef9..2b71d328cd0e 100644 --- a/arch/arm/dts/exynos4412-trats2.dts +++ b/arch/arm/dts/exynos4412-trats2.dts @@ -108,9 +108,7 @@ };
sdhci@12510000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 4 0>; status = "disabled"; }; @@ -432,33 +430,23 @@ };
&sdhci0 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; pwr-gpios = <&gpk0 4 0>; status = "disabled"; };
&sdhci2 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; cd-gpios = <&gpk2 2 0>; status = "okay"; };
&mshc_0 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <2 1 0>; samsung,dw-mshc-ciu-div = <0>; samsung,dw-mshc-sdr-timing = <2 1>; - samsung,removable = <0>; non-removable; - fifoth_val = <0x203f0040>; clock-frequency = <400000000>; - bus_hz = <400000000>; - div = <0x3>; index = <4>; fifo-depth = <0x80>; status = "okay"; diff --git a/arch/arm/dts/exynos5250-arndale.dts b/arch/arm/dts/exynos5250-arndale.dts index 7f84589c97a2..4c894f1712fa 100644 --- a/arch/arm/dts/exynos5250-arndale.dts +++ b/arch/arm/dts/exynos5250-arndale.dts @@ -27,9 +27,7 @@ };
mmc@12200000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 3>; }; @@ -39,9 +37,7 @@ };
mmc@12220000 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 2>; }; diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts index 882db2d1f608..f9f54cb63871 100644 --- a/arch/arm/dts/exynos5250-smdk5250.dts +++ b/arch/arm/dts/exynos5250-smdk5250.dts @@ -145,12 +145,9 @@ };
mmc@12200000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 3>; - samsung,removable = <0>; non-removable; };
@@ -159,12 +156,9 @@ };
mmc@12220000 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 2>; - samsung,removable = <1>; };
mmc@12230000 { diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index bcf04d5c07c0..ab7b5212ba7c 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -301,12 +301,9 @@ };
mmc@12200000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 3>; - samsung,removable = <0>; non-removable; };
@@ -315,12 +312,9 @@ };
mmc@12220000 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 2>; - samsung,removable = <1>; };
mmc@12230000 { diff --git a/arch/arm/dts/exynos5250-spring.dts b/arch/arm/dts/exynos5250-spring.dts index 7270a546a795..9c478837ba44 100644 --- a/arch/arm/dts/exynos5250-spring.dts +++ b/arch/arm/dts/exynos5250-spring.dts @@ -103,12 +103,9 @@ };
mmc@12200000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 3>; - samsung,removable = <0>; non-removable; };
diff --git a/arch/arm/dts/exynos5420-smdk5420.dts b/arch/arm/dts/exynos5420-smdk5420.dts index 1f27baafebaf..6ba1306e862a 100644 --- a/arch/arm/dts/exynos5420-smdk5420.dts +++ b/arch/arm/dts/exynos5420-smdk5420.dts @@ -106,12 +106,9 @@ };
mmc@12200000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 3>; - samsung,removable = <0>; non-removable; samsung,pre-init; }; @@ -121,12 +118,9 @@ };
mmc@12220000 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 2>; - samsung,removable = <1>; };
mmc@12230000 { diff --git a/arch/arm/dts/exynos5422-odroidxu3.dts b/arch/arm/dts/exynos5422-odroidxu3.dts index 767b3e415d58..ef25cf774470 100644 --- a/arch/arm/dts/exynos5422-odroidxu3.dts +++ b/arch/arm/dts/exynos5422-odroidxu3.dts @@ -280,12 +280,10 @@ };
mmc@12200000 { - fifoth_val = <0x201f0020>; fifo-depth = <0x40>; };
mmc@12220000 { - fifoth_val = <0x201f0020>; fifo-depth = <0x40>; };
diff --git a/arch/arm/dts/exynos54xx.dtsi b/arch/arm/dts/exynos54xx.dtsi index fd74166c7e48..5915ed697791 100644 --- a/arch/arm/dts/exynos54xx.dtsi +++ b/arch/arm/dts/exynos54xx.dtsi @@ -119,12 +119,9 @@ };
mmc@12200000 { - samsung,bus-width = <8>; bus-width = <8>; - samsung,timing = <1 3 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 3>; - samsung,removable = <0>; non-removable; samsung,pre-init; }; @@ -134,12 +131,9 @@ };
mmc@12220000 { - samsung,bus-width = <4>; bus-width = <4>; - samsung,timing = <1 2 3>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <1 2>; - samsung,removable = <1>; };
mmc@12230000 {

Enable MMC subsystem and DW MMC driver support to make eMMC functional. Also enable a couple of related commands so the user can make use of eMMC from U-Boot shell.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- configs/e850-96_defconfig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index bb41635ff784..17054f25fe6b 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -9,11 +9,19 @@ CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xf8c00000 CONFIG_DEFAULT_DEVICE_TREE="exynos850-e850-96" CONFIG_SYS_LOAD_ADDR=0x80000000 +CONFIG_ANDROID_BOOT_IMAGE=y # CONFIG_AUTOBOOT is not set # CONFIG_DISPLAY_CPUINFO is not set +CONFIG_HUSH_PARSER=y +CONFIG_CMD_ABOOTIMG=y +CONFIG_CMD_CLK=y +CONFIG_CMD_GPT=y +CONFIG_CMD_MMC=y +CONFIG_CMD_PART=y +CONFIG_CMD_TIME=y # CONFIG_NET is not set CONFIG_CLK_EXYNOS850=y -# CONFIG_MMC is not set +CONFIG_MMC_DW=y CONFIG_SOC_SAMSUNG=y CONFIG_EXYNOS_PMU=y CONFIG_EXYNOS_USI=y

eMMC is enabled on E850-96 board now. Mention that in the board documentation.
Signed-off-by: Sam Protsenko semen.protsenko@linaro.org --- doc/board/samsung/e850-96.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/doc/board/samsung/e850-96.rst b/doc/board/samsung/e850-96.rst index 0cb95473e536..0a7b6fc0c9dd 100644 --- a/doc/board/samsung/e850-96.rst +++ b/doc/board/samsung/e850-96.rst @@ -47,12 +47,13 @@ Build Procedure ---------------
.. warning:: - At the moment both eMMC and USB features are not enabled in U-Boot. Flashing + At the moment USB is not enabled in U-Boot for this board. Although eMMC is + enabled, you won't be able to flash images over USB (fastboot). So flashing U-Boot binary **WILL** effectively brick your board. The ``dltool`` [8]_ can be used then to perform USB boot and flash LittleKernel bootloader binary [7]_ to unbrick and revive the board. Flashing U-Boot binary might be helpful for developers or anybody who want to check current state of U-Boot enablement on - E850-96 (which is mostly serial console and related blocks). + E850-96 (which is mostly serial console, eMMC and related blocks).
Build U-Boot binary from source code (using AArch64 baremetal GCC toolchain):
participants (2)
-
Quentin Schulz
-
Sam Protsenko