[PATCH v2 39/40] configs: e850-96: Enable MMC

2024-06-09 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 
---
 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 38b9968c1671..29ad31d5f8ed 100644
--- a/configs/e850-96_defconfig
+++ b/configs/e850-96_defconfig
@@ -9,11 +9,19 @@ CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
 CONFIG_CUSTOM_SYS_INIT_SP_ADDR=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_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 v2 40/40] doc: samsung: Mention enabled eMMC in E850-96 board doc

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

Signed-off-by: Sam Protsenko 
---
 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 v2 38/40] arm: dts: exynos: Remove outdated DW MMC properties in all Exynos dts

2024-06-09 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 
---
 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 @@
 };
 
  {
-   samsung,bus-width = <4>;
bus-width = <4>;
-   samsung,timing = <1 2 3>;
cd-gpios = < 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 @@
 };
 
  {
-   samsung,bus-width = <8>;
bus-width = <8>;
-   samsung,timing = <1 3 3>;
pwr-gpios = < 2 0>;
status = "okay";
 };
 
  {
-   samsung,bus-width = <4>;
bus-width = <4>;
-   samsung,timing = <1 2 3>;
cd-gpios = < 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 @@
 };
 
  {
-   samsung,bus-width = <8>;
bus-width = <8>;
-   samsung,timing = <1 3 3>;
pwr-gpios = < 2 0>;
status = "okay";
 };
 
  {
-   samsung,bus-width = <4>;
bus-width = <4>;
-   samsung,timing = <1 2 3>;
cd-gpios = < 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 @@
 };
 
  {
-   samsung,bus-width = <4>;
-   samsung,timing = <1 2 3>;
cd-inverted;
cd-gpios = < 2 0>;
status = "okay";
 };
 
 _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 = < 4 0>;
status = "disabled";
};
@@ -432,33 +430,23 @@
 };
 
  {
-   samsung,bus-width = <8>;
bus-width = <8>;
-   samsung,timing = <1 3 3>;
pwr-gpios = < 4 0>;
status = "disabled";
 };
 
  {
-   samsung,bus-width = <4>;
bus-width = <4>;
-   samsung,timing = <1 2 3>;
cd-gpios = < 2 0>;
status = "okay";
 };
 
 _0 {
-   samsung,bus-width = <8>;
bus-width = <8>;
-   samsung,timing = <2 1 0>;
samsung,dw-mshc-ciu-div = <0>;
samsung,dw-mshc-sdr-timing = <2 1>;
-   samsung,removable = <0>;
non-removable;
-   fifoth_val = <0x203f0040>;
clock-frequency = <4>;
-   bus_hz = <4>;
-   div = <0x3>;
index = <4>;
fifo-depth = <0x80>;
status = 

[PATCH v2 36/40] mmc: exynos_dw_mmc: Use dev->name as driver's displayed name

2024-06-09 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 
---
 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 v2 37/40] mmc: exynos_dw_mmc: Improve coding style

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

No functional change.

Signed-off-by: Sam Protsenko 
---
 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 = >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_SDMMC0;
@@ -241,10 +236,6 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev)
host->dev_index = 2; /* SD card */
 #endif
 
-   /* Get the bus width from the device node (Default is 4bit 

[PATCH v2 35/40] mmc: exynos_dw_mmc: Don't call dwmci_setup_cfg() after add_dwmci()

2024-06-09 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 = >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 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(>cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ);
+   host->mmc = >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(>cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ);
-   host->mmc = >mmc;
host->mmc->priv = >host;
-   host->priv = dev;
upriv->mmc = host->mmc;
+   host->mmc->dev = dev;
+   host->priv = dev;
 
return dwmci_probe(dev);
 }
-- 
2.39.2



[PATCH v2 34/40] mmc: exynos_dw_mmc: Pull all init code into probe function

2024-06-09 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 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 = >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(>cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ);
host->mmc = >mmc;
-- 
2.39.2



[PATCH v2 33/40] mmc: exynos_dw_mmc: Remove common.h

2024-06-09 Thread Sam Protsenko
common.h header is marked for removal treewide and shouldn't be used.
Remove it from Exynos DW MMC driver.

No functional change.

Signed-off-by: Sam Protsenko 
---
 drivers/mmc/exynos_dw_mmc.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 2c8364ab8d40..29241edf7915 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -5,7 +5,6 @@
  */
 
 #include 
-#include 
 #include 
 #include 
 #include 
-- 
2.39.2



[PATCH v2 32/40] mmc: exynos_dw_mmc: Add support for ARM64 Exynos chips

2024-06-09 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 
---
 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 5be0cb009e4e..2c8364ab8d40 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -386,6 +386,11 @@ static const struct exynos_dwmmc_variant exynos5_drv_data 
= {
 #endif
 };
 
+static const struct exynos_dwmmc_variant exynos7_smu_drv_data = {
+   .clksel = DWMCI_CLKSEL64,
+   .quirks = DWMCI_QUIRK_DISABLE_SMU,
+};
+
 static const struct udevice_id exynos_dwmmc_ids[] = {
{
.compatible = "samsung,exynos4412-dw-mshc",
@@ -393,6 +398,9 @@ static const struct udevice_id exynos_dwmmc_ids[] = {
}, {
.compatible = "samsung,exynos-dwmmc",
.data   = (ulong)_drv_data,
+   }, {
+   .compatible = "samsung,exynos7-dw-mshc-smu",
+   .data   = (ulong)_smu_drv_data,
},
{ }
 };
-- 
2.39.2



[PATCH v2 31/40] mmc: exynos_dw_mmc: Set requested freq in get_mmc_clk() callback

2024-06-09 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 
---
 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 ef03eac5657b..5be0cb009e4e 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -170,7 +170,17 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
u8 clk_div;
int err;
 
+   /* Should be double rate for DDR mode */
+   if (host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8)
+   freq *= 2;
+
clk_div = exynos_dwmmc_get_ciu_div(host);
+   err = exynos_dwmmc_set_sclk(host, freq * clk_div);
+   if (err) {
+   printf("DWMMC%d: failed to set clock rate (%d); "
+  "continue anyway\n", host->dev_index, err);
+   }
+
err = exynos_dwmmc_get_sclk(host, );
if (err) {
printf("DWMMC%d: failed to get clock rate (%d)\n",
-- 
2.39.2



[PATCH v2 30/40] mmc: exynos_dw_mmc: Read and use DDR timing when available

2024-06-09 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 
---
 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 69ba19e9edca..ef03eac5657b 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -53,6 +53,7 @@ struct dwmci_exynos_priv_data {
 #endif
struct clk clk;
u32 sdr_timing;
+   u32 ddr_timing;
const struct exynos_dwmmc_variant *chip;
 };
 
@@ -128,8 +129,14 @@ static int exynos_dwmmc_set_sclk(struct dwmci_host *host, 
unsigned long rate)
 static int exynos_dwmci_clksel(struct dwmci_host *host)
 {
struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+   u32 timing;
 
-   dwmci_writel(host, priv->chip->clksel, priv->sdr_timing);
+   if (host->mmc->selected_mode == MMC_DDR_52)
+   timing = priv->ddr_timing;
+   else
+   timing = priv->sdr_timing;
+
+   dwmci_writel(host, priv->chip->clksel, timing);
 
return 0;
 }
@@ -306,6 +313,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 v2 29/40] mmc: exynos_dw_mmc: Move quirks from struct dwmci_host to chip data

2024-06-09 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 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 5ecee403c416..69ba19e9edca 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -26,6 +26,9 @@
 
 #define EXYNOS4412_FIXED_CIU_CLK_DIV   4
 
+/* Quirks */
+#define DWMCI_QUIRK_DISABLE_SMUBIT(0)
+
 #ifdef CONFIG_DM_MMC
 #include 
 DECLARE_GLOBAL_DATA_PTR;
@@ -40,6 +43,7 @@ struct exynos_mmc_plat {
 struct exynos_dwmmc_variant {
u32 clksel; /* CLKSEL register offset */
u8 div; /* (optional) fixed clock divider value: 0..7 */
+   u32 quirks; /* quirk flags - see DWMCI_QUIRK_... */
 };
 
 /* Exynos implmentation specific drver private data */
@@ -174,7 +178,7 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
 {
struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
 
-   if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) {
+   if (priv->chip->quirks & DWMCI_QUIRK_DISABLE_SMU) {
dwmci_writel(host, EMMCP_MPSBEGIN0, 0);
dwmci_writel(host, EMMCP_SEND0, 0);
dwmci_writel(host, EMMCP_CTRL0,
@@ -206,11 +210,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host)
}
 
host->name = "EXYNOS DWMMC";
-#ifdef CONFIG_EXYNOS5420
-   host->quirks = DWMCI_QUIRK_DISABLE_SMU;
-#endif
host->board_init = exynos_dwmci_board_init;
-
host->caps = MMC_MODE_DDR_52MHz;
host->clksel = exynos_dwmci_clksel;
host->get_mmc_clk = exynos_dwmci_get_clk;
@@ -353,6 +353,9 @@ static const struct exynos_dwmmc_variant exynos4_drv_data = 
{
 
 static const struct exynos_dwmmc_variant exynos5_drv_data = {
.clksel = DWMCI_CLKSEL,
+#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 v2 28/40] mmc: exynos_dw_mmc: Read common clock-frequency property

2024-06-09 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 
---
 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 01e6bbb123f1..5ecee403c416 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -307,7 +307,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 v2 27/40] mmc: exynos_dw_mmc: Read common bus-width property

2024-06-09 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 
---
 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 098468f56e87..01e6bbb123f1 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -275,7 +275,7 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev)
 #endif
 
/* Get the bus width from the device node (Default is 4bit buswidth) */
-   host->buswidth = dev_read_u32_default(dev, "samsung,bus-width", 4);
+   host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
 
/* Set the base address from the device node */
host->ioaddr = dev_read_addr_ptr(dev);
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
index 3b74feae68c7..0b49a9c5f2a9 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -167,7 +167,7 @@ static int sdhci_get_config(const void *blob, int node, 
struct sdhci_host *host)
host->index = dev_id - PERIPH_ID_SDMMC0;
 
/* Get bus width */
-   bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
+   bus_width = fdtdec_get_int(blob, node, "bus-width", 0);
if (bus_width <= 0) {
debug("MMC: Can't get bus-width\n");
return -EINVAL;
-- 
2.39.2



[PATCH v2 26/40] mmc: exynos_dw_mmc: Refactor fixed CIU clock divider

2024-06-09 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 
---
 drivers/mmc/ca_dw_mmc.c |  2 +-
 drivers/mmc/exynos_dw_mmc.c | 43 +
 include/dwmmc.h |  2 --
 3 files changed, 30 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c
index a17ed8c11cbe..342e09d02306 100644
--- a/drivers/mmc/ca_dw_mmc.c
+++ b/drivers/mmc/ca_dw_mmc.c
@@ -87,7 +87,7 @@ unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, 
uint freq)
clk_div = 1;
}
 
-   return SD_SCLK_MAX / clk_div / (host->div + 1);
+   return SD_SCLK_MAX / clk_div;
 }
 
 static int ca_dwmmc_of_to_plat(struct udevice *dev)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 901cfc635418..098468f56e87 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -24,6 +24,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;
@@ -37,6 +39,7 @@ struct exynos_mmc_plat {
 /* Chip specific data */
 struct exynos_dwmmc_variant {
u32 clksel; /* CLKSEL register offset */
+   u8 div; /* (optional) fixed clock divider value: 0..7 */
 };
 
 /* Exynos implmentation specific drver private data */
@@ -127,12 +130,18 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
return 0;
 }
 
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
+/**
+ * exynos_dwmmc_get_ciu_div - Get internal clock divider value
+ * @host: MMC controller object
+ *
+ * Returns: Divider value, in range of 1..8
+ */
+static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
 {
struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
-   unsigned long sclk;
-   int8_t clk_div;
-   int err;
+
+   if (priv->chip->div)
+   return priv->chip->div + 1;
 
/*
 * Since SDCLKIN is divided inside controller by the DIVRATIO
@@ -140,9 +149,17 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
 * clock value to calculate the CLKDIV value.
 * as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1)
 */
-   clk_div = ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT)
-   & DWMCI_DIVRATIO_MASK) + 1;
+   return ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT)
+   & DWMCI_DIVRATIO_MASK) + 1;
+}
 
+unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
+{
+   unsigned long sclk;
+   u8 clk_div;
+   int err;
+
+   clk_div = exynos_dwmmc_get_ciu_div(host);
err = exynos_dwmmc_get_sclk(host, );
if (err) {
printf("DWMMC%d: failed to get clock rate (%d)\n",
@@ -150,11 +167,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
return 0;
}
 
-   /*
-* Assume to know divider value.
-* When clock unit is broken, need to set "host->div"
-*/
-   return sclk / clk_div / (host->div + 1);
+   return sclk / clk_div;
 }
 
 static void exynos_dwmci_board_init(struct dwmci_host *host)
@@ -271,8 +284,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);
@@ -293,7 +308,6 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev)
 
host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
host->bus_hz = dev_read_u32_default(dev, "bus_hz", 0);
-   host->div = dev_read_u32_default(dev, "div", 0);
 
return 0;
 }
@@ -334,6 +348,7 @@ static int exynos_dwmmc_bind(struct udevice *dev)
 
 static const struct exynos_dwmmc_variant 

[PATCH v2 25/40] mmc: exynos_dw_mmc: Abstract CLKSEL register

2024-06-09 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 
---
 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 a6e4d76fb25a..901cfc635418 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -34,6 +34,11 @@ struct exynos_mmc_plat {
 };
 #endif
 
+/* Chip specific data */
+struct exynos_dwmmc_variant {
+   u32 clksel; /* CLKSEL register offset */
+};
+
 /* Exynos implmentation specific drver private data */
 struct dwmci_exynos_priv_data {
 #ifdef CONFIG_DM_MMC
@@ -41,6 +46,7 @@ struct dwmci_exynos_priv_data {
 #endif
struct clk clk;
u32 sdr_timing;
+   const struct exynos_dwmmc_variant *chip;
 };
 
 static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv(
@@ -116,13 +122,14 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
 {
struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
 
-   dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing);
+   dwmci_writel(host, priv->chip->clksel, priv->sdr_timing);
 
return 0;
 }
 
 unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
 {
+   struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
unsigned long sclk;
int8_t clk_div;
int err;
@@ -133,7 +140,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
 * clock value to calculate the CLKDIV value.
 * as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1)
 */
-   clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT)
+   clk_div = ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT)
& DWMCI_DIVRATIO_MASK) + 1;
 
err = exynos_dwmmc_get_sclk(host, );
@@ -230,6 +237,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);
@@ -323,9 +332,22 @@ static int exynos_dwmmc_bind(struct udevice *dev)
return dwmci_bind(dev, >mmc, >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)_drv_data,
+   }, {
+   .compatible = "samsung,exynos-dwmmc",
+   .data   = (ulong)_drv_data,
+   },
{ }
 };
 
-- 
2.39.2



[PATCH v2 24/40] mmc: exynos_dw_mmc: Read upstream SDR timing properties

2024-06-09 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 
---
 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 eb2d2d204def..a6e4d76fb25a 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -228,7 +228,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 = >host;
int err = 0;
-   u32 timing[3];
+   u32 div, timing[2];
 
 #ifdef CONFIG_CPU_V7A
const void *blob = gd->fdt_blob;
@@ -263,16 +263,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 v2 23/40] mmc: exynos_dw_mmc: Convert to use livetree API

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

No functional change.

Signed-off-by: Sam Protsenko 
---
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 95c64b9eda54..eb2d2d204def 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -7,9 +7,7 @@
 #include 
 #include 
 #include 
-#include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -227,18 +225,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 = >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;
 
@@ -254,7 +253,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);
@@ -264,7 +263,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);
@@ -284,8 +283,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 v2 22/40] mmc: exynos_dw_mmc: Use .of_to_plat for device tree parsing

2024-06-09 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 
---
 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 de39a8d7f3c1..95c64b9eda54 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -224,10 +224,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 = >host;
+   int node = dev_of_offset(dev);
int err = 0;
u32 timing[3];
 
@@ -287,7 +290,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);
@@ -302,10 +304,6 @@ static int exynos_dwmmc_probe(struct udevice *dev)
return err;
 #endif
 
-   err = exynos_dwmci_get_config(dev, gd->fdt_blob, dev_of_offset(dev),
- host, priv);
-   if (err)
-   return err;
err = do_dwmci_init(host);
if (err)
return err;
@@ -336,6 +334,7 @@ U_BOOT_DRIVER(exynos_dwmmc_drv) = {
.name   = "exynos_dwmmc",
.id = UCLASS_MMC,
.of_match   = exynos_dwmmc_ids,
+   .of_to_plat = exynos_dwmmc_of_to_plat,
.bind   = exynos_dwmmc_bind,
.ops= _dwmci_ops,
.probe  = exynos_dwmmc_probe,
-- 
2.39.2



[PATCH v2 21/40] mmc: exynos_dw_mmc: Obtain and use CIU clock via CCF API

2024-06-09 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 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 2ed8429d0a40..de39a8d7f3c1 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 
@@ -16,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #defineDWMMC_MAX_CH_NUM4
@@ -39,6 +41,7 @@ struct dwmci_exynos_priv_data {
 #ifdef CONFIG_DM_MMC
struct dwmci_host host;
 #endif
+   struct clk clk;
u32 sdr_timing;
 };
 
@@ -52,6 +55,61 @@ static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv(
 #endif
 }
 
+/**
+ * exynos_dwmmc_get_sclk - Get source clock (SDCLKIN) rate
+ * @host: MMC controller object
+ * @rate: Will contain clock rate, Hz
+ *
+ * Return: 0 on success or negative value on error
+ */
+static int exynos_dwmmc_get_sclk(struct dwmci_host *host, unsigned long *rate)
+{
+#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(>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, );
+   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(>clk, rate);
+   if (err < 0)
+   return err;
+#endif
+
+   return 0;
+}
+
 /*
  * Function used as callback function to initialise the
  * CLKSEL register for every mmc channel.
@@ -69,6 +127,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
 {
unsigned long sclk;
int8_t clk_div;
+   int err;
 
/*
 * Since SDCLKIN is divided inside controller by the DIVRATIO
@@ -78,7 +137,13 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
 */
clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT)
& DWMCI_DIVRATIO_MASK) + 1;
-   sclk = get_mmc_clk(host->dev_index);
+
+   err = exynos_dwmmc_get_sclk(host, );
+   if (err) {
+   printf("DWMMC%d: failed to get clock rate (%d)\n",
+  host->dev_index, err);
+   return 0;
+   }
 
/*
 * Assume to know divider value.
@@ -108,19 +173,19 @@ static void exynos_dwmci_board_init(struct dwmci_host 
*host)
 
 static int exynos_dwmci_core_init(struct dwmci_host *host)
 {
-   unsigned int div;
-   unsigned long freq, sclk;
+   unsigned long freq;
+   int err;
 
if (host->bus_hz)
freq = host->bus_hz;
else
freq = DWMMC_MAX_FREQ;
 
-   /* request mmc clock vlaue of 52MHz.  */
-   sclk = get_mmc_clk(host->dev_index);
-   div = DIV_ROUND_UP(sclk, freq);
-   /* set the clock divisor for mmc */
-   set_mmc_clk(host->dev_index, div);
+   err = exynos_dwmmc_set_sclk(host, freq);
+   if (err) {
+   printf("DWMMC%d: failed to set clock rate on probe (%d); "
+  "continue anyway\n", host->dev_index, err);
+   }
 
host->name = "EXYNOS 

[PATCH v2 20/40] mmc: exynos_dw_mmc: Don't call pinmux functions on ARM64 chips

2024-06-09 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 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 8e80bd6a059f..2ed8429d0a40 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -145,6 +145,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host)
 
 static int do_dwmci_init(struct dwmci_host *host)
 {
+#ifdef CONFIG_CPU_V7A
int flag, err;
 
flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
@@ -153,6 +154,7 @@ static int do_dwmci_init(struct dwmci_host *host)
printf("DWMMC%d not configure\n", host->dev_index);
return err;
}
+#endif
 
return exynos_dwmci_core_init(host);
 }
@@ -164,6 +166,7 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
int err = 0;
u32 timing[3];
 
+#ifdef CONFIG_CPU_V7A
/* Extract device id for each mmc channel */
host->dev_id = pinmux_decode_periph_id(blob, node);
 
@@ -175,6 +178,12 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
printf("DWMMC%d: Can't get the dev index\n", host->dev_index);
return -EINVAL;
}
+#else
+   if (dev_read_bool(dev, "non-removable"))
+   host->dev_index = 0; /* eMMC */
+   else
+   host->dev_index = 2; /* SD card */
+#endif
 
/* Get the bus width from the device node (Default is 4bit buswidth) */
host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4);
-- 
2.39.2



[PATCH v2 19/40] mmc: exynos_dw_mmc: Fix getting private data in exynos_dwmci_board_init()

2024-06-09 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 
---
 drivers/mmc/exynos_dw_mmc.c | 20 +---
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 1f69e193f601..8e80bd6a059f 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -42,18 +42,24 @@ struct dwmci_exynos_priv_data {
u32 sdr_timing;
 };
 
+static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv(
+   struct dwmci_host *host)
+{
+#ifdef CONFIG_DM_MMC
+   return container_of(host, struct dwmci_exynos_priv_data, host);
+#else
+   return host->priv;
+#endif
+}
+
 /*
  * Function used as callback function to initialise the
  * CLKSEL register for every mmc channel.
  */
 static int exynos_dwmci_clksel(struct dwmci_host *host)
 {
-#ifdef CONFIG_DM_MMC
-   struct dwmci_exynos_priv_data *priv =
-   container_of(host, struct dwmci_exynos_priv_data, host);
-#else
-   struct dwmci_exynos_priv_data *priv = host->priv;
-#endif
+   struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+
dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing);
 
return 0;
@@ -83,7 +89,7 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, 
uint freq)
 
 static void exynos_dwmci_board_init(struct dwmci_host *host)
 {
-   struct dwmci_exynos_priv_data *priv = host->priv;
+   struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
 
if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) {
dwmci_writel(host, EMMCP_MPSBEGIN0, 0);
-- 
2.39.2



[PATCH v2 18/40] mmc: exynos_dw_mmc: Fix obtaining the base address of controller

2024-06-09 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 
---
 drivers/mmc/exynos_dw_mmc.c | 7 +++
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 14cb0c05cb55..1f69e193f601 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -156,7 +156,7 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
   struct dwmci_exynos_priv_data *priv)
 {
int err = 0;
-   u32 base, timing[3];
+   u32 timing[3];
 
/* Extract device id for each mmc channel */
host->dev_id = pinmux_decode_periph_id(blob, node);
@@ -174,12 +174,11 @@ static int exynos_dwmci_get_config(struct udevice *dev, 
const void *blob,
host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4);
 
/* Set the base address from the device node */
-   base = fdtdec_get_addr(blob, node, "reg");
-   if (!base) {
+   host->ioaddr = dev_read_addr_ptr(dev);
+   if (!host->ioaddr) {
printf("DWMMC%d: Can't get base address\n", host->dev_index);
return -EINVAL;
}
-   host->ioaddr = (void *)base;
 
/* Extract the timing info from the node */
err =  fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3);
-- 
2.39.2



[PATCH v2 17/40] arm: exynos: Add header guard for dwmmc.h

2024-06-09 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 
---
 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 v2 15/40] arm: dts: exynos: Add upstream DW MMC properties to all Exynos dts

2024-06-09 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 
---
 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 @@
 
  {
samsung,bus-width = <4>;
+   bus-width = <4>;
samsung,timing = <1 2 3>;
cd-gpios = < 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 @@
 
  {
samsung,bus-width = <8>;
+   bus-width = <8>;
samsung,timing = <1 3 3>;
pwr-gpios = < 2 0>;
status = "okay";
@@ -248,6 +249,7 @@
 
  {
samsung,bus-width = <4>;
+   bus-width = <4>;
samsung,timing = <1 2 3>;
cd-gpios = < 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 @@
 
  {
samsung,bus-width = <8>;
+   bus-width = <8>;
samsung,timing = <1 3 3>;
pwr-gpios = < 2 0>;
status = "okay";
@@ -243,6 +244,7 @@
 
  {
samsung,bus-width = <4>;
+   bus-width = <4>;
samsung,timing = <1 2 3>;
cd-gpios = < 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 @@
 
 _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 = < 4 0>;
status = "disabled";
@@ -432,6 +433,7 @@
 
  {
samsung,bus-width = <8>;
+   bus-width = <8>;
samsung,timing = <1 3 3>;
pwr-gpios = < 4 0>;
status = "disabled";
@@ -439,6 +441,7 @@
 
  {
samsung,bus-width = <4>;
+   bus-width = <4>;
samsung,timing = <1 2 3>;
cd-gpios = < 2 0>;
status = "okay";
@@ -446,9 +449,14 @@
 
 _0 {
samsung,bus-width = <8>;
+   bus-width = <8>;
samsung,timing = <2 1 0>;
+   samsung,dw-mshc-ciu-div = <0>;
+   samsung,dw-mshc-sdr-timing = <2 1>;
samsung,removable = <0>;
+   non-removable;
fifoth_val = <0x203f0040>;
+   clock-frequency = <4>;
bus_hz = <4>;
div = <0x3>;
index = <4>;
diff --git a/arch/arm/dts/exynos5250-arndale.dts 

[PATCH v2 14/40] mmc: dw_mmc: Improve coding style

2024-06-09 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 v2:
  - Added R-b tag from Quentin

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

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 8af665a00222..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(,
-   (void*)data->dest,
+   (void *)data->dest,
data->blocksize *
data->blocks, GEN_BB_WRITE);
} else {
ret = bounce_buffer_start(,
-   (void*)data->src,
+   (void *)data->src,
data->blocksize *
data->blocks, GEN_BB_READ);
}
@@ -420,9 +418,9 @@ static int dwmci_send_cmd_common(struct dwmci_host *host, 
struct mmc_cmd *cmd,
if (cmd->resp_type & MMC_RSP_CRC)
flags |= DWMCI_CMD_CHECK_CRC;
 
-   flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG);
+   flags |= cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG;
 
-   debug("Sending CMD%d\n",cmd->cmdidx);
+   

[PATCH v2 16/40] dt-bindings: exynos: Update bindings doc for DW MMC controller

2024-06-09 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 
---
 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 v2 13/40] mmc: dw_mmc: Fix kernel-doc comments in dwmmc.h

2024-06-09 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 
---
 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 >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 >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 >mmc)
+ * @cfg:   Empty configuration structure (generally >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 >mmc)
- * @cfg:   Empty configuration structure (generally >cfg). This is
- * normally all zeroes at this point. The only purpose of passing
- * this in is to set mmc->cfg to it.
  * Return: 0 if OK, -ve if the block device could not be created
  */
 int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg);
@@ -320,12 +323,12 @@ int dwmci_bind(struct udevice *dev, struct mmc *mmc, 
struct mmc_config *cfg);
 #else
 /**
  * add_dwmci() - Add a new 

[PATCH v2 12/40] mmc: dw_mmc: Replace fifoth_val property with fifo-depth

2024-06-09 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 
---
 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 7aa3dbbe83ad..8af665a00222 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++);
@@ -656,16 +654,23 @@ static int dwmci_set_ios(struct mmc *mmc)
 
 static void dwmci_init_fifo(struct dwmci_host *host)
 {
-   if (!host->fifoth_val) {
+   u32 fifo_thr, fifoth_val;
+
+   if (!host->fifo_depth) {
u32 fifo_size;
 
+   /*
+* Automatically detect FIFO depth from FIFOTH register.
+* Power-on value of RX_WMark is FIFO_DEPTH-1.
+*/
fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
-   host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
-  TX_WMARK(fifo_size / 2);
+   host->fifo_depth = fifo_size;
}
 
-   dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
+   fifo_thr = host->fifo_depth / 2;
+   fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_thr - 1) | TX_WMARK(fifo_thr);
+   dwmci_writel(host, DWMCI_FIFOTH, fifoth_val);
 }
 
 static void dwmci_init_dma(struct dwmci_host *host)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 2f849c43b129..14cb0c05cb55 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -151,8 +151,8 @@ static int do_dwmci_init(struct dwmci_host *host)
return exynos_dwmci_core_init(host);
 }
 
-static int exynos_dwmci_get_config(const void *blob, int node,
-  struct dwmci_host *host,
+static int exynos_dwmci_get_config(struct udevice *dev, const void *blob,
+  int node, struct dwmci_host *host,
   struct dwmci_exynos_priv_data *priv)
 {
int err = 0;
@@ -201,7 +201,7 @@ static int exynos_dwmci_get_config(const void *blob, int 
node,
priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL;
}
 
-   host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0);
+   host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0);
host->div = fdtdec_get_int(blob, node, "div", 0);
 
@@ -217,8 +217,8 @@ static int exynos_dwmmc_probe(struct udevice *dev)
struct dwmci_host *host = >host;
int err;
 
-   err = exynos_dwmci_get_config(gd->fdt_blob, dev_of_offset(dev), host,
- priv);
+   err = exynos_dwmci_get_config(dev, gd->fdt_blob, dev_of_offset(dev),
+ host, priv);
if (err)
return err;
err = do_dwmci_init(host);
diff --git a/drivers/mmc/ftsdc010_mci.h b/drivers/mmc/ftsdc010_mci.h
index 782d92be2f5f..36187cfa04f6 100644
--- a/drivers/mmc/ftsdc010_mci.h
+++ b/drivers/mmc/ftsdc010_mci.h
@@ -28,7 +28,6 @@ struct 

[PATCH v2 11/40] mmc: dw_mmc: Add support for 64-bit IDMAC

2024-06-09 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 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 a77023ff4cd6..7aa3dbbe83ad 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,
+  void *cur_idmac, void *bounce_buffer)
 {
struct dwmci_idmac32 *desc32 = cur_idmac;
+

[PATCH v2 10/40] mmc: dw_mmc: Improve 32-bit IDMAC descriptor namings

2024-06-09 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 
---
 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 8bec43f0bd16..a77023ff4cd6 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 v2 09/40] mmc: dw_mmc: Extract setting the DMA descriptor into a separate routine

2024-06-09 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 v2:
  - Added R-b tag from Quentin

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

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 7b20f4936b62..8bec43f0bd16 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -53,47 +53,57 @@ static void dwmci_set_idma_desc(struct dwmci_idmac *idmac,
desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac);
 }
 
-static void dwmci_prepare_data(struct dwmci_host *host,
-  struct mmc_data *data,
+static void dwmci_prepare_desc(struct mmc_data *data,
   struct dwmci_idmac *cur_idmac,
   void *bounce_buffer)
 {
-   unsigned long ctrl;
-   unsigned int i = 0, flags, cnt, blk_cnt;
+   struct dwmci_idmac *desc = cur_idmac;
ulong data_start, data_end;
+   unsigned int blk_cnt, i;
 
-
+   data_start = (ulong)cur_idmac;
blk_cnt = data->blocks;
 
-   dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
+   for (i = 0;; i++) {
+   unsigned int flags, cnt;
 
-   /* Clear IDMAC interrupt */
-   dwmci_writel(host, DWMCI_IDSTS, 0x);
-
-   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 v2 08/40] mmc: dw_mmc: Extract DMA transfer handling code into a separate routine

2024-06-09 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 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 37a1125fba4f..7b20f4936b62 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -233,6 +233,33 @@ static int dwmci_data_transfer(struct dwmci_host *host, 
struct mmc_data *data)
return ret;
 }
 
+static int dwmci_dma_transfer(struct dwmci_host *host, uint flags,
+ struct bounce_buffer *bbstate)
+{
+   int ret;
+   u32 mask, ctrl;
+
+   if (flags == MMC_DATA_READ)
+   mask = DWMCI_IDINTEN_RI;
+   else
+   mask = DWMCI_IDINTEN_TI;
+
+   ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS,
+   mask, true, 1000, false);
+   if (ret)
+   debug("%s: DWMCI_IDINTEN mask 0x%x timeout\n", __func__, mask);
+
+   /* Clear interrupts */
+   dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK);
+
+   ctrl = dwmci_readl(host, DWMCI_CTRL);
+   ctrl &= ~DWMCI_DMA_EN;
+   dwmci_writel(host, DWMCI_CTRL, ctrl);
+
+   bounce_buffer_stop(bbstate);
+   return ret;
+}
+
 static int dwmci_set_transfer_mode(struct dwmci_host *host,
struct mmc_data *data)
 {
@@ -275,7 +302,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd 
*cmd,
 data ? DIV_ROUND_UP(data->blocks, 8) : 0);
int ret = 0, flags = 0, i;
u32 retry = 10;
-   u32 mask, ctrl;
+   u32 mask;
struct bounce_buffer bbstate;
 
dwmci_wait_while_busy(host, cmd);
@@ -384,26 +411,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd 
*cmd,
 
if (data) {
ret = dwmci_data_transfer(host, data);
-
-   /* only dma mode need it */
-   if (!host->fifo_mode) {
-   if (data->flags == MMC_DATA_READ)
-   mask = DWMCI_IDINTEN_RI;
-   else
-   mask = DWMCI_IDINTEN_TI;
-   ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS,
-   mask, true, 1000, false);
-   if (ret)
-   debug("%s: DWMCI_IDINTEN mask 0x%x timeout.\n",
- __func__, mask);
-   /* clear interrupts */
-   dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK);
-
-   ctrl = dwmci_readl(host, DWMCI_CTRL);
-   ctrl &= ~(DWMCI_DMA_EN);
-   dwmci_writel(host, DWMCI_CTRL, ctrl);
-   bounce_buffer_stop();
-   }
+   if (!host->fifo_mode)
+   ret = dwmci_dma_transfer(host, data->flags, );
}
 
udelay(100);
-- 
2.39.2



[PATCH v2 07/40] mmc: dw_mmc: Extract FIFO data transfer into a separate routine

2024-06-09 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 
---
 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 7bf3279ee9ec..37a1125fba4f 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -140,25 +140,67 @@ static unsigned int dwmci_get_timeout(struct mmc *mmc, 
const unsigned int size)
return timeout;
 }
 
-static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
+static int dwmci_data_transfer_fifo(struct dwmci_host *host,
+   struct mmc_data *data, u32 mask)
 {
-   struct mmc *mmc = host->mmc;
+   const u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >>
+RX_WMARK_SHIFT) + 1) * 2;
+   const u32 int_rx = mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO);
+   const u32 int_tx = mask & DWMCI_INTMSK_TXDR;
int ret = 0;
-   u32 timeout, mask, size, i, len = 0;
-   u32 *buf = NULL;
-   ulong start = get_timer(0);
-   u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >>
-   RX_WMARK_SHIFT) + 1) * 2;
+   u32 len = 0, size, i;
+   u32 *buf;
+
+   size = (data->blocksize * data->blocks) / 4;
+   if (!host->fifo_mode || !size)
+   return 0;
 
-   size = data->blocksize * data->blocks;
if (data->flags == MMC_DATA_READ)
buf = (unsigned int *)data->dest;
else
buf = (unsigned int *)data->src;
 
-   timeout = dwmci_get_timeout(mmc, size);
+   if (data->flags == MMC_DATA_READ && int_rx) {
+   dwmci_writel(host, DWMCI_RINTSTS, int_rx);
+   while (size) {
+   ret = dwmci_fifo_ready(host, DWMCI_FIFO_EMPTY, );
+   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, );
+   if (ret < 0)
+   break;
+
+   len = fifo_depth - ((len >> DWMCI_FIFO_SHIFT) &
+   DWMCI_FIFO_MASK);
+   len = min(size, len);
+   for (i = 0; i < len; i++)
+   dwmci_writel(host, DWMCI_DATA, *buf++);
+   size = size > len ? (size - len) : 0;
+   }
+   dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR);
+   }
 
-   size /= 4;
+   return ret;
+}
+
+static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
+{
+   struct mmc *mmc = host->mmc;
+   int ret = 0;
+   u32 timeout, mask, size;
+   ulong start = get_timer(0);
+
+   size = data->blocksize * data->blocks;
+   timeout = dwmci_get_timeout(mmc, size);
 
for (;;) {
mask = dwmci_readl(host, DWMCI_RINTSTS);
@@ -169,50 +211,7 @@ static int dwmci_data_transfer(struct dwmci_host *host, 
struct mmc_data *data)
break;
}
 
-   if (host->fifo_mode && size) {
-   len = 0;
-   if (data->flags == MMC_DATA_READ &&
-   (mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO))) {
-   dwmci_writel(host, DWMCI_RINTSTS,
-mask & (DWMCI_INTMSK_RXDR |
-DWMCI_INTMSK_DTO));
-   while (size) {
-   ret = dwmci_fifo_ready(host,
-   DWMCI_FIFO_EMPTY,
-   );
-   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 

[PATCH v2 06/40] mmc: dw_mmc: Extract divider update to a separate function

2024-06-09 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 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 cbfb8d3b8683..7bf3279ee9ec 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -412,11 +412,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;
@@ -439,35 +484,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 v2 02/40] mmc: dw_mmc: Remove unused version field from struct dwmci_host

2024-06-09 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 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 v2 05/40] mmc: dw_mmc: Extract FIFO init into a separate routine

2024-06-09 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 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 a82f6a96db39..cbfb8d3b8683 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -540,6 +540,20 @@ static int dwmci_set_ios(struct mmc *mmc)
return 0;
 }
 
+static void dwmci_init_fifo(struct dwmci_host *host)
+{
+   if (!host->fifoth_val) {
+   u32 fifo_size;
+
+   fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
+   fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
+   host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
+  TX_WMARK(fifo_size / 2);
+   }
+
+   dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
+}
+
 static int dwmci_init(struct mmc *mmc)
 {
struct dwmci_host *host = mmc->priv;
@@ -564,16 +578,7 @@ static int dwmci_init(struct mmc *mmc)
 
dwmci_writel(host, DWMCI_IDINTEN, 0);
dwmci_writel(host, DWMCI_BMOD, 1);
-
-   if (!host->fifoth_val) {
-   uint32_t fifo_size;
-
-   fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
-   fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
-   host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
-   TX_WMARK(fifo_size / 2);
-   }
-   dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
+   dwmci_init_fifo(host);
 
dwmci_writel(host, DWMCI_CLKENA, 0);
dwmci_writel(host, DWMCI_CLKSRC, 0);
-- 
2.39.2



[PATCH v2 04/40] mmc: dw_mmc: Extract waiting for data busy into a separate routine

2024-06-09 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 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 9d668b3d8813..a82f6a96db39 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -246,6 +246,21 @@ static int dwmci_set_transfer_mode(struct dwmci_host *host,
return mode;
 }
 
+static void dwmci_wait_while_busy(struct dwmci_host *host, struct mmc_cmd *cmd)
+{
+   unsigned int timeout = 500; /* msec */
+   ulong start;
+
+   start = get_timer(0);
+   while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) {
+   if (get_timer(start) > timeout) {
+   debug("%s: Timeout on data busy, continue anyway\n",
+ __func__);
+   break;
+   }
+   }
+}
+
 #ifdef CONFIG_DM_MMC
 static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
   struct mmc_data *data)
@@ -260,19 +275,11 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd 
*cmd,
ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac,
 data ? DIV_ROUND_UP(data->blocks, 8) : 0);
int ret = 0, flags = 0, i;
-   unsigned int timeout = 500;
u32 retry = 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 v2 03/40] mmc: dw_mmc: Move struct idmac to dw_mmc.c

2024-06-09 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 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 e6107c770fe3..9d668b3d8813 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -20,6 +20,13 @@
 
 #define PAGE_SIZE 4096
 
+struct dwmci_idmac {
+   u32 flags;
+   u32 cnt;
+   u32 addr;
+   u32 next_addr;
+} __aligned(ARCH_DMA_MINALIGN);
+
 static int dwmci_wait_reset(struct dwmci_host *host, u32 value)
 {
unsigned long timeout = 1000;
diff --git a/include/dwmmc.h b/include/dwmmc.h
index 39024fb38aaa..7e4acf096dce 100644
--- a/include/dwmmc.h
+++ b/include/dwmmc.h
@@ -198,13 +198,6 @@ struct dwmci_host {
bool fifo_mode;
 };
 
-struct dwmci_idmac {
-   u32 flags;
-   u32 cnt;
-   u32 addr;
-   u32 next_addr;
-} __aligned(ARCH_DMA_MINALIGN);
-
 static inline void dwmci_writel(struct dwmci_host *host, int reg, u32 val)
 {
writel(val, host->ioaddr + reg);
-- 
2.39.2



[PATCH v2 01/40] mmc: dw_mmc: Remove common.h

2024-06-09 Thread Sam Protsenko
common.h header is marked for removal treewide and shouldn't be used.
Remove it from DW MMC driver.

No functional change.

Signed-off-by: Sam Protsenko 
Reviewed-by: Quentin Schulz 
---
Changes in v2:
  - Added R-b tag from Quentin

 drivers/mmc/dw_mmc.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index e1036641452a..e6107c770fe3 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -6,7 +6,6 @@
  */
 
 #include 
-#include 
 #include 
 #include 
 #include 
-- 
2.39.2



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

2024-06-09 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 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 (40):
  mmc: dw_mmc: Remove common.h
  mmc: dw_mmc: Remove unused version field from struct dwmci_host
  mmc: dw_mmc: Move struct idmac to dw_mmc.c
  mmc: dw_mmc: Extract waiting for data busy into a separate routine
  mmc: dw_mmc: Extract FIFO init into a separate routine
  mmc: dw_mmc: Extract 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: dw_mmc: Improve coding style
  arm: dts: exynos: Add upstream DW MMC properties to all Exynos dts
  dt-bindings: exynos: Update bindings doc for DW MMC controller
  arm: exynos: Add header guard for dwmmc.h
  mmc: exynos_dw_mmc: Fix obtaining the base address of controller
  mmc: exynos_dw_mmc: Fix getting private data in
exynos_dwmci_board_init()
  mmc: exynos_dw_mmc: Don't call pinmux functions on ARM64 chips
  mmc: exynos_dw_mmc: 

Re: [PATCH] arm: dts: mvebu: Update DTS for Thecus N2350 board

2024-06-09 Thread Dragan Simic

Hello Tony,

Please see a few comments below.

On 2024-06-10 02:34, Tony Dinh wrote:

- Change the spi-max-frequency to 5000 (50 Mhz). According to the
data sheet[1], the MX25L3205D max frequency is 86 Mhz. Using 50 Mhz in
the DTS to ensure u-boot is consistent with what Linux kernel expected.
- Update GPIO fan to conform to the latest DT binding.


There's no need for the bullet points, plain prose will do fine instead.

[1] 
MX25L3205D-MX25L1605D-MX25L6405D-Macronix-MX25L3205DM2I-12G-datasheet.pdf


Unless you can provide a real, working URL for the datasheet, this 
reference

should actually be deleted.


Signed-off-by: Tony Dinh 
---

 arch/arm/dts/armada-385-thecus-n2350.dts | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/arm/dts/armada-385-thecus-n2350.dts
b/arch/arm/dts/armada-385-thecus-n2350.dts
index 253cf01130..fdaa444e51 100644
--- a/arch/arm/dts/armada-385-thecus-n2350.dts
+++ b/arch/arm/dts/armada-385-thecus-n2350.dts
@@ -2,7 +2,7 @@
 /*
  * Device Tree file for Thecus N2350 board
  *
- * Copyright (C) 2018-2023 Tony Dinh 
+ * Copyright (C) 2018-2024 Tony Dinh 
  * Copyright (C) 2018 Manuel Jung 
  */

@@ -143,9 +143,9 @@
fan {
compatible = "gpio-fan";
gpios = < 16 GPIO_ACTIVE_HIGH>;
-   gpio-fan,speed-map = < 0  0
-   600  1
-   3000 2 >;
+   gpio-fan,speed-map = < 0  0>,
+   <600  1>,
+   <3000 2 >;
pinctrl-0 = <_fan>;
pinctrl-names = "default";
};
@@ -415,7 +415,7 @@
compatible = "jedec,spi-nor";
reg = <0>;

-   spi-max-frequency = <10800>;
+   spi-max-frequency = <5000>;
spi-cpha;

partition@0 {


[PATCH] arm: dts: mvebu: Update DTS for Thecus N2350 board

2024-06-09 Thread Tony Dinh
- Change the spi-max-frequency to 5000 (50 Mhz). According to the
data sheet[1], the MX25L3205D max frequency is 86 Mhz. Using 50 Mhz in
the DTS to ensure u-boot is consistent with what Linux kernel expected.
- Update GPIO fan to conform to the latest DT binding.

[1] MX25L3205D-MX25L1605D-MX25L6405D-Macronix-MX25L3205DM2I-12G-datasheet.pdf

Signed-off-by: Tony Dinh 
---

 arch/arm/dts/armada-385-thecus-n2350.dts | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/arm/dts/armada-385-thecus-n2350.dts 
b/arch/arm/dts/armada-385-thecus-n2350.dts
index 253cf01130..fdaa444e51 100644
--- a/arch/arm/dts/armada-385-thecus-n2350.dts
+++ b/arch/arm/dts/armada-385-thecus-n2350.dts
@@ -2,7 +2,7 @@
 /*
  * Device Tree file for Thecus N2350 board
  *
- * Copyright (C) 2018-2023 Tony Dinh 
+ * Copyright (C) 2018-2024 Tony Dinh 
  * Copyright (C) 2018 Manuel Jung 
  */
 
@@ -143,9 +143,9 @@
fan {
compatible = "gpio-fan";
gpios = < 16 GPIO_ACTIVE_HIGH>;
-   gpio-fan,speed-map = <0  0
-   600  1
-   3000 2 >;
+   gpio-fan,speed-map = <0  0>,
+   <600  1>,
+   <3000 2 >;
pinctrl-0 = <_fan>;
pinctrl-names = "default";
};
@@ -415,7 +415,7 @@
compatible = "jedec,spi-nor";
reg = <0>;
 
-   spi-max-frequency = <10800>;
+   spi-max-frequency = <5000>;
spi-cpha;
 
partition@0 {
-- 
2.39.2



Re: [PATCH 06/42] mmc: dw_mmc: Extract clock on/off code into a separate routine

2024-06-09 Thread Sam Protsenko
On Mon, Jun 3, 2024 at 3:52 AM Quentin Schulz  wrote:
>
> Hi Sam,
>
> On 5/30/24 2:06 AM, Sam Protsenko wrote:
> > On Thu, May 23, 2024 at 9:36 AM Quentin Schulz  
> > wrote:
> >>
> >> Hi Sam,
> >>
> >
> > Hi Quentin,
> >
> > Thanks for reviewing this series! My answers are below (inline).
> >
> >> On 5/23/24 1:30 AM, Sam Protsenko wrote:
> >>> Extract clock control code into a separate routine to avoid code
> >>> duplication in dwmci_setup_bus().
> >>>
> >>> No functional change.
> >>>
> >>
> >> There are some differences though.
> >>
> >
> > With everything discussed below, I guess it still can be argued that
> > this patch doesn't change the functionality of the driver? Depends on
> > definition though. Please let me know if you don't like this bit and
> > I'll remove it in v2.
> >
> > [snip]
> >
> >>> @@ -439,35 +461,19 @@ static int dwmci_setup_bus(struct dwmci_host *host, 
> >>> u32 freq)
> >>>else
> >>>div = DIV_ROUND_UP(sclk, 2 * freq);
> >>>
> >>> - dwmci_writel(host, DWMCI_CLKENA, 0);
> >>> + /* Disable clock */
> >>>dwmci_writel(host, DWMCI_CLKSRC, 0);
> >>> + ret = dwmci_control_clken(host, false);
> >>
> >> Here, CLKENA and CLKSRC are swapped. The kernel still calls CLKENA
> >> before CLKSRC, is this really safe? (I have no documentation so cannot
> >> tell). E.g., we could have CLKSRC register be a way to select the clock
> >> parent/source, 0 could be 24MHz crystal for example, so switching to
> >> this new parent before disabling the clock output would likely result in
> >> glitches?
> >>
> >
> > Yes, you are right. In fact, Exynos850 TRM specifically mentions this.
> > Here is how TRM describes the whole procedure for "CIU Update":
> >
> > 8<->8
> > To prevent a clock glitch, ensure that software stops the clock when
> > it updates the clock divider and clock source registers.
> >
> > Software uses the following sequence to update the clock divider settings:
> > 1. Disables the clock.
> > 2. Updates the clock divider and/or the clock source registers.
> > 3, Enables the clock.
> >
> > Code Sample to Update CIU:
> >
> > // Disable clock
> > while (rSTATUS & (1 << 9));
> > rCLKENA = 0x0;
> > rCMD = 0x80202000; // inform CIU
> > while (rCMD & 0x8000);
> >
> > // Update divider and source
> > rCLKDIV = clock_divider;
> > rCLKSRC = clock_source;
> >
> > // Enable clock
> > while (rSTATUS & (1 << 9));
> > rCLKENA= 0x1;
> > rCMD = 0x80202000; // inform CIU
> > while (rCMD & 0x8000);
> > 8<->8
> >
> > It's an overlook on my part. And although CLKSRC reset value is 0x0
> > and it's only being written with 0x0 in the driver, it still has to be
> > fixed. I'll move CLKSRC modification below CLKDIV to make it look like
> > TRM suggests.
> >
>
> Did you mean "move CLKENA before CLKSRC"? Because if the clock is
> disabled, I don't think the order in which the parent clock and its
> divider are set matters?
>

Yes, in v2 I'll just pull CLKSRC modification down, so it's executed
after disabling the clock, as recommended in TRM. Something like this:

8<->8
/* Disable clock */
-   dwmci_writel(host, DWMCI_CLKSRC, 0);
ret = dwmci_control_clken(host, false);
if (ret)
return ret;

/* Set clock to desired speed */
dwmci_writel(host, DWMCI_CLKDIV, div);
+   dwmci_writel(host, DWMCI_CLKSRC, 0);

/* Enable clock */
ret = dwmci_control_clken(host, true);
8<->8

> > Linux kernel implementation also doesn't follow the sequence
> > recommended in TRM as you can see, but it works fine because CLKSRC is
> > always written to 0 there (and it's also 0 at reset time).
> >
>
> OK, so wrong logic but it works because we essentially never change
> CLKSRC so it's virtually a no-op and thus there are no change to the
> parent clock when the clock is enabled, resulting in no glitch. Not sure
> we should ignore the improper logic though? What are you suggesting we
> do here?
>

Yes, exactly. As for the possible actions, I'd say it'd nice to fix it
in kernel too, though I kinda see it as a very minor issue right now.
Not sure if I can find the time to do that soon though.

On a philosophical note: I'm sure a lot of drivers don't follow the
sequences intended by the hardware exactly, but they still work
because IP core design is permissive enough. Over the years I found
peace with that somehow :)

> >>> + if (ret)
> >>> + return ret;
> >>>
> >>> + /* Set clock to desired speed */
> >>>dwmci_writel(host, DWMCI_CLKDIV, div);
> >>
> >> Same here, CLKDIV is set now after the CIU is informed, is this an
> >> issue? We may want to set the clock speed before we enable the clock
> >> again. Right now it's setting the desired speed while disabled, 

Re: [PATCH 14/42] mmc: dw_mmc: Use CONFIG_IS_ENABLED() to check config options

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

Thanks for noticing this! I completely forgot about SPL as it's not
built for my board. I tried to check with buildman and it seems like
replacing #ifdefs with CONFIG_IS_ENABLED() doesn't change anything,
but I'm not sure if buildman actually checks SPL at all (it probably
only checks the U-Boot proper binary?).

In my v2 I'll stick to #ifdef and avoid using CONFIG_IS_ENABLED(). As
you said, correct rework probably takes much more than just replacing
#ifdefs. It can be done later in a separate series.

> Cheers,
> Quentin


[PATCH 6/6] usb: dwc3: gadget: Convert epautoconf workaround to match_ep callback

2024-06-09 Thread Marek Vasut
Use the .match_ep() callback instead of workaround in core code.
Replace descriptor parsing with ch9 macros with the same effect.
Drop the SPL specific behavior, it is unclear why SPL should even
be special.

Signed-off-by: Marek Vasut 
---
Cc: Alexander Sverdlin 
Cc: Felipe Balbi 
Cc: Lukasz Majewski 
Cc: Mattijs Korpershoek 
Cc: Nishanth Menon 
Cc: Simon Glass 
Cc: Thinh Nguyen 
Cc: Tom Rini 
Cc: u-boot@lists.denx.de
---
 drivers/usb/dwc3/gadget.c   | 33 +++
 drivers/usb/gadget/epautoconf.c | 46 +
 2 files changed, 34 insertions(+), 45 deletions(-)

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index fab32575647..3ef2f016a60 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1606,6 +1606,38 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
return 0;
 }
 
+static struct usb_ep *dwc3_find_ep(struct usb_gadget *gadget, const char *name)
+{
+   struct usb_ep *ep;
+
+   list_for_each_entry(ep, >ep_list, ep_list)
+   if (!strcmp(ep->name, name))
+   return ep;
+
+   return NULL;
+}
+
+static struct
+usb_ep *dwc3_gadget_match_ep(struct usb_gadget *gadget,
+struct usb_endpoint_descriptor *desc,
+struct usb_ss_ep_comp_descriptor *comp_desc)
+{
+   /*
+* First try standard, common configuration: ep1in-bulk,
+* ep2out-bulk, ep3in-int to match other udc drivers to avoid
+* confusion in already deployed software (endpoint numbers
+* hardcoded in userspace software/drivers)
+*/
+   if (usb_endpoint_is_bulk_in(desc))
+   return dwc3_find_ep(gadget, "ep1in");
+   if (usb_endpoint_is_bulk_out(desc))
+   return dwc3_find_ep(gadget, "ep2out");
+   if (usb_endpoint_is_int_in(desc))
+   return dwc3_find_ep(gadget, "ep3in");
+
+   return NULL;
+}
+
 static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame  = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
@@ -1613,6 +1645,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.pullup = dwc3_gadget_pullup,
.udc_start  = dwc3_gadget_start,
.udc_stop   = dwc3_gadget_stop,
+   .match_ep   = dwc3_gadget_match_ep,
 };
 
 /* -- 
*/
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 66599ce8efa..a4da4f72de9 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -166,18 +166,6 @@ static int ep_matches(
return 1;
 }
 
-static struct usb_ep *
-find_ep(struct usb_gadget *gadget, const char *name)
-{
-   struct usb_ep   *ep;
-
-   list_for_each_entry(ep, >ep_list, ep_list) {
-   if (0 == strcmp(ep->name, name))
-   return ep;
-   }
-   return NULL;
-}
-
 /**
  * usb_ep_autoconfig - choose an endpoint matching the descriptor
  * @gadget: The device to which the endpoint must belong.
@@ -213,39 +201,7 @@ struct usb_ep *usb_ep_autoconfig(
struct usb_endpoint_descriptor  *desc
 )
 {
-   struct usb_ep   *ep = NULL;
-   u8  type;
-
-   type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
-
-   /* First, apply chip-specific "best usage" knowledge.
-* This might make a good usb_gadget_ops hook ...
-*/
-   if (!IS_ENABLED(CONFIG_SPL_BUILD) &&
-   IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
-   !strcmp("dwc3-gadget", gadget->name)) {
-   const char *name = NULL;
-   /*
-* First try standard, common configuration: ep1in-bulk,
-* ep2out-bulk, ep3in-int to match other udc drivers to avoid
-* confusion in already deployed software (endpoint numbers
-* hardcoded in userspace software/drivers)
-*/
-   if ((desc->bEndpointAddress & USB_DIR_IN) &&
-   type == USB_ENDPOINT_XFER_BULK)
-   name = "ep1in";
-   else if ((desc->bEndpointAddress & USB_DIR_IN) == 0 &&
-type == USB_ENDPOINT_XFER_BULK)
-   name = "ep2out";
-   else if ((desc->bEndpointAddress & USB_DIR_IN) &&
-type == USB_ENDPOINT_XFER_INT)
-   name = "ep3in";
-
-   if (name)
-   ep = find_ep(gadget, name);
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-   }
+   struct usb_ep *ep;
 
if (gadget->ops->match_ep) {
ep = gadget->ops->match_ep(gadget, desc, NULL);
-- 
2.43.0



[PATCH 5/6] usb: gadget: Add full ep_matches() check past .match_ep() callback

2024-06-09 Thread Marek Vasut
If .match_ep() callback returns non-NULL endpoint, immediately check
its usability and if the returned endpoint is usable, stop search and
return the endpoint. Otherwise, continue with best effort search for
usable endpoint.

Currently the code would attempt the best effort search in any case,
which may find another unexpected endpoint. It is likely that the
intention of the original code was to stop the search early.

Fixes: 77dcbdf3c1ce ("usb: gadget: Add match_ep() op to usb_gadget_ops")
Signed-off-by: Marek Vasut 
---
Cc: Alexander Sverdlin 
Cc: Felipe Balbi 
Cc: Lukasz Majewski 
Cc: Mattijs Korpershoek 
Cc: Nishanth Menon 
Cc: Simon Glass 
Cc: Thinh Nguyen 
Cc: Tom Rini 
Cc: u-boot@lists.denx.de
---
 drivers/usb/gadget/epautoconf.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 09950ceeaed..66599ce8efa 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -247,8 +247,11 @@ struct usb_ep *usb_ep_autoconfig(
return ep;
}
 
-   if (gadget->ops->match_ep)
+   if (gadget->ops->match_ep) {
ep = gadget->ops->match_ep(gadget, desc, NULL);
+   if (ep && ep_matches(gadget, ep, desc))
+   return ep;
+   }
 
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry(ep, >ep_list, ep_list) {
-- 
2.43.0



[PATCH 4/6] usb: gadget: Drop all gadget_is_*() functions

2024-06-09 Thread Marek Vasut
The only actually used gadget_is_*() functions are the one for DWC3
used in epautoconf.c usb_ep_autoconfig() and one for MUSB in ether.c.
The DWC3 one should be fixed in some separate patch.

Inline the gadget_is_dwc3() and stop using ifdefs in favor of
IS_ENABLED() macro.

The rest of gadget_is_*() calls in usb_ep_autoconfig() can never
be anything but 0, since those gadgets are not supported in U-Boot,
so remove all that unused code. Remove gadget_chips.h as well.

Signed-off-by: Marek Vasut 
---
Cc: Alexander Sverdlin 
Cc: Felipe Balbi 
Cc: Lukasz Majewski 
Cc: Mattijs Korpershoek 
Cc: Nishanth Menon 
Cc: Simon Glass 
Cc: Thinh Nguyen 
Cc: Tom Rini 
Cc: u-boot@lists.denx.de
---
 drivers/usb/gadget/epautoconf.c   |  40 +---
 drivers/usb/gadget/ether.c|   8 +-
 drivers/usb/gadget/gadget_chips.h | 148 --
 3 files changed, 6 insertions(+), 190 deletions(-)
 delete mode 100644 drivers/usb/gadget/gadget_chips.h

diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 0a70035ce04..09950ceeaed 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -12,7 +12,6 @@
 #include 
 #include 
 #include 
-#include "gadget_chips.h"
 
 #define isdigit(c)  ('0' <= (c) && (c) <= '9')
 
@@ -222,41 +221,9 @@ struct usb_ep *usb_ep_autoconfig(
/* First, apply chip-specific "best usage" knowledge.
 * This might make a good usb_gadget_ops hook ...
 */
-   if (gadget_is_net2280(gadget) && type == USB_ENDPOINT_XFER_INT) {
-   /* ep-e, ep-f are PIO with only 64 byte fifos */
-   ep = find_ep(gadget, "ep-e");
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-   ep = find_ep(gadget, "ep-f");
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-
-   } else if (gadget_is_goku(gadget)) {
-   if (USB_ENDPOINT_XFER_INT == type) {
-   /* single buffering is enough */
-   ep = find_ep(gadget, "ep3-bulk");
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-   } else if (USB_ENDPOINT_XFER_BULK == type
-   && (USB_DIR_IN & desc->bEndpointAddress)) {
-   /* DMA may be available */
-   ep = find_ep(gadget, "ep2-bulk");
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-   }
-
-   } else if (gadget_is_sh(gadget) && USB_ENDPOINT_XFER_INT == type) {
-   /* single buffering is enough; maybe 8 byte fifo is too */
-   ep = find_ep(gadget, "ep3in-bulk");
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-
-   } else if (gadget_is_mq11xx(gadget) && USB_ENDPOINT_XFER_INT == type) {
-   ep = find_ep(gadget, "ep1-bulk");
-   if (ep && ep_matches(gadget, ep, desc))
-   return ep;
-#ifndef CONFIG_SPL_BUILD
-   } else if (gadget_is_dwc3(gadget)) {
+   if (!IS_ENABLED(CONFIG_SPL_BUILD) &&
+   IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
+   !strcmp("dwc3-gadget", gadget->name)) {
const char *name = NULL;
/*
 * First try standard, common configuration: ep1in-bulk,
@@ -278,7 +245,6 @@ struct usb_ep *usb_ep_autoconfig(
ep = find_ep(gadget, name);
if (ep && ep_matches(gadget, ep, desc))
return ep;
-#endif
}
 
if (gadget->ops->match_ep)
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index e76464e121b..b7b7bacb00d 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -1989,13 +1989,11 @@ static int eth_bind(struct usb_gadget *gadget)
 * standard protocol is _strongly_ preferred for interop purposes.
 * (By everyone except Microsoft.)
 */
-   if (gadget_is_musbhdrc(gadget)) {
+
+   if (IS_ENABLED(CONFIG_USB_MUSB_GADGET) &&
+   !strcmp("musb-hdrc", gadget->name)) {
/* reduce tx dma overhead by avoiding special cases */
zlp = 0;
-   } else if (gadget_is_sh(gadget)) {
-   /* sh doesn't support multiple interfaces or configs */
-   cdc = 0;
-   rndis = 0;
}
 
gcnum = (U_BOOT_VERSION_NUM << 4) | U_BOOT_VERSION_NUM_PATCH;
diff --git a/drivers/usb/gadget/gadget_chips.h 
b/drivers/usb/gadget/gadget_chips.h
deleted file mode 100644
index 316051686c4..000
--- a/drivers/usb/gadget/gadget_chips.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * USB device controllers have lots of quirks.  Use these macros in
- * gadget drivers or other code that needs to deal with them, and which
- * autoconfigures instead of using early binding to the hardware.
- *
- * 

[PATCH 3/6] usb: gadget: Drop usb_gadget_controller_number()

2024-06-09 Thread Marek Vasut
The bcdDevice field is defined as
|Device release number in binary-coded decimal
in the USB 2.0 specification. We use this field to distinguish the UDCs
from each other. In theory this could be used on the host side to apply
certain quirks if the "special" UDC in combination with this gadget is
used. This hasn't been done as far as I am aware. In practice it would
be better to fix the UDC driver before shipping since a later release
might not need this quirk anymore.

This patch removes the newly unused function. Linux stopped using this
functionality in 2012, remove it from U-Boot as well.

Matching Linux kernel commit:
ed9cbda63d45 ("usb: gadget: remove usb_gadget_controller_number()")

Signed-off-by: Marek Vasut 
---
Cc: Alexander Sverdlin 
Cc: Felipe Balbi 
Cc: Lukasz Majewski 
Cc: Mattijs Korpershoek 
Cc: Nishanth Menon 
Cc: Simon Glass 
Cc: Thinh Nguyen 
Cc: Tom Rini 
Cc: u-boot@lists.denx.de
---
 drivers/usb/gadget/gadget_chips.h | 62 ---
 1 file changed, 62 deletions(-)

diff --git a/drivers/usb/gadget/gadget_chips.h 
b/drivers/usb/gadget/gadget_chips.h
index 98156c312d2..316051686c4 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -146,65 +146,3 @@
 #else
 #define gadget_is_dwc2(g)0
 #endif
-
-/**
- * usb_gadget_controller_number - support bcdDevice id convention
- * @gadget: the controller being driven
- *
- * Return a 2-digit BCD value associated with the peripheral controller,
- * suitable for use as part of a bcdDevice value, or a negative error code.
- *
- * NOTE:  this convention is purely optional, and has no meaning in terms of
- * any USB specification.  If you want to use a different convention in your
- * gadget driver firmware -- maybe a more formal revision ID -- feel free.
- *
- * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
- * to change their behavior accordingly.  For example it might help avoiding
- * some chip bug.
- */
-static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
-{
-   if (gadget_is_net2280(gadget))
-   return 0x01;
-   else if (gadget_is_dummy(gadget))
-   return 0x02;
-   else if (gadget_is_sh(gadget))
-   return 0x04;
-   else if (gadget_is_goku(gadget))
-   return 0x06;
-   else if (gadget_is_mq11xx(gadget))
-   return 0x07;
-   else if (gadget_is_omap(gadget))
-   return 0x08;
-   else if (gadget_is_n9604(gadget))
-   return 0x09;
-   else if (gadget_is_at91(gadget))
-   return 0x12;
-   else if (gadget_is_imx(gadget))
-   return 0x13;
-   else if (gadget_is_musbhsfc(gadget))
-   return 0x14;
-   else if (gadget_is_musbhdrc(gadget))
-   return 0x15;
-   else if (gadget_is_atmel_usba(gadget))
-   return 0x17;
-   else if (gadget_is_fsl_usb2(gadget))
-   return 0x18;
-   else if (gadget_is_amd5536udc(gadget))
-   return 0x19;
-   else if (gadget_is_m66592(gadget))
-   return 0x20;
-   else if (gadget_is_ci(gadget))
-   return 0x21;
-   else if (gadget_is_dwc3(gadget))
-   return 0x23;
-   else if (gadget_is_cdns3(gadget))
-   return 0x24;
-   else if (gadget_is_max3420(gadget))
-   return 0x25;
-   else if (gadget_is_mtu3(gadget))
-   return 0x26;
-   else if (gadget_is_dwc2(gadget))
-   return 0x27;
-   return -ENOENT;
-}
-- 
2.43.0



[PATCH 2/6] usb: gadget: ether: Drop usb_gadget_controller_number()

2024-06-09 Thread Marek Vasut
The bcdDevice field is defined as
|Device release number in binary-coded decimal
in the USB 2.0 specification. We use this field to distinguish the UDCs
from each other. In theory this could be used on the host side to apply
certain quirks if the "special" UDC in combination with this gadget is
used. This hasn't been done as far as I am aware. In practice it would
be better to fix the UDC driver before shipping since a later release
might not need this quirk anymore.

This patch converts this gadget to use the U-Boot version instead of a
random 2 or 3 plus the UDC number. Linux stopped using this functionality
in 2012, remove it from U-Boot as well.

Matching Linux kernel commit:
ed9cbda63d45 ("usb: gadget: remove usb_gadget_controller_number()")

Signed-off-by: Marek Vasut 
---
Cc: Alexander Sverdlin 
Cc: Felipe Balbi 
Cc: Lukasz Majewski 
Cc: Mattijs Korpershoek 
Cc: Nishanth Menon 
Cc: Simon Glass 
Cc: Thinh Nguyen 
Cc: Tom Rini 
Cc: u-boot@lists.denx.de
---
 drivers/usb/gadget/ether.c | 17 +++--
 1 file changed, 3 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index b8b29d399b1..e76464e121b 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -22,8 +22,8 @@
 #include 
 #include 
 #include 
+#include 
 
-#include "gadget_chips.h"
 #include "rndis.h"
 
 #include 
@@ -1998,19 +1998,8 @@ static int eth_bind(struct usb_gadget *gadget)
rndis = 0;
}
 
-   gcnum = usb_gadget_controller_number(gadget);
-   if (gcnum >= 0)
-   device_desc.bcdDevice = cpu_to_le16(0x0300 + gcnum);
-   else {
-   /*
-* can't assume CDC works.  don't want to default to
-* anything less functional on CDC-capable hardware,
-* so we fail in this case.
-*/
-   pr_err("controller '%s' not recognized",
-   gadget->name);
-   return -ENODEV;
-   }
+   gcnum = (U_BOOT_VERSION_NUM << 4) | U_BOOT_VERSION_NUM_PATCH;
+   device_desc.bcdDevice = cpu_to_le16(gcnum);
 
/*
 * If there's an RNDIS configuration, that's what Windows wants to
-- 
2.43.0



[PATCH 1/6] usb: gadget: g_dnl: Drop usb_gadget_controller_number()

2024-06-09 Thread Marek Vasut
The bcdDevice field is defined as
|Device release number in binary-coded decimal
in the USB 2.0 specification. We use this field to distinguish the UDCs
from each other. In theory this could be used on the host side to apply
certain quirks if the "special" UDC in combination with this gadget is
used. This hasn't been done as far as I am aware. In practice it would
be better to fix the UDC driver before shipping since a later release
might not need this quirk anymore.

This patch converts this gadget to use the U-Boot version instead of a
random 2 or 3 plus the UDC number. Linux stopped using this functionality
in 2012, remove it from U-Boot as well.

Matching Linux kernel commit:
ed9cbda63d45 ("usb: gadget: remove usb_gadget_controller_number()")

Signed-off-by: Marek Vasut 
---
Cc: Alexander Sverdlin 
Cc: Felipe Balbi 
Cc: Lukasz Majewski 
Cc: Mattijs Korpershoek 
Cc: Nishanth Menon 
Cc: Simon Glass 
Cc: Thinh Nguyen 
Cc: Tom Rini 
Cc: u-boot@lists.denx.de
---
 drivers/usb/gadget/g_dnl.c | 17 +++--
 1 file changed, 3 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c
index b5b5f5d8c11..631969b3405 100644
--- a/drivers/usb/gadget/g_dnl.c
+++ b/drivers/usb/gadget/g_dnl.c
@@ -17,10 +17,10 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 
-#include "gadget_chips.h"
 #include "composite.c"
 
 /*
@@ -199,18 +199,6 @@ void g_dnl_clear_detach(void)
g_dnl_detach_request = false;
 }
 
-static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev)
-{
-   struct usb_gadget *gadget = cdev->gadget;
-   int gcnum;
-
-   gcnum = usb_gadget_controller_number(gadget);
-   if (gcnum > 0)
-   gcnum += 0x200;
-
-   return g_dnl_get_board_bcd_device_number(gcnum);
-}
-
 /**
  * Update internal serial number variable when the "serial#" env var changes.
  *
@@ -261,7 +249,8 @@ static int g_dnl_bind(struct usb_composite_dev *cdev)
if (ret)
goto error;
 
-   gcnum = g_dnl_get_bcd_device_number(cdev);
+   gcnum = g_dnl_get_board_bcd_device_number((U_BOOT_VERSION_NUM << 4) |
+ U_BOOT_VERSION_NUM_PATCH);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(gcnum);
else {
-- 
2.43.0



Re: [PATCH] arm: mvebu: Enable bootstd and other modernization for Synology DS414 (Armada XP) board

2024-06-09 Thread Tony Dinh
Hi Phil,

On Sat, Jun 8, 2024 at 4:32 PM Phil Sutter  wrote:
>
> On Fri, Jun 07, 2024 at 04:14:26PM -0700, Tony Dinh wrote:
> > On Fri, Jun 7, 2024 at 6:45 AM Phil Sutter  wrote:
> > > On Thu, Jun 06, 2024 at 04:44:36PM -0700, Tony Dinh wrote:
> > > > - Switch to standard boot (in include/configs/ds414.h and
> > > > configs/ds414_defconfig)
> > > > - Implement board_late_init() to ensure successful enumeration
> > > > of USB3 devices
> > >
> > > Oh, this makes the rear USB3 ports usable? I had failed to get there
> > > when working on DS414 support.
> >
> > Yes! There have been a lot of updates in the PCIe MVEBU area. So it
> > could have been a different reason why it did not work when you added
> > support for this board.
>
> Nice!
>
> > Still, without this board_late_init(), we would need to run "pci enum"
> > and then "usb start" or "usb reset" to bring up the USB3 ports. The
> > USB3 controller is on the PCIe bus, and MVEBU PCIe drivers don't
> > initialize everything when it comes up.
>
> Yeah, at some point I still had
> | CONFIG_PREBOOT="usb start; sf probe"
> in ds414_defconfig. Looking at my initial DS414 support patch from 2015,
> at least 'usb start' seemed to be necessary for USB to work in Linux.
> This might have changed later as I dropped the config symbol six years
> later in e471ddf0f3472 ("arm: mvebu: board/Synology: Unify legacy kernel
> support").
>
> > > > - Remove unnecessary misc_init_r(), since NET_RANDOM_ETHADDR is now
> > > > configured in
> > >
> > > So u-boot will assign random MAC addresses to the NICs instead of the
> > > real ones? Won't this potentially break PXE boot setups?
> >
> > I did not see any problem using a random MAC address in my test using
> > bootstd. Is there a setup where this board MAC address must be known
> > when we boot with PXE? my impression is that only ipaddr and serverip
> > are needed, and the MAC address could be whatever we define for the
> > board.
>
> I haven't played with PXE for a long time, but I assumed the DHCP daemon
> might send different options to different hosts identified by their MAC
> address. The latter being random obviously defeats such setups.
>
> Does the random MAC persist in Linux, BTW? I don't recall Linux
> extracting data from the vendor-specific flash storage. Unless I'm
> mistaken, it should be u-boot's responsibility to setup MAC addresses in
> the NICs (to the vendor-assigned ones).

Yes, it persists. Whatever was assigned to ethaddr and eth1addr are
passed to the Linux kernel. So the random local MAC address is
recognized as the hardware MAC address.

As an aside, there is a bug in bootstd that "bootflow scan -l" will go
into a seemingly infinite loop during PXE hunting if eth1addr is not
defined. I've not figured out how to fix this error recovery. I might
just give up and send the bug report to Simon :)

>
> > > > - Remove unnecessary checkboard()
> > > > - Updated IDENT_STRING to indicate this u-boot supports both Synology
> > > > DS414 and DS214+ boards
> > > > - Add SYS_THUMB_BUILD to reduce binary size
> > > > - Add NET_RANDOM_ETHADDR
> > > > - Add CONFIG_LBA48 and CONFIG_SYS_64BIT_LBA to support >2TB HDD/SDD
> > > >
> > > > Signed-off-by: Tony Dinh 
> > > > ---
> > > >
> > > >  board/Synology/ds414/ds414.c | 15 +++
> > > >  configs/ds414_defconfig  | 20 ++
> > > >  include/configs/ds414.h  | 51 ++--
> > > >  3 files changed, 42 insertions(+), 44 deletions(-)
> > > >
> > > > diff --git a/board/Synology/ds414/ds414.c b/board/Synology/ds414/ds414.c
> > > > index abe6f9eb5e..f0b55fa095 100644
> > > > --- a/board/Synology/ds414/ds414.c
> > > > +++ b/board/Synology/ds414/ds414.c
> > > > @@ -181,18 +181,9 @@ int board_init(void)
> > > >   return 0;
> > > >  }
> > > >
> > > > -int misc_init_r(void)
> > > > +int board_late_init(void)
> > > >  {
> > > > - if (!env_get("ethaddr")) {
> > > > - puts("Incomplete environment, populating from SPI 
> > > > flash\n");
> > > > - do_syno_populate(0, NULL);
> > > > - }
> > > > - return 0;
> > > > -}
> > >
> > > This is useful for first boot of flashed vanilla u-boot. Can this be
> > > retained somehow?
> >
> > We could, but with NET_RANDOM_ETHADDR configured in, this routine will
> > do nothing. Perhaps the fact that the envs will show CRC errors in the
> > 1st boot should be an indicator that it needs to be populated?
>
> Maybe this env population approach is not the right one to begin with.
> Reading doc/README.enetaddr, it seems we want something like step 1 in
> "Correct flow of setting up the MAC address", though I'm not sure which
> initialize() function this is about (board_eth_init() maybe?). Also we
> need a working spi flash driver at that point.
>
> > With standard boot enabled in this patch, the default envs can be
> > scripted in /boot/boot.scr or /boot.scr on the rootfs disk storage, or
> > on some flash location, or on the network . So we don't even need the
> >