Re: [PATCH] android_ab: Fix backup offset calculation

2024-08-27 Thread Sam Protsenko
On Wed, Aug 7, 2024 at 9:14 AM Joshua Watt  wrote:
>
> The backup offset is in bytes, but was incorrectly be interpreted as
> blocks, leading to it being written to the wrong location. Fix the
> calculation and clarify that ANDROID_AB_BACKUP_OFFSET is in bytes and
> must be a multiple of the block size.
>
> Signed-off-by: Joshua Watt 
> ---

In addition to what Mattijs said, I think it makes sense to add
"Fixes:" tag for this patch.

>  boot/android_ab.c | 4 ++--
>  common/Kconfig| 3 ++-
>  2 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/boot/android_ab.c b/boot/android_ab.c
> index 143f373aae..b253200161 100644
> --- a/boot/android_ab.c
> +++ b/boot/android_ab.c
> @@ -139,8 +139,8 @@ static int ab_control_store(struct blk_desc *dev_desc,
>  {
> ulong abc_offset, abc_blocks, ret;
>
> -   abc_offset = offset +
> -offsetof(struct bootloader_message_ab, slot_suffix) /
> +   abc_offset = (offset +
> + offsetof(struct bootloader_message_ab, slot_suffix)) /
>  part_info->blksz;
> abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control),
>   part_info->blksz);
> diff --git a/common/Kconfig b/common/Kconfig
> index 83c81edac2..e1b8557e0c 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -986,7 +986,8 @@ config ANDROID_AB_BACKUP_OFFSET
> help
>   If non-zero, a backup bootloader message starting at this offset in
>   the partition will tried in the event that the primary one (starting
> - at offset 0) fails its checksum.
> + at offset 0) fails its checksum. The offset is in bytes and must be
> + multiple of the block size.
>
>  endmenu
>
> --
> 2.45.2
>


Re: [PATCH v5 00/38] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-08-20 Thread Sam Protsenko
On Tue, Aug 20, 2024 at 3:16 PM Tom Rini  wrote:
>
> On Tue, Aug 20, 2024 at 02:48:59PM -0500, Sam Protsenko wrote:
> > On Mon, Aug 19, 2024 at 8:45 PM Minkyu Kang  wrote:
> > >
> > > Hi,
> > >
> > > 2024년 8월 14일 (수) 10:15, Sam Protsenko 님이 작성:
> > >>
> > >> Hi Minkyu,
> > >>
> > >> What do you think about this series? It's been pending for a while
> > >> now, so may I ask you to take care of it?
> > >>
> > >
> > > applied to u-boot-samsung.
> > >
> >
> > Thank you for handling this, Minkyu! Is there any chance this series
> > can be merged in 2024.10, or is it too late in the cycle?
>
> At this point I would like to see this for -next and not master, sorry.
>

Makes sense, being at -rc3 doesn't leave us much room for testing it.
I'm just glad this series is finally applied :)

I also hope that my clock propagation patch [1] can be taken for
-next, it's been kinda ignored for a long time now. Although MMC
series will be functional on E850-96 in SDR mode as is, applying [1]
would make it work in DDR, which is definitely better.

Thanks!

[1] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html

> --
> Tom


Re: [PATCH v5 00/38] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-08-20 Thread Sam Protsenko
On Mon, Aug 19, 2024 at 8:45 PM Minkyu Kang  wrote:
>
> Hi,
>
> 2024년 8월 14일 (수) 10:15, Sam Protsenko 님이 작성:
>>
>> Hi Minkyu,
>>
>> What do you think about this series? It's been pending for a while
>> now, so may I ask you to take care of it?
>>
>
> applied to u-boot-samsung.
>

Thank you for handling this, Minkyu! Is there any chance this series
can be merged in 2024.10, or is it too late in the cycle?

> Thanks.
> Minkyu Kang.


Re: [PATCH] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present

2024-08-13 Thread Sam Protsenko
Hi Sean,

This series has been pending for 5 months now. If there are no
objections, can you please apply it?

Thanks!

On Wed, Jul 31, 2024 at 3:05 PM Sam Protsenko
 wrote:
>
> On Thu, Jul 25, 2024 at 3:57 PM Sam Protsenko
>  wrote:
> >
> > [snip]
> >
> > >>
> > >> If anything, that shows there is a rising need in this propagation
> > >> feature. I'd like to encourage the maintainers to take a look at both
> > >> implementations, and maybe at least comment on why there is hesitance
> > >> to accept this patch. It's been almost 5 months that I'm trying to
> > >> push that through, and my MMC work (DDR mode on E850-96 board to be
> > >> precise) still very much depends on this.
> > >
> > > My series is still not ready and I have no problem to go with your while 
> > > for now
> > >
> >
> > Hi Sean,
> >
> > Can you please apply this patch, if it's ok with you?
> >
>
> Gentle reminder.
>
> > Thanks!
> >
> > [snip]


Re: [PATCH v5 00/38] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-08-13 Thread Sam Protsenko
Hi Minkyu,

What do you think about this series? It's been pending for a while
now, so may I ask you to take care of it?

Thanks!

On Wed, Aug 7, 2024 at 10:14 PM Sam Protsenko
 wrote:
>
> 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.
> Next Exynos chips are known to have 64-bit DW MMC block:
>
>   - Exynos3475
>   - Exynos5433
>   - Exynos7420
>   - Exynos7570
>   - Exynos7580
>   - Exynos7870
>   - Exynos7872
>   - Exynos7880
>   - Exynos7885
>   - Exynos7890
>   - Exynos850 (Exynos3830)
>
> 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 0x8a00
> env set bootaddr 0x9400
> 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 0x10
> cp.b $boot_kerneladdr $loadaddr 200
> 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: 5200
> 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'):
>
> 39975gout_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.
>
> Changes in v5:
>   - Added missing bus-width property to sdhci2 node (SD card) in
> exynos4412-odroid.dts (patch 14/38)
>
> Changes in v4:
>   - Rebased all patches on top of the most recent master; patches that
> were affected by recent changes: 08/38, 13/38, 37/38
>
> Changes in v3:
>   - Rebased all patches on top of the most recent master, which in turn
> dropped next patches:
> - [PATCH v2 1/40] mmc: dw_mmc: Remove common.h
> - [PATCH v2 33/40] mmc: exynos_dw_mmc: Remove common.h
>
> Changes in v2:
>   - Rebased on top of the most recent U-Boot/master
>   - Added pending R-b tags from the mailing list
>   - Addressed the comment from Quentin about CONFIG_IS_ENABLED() not
> playing nice in SPL case:
> 1. Dropped [PATCH 35/42]
>("mmc: exynos_dw_mmc: Use CONFIG_IS_ENABLED() to check config
> options")
> 2. Dropped [PATCH 14/42]
>("mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options")
> 3. Got rid of all other CONFIG_IS_ENABLED() changes (used #ifdef for
>now)
>   - Addressed the comment from Quentin about incorrect logic for divider
> update in [PATCH 06/40]
>   - Guarded exynos_dwmmc_of_to_plat() with #ifded CONFIG_DM_MMC in
> [PATCH 22/40]
>   - Added list of Exynos chips implementing 64-bit IDMAC descriptor (in
>   

[PATCH v5 38/38] doc: samsung: Mention enabled eMMC in E850-96 board doc

2024-08-07 Thread Sam Protsenko
eMMC is enabled on E850-96 board now. Mention that in the board
documentation.

Signed-off-by: Sam Protsenko 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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):
 
-- 
2.39.2



[PATCH v5 35/38] mmc: exynos_dw_mmc: Improve coding style

2024-08-07 Thread Sam Protsenko
Fix most of checkpatch warnings and other obvious style issues.

No functional change.

Signed-off-by: Sam Protsenko 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 
 
-#define EMMCP_MPSBEGIN00x1200
-#define EMMCP_SEND00x1204
-#define EMMCP_CTRL00x120C
+#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_BITBIT(7)
+#define MPSCTRL_SECURE_WRITE_BIT   BIT(6)
+#define MPSCTRL_NON_SECURE_READ_BITBIT(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 a9e240fd91d6..c8bf89d6d355 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 {
 #ifdef CONFIG_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);
 }
@@ -214,8 +210,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);
 
@@ -223,9 +219,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->

[PATCH v5 37/38] configs: e850-96: Enable MMC

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - Rebased on top of the most recent master

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 2949da24267f..e5d90996234e 100644
--- a/configs/e850-96_defconfig
+++ b/configs/e850-96_defconfig
@@ -9,12 +9,20 @@ CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
 CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xf8c0
 CONFIG_DEFAULT_DEVICE_TREE="exynos/exynos850-e850-96"
 CONFIG_SYS_LOAD_ADDR=0x8000
+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_CMD_RNG=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
-- 
2.39.2



[PATCH v5 32/38] mmc: exynos_dw_mmc: Pull all init code into probe function

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 29241edf7915..41fb32c579ef 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;
-
-#ifndef CONFIG_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)
-{
-#ifdef CONFIG_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);
-}
-
 #ifdef CONFIG_DM_MMC
 static int exynos_dwmmc_of_to_plat(struct udevice *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;
 
 #ifndef CONFIG_CPU_V7A
@@ -353,9 +305,41 @@ static int exynos_dwmmc_probe(struct udevice *dev)
return err;
 #endif
 
-   err = do_dwmci_init(host);
-   if (err)
+#ifdef CONFIG_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;
+
+#ifndef CONFIG_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;
-- 
2.39.2



[PATCH v5 34/38] mmc: exynos_dw_mmc: Use dev->name as driver's displayed name

2024-08-07 Thread Sam Protsenko
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@1210".

Signed-off-by: Sam Protsenko 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 43111b749a6b..a9e240fd91d6 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;
-- 
2.39.2



[PATCH v5 36/38] arm: dts: exynos: Remove outdated DW MMC properties in all Exynos dts

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - Rebased on top of changed patch 14/38

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 a405b9708719..346e0f55c768 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -234,27 +234,19 @@
 };
 
 &sdhci2 {
-   samsung,bus-width = <4>;
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 = <4>;
-   bus_hz = <4>;
-   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@1251 {
-   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 

[PATCH v5 33/38] mmc: exynos_dw_mmc: Don't call dwmci_setup_cfg() after add_dwmci()

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 41fb32c579ef..43111b749a6b 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;
 
-#ifndef CONFIG_DM_MMC
-   /* Add the mmc channel to be registered with mmc core */
-   if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) {
+#ifdef CONFIG_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);
 }
-- 
2.39.2



[PATCH v5 31/38] mmc: exynos_dw_mmc: Add support for ARM64 Exynos chips

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 ee3ae9f5e650..29241edf7915 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -385,6 +385,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",
@@ -392,6 +397,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,
},
{ }
 };
-- 
2.39.2



[PATCH v5 29/38] mmc: exynos_dw_mmc: Read and use DDR timing when available

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 b9d655c0d5c8..32f3ea168b6c 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -52,6 +52,7 @@ struct dwmci_exynos_priv_data {
 #endif
struct clk clk;
u32 sdr_timing;
+   u32 ddr_timing;
const struct exynos_dwmmc_variant *chip;
 };
 
@@ -127,8 +128,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);
 
-- 
2.39.2



[PATCH v5 30/38] mmc: exynos_dw_mmc: Set requested freq in get_mmc_clk() callback

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 32f3ea168b6c..ee3ae9f5e650 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -169,7 +169,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",
-- 
2.39.2



[PATCH v5 28/38] mmc: exynos_dw_mmc: Move quirks from struct dwmci_host to chip data

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 bed8bd8a0fcf..b9d655c0d5c8 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -25,6 +25,9 @@
 
 #define EXYNOS4412_FIXED_CIU_CLK_DIV   4
 
+/* Quirks */
+#define DWMCI_QUIRK_DISABLE_SMUBIT(0)
+
 #ifdef CONFIG_DM_MMC
 #include 
 DECLARE_GLOBAL_DATA_PTR;
@@ -39,6 +42,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 */
@@ -173,7 +177,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,
@@ -205,11 +209,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;
@@ -352,6 +352,9 @@ static const struct exynos_dwmmc_variant exynos4_drv_data = 
{
 
 static const struct exynos_dwmmc_variant exynos5_drv_data = {
.clksel = DWMCI_CLKSEL,
+#ifdef CONFIG_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 8c2500da9efe..6edb9e1a59c5 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_SMUBIT(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;
-- 
2.39.2



[PATCH v5 27/38] mmc: exynos_dw_mmc: Read common clock-frequency property

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 612c2d0c615d..bed8bd8a0fcf 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;
 }
-- 
2.39.2



[PATCH v5 26/38] mmc: exynos_dw_mmc: Read common bus-width property

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 b5c8f592c7e8..612c2d0c615d 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 80dbb38c9b36..278019f02abb 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -166,7 +166,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;
-- 
2.39.2



[PATCH v5 25/38] mmc: exynos_dw_mmc: Refactor fixed CIU clock divider

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 54a2ba4795e3..1af5ec0532ea 100644
--- a/drivers/mmc/ca_dw_mmc.c
+++ b/drivers/mmc/ca_dw_mmc.c
@@ -86,7 +86,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 fd2ced3d711d..b5c8f592c7e8 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -23,6 +23,8 @@
 #defineDWMMC_MMC0_SDR_TIMING_VAL   0x03030001
 #defineDWMMC_MMC2_SDR_TIMING_VAL   0x03020001
 
+#define EXYNOS4412_FIXED_CIU_CLK_DIV   4
+
 #ifdef CONFIG_DM_MMC
 #include 
 DECLARE_GLOBAL_DATA_PTR;
@@ -36,6 +38,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 */
@@ -126,12 +129,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
@@ -139,9 +148,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",
@@ -149,11 +166,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&

[PATCH v5 24/38] mmc: exynos_dw_mmc: Abstract CLKSEL register

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 4108af47e518..fd2ced3d711d 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -33,6 +33,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
@@ -40,6 +45,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(
@@ -115,13 +121,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;
@@ -132,7 +139,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);
+
 #ifdef CONFIG_CPU_V7A
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
@@ -322,9 +331,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,
+   },
{ }
 };
 
-- 
2.39.2



[PATCH v5 23/38] mmc: exynos_dw_mmc: Read upstream SDR timing properties

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 50d0208b29fd..4108af47e518 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];
 
 #ifdef CONFIG_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) {
-- 
2.39.2



[PATCH v5 22/38] mmc: exynos_dw_mmc: Convert to use livetree API

2024-08-07 Thread Sam Protsenko
Update the driver to use livetree API instead of FDT one.

No functional change.

Signed-off-by: Sam Protsenko 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Moved exynos_dwmmc_of_to_plat() under #ifdef CONFIG_DM_MMC stanza

 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 09a59fdf0120..50d0208b29fd 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -6,9 +6,7 @@
 
 #include 
 #include 
-#include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -226,18 +224,19 @@ static int do_dwmci_init(struct dwmci_host *host)
 #ifdef CONFIG_DM_MMC
 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];
 
 #ifdef CONFIG_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;
 }
-- 
2.39.2



[PATCH v5 19/38] mmc: exynos_dw_mmc: Don't call pinmux functions on ARM64 chips

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 aa542c13fea8..ed2752391603 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -144,6 +144,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host)
 
 static int do_dwmci_init(struct dwmci_host *host)
 {
+#ifdef CONFIG_CPU_V7A
int flag, err;
 
flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
@@ -152,6 +153,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);
 }
@@ -163,6 +165,7 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
int err = 0;
u32 timing[3];
 
+#ifdef CONFIG_CPU_V7A
/* Extract device id for each mmc channel */
host->dev_id = pinmux_decode_periph_id(blob, node);
 
@@ -174,6 +177,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);
-- 
2.39.2



[PATCH v5 21/38] mmc: exynos_dw_mmc: Use .of_to_plat for device tree parsing

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 drivers/mmc/exynos_dw_mmc.c | 15 +++
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 4d28ede0d756..09a59fdf0120 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -223,10 +223,13 @@ 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)
+#ifdef CONFIG_DM_MMC
+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];
 
@@ -286,7 +289,6 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
return 0;
 }
 
-#ifdef CONFIG_DM_MMC
 static int exynos_dwmmc_probe(struct udevice *dev)
 {
struct exynos_mmc_plat *plat = dev_get_plat(dev);
@@ -301,10 +303,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;
@@ -335,6 +333,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,
-- 
2.39.2



[PATCH v5 20/38] mmc: exynos_dw_mmc: Obtain and use CIU clock via CCF API

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 ed2752391603..4d28ede0d756 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -4,6 +4,7 @@
  * Jaehoon Chung 
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -15,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #defineDWMMC_MAX_CH_NUM4
@@ -38,6 +40,7 @@ struct dwmci_exynos_priv_data {
 #ifdef CONFIG_DM_MMC
struct dwmci_host host;
 #endif
+   struct clk clk;
u32 sdr_timing;
 };
 
@@ -51,6 +54,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)
+{
+#ifdef CONFIG_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;
+
+#ifdef CONFIG_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.
@@ -68,6 +126,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
@@ -77,7 +136,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.
@@ -107,19 +172,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(hos

[PATCH v5 18/38] mmc: exynos_dw_mmc: Fix getting private data in exynos_dwmci_board_init()

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 a86d58663c1b..aa542c13fea8 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -41,18 +41,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;
@@ -82,7 +88,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);
-- 
2.39.2



[PATCH v5 17/38] mmc: exynos_dw_mmc: Fix obtaining the base address of controller

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 377c9e6d90b9..a86d58663c1b 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -155,7 +155,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);
@@ -173,12 +173,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);
-- 
2.39.2



[PATCH v5 16/38] arm: exynos: Add header guard for dwmmc.h

2024-08-07 Thread Sam Protsenko
Add missing header guard to prevent possible build errors.

Fixes: 77b55e8cfcee ("ARM: exynos: move SoC sources to mach-exynos")
Signed-off-by: Sam Protsenko 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 
  */
 
+#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_MASK0x7
+
+#endif /* __ASM_ARM_ARCH_DWMMC_H */
-- 
2.39.2



[PATCH v5 15/38] dt-bindings: exynos: Update bindings doc for DW MMC controller

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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@1220 {
-   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.
-- 
2.39.2



[PATCH v5 14/38] arm: dts: exynos: Add upstream DW MMC properties to all Exynos dts

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - Added missing bus-width property to sdhci2 node (SD card) in
exynos4412-odroid.dts

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 | 7 +++
 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, 60 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..a405b9708719 100644
--- a/arch/arm/dts/exynos4412-odroid.dts
+++ b/arch/arm/dts/exynos4412-odroid.dts
@@ -235,6 +235,7 @@
 
 &sdhci2 {
samsung,bus-width = <4>;
+   bus-width = <4>;
samsung,timing = <1 2 3>;
cd-inverted;
cd-gpios = <&gpk2 2 0>;
@@ -243,9 +244,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 = <4>;
bus_hz = <4>;
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@1251 {
samsung,bus-width = <8>;
+   bus-width = <8>;
samsung,timing = <1 3 3>;
pwr-gpios = <&gpk0 4 0>;
status = "disabled&qu

[PATCH v5 13/38] mmc: dw_mmc: Improve coding style

2024-08-07 Thread Sam Protsenko
Fix most of checkpatch warnings and other obvious style issues.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - Rebased on top of the most recent master

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 drivers/mmc/dw_mmc.c |  68 ++--
 include/dwmmc.h  | 149 ++-
 2 files changed, 110 insertions(+), 107 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 47dcc37c2c7c..8551eac70185 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) & 0x;
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 

[PATCH v5 12/38] mmc: dw_mmc: Fix kernel-doc comments in dwmmc.h

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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. 15000)
+ * @min_clk:   Minimum supported clock speed in Hz (e.g. 40)
  *
  * 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. 15000)
- * @min_clk:   Minimum supported clock speed in HZ (e.g. 40)
  */
 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(st

[PATCH v5 11/38] mmc: dw_mmc: Replace fifoth_val property with fifo-depth

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 55eb8a54f594..47dcc37c2c7c 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++);
@@ -655,16 +653,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 a51f762988da..377c9e6d90b9 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -150,8 +150,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;
@@ -200,7 +200,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);
 
@@ -216,8 +216,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)

[PATCH v5 10/38] mmc: dw_mmc: Add support for 64-bit IDMAC

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 3e7073f7de1b..55eb8a54f594 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 & 0x;
+   desc->des5 = buf_addr >> 32;
+   desc->des6 = next_desc_phys & 0x;
+   desc->des7 = next_desc_phys >> 32;
+}
+
+static void dwmci_prepare_desc(struct dwmci_ho

[PATCH v5 09/38] mmc: dw_mmc: Improve 32-bit IDMAC descriptor namings

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 d73d58bcf7d7..3e7073f7de1b 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 = 10;
-- 
2.39.2



[PATCH v5 07/38] mmc: dw_mmc: Extract DMA transfer handling code into a separate routine

2024-08-07 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 b99f63a6f8bd..8fc26399034b 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -232,6 +232,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)
 {
@@ -274,7 +301,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 = 10;
-   u32 mask, ctrl;
+   u32 mask;
struct bounce_buffer bbstate;
 
dwmci_wait_while_busy(host, cmd);
@@ -382,26 +409,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);
-- 
2.39.2



[PATCH v5 08/38] mmc: dw_mmc: Extract setting the DMA descriptor into a separate routine

2024-08-07 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - Rebased on top of the most recent master

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 drivers/mmc/dw_mmc.c | 51 +++-
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 8fc26399034b..d73d58bcf7d7 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -53,46 +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);
-
-   /* Clear IDMAC interrupt */
-   dwmci_writel(host, DWMCI_IDSTS, 0x);
+   for (i = 0;; i++) {
+   unsigned int flags, cnt;
 
-   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, 0x);
+
+   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;
-- 
2.39.2



[PATCH v5 06/38] mmc: dw_mmc: Extract FIFO data transfer into a separate routine

2024-08-07 Thread Sam Protsenko
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 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 b9c14fe6857b..b99f63a6f8bd 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -139,25 +139,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);
@@ -168,50 +210,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_M

[PATCH v5 04/38] mmc: dw_mmc: Extract FIFO init into a separate routine

2024-08-07 Thread Sam Protsenko
Move FIFO threshold initialization into a separate function to make
dwmci_init() more readable.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 395b699b0a0a..4a9e57165fbb 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -538,6 +538,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;
@@ -562,16 +576,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);
-- 
2.39.2



[PATCH v5 05/38] mmc: dw_mmc: Extract divider update to a separate function

2024-08-07 Thread Sam Protsenko
Extract the clock divider update into dwmci_update_div() function. It's
a procedure recommended in TRM, so it's better to keep it in a dedicated
function to make the code clearer.

While at it also extract the clock control code into a separate routine
to avoid code duplication in dwmci_setup_bus().

No functional change.

Signed-off-by: Sam Protsenko 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Moved CLKSRC after disabling the clock as recommended in TRM, to
avoid possible glitch
  - Extracted div update into a separate function with corresponding
comment
  - Changed the patch subject and reworked the commit message

 drivers/mmc/dw_mmc.c | 81 +++-
 1 file changed, 50 insertions(+), 31 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 4a9e57165fbb..b9c14fe6857b 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -410,11 +410,56 @@ 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 = 1;
+   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;
+}
+
+/*
+ * Update the clock divider.
+ *
+ * To prevent a clock glitch keep the clock stopped during the update of
+ * clock divider and clock source.
+ */
+static int dwmci_update_div(struct dwmci_host *host, u32 div)
+{
+   int ret;
+
+   /* Disable clock */
+   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 */
+   return dwmci_control_clken(host, true);
+}
+
+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;
@@ -437,35 +482,9 @@ 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);
-   dwmci_writel(host, DWMCI_CLKSRC, 0);
-
-   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 = 1;
-   do {
-   status = dwmci_readl(host, DWMCI_CMD);
-   if (timeout-- < 0) {
-   debug("%s: Timeout!\n", __func__);
-   return -ETIMEDOUT;
-   }
-   } while (status & DWMCI_CMD_START);
+   ret = dwmci_update_div(host, div);
+   if (ret)
+   return ret;
 
host->clock = freq;
 
-- 
2.39.2



[PATCH v5 03/38] mmc: dw_mmc: Extract waiting for data busy into a separate routine

2024-08-07 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 45665e517068..395b699b0a0a 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -245,6 +245,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)
@@ -259,19 +274,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 = 10;
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) {
-- 
2.39.2



[PATCH v5 02/38] mmc: dw_mmc: Move struct idmac to dw_mmc.c

2024-08-07 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 f4ecd7422cef..45665e517068 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);
-- 
2.39.2



[PATCH v5 01/38] mmc: dw_mmc: Remove unused version field from struct dwmci_host

2024-08-07 Thread Sam Protsenko
Nobody seems to use it, so just remove it.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v5:
  - (none)

Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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;
-- 
2.39.2



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

2024-08-07 Thread Sam Protsenko
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.
Next Exynos chips are known to have 64-bit DW MMC block:

  - Exynos3475
  - Exynos5433
  - Exynos7420
  - Exynos7570
  - Exynos7580
  - Exynos7870
  - Exynos7872
  - Exynos7880
  - Exynos7885
  - Exynos7890
  - Exynos850 (Exynos3830)

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 0x8a00
env set bootaddr 0x9400
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 0x10
cp.b $boot_kerneladdr $loadaddr 200
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: 5200
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'):

39975gout_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.

Changes in v5:
  - Added missing bus-width property to sdhci2 node (SD card) in
exynos4412-odroid.dts (patch 14/38)

Changes in v4:
  - Rebased all patches on top of the most recent master; patches that
were affected by recent changes: 08/38, 13/38, 37/38

Changes in v3:
  - Rebased all patches on top of the most recent master, which in turn
dropped next patches:
- [PATCH v2 1/40] mmc: dw_mmc: Remove common.h
- [PATCH v2 33/40] mmc: exynos_dw_mmc: Remove common.h

Changes in v2:
  - Rebased on top of the most recent U-Boot/master
  - Added pending R-b tags from the mailing list
  - Addressed the comment from Quentin about CONFIG_IS_ENABLED() not
playing nice in SPL case:
1. Dropped [PATCH 35/42]
   ("mmc: exynos_dw_mmc: Use CONFIG_IS_ENABLED() to check config
options")
2. Dropped [PATCH 14/42]
   ("mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options")
3. Got rid of all other CONFIG_IS_ENABLED() changes (used #ifdef for
   now)
  - Addressed the comment from Quentin about incorrect logic for divider
update in [PATCH 06/40]
  - Guarded exynos_dwmmc_of_to_plat() with #ifded CONFIG_DM_MMC in
[PATCH 22/40]
  - Added list of Exynos chips implementing 64-bit IDMAC descriptor (in
[PATCH 00/40])

[1] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html

Sam Protsenko (38):
  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 divider update to a separate function
  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 I

Re: [PATCH v4 14/38] arm: dts: exynos: Add upstream DW MMC properties to all Exynos dts

2024-08-07 Thread Sam Protsenko
On Sat, Aug 3, 2024 at 6:33 PM Henrik Grimler  wrote:
>
> Hi Sam,
>
> Thanks for working on this, makes switching to upstream dts'es easier
> :)
>
> I tested the series on exynos4412-odroid-u2 and exynos5422-odroid-xu4.
> One issue for exynos4412-odroid to resolve below, with that fixed
> booting from both eMMC and sdcard works on my devices.
>

Thanks for testing it, and good catch! I've checked all other
occurrences and this node (sdhci2 in exynos4412-odroid.dts) is the
only place where I forgot to add 'bus-width'. Will fix it in v5 and
send it out soon.

[snip]


Re: [PATCH] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present

2024-07-31 Thread Sam Protsenko
On Thu, Jul 25, 2024 at 3:57 PM Sam Protsenko
 wrote:
>
> [snip]
>
> >>
> >> If anything, that shows there is a rising need in this propagation
> >> feature. I'd like to encourage the maintainers to take a look at both
> >> implementations, and maybe at least comment on why there is hesitance
> >> to accept this patch. It's been almost 5 months that I'm trying to
> >> push that through, and my MMC work (DDR mode on E850-96 board to be
> >> precise) still very much depends on this.
> >
> > My series is still not ready and I have no problem to go with your while 
> > for now
> >
>
> Hi Sean,
>
> Can you please apply this patch, if it's ok with you?
>

Gentle reminder.

> Thanks!
>
> [snip]


Re: [PATCH v4 00/38] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-07-31 Thread Sam Protsenko
Hi Minkyu,

Have you by chance checked this series? Don't mean to push, but it's
been pending for a while now. And further E850-96 platform enablement
depends on this series, so I'd really appreciate it if you can look at
it and possibly apply it.

Thanks!

On Thu, Jul 25, 2024 at 3:45 PM Sam Protsenko
 wrote:
>
> 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.
> Next Exynos chips are known to have 64-bit DW MMC block:
>
>   - Exynos3475
>   - Exynos5433
>   - Exynos7420
>   - Exynos7570
>   - Exynos7580
>   - Exynos7870
>   - Exynos7872
>   - Exynos7880
>   - Exynos7885
>   - Exynos7890
>   - Exynos850 (Exynos3830)
>
> 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 0x8a00
> env set bootaddr 0x9400
> 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 0x10
> cp.b $boot_kerneladdr $loadaddr 200
> 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: 5200
> 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'):
>
> 39975gout_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.
>
> Changes in v4:
>   - Rebased all patches on top of the most recent master; patches that
> were affected by recent changes: 08/38, 13/38, 37/38
>
> Changes in v3:
>   - Rebased all patches on top of the most recent master, which in turn
> dropped next patches:
> - [PATCH v2 1/40] mmc: dw_mmc: Remove common.h
> - [PATCH v2 33/40] mmc: exynos_dw_mmc: Remove common.h
>
> Changes in v2:
>   - Rebased on top of the most recent U-Boot/master
>   - Added pending R-b tags from the mailing list
>   - Addressed the comment from Quentin about CONFIG_IS_ENABLED() not
> playing nice in SPL case:
> 1. Dropped [PATCH 35/42]
>("mmc: exynos_dw_mmc: Use CONFIG_IS_ENABLED() to check config
> options")
> 2. Dropped [PATCH 14/42]
>("mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options")
> 3. Got rid of all other CONFIG_IS_ENABLED() changes (used #ifdef for
>now)
>   - Addressed the comment from Quentin about incorrect logic for divider
> update in [PATCH 06/40]
>   - Guarded exynos_dwmmc_of_to_plat() with #ifded CONFIG_DM_MMC in
> [PATCH 22/40]
>   - Added list of Exynos chips implementing 64-bit IDMAC descriptor (in
> 

Re: [PATCH v2 00/40] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-07-26 Thread Sam Protsenko
On Fri, Jul 26, 2024 at 12:27 PM Minkyu Kang  wrote:
>
> Hello,
>
>
> 2024년 7월 26일 (금) 05:58, Sam Protsenko 님이 작성:
>>
>> On Thu, Jul 25, 2024 at 1:25 PM Michael Nazzareno Trimarchi
>>  wrote:
>> >
>> > Hi all
>> >
>> > Il mar 9 lug 2024, 02:48 Sam Protsenko  ha 
>> > scritto:
>> >>
>> >> On Thu, Jun 27, 2024 at 9:42 AM Tom Rini  wrote:
>> >> >
>> >> > On Wed, Jun 26, 2024 at 10:12:12PM +0530, Anand Moon wrote:
>> >> > > Hi Sam,
>> >> > >
>> >> > > On Wed, 19 Jun 2024 at 02:26, Sam Protsenko 
>> >> > >  wrote:
>> >> > > >
>> >> > > > If there are no new comments on this series, can you please apply 
>> >> > > > it?
>> >> > > >
>> >> > > > Thanks!
>> >> > >
>> >> > > Tested on Odroid XU4 and Odrroid U3 boards
>> >> > >
>> >> > > Please add my
>> >> > >
>> >> > > Reviewed-by: Anand Moon 
>> >> > > Tested-by: Anand Moon 
>> >> >
>> >> > Thanks, Minkyu will you be able to pick these up (and other outstanding
>> >> > patches) for a pull request to -next soon?
>> >> >
>> >>
>> >> Hi Minkyu,
>> >>
>> >> Gentle nudge :) Could you please apply this series? I'm working on
>> >> some other features requiring eMMC enabled, so it would be great to
>> >> have it merged soon.
>
>
> I'll check at next week.
>

Thank you, Minkyu! You probably already saw it, but letting you know
just in case: I submitted v4 for this series yesterday [1]. It should
apply cleanly on current U-Boot/master.

[1] https://lists.denx.de/pipermail/u-boot/2024-July/560057.html

>>
>> >
>> >
>> > I think that we should merge your clock patch. I will rethink my series 
>> > and make a differente design but agree that 5 months are too much
>> >
>>
>> Thanks, Michael! Please let me know if you need any help with further clk 
>> work.
>>
>> > Michael
>> >>
>> >>
>> >> Thanks!
>> >>
>> >> > --
>> >> > Tom
>
>
> Thanks.
> Minkyu Kang.


Re: [PATCH v2 00/40] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-07-25 Thread Sam Protsenko
On Thu, Jul 25, 2024 at 1:25 PM Michael Nazzareno Trimarchi
 wrote:
>
> Hi all
>
> Il mar 9 lug 2024, 02:48 Sam Protsenko  ha 
> scritto:
>>
>> On Thu, Jun 27, 2024 at 9:42 AM Tom Rini  wrote:
>> >
>> > On Wed, Jun 26, 2024 at 10:12:12PM +0530, Anand Moon wrote:
>> > > Hi Sam,
>> > >
>> > > On Wed, 19 Jun 2024 at 02:26, Sam Protsenko  
>> > > wrote:
>> > > >
>> > > > If there are no new comments on this series, can you please apply it?
>> > > >
>> > > > Thanks!
>> > >
>> > > Tested on Odroid XU4 and Odrroid U3 boards
>> > >
>> > > Please add my
>> > >
>> > > Reviewed-by: Anand Moon 
>> > > Tested-by: Anand Moon 
>> >
>> > Thanks, Minkyu will you be able to pick these up (and other outstanding
>> > patches) for a pull request to -next soon?
>> >
>>
>> Hi Minkyu,
>>
>> Gentle nudge :) Could you please apply this series? I'm working on
>> some other features requiring eMMC enabled, so it would be great to
>> have it merged soon.
>
>
> I think that we should merge your clock patch. I will rethink my series and 
> make a differente design but agree that 5 months are too much
>

Thanks, Michael! Please let me know if you need any help with further clk work.

> Michael
>>
>>
>> Thanks!
>>
>> > --
>> > Tom


Re: [PATCH] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present

2024-07-25 Thread Sam Protsenko
[snip]

>>
>> If anything, that shows there is a rising need in this propagation
>> feature. I'd like to encourage the maintainers to take a look at both
>> implementations, and maybe at least comment on why there is hesitance
>> to accept this patch. It's been almost 5 months that I'm trying to
>> push that through, and my MMC work (DDR mode on E850-96 board to be
>> precise) still very much depends on this.
>
> My series is still not ready and I have no problem to go with your while for 
> now
>

Hi Sean,

Can you please apply this patch, if it's ok with you?

Thanks!

[snip]


[PATCH v4 38/38] doc: samsung: Mention enabled eMMC in E850-96 board doc

2024-07-25 Thread Sam Protsenko
eMMC is enabled on E850-96 board now. Mention that in the board
documentation.

Signed-off-by: Sam Protsenko 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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):
 
-- 
2.39.2



[PATCH v4 37/38] configs: e850-96: Enable MMC

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - Rebased on top of the most recent master

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 2949da24267f..e5d90996234e 100644
--- a/configs/e850-96_defconfig
+++ b/configs/e850-96_defconfig
@@ -9,12 +9,20 @@ CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
 CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0xf8c0
 CONFIG_DEFAULT_DEVICE_TREE="exynos/exynos850-e850-96"
 CONFIG_SYS_LOAD_ADDR=0x8000
+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_CMD_RNG=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
-- 
2.39.2



[PATCH v4 36/38] arm: dts: exynos: Remove outdated DW MMC properties in all Exynos dts

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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..6822c68a 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 = <4>;
-   bus_hz = <4>;
-   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@1251 {
-   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 = "disabl

[PATCH v4 35/38] mmc: exynos_dw_mmc: Improve coding style

2024-07-25 Thread Sam Protsenko
Fix most of checkpatch warnings and other obvious style issues.

No functional change.

Signed-off-by: Sam Protsenko 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 
 
-#define EMMCP_MPSBEGIN00x1200
-#define EMMCP_SEND00x1204
-#define EMMCP_CTRL00x120C
+#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_BITBIT(7)
+#define MPSCTRL_SECURE_WRITE_BIT   BIT(6)
+#define MPSCTRL_NON_SECURE_READ_BITBIT(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 a9e240fd91d6..c8bf89d6d355 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 {
 #ifdef CONFIG_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);
 }
@@ -214,8 +210,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);
 
@@ -223,9 +219,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_SDMM

[PATCH v4 34/38] mmc: exynos_dw_mmc: Use dev->name as driver's displayed name

2024-07-25 Thread Sam Protsenko
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@1210".

Signed-off-by: Sam Protsenko 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 43111b749a6b..a9e240fd91d6 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;
-- 
2.39.2



[PATCH v4 33/38] mmc: exynos_dw_mmc: Don't call dwmci_setup_cfg() after add_dwmci()

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 41fb32c579ef..43111b749a6b 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;
 
-#ifndef CONFIG_DM_MMC
-   /* Add the mmc channel to be registered with mmc core */
-   if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) {
+#ifdef CONFIG_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);
 }
-- 
2.39.2



[PATCH v4 32/38] mmc: exynos_dw_mmc: Pull all init code into probe function

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 29241edf7915..41fb32c579ef 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;
-
-#ifndef CONFIG_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)
-{
-#ifdef CONFIG_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);
-}
-
 #ifdef CONFIG_DM_MMC
 static int exynos_dwmmc_of_to_plat(struct udevice *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;
 
 #ifndef CONFIG_CPU_V7A
@@ -353,9 +305,41 @@ static int exynos_dwmmc_probe(struct udevice *dev)
return err;
 #endif
 
-   err = do_dwmci_init(host);
-   if (err)
+#ifdef CONFIG_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;
+
+#ifndef CONFIG_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;
-- 
2.39.2



[PATCH v4 31/38] mmc: exynos_dw_mmc: Add support for ARM64 Exynos chips

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 ee3ae9f5e650..29241edf7915 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -385,6 +385,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",
@@ -392,6 +397,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,
},
{ }
 };
-- 
2.39.2



[PATCH v4 30/38] mmc: exynos_dw_mmc: Set requested freq in get_mmc_clk() callback

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 32f3ea168b6c..ee3ae9f5e650 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -169,7 +169,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",
-- 
2.39.2



[PATCH v4 29/38] mmc: exynos_dw_mmc: Read and use DDR timing when available

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 b9d655c0d5c8..32f3ea168b6c 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -52,6 +52,7 @@ struct dwmci_exynos_priv_data {
 #endif
struct clk clk;
u32 sdr_timing;
+   u32 ddr_timing;
const struct exynos_dwmmc_variant *chip;
 };
 
@@ -127,8 +128,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);
 
-- 
2.39.2



[PATCH v4 28/38] mmc: exynos_dw_mmc: Move quirks from struct dwmci_host to chip data

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 bed8bd8a0fcf..b9d655c0d5c8 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -25,6 +25,9 @@
 
 #define EXYNOS4412_FIXED_CIU_CLK_DIV   4
 
+/* Quirks */
+#define DWMCI_QUIRK_DISABLE_SMUBIT(0)
+
 #ifdef CONFIG_DM_MMC
 #include 
 DECLARE_GLOBAL_DATA_PTR;
@@ -39,6 +42,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 */
@@ -173,7 +177,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,
@@ -205,11 +209,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;
@@ -352,6 +352,9 @@ static const struct exynos_dwmmc_variant exynos4_drv_data = 
{
 
 static const struct exynos_dwmmc_variant exynos5_drv_data = {
.clksel = DWMCI_CLKSEL,
+#ifdef CONFIG_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 8c2500da9efe..6edb9e1a59c5 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_SMUBIT(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;
-- 
2.39.2



[PATCH v4 27/38] mmc: exynos_dw_mmc: Read common clock-frequency property

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 612c2d0c615d..bed8bd8a0fcf 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;
 }
-- 
2.39.2



[PATCH v4 26/38] mmc: exynos_dw_mmc: Read common bus-width property

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 b5c8f592c7e8..612c2d0c615d 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 80dbb38c9b36..278019f02abb 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -166,7 +166,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;
-- 
2.39.2



[PATCH v4 25/38] mmc: exynos_dw_mmc: Refactor fixed CIU clock divider

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 54a2ba4795e3..1af5ec0532ea 100644
--- a/drivers/mmc/ca_dw_mmc.c
+++ b/drivers/mmc/ca_dw_mmc.c
@@ -86,7 +86,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 fd2ced3d711d..b5c8f592c7e8 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -23,6 +23,8 @@
 #defineDWMMC_MMC0_SDR_TIMING_VAL   0x03030001
 #defineDWMMC_MMC2_SDR_TIMING_VAL   0x03020001
 
+#define EXYNOS4412_FIXED_CIU_CLK_DIV   4
+
 #ifdef CONFIG_DM_MMC
 #include 
 DECLARE_GLOBAL_DATA_PTR;
@@ -36,6 +38,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 */
@@ -126,12 +129,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
@@ -139,9 +148,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",
@@ -149,11 +166,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);
ho

[PATCH v4 24/38] mmc: exynos_dw_mmc: Abstract CLKSEL register

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 4108af47e518..fd2ced3d711d 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -33,6 +33,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
@@ -40,6 +45,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(
@@ -115,13 +121,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;
@@ -132,7 +139,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);
+
 #ifdef CONFIG_CPU_V7A
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
@@ -322,9 +331,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,
+   },
{ }
 };
 
-- 
2.39.2



[PATCH v4 23/38] mmc: exynos_dw_mmc: Read upstream SDR timing properties

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 50d0208b29fd..4108af47e518 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];
 
 #ifdef CONFIG_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) {
-- 
2.39.2



[PATCH v4 22/38] mmc: exynos_dw_mmc: Convert to use livetree API

2024-07-25 Thread Sam Protsenko
Update the driver to use livetree API instead of FDT one.

No functional change.

Signed-off-by: Sam Protsenko 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Moved exynos_dwmmc_of_to_plat() under #ifdef CONFIG_DM_MMC stanza

 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 09a59fdf0120..50d0208b29fd 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -6,9 +6,7 @@
 
 #include 
 #include 
-#include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -226,18 +224,19 @@ static int do_dwmci_init(struct dwmci_host *host)
 #ifdef CONFIG_DM_MMC
 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];
 
 #ifdef CONFIG_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;
 }
-- 
2.39.2



[PATCH v4 20/38] mmc: exynos_dw_mmc: Obtain and use CIU clock via CCF API

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 ed2752391603..4d28ede0d756 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -4,6 +4,7 @@
  * Jaehoon Chung 
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -15,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #defineDWMMC_MAX_CH_NUM4
@@ -38,6 +40,7 @@ struct dwmci_exynos_priv_data {
 #ifdef CONFIG_DM_MMC
struct dwmci_host host;
 #endif
+   struct clk clk;
u32 sdr_timing;
 };
 
@@ -51,6 +54,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)
+{
+#ifdef CONFIG_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;
+
+#ifdef CONFIG_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.
@@ -68,6 +126,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
@@ -77,7 +136,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.
@@ -107,19 +172,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(hos

[PATCH v4 21/38] mmc: exynos_dw_mmc: Use .of_to_plat for device tree parsing

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 drivers/mmc/exynos_dw_mmc.c | 15 +++
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 4d28ede0d756..09a59fdf0120 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -223,10 +223,13 @@ 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)
+#ifdef CONFIG_DM_MMC
+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];
 
@@ -286,7 +289,6 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
return 0;
 }
 
-#ifdef CONFIG_DM_MMC
 static int exynos_dwmmc_probe(struct udevice *dev)
 {
struct exynos_mmc_plat *plat = dev_get_plat(dev);
@@ -301,10 +303,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;
@@ -335,6 +333,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,
-- 
2.39.2



[PATCH v4 19/38] mmc: exynos_dw_mmc: Don't call pinmux functions on ARM64 chips

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 aa542c13fea8..ed2752391603 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -144,6 +144,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host)
 
 static int do_dwmci_init(struct dwmci_host *host)
 {
+#ifdef CONFIG_CPU_V7A
int flag, err;
 
flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
@@ -152,6 +153,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);
 }
@@ -163,6 +165,7 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
int err = 0;
u32 timing[3];
 
+#ifdef CONFIG_CPU_V7A
/* Extract device id for each mmc channel */
host->dev_id = pinmux_decode_periph_id(blob, node);
 
@@ -174,6 +177,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);
-- 
2.39.2



[PATCH v4 18/38] mmc: exynos_dw_mmc: Fix getting private data in exynos_dwmci_board_init()

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 a86d58663c1b..aa542c13fea8 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -41,18 +41,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;
@@ -82,7 +88,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);
-- 
2.39.2



[PATCH v4 15/38] dt-bindings: exynos: Update bindings doc for DW MMC controller

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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@1220 {
-   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.
-- 
2.39.2



[PATCH v4 16/38] arm: exynos: Add header guard for dwmmc.h

2024-07-25 Thread Sam Protsenko
Add missing header guard to prevent possible build errors.

Fixes: 77b55e8cfcee ("ARM: exynos: move SoC sources to mach-exynos")
Signed-off-by: Sam Protsenko 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 
  */
 
+#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_MASK0x7
+
+#endif /* __ASM_ARM_ARCH_DWMMC_H */
-- 
2.39.2



[PATCH v4 17/38] mmc: exynos_dw_mmc: Fix obtaining the base address of controller

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 377c9e6d90b9..a86d58663c1b 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -155,7 +155,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);
@@ -173,12 +173,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);
-- 
2.39.2



[PATCH v4 14/38] arm: dts: exynos: Add upstream DW MMC properties to all Exynos dts

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 = <4>;
bus_hz = <4>;
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@1251 {
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>;
+   bu

[PATCH v4 13/38] mmc: dw_mmc: Improve coding style

2024-07-25 Thread Sam Protsenko
Fix most of checkpatch warnings and other obvious style issues.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - Rebased on top of the most recent master

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 drivers/mmc/dw_mmc.c |  68 ++--
 include/dwmmc.h  | 149 ++-
 2 files changed, 110 insertions(+), 107 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 47dcc37c2c7c..8551eac70185 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) & 0x;
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)
  

[PATCH v4 11/38] mmc: dw_mmc: Replace fifoth_val property with fifo-depth

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 55eb8a54f594..47dcc37c2c7c 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++);
@@ -655,16 +653,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 a51f762988da..377c9e6d90b9 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -150,8 +150,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;
@@ -200,7 +200,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);
 
@@ -216,8 +216,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;
   

[PATCH v4 12/38] mmc: dw_mmc: Fix kernel-doc comments in dwmmc.h

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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. 15000)
+ * @min_clk:   Minimum supported clock speed in Hz (e.g. 40)
  *
  * 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. 15000)
- * @min_clk:   Minimum supported clock speed in HZ (e.g. 40)
  */
 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 mm

[PATCH v4 10/38] mmc: dw_mmc: Add support for 64-bit IDMAC

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Replaced CONFIG_IS_ENABLED() with #ifdef

 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 3e7073f7de1b..55eb8a54f594 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 & 0x;
+   desc->des5 = buf_addr >> 32;
+   desc->des6 = next_desc_phys & 0x;
+   desc->des7 = next_desc_phys >> 32;
+}
+
+static void dwmci_prepare_desc(struct dwmci_host *host, struct mmc_data *data,

[PATCH v4 09/38] mmc: dw_mmc: Improve 32-bit IDMAC descriptor namings

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 d73d58bcf7d7..3e7073f7de1b 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 = 10;
-- 
2.39.2



[PATCH v4 08/38] mmc: dw_mmc: Extract setting the DMA descriptor into a separate routine

2024-07-25 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - Rebased on top of the most recent master

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 drivers/mmc/dw_mmc.c | 51 +++-
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 8fc26399034b..d73d58bcf7d7 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -53,46 +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);
-
-   /* Clear IDMAC interrupt */
-   dwmci_writel(host, DWMCI_IDSTS, 0x);
+   for (i = 0;; i++) {
+   unsigned int flags, cnt;
 
-   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, 0x);
+
+   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;
-- 
2.39.2



[PATCH v4 07/38] mmc: dw_mmc: Extract DMA transfer handling code into a separate routine

2024-07-25 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 b99f63a6f8bd..8fc26399034b 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -232,6 +232,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)
 {
@@ -274,7 +301,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 = 10;
-   u32 mask, ctrl;
+   u32 mask;
struct bounce_buffer bbstate;
 
dwmci_wait_while_busy(host, cmd);
@@ -382,26 +409,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);
-- 
2.39.2



[PATCH v4 06/38] mmc: dw_mmc: Extract FIFO data transfer into a separate routine

2024-07-25 Thread Sam Protsenko
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 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - (none)

 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 b9c14fe6857b..b99f63a6f8bd 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -139,25 +139,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);
@@ -168,50 +210,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_M

[PATCH v4 05/38] mmc: dw_mmc: Extract divider update to a separate function

2024-07-25 Thread Sam Protsenko
Extract the clock divider update into dwmci_update_div() function. It's
a procedure recommended in TRM, so it's better to keep it in a dedicated
function to make the code clearer.

While at it also extract the clock control code into a separate routine
to avoid code duplication in dwmci_setup_bus().

No functional change.

Signed-off-by: Sam Protsenko 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Moved CLKSRC after disabling the clock as recommended in TRM, to
avoid possible glitch
  - Extracted div update into a separate function with corresponding
comment
  - Changed the patch subject and reworked the commit message

 drivers/mmc/dw_mmc.c | 81 +++-
 1 file changed, 50 insertions(+), 31 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 4a9e57165fbb..b9c14fe6857b 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -410,11 +410,56 @@ 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 = 1;
+   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;
+}
+
+/*
+ * Update the clock divider.
+ *
+ * To prevent a clock glitch keep the clock stopped during the update of
+ * clock divider and clock source.
+ */
+static int dwmci_update_div(struct dwmci_host *host, u32 div)
+{
+   int ret;
+
+   /* Disable clock */
+   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 */
+   return dwmci_control_clken(host, true);
+}
+
+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;
@@ -437,35 +482,9 @@ 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);
-   dwmci_writel(host, DWMCI_CLKSRC, 0);
-
-   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 = 1;
-   do {
-   status = dwmci_readl(host, DWMCI_CMD);
-   if (timeout-- < 0) {
-   debug("%s: Timeout!\n", __func__);
-   return -ETIMEDOUT;
-   }
-   } while (status & DWMCI_CMD_START);
+   ret = dwmci_update_div(host, div);
+   if (ret)
+   return ret;
 
host->clock = freq;
 
-- 
2.39.2



[PATCH v4 04/38] mmc: dw_mmc: Extract FIFO init into a separate routine

2024-07-25 Thread Sam Protsenko
Move FIFO threshold initialization into a separate function to make
dwmci_init() more readable.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 395b699b0a0a..4a9e57165fbb 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -538,6 +538,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;
@@ -562,16 +576,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);
-- 
2.39.2



[PATCH v4 03/38] mmc: dw_mmc: Extract waiting for data busy into a separate routine

2024-07-25 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 45665e517068..395b699b0a0a 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -245,6 +245,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)
@@ -259,19 +274,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 = 10;
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) {
-- 
2.39.2



[PATCH v4 02/38] mmc: dw_mmc: Move struct idmac to dw_mmc.c

2024-07-25 Thread Sam Protsenko
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 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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 f4ecd7422cef..45665e517068 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);
-- 
2.39.2



[PATCH v4 01/38] mmc: dw_mmc: Remove unused version field from struct dwmci_host

2024-07-25 Thread Sam Protsenko
Nobody seems to use it, so just remove it.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v4:
  - (none)

Changes in v3:
  - (none)

Changes in v2:
  - Added R-b tag from Quentin

 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;
-- 
2.39.2



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

2024-07-25 Thread Sam Protsenko
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.
Next Exynos chips are known to have 64-bit DW MMC block:

  - Exynos3475
  - Exynos5433
  - Exynos7420
  - Exynos7570
  - Exynos7580
  - Exynos7870
  - Exynos7872
  - Exynos7880
  - Exynos7885
  - Exynos7890
  - Exynos850 (Exynos3830)

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 0x8a00
env set bootaddr 0x9400
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 0x10
cp.b $boot_kerneladdr $loadaddr 200
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: 5200
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'):

39975gout_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.

Changes in v4:
  - Rebased all patches on top of the most recent master; patches that
were affected by recent changes: 08/38, 13/38, 37/38

Changes in v3:
  - Rebased all patches on top of the most recent master, which in turn
dropped next patches:
- [PATCH v2 1/40] mmc: dw_mmc: Remove common.h
- [PATCH v2 33/40] mmc: exynos_dw_mmc: Remove common.h

Changes in v2:
  - Rebased on top of the most recent U-Boot/master
  - Added pending R-b tags from the mailing list
  - Addressed the comment from Quentin about CONFIG_IS_ENABLED() not
playing nice in SPL case:
1. Dropped [PATCH 35/42]
   ("mmc: exynos_dw_mmc: Use CONFIG_IS_ENABLED() to check config
options")
2. Dropped [PATCH 14/42]
   ("mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options")
3. Got rid of all other CONFIG_IS_ENABLED() changes (used #ifdef for
   now)
  - Addressed the comment from Quentin about incorrect logic for divider
update in [PATCH 06/40]
  - Guarded exynos_dwmmc_of_to_plat() with #ifded CONFIG_DM_MMC in
[PATCH 22/40]
  - Added list of Exynos chips implementing 64-bit IDMAC descriptor (in
[PATCH 00/40])

[1] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html

Sam Protsenko (38):
  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 divider update to a separate function
  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: 

Re: [PATCH v3 00/38] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-07-25 Thread Sam Protsenko
On Wed, Jul 24, 2024 at 11:30 PM Sam Protsenko
 wrote:
>
> Hi Jaehoon,
>
> Can you please check this series, and if it looks good, apply it?
>

v3 doesn't apply anymore, due to the recent changes in U-Boot/master.
I'll rebase and re-submit v4 today.

Thanks!

[snip]


Re: [PATCH v3 00/38] mmc: dw_mmc: Enable eMMC on E850-96 board

2024-07-24 Thread Sam Protsenko
Hi Jaehoon,

Can you please check this series, and if it looks good, apply it?

Thanks!

On Mon, Jul 22, 2024 at 12:57 PM Sam Protsenko
 wrote:
>
> 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.
> Next Exynos chips are known to have 64-bit DW MMC block:
>
>   - Exynos3475
>   - Exynos5433
>   - Exynos7420
>   - Exynos7570
>   - Exynos7580
>   - Exynos7870
>   - Exynos7872
>   - Exynos7880
>   - Exynos7885
>   - Exynos7890
>   - Exynos850 (Exynos3830)
>
> 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 0x8a00
> env set bootaddr 0x9400
> 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 0x10
> cp.b $boot_kerneladdr $loadaddr 200
> 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: 5200
> 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'):
>
> 39975gout_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.
>
> Changes in v3:
>   - Rebased all patches on top of the most recent master, which in turn
> dropped next patches:
> - [PATCH v2 1/40] mmc: dw_mmc: Remove common.h
> - [PATCH v2 33/40] mmc: exynos_dw_mmc: Remove common.h
>
> Changes in v2:
>   - Rebased on top of the most recent U-Boot/master
>   - Added pending R-b tags from the mailing list
>   - Addressed the comment from Quentin about CONFIG_IS_ENABLED() not
> playing nice in SPL case:
> 1. Dropped [PATCH 35/42]
>("mmc: exynos_dw_mmc: Use CONFIG_IS_ENABLED() to check config
> options")
> 2. Dropped [PATCH 14/42]
>("mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options")
> 3. Got rid of all other CONFIG_IS_ENABLED() changes (used #ifdef for
>    now)
>   - Addressed the comment from Quentin about incorrect logic for divider
> update in [PATCH 06/40]
>   - Guarded exynos_dwmmc_of_to_plat() with #ifded CONFIG_DM_MMC in
> [PATCH 22/40]
>   - Added list of Exynos chips implementing 64-bit IDMAC descriptor (in
> [PATCH 00/40])
>
> [1] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html
>
> Sam Protsenko (38):
>   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 FI

Re: [PATCH V2] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux

2024-07-24 Thread Sam Protsenko
On Wed, Jul 24, 2024 at 4:44 AM Michael Nazzareno Trimarchi
 wrote:

[snip]

> > Ok , so you suggest to have something like in the core
>
> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index 8faf5a56e1..a309108dd4 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -467,8 +467,14 @@ ulong clk_get_rate(struct clk *clk)
> return 0;
> ops = clk_dev_ops(clk->dev);
>
> -   if (!ops->get_rate)
> -   return -ENOSYS;
> +   if (!ops->get_rate) {
> +   parent = clk_get_parent(clk);
> +   if (!clk_is_valid(parent))
> +   return -ENOSYS;
> +   ops = clk_dev_ops(parent->dev);
> +   if (!ops->get_rate)
> +   return -ENOSYS;
> +   }
>
> return ops->get_rate(clk);
>  }
>
> You suggest to remove get_rate, set_rate function in mux and gate and
> use some propagation in the core, am I right?

Something like that, yes. But maybe instead of

if (!ops->get_rate)

use something like:

while (!ops->get_rate)

like I did in my patch [1] for clk_set_rate(). That while loop is
needed to handle cases when there are multiple clocks without
.get_rate() operation connected together (like mux or gate clocks). So
you'd have to iterate them all up the tree to find some DIV clock
which actually has .get_rate() defined.

But at the moment I'm more concerned about .set_rate() propagation of
course, which is really needed for my E850-96 MMC enablement series
[2], which has already been pending on the review for a while.

[1] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html
[2] https://lists.denx.de/pipermail/u-boot/2024-July/559602.html

[snip]

> > > implementations simple and brings the propagation logic to the actual
> > > clk API (clk_set_rate()), which in turn is able to cover all possible
> > > cases: even if some new clock types are implemented later, they will
> > > be covered already. That also reduces some code duplication and makes
> > > it easier to control that behavior in a centralized manner. The patch
> > > with the discussed implementation was already submitted a while ago
> > > [2], and still pending on the review.
> >
> > I have seen it and because I'm extending the clk framework a bit more [1]
> > I was thinking about a more complete approach. Nothing against your
> > patch, but just
> > I have observed in my use case (display) that I need more from the
> > clock framework we have
> >

I see. Do you think it'd possible for you to use my patch for
clk_set_rate() propagation for your case, and base your patches (for
CLK_OPS_PARENT_ENABLE, etc) on top of that? Will it cover your
requirements?

Would be also nice to hear what maintainers think about that :)

Thanks!

> > Michael
> >
> > [1] https://patchwork.amarulasolutions.com/patch/3291/
> >
> > >

[snip]


Re: [PATCH] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present

2024-07-23 Thread Sam Protsenko
On Wed, Jul 3, 2024 at 10:00 AM Michael Nazzareno Trimarchi
 wrote:
>
> Hi all
>
> Working on the clock now. I have done a different implementation
>
> clk-mux, clk-gate I have added a clk_generic_set_rate that is a stub
> that propagate to the parent, I think that should work the same but
> wihout this while and even on preparation on more
> changes I need. Can be work on your case too?
>

Hi Michael,

I've provided some thoughts on your gate/mux implementation here: [1].
As you've submitted your (different) implementation of the same
feature, but 4 months later, I guess my patch doesn't cover your
cases, or perhaps it has some design flaws? Please let me know.

If anything, that shows there is a rising need in this propagation
feature. I'd like to encourage the maintainers to take a look at both
implementations, and maybe at least comment on why there is hesitance
to accept this patch. It's been almost 5 months that I'm trying to
push that through, and my MMC work (DDR mode on E850-96 board to be
precise) still very much depends on this.

Thanks!

[1] https://lists.denx.de/pipermail/u-boot/2024-July/559753.html


> Michael
>
> On Thu, Jun 13, 2024 at 5:33 PM Tom Rini  wrote:
> >
> > On Mon, Jun 10, 2024 at 02:43:17PM -0500, Sam Protsenko wrote:
> > > On Tue, May 14, 2024 at 3:26 PM Sam Protsenko
> > >  wrote:
> > > >
> > > > On Wed, May 1, 2024 at 4:12 PM Sam Protsenko 
> > > >  wrote:
> > > > >
> > > > > On Wed, Apr 10, 2024 at 9:53 PM Sean Anderson  
> > > > > wrote:
> > > > > >
> > > > > > On 3/7/24 19:04, Sam Protsenko wrote:
> > > > > > > Sometimes clocks provided to a consumer might not have .set_rate
> > > > > > > operation (like gate or mux clocks), but have CLK_SET_PARENT_RATE 
> > > > > > > flag
> > > > > > > set. In that case it's usually possible to find a parent up the 
> > > > > > > tree
> > > > > > > which is capable of setting the rate (div, pll, etc). Implement a 
> > > > > > > simple
> > > > > > > lookup procedure for such cases, to traverse the clock tree until
> > > > > > > .set_rate capable parent is found, and use that parent to actually
> > > > > > > change the rate. The search will stop once the first .set_rate 
> > > > > > > capable
> > > > > > > clock is found, which is usually enough to handle most cases.
> > > > > > >
> > > > > > > Signed-off-by: Sam Protsenko 
> > > > > > > ---
> > > > >
> > > > > [snip]
> > > > >
> > > > > >
> > > > > > Can you give an example of where this is needed?
> > > > > >
> > > > >
> > > > > Sure. In my case it's needed for eMMC enablement on E850-96 board.
> > > > > eMMC node in the device tree [1] is a gate clock
> > > > > (CLK_GOUT_MMC_EMBD_SDCLKIN), so the MMC driver won't be able to change
> > > > > its rate. But that clock actually has CLK_SET_RATE_PARENT flag set in
> > > > > the clock driver [2]. So the right thing to do in this case (and
> > > > > that's basically how it's done in Linux kernel too) is to traverse the
> > > > > clock tree upwards, and try to find the parent capable to do .set_rate
> > > > > (which is usually a divider clock), and use it to change the clock
> > > > > rate. I'm working on exynos_dw_mmc driver [3] right now, making it use
> > > > > CCF clocks, but I can't do that before this patch is applied.
> > > > >
> > > > > Grepping the U-Boot tree I can see the CLK_SET_RATE_PARENT flag is
> > > > > also used in various IMX clock drivers and STM32MP13 clock driver. I
> > > > > guess without this change those flags will be basically ignored.
> > > > >
> > > > > Thanks!
> > > > >
> > > >
> > > > Hi Sean,
> > > >
> > > > Just wanted to check if you think my explanation above is ok and the
> > > > patch can be applied? I'm finishing my new patch series for enabling
> > > > MMC on E850-96, but this patch has to be applied first.
> > > >
> > > > Thanks!
> > > >
> > >
> > > Hi Tom,
> > >
> > > Would it be reasonable to ask you to take care of this patch? It's
> > > been ignored for 3 months now, but it's needed for eMMC enablement on
> > > E850-96 board.
> >
> > Any comments at this point Sean?
> >
> > --
> > Tom
>
>
>
> --
> Michael Nazzareno Trimarchi
> Co-Founder & Chief Executive Officer
> M. +39 347 913 2170
> mich...@amarulasolutions.com
> __
>
> Amarula Solutions BV
> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> T. +31 (0)85 111 9172
> i...@amarulasolutions.com
> www.amarulasolutions.com


Re: [PATCH V2] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux

2024-07-23 Thread Sam Protsenko
On Fri, Jul 5, 2024 at 2:14 AM Michael Trimarchi
 wrote:
>
> Gate and mux does not have .set_rate operation, but they could have
> CLK_SET_PARENT_RATE flag set. In that case it's usually possible to find a
> parent up the tree which is capable of setting the rate (div, pll, etc).
> Add clk_generic_set_rate to allow them to trasverse the clock tree.
>
> Cc: Sam Protsenko 
> Signed-off-by: Michael Trimarchi 
> ---
>  drivers/clk/clk-gate.c   |  1 +
>  drivers/clk/clk-mux.c|  2 +-
>  drivers/clk/clk-uclass.c | 20 
>  drivers/clk/clk.c|  9 +
>  include/clk.h|  9 +
>  include/linux/clk-provider.h |  1 +
>  6 files changed, 41 insertions(+), 1 deletion(-)
> ---
> V1->V2:
> Fix missing include
> ---
> diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
> index cfd90b717e7..c86083ac5a3 100644
> --- a/drivers/clk/clk-gate.c
> +++ b/drivers/clk/clk-gate.c
> @@ -116,6 +116,7 @@ const struct clk_ops clk_gate_ops = {
> .enable = clk_gate_enable,
> .disable = clk_gate_disable,
> .get_rate = clk_generic_get_rate,
> +   .set_rate = clk_generic_set_rate,

I can see that clk_generic_get_rate() already exists here, and adding
clk_generic_set_rate() tries to mimic that existing design. But
frankly, I'm not sure it was a very good design choice in the first
place. I mean, to have get/set rate operations for the clock types
that clearly don't have such ability. I think it would be better to
model the CCF clocks after real world clocks, and handle the
propagation on the clock logic level. In fact, that's how it's already
implemented in Linux kernel [1]. That keeps the particular clock
implementations simple and brings the propagation logic to the actual
clk API (clk_set_rate()), which in turn is able to cover all possible
cases: even if some new clock types are implemented later, they will
be covered already. That also reduces some code duplication and makes
it easier to control that behavior in a centralized manner. The patch
with the discussed implementation was already submitted a while ago
[2], and still pending on the review.

Can I ask you if patch [2] doesn't cover some of your cases? Or maybe
I'm missing something out and having .set_rate op in gate/mux would
actually be better for some particular reason?

Thanks!

[1] 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/clk/clk-gate.c#n120
[2] https://lists.denx.de/pipermail/u-boot/2024-March/547719.html


>  };
>
>  struct clk *clk_register_gate(struct device *dev, const char *name,
> diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
> index e3481be95fa..f99a67ebd35 100644
> --- a/drivers/clk/clk-mux.c
> +++ b/drivers/clk/clk-mux.c
> @@ -151,13 +151,13 @@ static int clk_mux_set_parent(struct clk *clk, struct 
> clk *parent)
>  #else
> writel(reg, mux->reg);
>  #endif
> -
> return 0;
>  }
>
>  const struct clk_ops clk_mux_ops = {
> .get_rate = clk_generic_get_rate,
> .set_parent = clk_mux_set_parent,
> +   .set_rate = clk_generic_set_rate,
>  };
>
>  struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index ed6e60bc484..638864e6774 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -517,6 +517,26 @@ ulong clk_get_parent_rate(struct clk *clk)
> return pclk->rate;
>  }
>
> +ulong clk_set_parent_rate(struct clk *clk, ulong rate)
> +{
> +   const struct clk_ops *ops;
> +   struct clk *pclk;
> +
> +   debug("%s(clk=%p)\n", __func__, clk);
> +   if (!clk_valid(clk))
> +   return 0;
> +
> +   pclk = clk_get_parent(clk);
> +   if (IS_ERR(pclk))
> +   return -ENODEV;
> +
> +   ops = clk_dev_ops(pclk->dev);
> +   if (!ops->set_rate)
> +   return -ENOSYS;
> +
> +   return clk_set_rate(pclk, rate);
> +}
> +
>  ulong clk_round_rate(struct clk *clk, ulong rate)
>  {
> const struct clk_ops *ops;
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 6ede1b4d4dc..febd5314df2 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -14,6 +14,7 @@
>  #include 
>  #include 
>  #include 
> +#include 
>
>  int clk_register(struct clk *clk, const char *drv_name,
>  const char *name, const char *parent_name)
> @@ -61,6 +62,14 @@ ulong clk_generic_get_rate(struct clk *clk)
> return clk_get_parent_rate(clk);
>  }
>
> +ulong clk_generic_set_rate(struct clk *clk, ulong rate)
> +{
> +   if (c

Re: [PATCH v2 0/5] arm: exynos: Enable TRNG for E850-96 board

2024-07-23 Thread Sam Protsenko
[snip]

>
> Your patch was failed to apply to u-boot-samsung due to the e850-96_defconfig.
> please rebase the patch.
>

Just sent v3, please take a look.

> and about mmc patches, those have delegated to mmc maintainers.
>

Do you know if I should I ask some specific MMC maintainer to apply
that series? That series was pending for a while now and I really want
to get it merged as soon as possible :)

Thanks!

> Thanks,
> Minkyu Kang.
>


[PATCH v3 5/5] arm: exynos: Enable TRNG on E850-96 board

2024-07-23 Thread Sam Protsenko
Enable True Random Number Generator (TRNG) on E850-96 board. To do so:
  1. Enable DM_RNG and RNG_EXYNOS for TARGET_E850_96
  2. Add TRNG node to E850-96 device tree
  3. Enable 'rng' command support for easy TRNG testing

TRNG node is already applied in Linux kernel device tree, but it hasn't
appeared in upstream dts yet. Add it in U-Boot override dtsi file
temporarily; it can be removed once it appears in upstream dts.

Signed-off-by: Sam Protsenko 
---
Changes in v3:
  - Fixed applying the defconfig changes on U-Boot/master branch

Changes in v2:
  - (none)

 arch/arm/dts/exynos850-e850-96-u-boot.dtsi | 11 +++
 arch/arm/mach-exynos/Kconfig   |  2 ++
 configs/e850-96_defconfig  |  1 +
 3 files changed, 14 insertions(+)

diff --git a/arch/arm/dts/exynos850-e850-96-u-boot.dtsi 
b/arch/arm/dts/exynos850-e850-96-u-boot.dtsi
index 6d7148f7264a..3aa5d8bb10d0 100644
--- a/arch/arm/dts/exynos850-e850-96-u-boot.dtsi
+++ b/arch/arm/dts/exynos850-e850-96-u-boot.dtsi
@@ -3,6 +3,17 @@
  * Copyright (c) 2023 Linaro Ltd.
  */
 
+&soc {
+   /* TODO: Remove this node once it appears in upstream dts */
+   trng: rng@12081400 {
+   compatible = "samsung,exynos850-trng";
+   reg = <0x12081400 0x100>;
+   clocks = <&cmu_core CLK_GOUT_SSS_ACLK>,
+<&cmu_core CLK_GOUT_SSS_PCLK>;
+   clock-names = "secss", "pclk";
+   };
+};
+
 &pmu_system_controller {
bootph-all;
samsung,uart-debug-1;
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index cad8bb044cf0..3fee5a4299b8 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -250,6 +250,8 @@ config TARGET_E850_96
select PINCTRL
select PINCTRL_EXYNOS850
imply OF_UPSTREAM
+   imply DM_RNG
+   imply RNG_EXYNOS
 
 endchoice
 endif
diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig
index 38b9968c1671..2949da24267f 100644
--- a/configs/e850-96_defconfig
+++ b/configs/e850-96_defconfig
@@ -11,6 +11,7 @@ CONFIG_DEFAULT_DEVICE_TREE="exynos/exynos850-e850-96"
 CONFIG_SYS_LOAD_ADDR=0x8000
 # CONFIG_AUTOBOOT is not set
 # CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_CMD_RNG=y
 # CONFIG_NET is not set
 CONFIG_CLK_EXYNOS850=y
 # CONFIG_MMC is not set
-- 
2.39.2



[PATCH v3 4/5] rng: Add Exynos TRNG driver

2024-07-23 Thread Sam Protsenko
Add True Random Number Generator (TRNG) driver for Exynos chips. This
implementation is heavily based on Linux kernel's counterpart [1]. It
also follows upstream dt-bindings [2].

TRNG block is usually a part of SSS (Security Sub System) IP-core on
Exynos chips. Because SSS access on Exynos850 is protected by TZPC
(TrustZone Protection Control), it's not possible to read/write TRNG
registers from U-Boot, as it's running in EL1 mode. Instead, the
corresponding SMC calls should be used to make the secure software
running in EL3 mode access it for us. Those SMC calls are handled by
LDFW (Loadable Firmware), which has to be loaded first. For example, for
E850-96 board it's done in its board_init(), so by the time RNG
capabilities are needed the LDFW should be already loaded and TRNG
should be functional.

[1] drivers/char/hw_random/exynos-trng.c
[2] dts/upstream/Bindings/rng/samsung,exynos5250-trng.yaml

Signed-off-by: Sam Protsenko 
---
Changes in v3:
  - (none)

Changes in v2:
  - Mentioned LDFW firmware in help section for RNG_EXYNOS config option
  - Renamed struct exynos_trng -> struct exynos_trng_priv
  - Added kernel-doc comments for struct exynos_trng_priv
  - Added kernel-doc comments for struct exynos_trng_variant
  - Fixed error codes in exynos_trng_of_to_plat()
  - Renamed 'err' variable to 'ret' in exynos_trng_probe()

 drivers/rng/Kconfig   |  13 ++
 drivers/rng/Makefile  |   1 +
 drivers/rng/exynos-trng.c | 291 ++
 3 files changed, 305 insertions(+)
 create mode 100644 drivers/rng/exynos-trng.c

diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig
index 5758ae192a66..b35d8c66b9c6 100644
--- a/drivers/rng/Kconfig
+++ b/drivers/rng/Kconfig
@@ -120,4 +120,17 @@ config RNG_TURRIS_RWTM
  on other Armada-3700 devices (like EspressoBin) if Secure
  Firmware from CZ.NIC is used.
 
+config RNG_EXYNOS
+   bool "Samsung Exynos True Random Number Generator support"
+   depends on DM_RNG
+   help
+ Enable support for True Random Number Generator (TRNG) available on
+ Exynos SoCs.
+
+ On some chips (like Exynos850) TRNG registers are protected with TZPC
+ (TrustZone Protection Control). For such chips the driver provides an
+ implementation based on SMC calls to EL3 monitor program. In that
+ case the LDFW (Loadable Firmware) has to be loaded first, as it
+ actually implements TRNG SMC calls.
+
 endif
diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile
index c1f1c616e009..30553c9d6e99 100644
--- a/drivers/rng/Makefile
+++ b/drivers/rng/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_RNG_ARM_RNDR) += arm_rndr.o
 obj-$(CONFIG_TPM_RNG) += tpm_rng.o
 obj-$(CONFIG_RNG_JH7110) += jh7110_rng.o
 obj-$(CONFIG_RNG_TURRIS_RWTM) += turris_rwtm_rng.o
+obj-$(CONFIG_RNG_EXYNOS) += exynos-trng.o
diff --git a/drivers/rng/exynos-trng.c b/drivers/rng/exynos-trng.c
new file mode 100644
index ..d2479d244ed5
--- /dev/null
+++ b/drivers/rng/exynos-trng.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Linaro Ltd.
+ * Author: Sam Protsenko 
+ *
+ * Samsung Exynos TRNG driver (True Random Number Generator).
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define EXYNOS_TRNG_CLKDIV 0x0
+#define EXYNOS_TRNG_CLKDIV_MASKGENMASK(15, 0)
+#define EXYNOS_TRNG_CLOCK_RATE 50
+
+#define EXYNOS_TRNG_CTRL   0x20
+#define EXYNOS_TRNG_CTRL_RNGEN BIT(31)
+
+#define EXYNOS_TRNG_POST_CTRL  0x30
+#define EXYNOS_TRNG_ONLINE_CTRL0x40
+#define EXYNOS_TRNG_ONLINE_STAT0x44
+#define EXYNOS_TRNG_ONLINE_MAXCHI2 0x48
+#define EXYNOS_TRNG_FIFO_CTRL  0x50
+#define EXYNOS_TRNG_FIFO_0 0x80
+#define EXYNOS_TRNG_FIFO_1 0x84
+#define EXYNOS_TRNG_FIFO_2 0x88
+#define EXYNOS_TRNG_FIFO_3 0x8c
+#define EXYNOS_TRNG_FIFO_4 0x90
+#define EXYNOS_TRNG_FIFO_5 0x94
+#define EXYNOS_TRNG_FIFO_6 0x98
+#define EXYNOS_TRNG_FIFO_7 0x9c
+#define EXYNOS_TRNG_FIFO_LEN   8
+#define EXYNOS_TRNG_FIFO_TIMEOUT   (1 * USEC_PER_SEC)
+
+#define EXYNOS_SMC_CALL_VAL(func_num)  \
+   ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+  ARM_SMCCC_SMC_32,\
+  ARM_SMCCC_OWNER_SIP, \
+  func_num)
+
+/* SMC command for DTRNG access */
+#define SMC_CMD_RANDOM EXYNOS_SMC_CALL_VAL(0x1012)
+
+/* SMC_CMD_RANDOM: arguments */
+#define HWRNG_INIT 0x0
+#define HWRNG_EXIT 0x1
+#define HWRNG_GET_DATA 0x2
+
+/* SMC_CMD_RANDOM: return values */
+#define HWRNG_RET_OK   0x0
+#define HWRNG_RE

[PATCH v3 3/5] clk: exynos: Add SSS clocks for Exynos850

2024-07-23 Thread Sam Protsenko
Add ACLK (operating clock) and PCLK (bus clock) for Security Sub System
(SSS) in Exynos850. Those clocks are needed for RNG enablement.

Signed-off-by: Sam Protsenko 
---
Changes in v3:
  - (none)

Changes in v2:
  - (none)

 drivers/clk/exynos/clk-exynos850.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/clk/exynos/clk-exynos850.c 
b/drivers/clk/exynos/clk-exynos850.c
index 0c09ba02de4a..8cbc626f31e8 100644
--- a/drivers/clk/exynos/clk-exynos850.c
+++ b/drivers/clk/exynos/clk-exynos850.c
@@ -323,14 +323,18 @@ U_BOOT_DRIVER(exynos850_cmu_peri) = {
 /* Register Offset definitions for CMU_CORE (0x1200) */
 #define PLL_CON0_MUX_CLKCMU_CORE_BUS_USER  0x0600
 #define PLL_CON0_MUX_CLKCMU_CORE_MMC_EMBD_USER 0x0620
+#define PLL_CON0_MUX_CLKCMU_CORE_SSS_USER  0x0630
 #define CLK_CON_DIV_DIV_CLK_CORE_BUSP  0x1800
 #define CLK_CON_GAT_GOUT_CORE_MMC_EMBD_I_ACLK  0x20e8
 #define CLK_CON_GAT_GOUT_CORE_MMC_EMBD_SDCLKIN 0x20ec
+#define CLK_CON_GAT_GOUT_CORE_SSS_I_ACLK   0x2128
+#define CLK_CON_GAT_GOUT_CORE_SSS_I_PCLK   0x212c
 
 /* List of parent clocks for Muxes in CMU_CORE */
 PNAME(mout_core_bus_user_p)= { "clock-oscclk", "dout_core_bus" };
 PNAME(mout_core_mmc_embd_user_p)   = { "clock-oscclk",
"dout_core_mmc_embd" };
+PNAME(mout_core_sss_user_p)= { "clock-oscclk", "dout_core_sss" };
 
 static const struct samsung_mux_clock core_mux_clks[] = {
MUX(CLK_MOUT_CORE_BUS_USER, "mout_core_bus_user", mout_core_bus_user_p,
@@ -338,6 +342,8 @@ static const struct samsung_mux_clock core_mux_clks[] = {
MUX_F(CLK_MOUT_CORE_MMC_EMBD_USER, "mout_core_mmc_embd_user",
  mout_core_mmc_embd_user_p, PLL_CON0_MUX_CLKCMU_CORE_MMC_EMBD_USER,
  4, 1, CLK_SET_RATE_PARENT, 0),
+   MUX(CLK_MOUT_CORE_SSS_USER, "mout_core_sss_user", mout_core_sss_user_p,
+   PLL_CON0_MUX_CLKCMU_CORE_SSS_USER, 4, 1),
 };
 
 static const struct samsung_div_clock core_div_clks[] = {
@@ -351,6 +357,10 @@ static const struct samsung_gate_clock core_gate_clks[] = {
GATE(CLK_GOUT_MMC_EMBD_SDCLKIN, "gout_mmc_embd_sdclkin",
 "mout_core_mmc_embd_user", CLK_CON_GAT_GOUT_CORE_MMC_EMBD_SDCLKIN,
 21, CLK_SET_RATE_PARENT, 0),
+   GATE(CLK_GOUT_SSS_ACLK, "gout_sss_aclk", "mout_core_sss_user",
+CLK_CON_GAT_GOUT_CORE_SSS_I_ACLK, 21, 0, 0),
+   GATE(CLK_GOUT_SSS_PCLK, "gout_sss_pclk", "dout_core_busp",
+CLK_CON_GAT_GOUT_CORE_SSS_I_PCLK, 21, 0, 0),
 };
 
 static const struct samsung_clk_group core_cmu_clks[] = {
-- 
2.39.2



[PATCH v3 1/5] board: samsung: e850-96: Add default partitions

2024-07-23 Thread Sam Protsenko
Add an environment file for E850-96 board with default eMMC partition
list. It follows the Samsung's partition list used for Android-Q on
Exynos850 devices. It was verified on E850-96 board with:

=> gpt verify mmc 0 "$partitions"
   Verify GPT: success!

Signed-off-by: Sam Protsenko 
---
Changes in v3:
  - (none)

Changes in v2:
  - (none)

 board/samsung/e850-96/e850-96.env | 26 ++
 1 file changed, 26 insertions(+)
 create mode 100644 board/samsung/e850-96/e850-96.env

diff --git a/board/samsung/e850-96/e850-96.env 
b/board/samsung/e850-96/e850-96.env
new file mode 100644
index ..f36f90be9509
--- /dev/null
+++ b/board/samsung/e850-96/e850-96.env
@@ -0,0 +1,26 @@
+partitions=
+   uuid_disk=${uuid_gpt_disk};
+   name=efs,start=512K,size=20M,uuid=${uuid_gpt_efs};
+   name=env,size=16K,uuid=${uuid_gpt_env};
+   name=kernel,size=30M,uuid=${uuid_gpt_kernel};
+   name=ramdisk,size=26M,uuid=${uuid_gpt_ramdisk};
+   name=dtbo,size=1M,uuid=${uuid_gpt_dtbo};
+   name=ldfw,size=4016K,uuid=${uuid_gpt_ldfw};
+   name=keystorage,size=8K,uuid=${uuid_gpt_keystorage};
+   name=tzsw,size=1M,uuid=${uuid_gpt_tzsw};
+   name=harx,size=2M,uuid=${uuid_gpt_harx};
+   name=harx_rkp,size=2M,uuid=${uuid_gpt_harx_rkp};
+   name=logo,size=40M,uuid=${uuid_gpt_logo};
+   name=super,size=3600M,uuid=${uuid_gpt_super};
+   name=cache,size=300M,uuid=${uuid_gpt_cache};
+   name=modem,size=100M,uuid=${uuid_gpt_modem};
+   name=boot,size=100M,uuid=${uuid_gpt_boot};
+   name=persist,size=30M,uuid=${uuid_gpt_persist};
+   name=recovery,size=40M,uuid=${uuid_gpt_recovery};
+   name=misc,size=40M,uuid=${uuid_gpt_misc};
+   name=mnv,size=20M,uuid=${uuid_gpt_mnv};
+   name=frp,size=512K,uuid=${uuid_gpt_frp};
+   name=vbmeta,size=64K,uuid=${uuid_gpt_vbmeta};
+   name=metadata,size=16M,uuid=${uuid_gpt_metadata};
+   name=dtb,size=1M,uuid=${uuid_gpt_dtb};
+   name=userdata,size=-,uuid=${uuid_gpt_userdata}
-- 
2.39.2



[PATCH v3 2/5] board: samsung: e850-96: Load LDFW firmware on board init

2024-07-23 Thread Sam Protsenko
LDFW is a Loadable Firmware which provides additional security
capabilities in EL3 monitor. For example, True Random Number Generator
(TRNG) block registers can't be accessed from EL1 (where U-Boot and
Linux kernel are running), but it's possible to access TRNG capabilities
via corresponding SMC calls, which in turn are handled by LDFW. To do
so, LDFW firmware has to be loaded first. It's stored on a raw eMMC
partition, so it has to be read into NWD (Normal World) RAM buffer, and
then loaded to SWD (Secure World) memory using the special SMC call to
EL3 monitor program. EL3_MON will load LDFW to SWD memory, more
specifically to the area starting at 0xbf70 (with size of 7.5 MiB).
That memory area is reserved in device tree, so there shouldn't be any
collisions. After that LDFW becomes functional.

Implement LDFW firmware loading on board init. While at it, fix the
copyright date in header comments, as this board support was actually
added in 2024, not in 2020: it was probably a copy-paste mistake.

Signed-off-by: Sam Protsenko 
---
Changes in v3:
  - (none)

Changes in v2:
  - (none)

 board/samsung/e850-96/Makefile  |   4 +-
 board/samsung/e850-96/e850-96.c |   6 +-
 board/samsung/e850-96/fw.c  | 131 
 board/samsung/e850-96/fw.h  |  12 +++
 4 files changed, 149 insertions(+), 4 deletions(-)
 create mode 100644 board/samsung/e850-96/fw.c
 create mode 100644 board/samsung/e850-96/fw.h

diff --git a/board/samsung/e850-96/Makefile b/board/samsung/e850-96/Makefile
index 301c22337119..71d46ea3d2b4 100644
--- a/board/samsung/e850-96/Makefile
+++ b/board/samsung/e850-96/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0+
 #
-# Copyright (C) 2020, Linaro Limited
+# Copyright (C) 2024, Linaro Limited
 # Sam Protsenko 
 
-obj-y  := e850-96.o
+obj-y  := e850-96.o fw.o
diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c
index a00d81b5d4c3..c5cef6f19d22 100644
--- a/board/samsung/e850-96/e850-96.c
+++ b/board/samsung/e850-96/e850-96.c
@@ -1,10 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2020, Linaro Limited
- * Sam Protsenko 
+ * Copyright (c) 2024, Linaro Ltd.
+ * Author: Sam Protsenko 
  */
 
 #include 
+#include "fw.h"
 
 int dram_init(void)
 {
@@ -18,5 +19,6 @@ int dram_init_banksize(void)
 
 int board_init(void)
 {
+   load_ldfw();
return 0;
 }
diff --git a/board/samsung/e850-96/fw.c b/board/samsung/e850-96/fw.c
new file mode 100644
index ..82a0b224c670
--- /dev/null
+++ b/board/samsung/e850-96/fw.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2024 Linaro Ltd.
+ * Author: Sam Protsenko 
+ *
+ * Firmware loading code.
+ */
+
+#include 
+#include 
+#include "fw.h"
+
+#define EMMC_IFACE "mmc"
+#define EMMC_DEV_NUM   0
+
+/* LDFW constants */
+#define LDFW_PART_NAME "ldfw"
+#define LDFW_NWD_ADDR  0x8800
+#define LDFW_MAGIC 0x10adab1e
+#define SMC_CMD_LOAD_LDFW  -0x500
+#define SDM_HW_RESET_STATUS0x1230
+#define SDM_SW_RESET_STATUS0x1231
+#define SB_ERROR_PREFIX0xfdaa
+
+struct ldfw_header {
+   u32 magic;
+   u32 size;
+   u32 init_entry;
+   u32 entry_point;
+   u32 suspend_entry;
+   u32 resume_entry;
+   u32 start_smc_id;
+   u32 version;
+   u32 set_runtime_entry;
+   u32 reserved[3];
+   char fw_name[16];
+};
+
+static int read_fw(const char *part_name, void *buf)
+{
+   struct blk_desc *blk_desc;
+   struct disk_partition part;
+   unsigned long cnt;
+   int part_num;
+
+   blk_desc = blk_get_dev(EMMC_IFACE, EMMC_DEV_NUM);
+   if (!blk_desc) {
+   debug("%s: Can't get eMMC device\n", __func__);
+   return -ENODEV;
+   }
+
+   part_num = part_get_info_by_name(blk_desc, part_name, &part);
+   if (part_num < 0) {
+   debug("%s: Can't get LDWF partition\n", __func__);
+   return -ENOENT;
+   }
+
+   cnt = blk_dread(blk_desc, part.start, part.size, buf);
+   if (cnt != part.size) {
+   debug("%s: Can't read LDFW partition\n", __func__);
+   return -EIO;
+   }
+
+   return 0;
+}
+
+int load_ldfw(void)
+{
+   const phys_addr_t addr = (phys_addr_t)LDFW_NWD_ADDR;
+   struct ldfw_header *hdr;
+   struct arm_smccc_res res;
+   void *buf = (void *)addr;
+   u64 size = 0;
+   int err, i;
+
+   /* Load LDFW from the block device partition into RAM buffer */
+   err = read_fw(LDFW_PART_NAME, buf);
+   if (err)
+   return err;
+
+   /* Validate LDFW by magic number in its header */
+   hdr = buf;
+   if (hdr->magic != LDFW_MAGIC) {
+   debug("%s: Wrong LDFW magic; is LDFW flashed?\n", __func__);
+   return -EINV

  1   2   3   4   5   6   7   8   9   >