All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] Add MMC/SD support for S700
@ 2020-12-13 10:08 Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 1/6] clk: actions: Introduce dummy get/set_rate callbacks Amit Singh Tomar
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

At the moment on S700 based platforms, only way to load/boot the Kernel
is from Ethernet, and with these patches one can now load/boot the
Kernel from uSD card.

Patches(1/6 and 2/6) adds changes needed for MMC/SD clock. It introduces
set/get callback routine and get/set MMC/SD clock rate based on device id.

Patch 4/6 adds MMC/SD node in U-boot specific dtsi file, which is used by MMC/SD
driver to read controller base address later on.

Patch 5/6 adds driver for MMC/SD controller present on S700 SoC, and its based
on Mainline Linux driver and DMA related bits is picked and simpilified from 
vendor source.

Final patch 6/6 enables the driver support along with MMC commands in
Cubieboard7 config file.

Also, while at it just took the opportunity to synchronize the S700 SoC DT with
Linux in patch 3/6.

This patch-set is tested on Cubieboard7-lite board with following results:

U-Boot 2021.01-rc1-04434-g6589149-dirty (Dec 13 2020 - 13:51:07 +0530)
cubieboard7

DRAM:  1 GiB
PSCI:  v0.2
MMC:   mmc at e0210000: 0
In:    serial at e0126000
Out:   serial at e0126000
Err:   serial at e0126000
Net:   eth0: ethernet at e0220000
Hit any key to stop autoboot:  0 
U-Boot => 
U-Boot => 
U-Boot => 
U-Boot => mmc info
Device: mmc at e0210000
Manufacturer ID: 3
OEM: 5344
Name: SC16G 
Bus Speed: 50000000
Mode: SD High Speed (50MHz)
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 14.8 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes
U-Boot => setenv bootargs console=ttyOWL3,115200n8 earlycon=owl,0xe0126000 init=/sbin/init  root=/dev/mmcblk0p2 rw rootwait
U-Boot => setenv kernel_addr_r 0x80000;setenv fdt_addr_r 0x10000000;
U-Boot => fatload mmc 0:1 ${kernel_addr_r} image ;fatload mmc 0:1 ${fdt_addr_r} s700-cubieboard7.dtb
27480576 bytes read in 1041 ms (25.2 MiB/s)
7056 bytes read in 2 ms (3.4 MiB/s)
U-Boot => booti $kernel_addr_r - $fdt_addr_r
## Flattened Device Tree blob at 10000000
   Booting using the fdt blob at 0x10000000
   Loading Device Tree to 000000003df56000, end 000000003df5ab8f ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
[    0.000000] Linux version 5.7.0-rc6-00022-g99f1c330923b-dirty (amit at amit-ThinkPad-X230) (gcc version 7.3.1 20180425 [linaro-7.3-2018.05 revision d29120a424ecfbc167ef90065c0eeb7f91977701] (Linaro GCC 7.3-2018.05), GNU ld (GNU Binutils for Ubuntu) 2.26.1) #474 SMP PREEMPT Thu Aug 13 15:27:10 IST 2020
[    0.000000] Machine model: CubieBoard7
[    0.000000] earlycon: owl0 at MMIO 0x00000000e0126000 (options '')
[    0.000000] printk: bootconsole [owl0] enabled
[    0.000000] efi: UEFI not found.
[    0.000000] cma: Reserved 32 MiB at 0x000000003e000000

Amit Singh Tomar (6):
  clk: actions: Introduce dummy get/set_rate callbacks
  clk: actions: Add SD/MMC clocks
  ARM: dts: sync Actions Semi S700 DT from Linux 5.10-rc7
  ARM: dts: s700: add MMC/SD controller node
  mmc: actions: add MMC driver for Actions OWL S700
  configs: Enable mmc support

 arch/arm/dts/s700-u-boot.dtsi                  |  10 +
 arch/arm/dts/s700.dtsi                         |  17 +-
 configs/cubieboard7_defconfig                  |   3 +
 drivers/clk/owl/clk_owl.c                      |  94 ++++++
 drivers/clk/owl/clk_owl.h                      |   2 +
 drivers/mmc/Kconfig                            |   7 +
 drivers/mmc/Makefile                           |   1 +
 drivers/mmc/owl_mmc.c                          | 404 +++++++++++++++++++++++++
 include/dt-bindings/power/owl-s700-powergate.h |  19 ++
 9 files changed, 556 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mmc/owl_mmc.c
 create mode 100644 include/dt-bindings/power/owl-s700-powergate.h

-- 
2.7.4

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

* [PATCH 1/6] clk: actions: Introduce dummy get/set_rate callbacks
  2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
@ 2020-12-13 10:08 ` Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 2/6] clk: actions: Add SD/MMC clocks Amit Singh Tomar
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

This commit introduces get/set_rate callbacks, these are dummy at
the moment, and can be used to get/set clock for various devices
based on the clk id.

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
---
 drivers/clk/owl/clk_owl.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/clk/owl/clk_owl.c b/drivers/clk/owl/clk_owl.c
index 1999c87..c9bc5c2 100644
--- a/drivers/clk/owl/clk_owl.c
+++ b/drivers/clk/owl/clk_owl.c
@@ -128,6 +128,32 @@ int owl_clk_disable(struct clk *clk)
 	return 0;
 }
 
+static ulong owl_clk_get_rate(struct clk *clk)
+{
+	struct owl_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong rate;
+
+	switch (clk->id) {
+	default:
+		return -ENOENT;
+	}
+
+	return rate;
+}
+
+static ulong owl_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct owl_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong new_rate;
+
+	switch (clk->id) {
+	default:
+		return -ENOENT;
+	}
+
+	return new_rate;
+}
+
 static int owl_clk_probe(struct udevice *dev)
 {
 	struct owl_clk_priv *priv = dev_get_priv(dev);
@@ -145,6 +171,8 @@ static int owl_clk_probe(struct udevice *dev)
 static const struct clk_ops owl_clk_ops = {
 	.enable = owl_clk_enable,
 	.disable = owl_clk_disable,
+	.get_rate = owl_clk_get_rate,
+	.set_rate = owl_clk_set_rate,
 };
 
 static const struct udevice_id owl_clk_ids[] = {
-- 
2.7.4

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

* [PATCH 2/6] clk: actions: Add SD/MMC clocks
  2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 1/6] clk: actions: Introduce dummy get/set_rate callbacks Amit Singh Tomar
@ 2020-12-13 10:08 ` Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 3/6] ARM: dts: sync Actions Semi S700 DT from Linux 5.10-rc7 Amit Singh Tomar
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

This commit adds SD/MMC clocks, and provides .set/get_rate callbacks
for SD/MMC device present on Actions OWL S700 SoCs.

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
---
 drivers/clk/owl/clk_owl.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/owl/clk_owl.h |  2 ++
 2 files changed, 68 insertions(+)

diff --git a/drivers/clk/owl/clk_owl.c b/drivers/clk/owl/clk_owl.c
index c9bc5c2..49a492c 100644
--- a/drivers/clk/owl/clk_owl.c
+++ b/drivers/clk/owl/clk_owl.c
@@ -92,6 +92,9 @@ int owl_clk_enable(struct clk *clk)
 		setbits_le32(priv->base + CMU_DEVCLKEN1, CMU_DEVCLKEN1_ETH);
 		setbits_le32(priv->base + CMU_ETHERNETPLL, 5);
 		break;
+	case CLK_SD0:
+		setbits_le32(priv->base + CMU_DEVCLKEN0, CMU_DEVCLKEN0_SD0);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -121,6 +124,9 @@ int owl_clk_disable(struct clk *clk)
 	case CLK_ETHERNET:
 		clrbits_le32(priv->base + CMU_DEVCLKEN1, CMU_DEVCLKEN1_ETH);
 		break;
+	case CLK_SD0:
+		clrbits_le32(priv->base + CMU_DEVCLKEN0, CMU_DEVCLKEN0_SD0);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -128,12 +134,69 @@ int owl_clk_disable(struct clk *clk)
 	return 0;
 }
 
+static ulong owl_get_sd_clk_rate(struct owl_clk_priv *priv, int sd_index)
+{
+	ulong parent_rate;
+	uint div = 1;
+
+	/* Clock output of DEV_PLL
+	 * Range: 48M ~ 756M
+	 * Frequency= DEVPLLCLK * 6
+	 */
+	parent_rate = readl(priv->base + CMU_DEVPLL) & 0x7f;
+	parent_rate *= 6000000;
+
+	div += readl(priv->base + (CMU_SD0CLK + sd_index*0x4)) & 0x1f;
+
+	return (parent_rate / div);
+}
+
+static ulong owl_set_sd_clk_rate(struct owl_clk_priv *priv, ulong rate,
+				 int sd_index)
+{
+	ulong parent_rate;
+	uint div = 1, val;
+
+	/* Clock output of DEV_PLL
+	 * Range: 48M ~ 756M
+	 * Frequency= DEVPLLCLK * 6
+	 */
+	parent_rate = readl(priv->base + CMU_DEVPLL) & 0x7f;
+	parent_rate *= 6000000;
+
+	rate *= 2;
+	div = (parent_rate / rate) - 1;
+
+	if (div >= 128)
+		div |= 0x100;
+
+	val = readl(priv->base + (CMU_SD0CLK + sd_index*0x4));
+	/* Bits 4..0 is used to program div value */
+	val &= ~0x1f;
+	val |= div;
+	/* As per Manuals Bits 31..10 are reserved but Bits 11 and
+	 * 10 needed to be set for proper operation
+	 */
+	val |= 0xc00;
+	/* Bit 9 and 8 must be cleared */
+	if (div < 128)
+		val &= ~0x300;
+	else
+		val &= ~0x200;
+	writel(val, priv->base + (CMU_SD0CLK + sd_index*0x4));
+
+	return owl_get_sd_clk_rate(priv, 0);
+}
+
 static ulong owl_clk_get_rate(struct clk *clk)
 {
 	struct owl_clk_priv *priv = dev_get_priv(clk->dev);
 	ulong rate;
 
 	switch (clk->id) {
+	case CLK_SD0:
+		rate = owl_get_sd_clk_rate(priv, 0);
+		break;
 	default:
 		return -ENOENT;
 	}
@@ -147,6 +210,9 @@ static ulong owl_clk_set_rate(struct clk *clk, ulong rate)
 	ulong new_rate;
 
 	switch (clk->id) {
+	case CLK_SD0:
+		new_rate = owl_set_sd_clk_rate(priv, rate, 0);
+		break;
 	default:
 		return -ENOENT;
 	}
diff --git a/drivers/clk/owl/clk_owl.h b/drivers/clk/owl/clk_owl.h
index a01f81a..ee5eba4 100644
--- a/drivers/clk/owl/clk_owl.h
+++ b/drivers/clk/owl/clk_owl.h
@@ -62,4 +62,6 @@ struct owl_clk_priv {
 #define CMU_DEVCLKEN1_UART5	BIT(21)
 #define CMU_DEVCLKEN1_UART3	BIT(11)
 
+#define CMU_DEVCLKEN0_SD0	BIT(22)
+
 #endif
-- 
2.7.4

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

* [PATCH 3/6] ARM: dts: sync Actions Semi S700 DT from Linux 5.10-rc7
  2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 1/6] clk: actions: Introduce dummy get/set_rate callbacks Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 2/6] clk: actions: Add SD/MMC clocks Amit Singh Tomar
@ 2020-12-13 10:08 ` Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 4/6] ARM: dts: s700: add MMC/SD controller node Amit Singh Tomar
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

This Synchronizes the Actions Semi S700 SoC DT changes from
commit "0477e9288185" ("Linux 5.10-rc7").

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
---
 arch/arm/dts/s700.dtsi                         | 17 ++++++++++++++++-
 include/dt-bindings/power/owl-s700-powergate.h | 19 +++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 include/dt-bindings/power/owl-s700-powergate.h

diff --git a/arch/arm/dts/s700.dtsi b/arch/arm/dts/s700.dtsi
index 2006ad5..2c78cae 100644
--- a/arch/arm/dts/s700.dtsi
+++ b/arch/arm/dts/s700.dtsi
@@ -5,6 +5,7 @@
 
 #include <dt-bindings/clock/actions,s700-cmu.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/power/owl-s700-powergate.h>
 #include <dt-bindings/reset/actions,s700-reset.h>
 
 / {
@@ -231,7 +232,7 @@
 
 		pinctrl: pinctrl at e01b0000 {
 			compatible = "actions,s700-pinctrl";
-			reg = <0x0 0xe01b0000 0x0 0x1000>;
+			reg = <0x0 0xe01b0000 0x0 0x100>;
 			clocks = <&cmu CLK_GPIO>;
 			gpio-controller;
 			gpio-ranges = <&pinctrl 0 0 136>;
@@ -244,5 +245,19 @@
 				     <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
 		};
+
+		dma: dma-controller at e0230000 {
+			compatible = "actions,s700-dma";
+			reg = <0x0 0xe0230000 0x0 0x1000>;
+			interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+			#dma-cells = <1>;
+			dma-channels = <10>;
+			dma-requests = <44>;
+			clocks = <&cmu CLK_DMAC>;
+			power-domains = <&sps S700_PD_DMA>;
+		};
 	};
 };
diff --git a/include/dt-bindings/power/owl-s700-powergate.h b/include/dt-bindings/power/owl-s700-powergate.h
new file mode 100644
index 0000000..4cf1aef
--- /dev/null
+++ b/include/dt-bindings/power/owl-s700-powergate.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Actions Semi S700 SPS
+ *
+ * Copyright (c) 2017 Andreas F?rber
+ */
+#ifndef DT_BINDINGS_POWER_OWL_S700_POWERGATE_H
+#define DT_BINDINGS_POWER_OWL_S700_POWERGATE_H
+
+#define S700_PD_VDE	0
+#define S700_PD_VCE_SI	1
+#define S700_PD_USB2_1	2
+#define S700_PD_HDE	3
+#define S700_PD_DMA	4
+#define S700_PD_DS	5
+#define S700_PD_USB3	6
+#define S700_PD_USB2_0	7
+
+#endif
-- 
2.7.4

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

* [PATCH 4/6] ARM: dts: s700: add MMC/SD controller node
  2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
                   ` (2 preceding siblings ...)
  2020-12-13 10:08 ` [PATCH 3/6] ARM: dts: sync Actions Semi S700 DT from Linux 5.10-rc7 Amit Singh Tomar
@ 2020-12-13 10:08 ` Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700 Amit Singh Tomar
  2020-12-13 10:08 ` [PATCH 6/6] configs: Enable mmc support Amit Singh Tomar
  5 siblings, 0 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

This patch adds node for ethernet controller found on Action Semi OWL
S700 SoC.

Since, upstream Linux binding has not been merged for S700 MMC/SD
controller, Changes are put in u-boot specific dtsi file.

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
---
 arch/arm/dts/s700-u-boot.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm/dts/s700-u-boot.dtsi b/arch/arm/dts/s700-u-boot.dtsi
index 1b27682..3c3396b 100644
--- a/arch/arm/dts/s700-u-boot.dtsi
+++ b/arch/arm/dts/s700-u-boot.dtsi
@@ -19,6 +19,16 @@
 			status = "okay";
                 };
 
+		mmc0: mmc at e0210000 {
+			compatible = "actions,s700-mmc", "actions,owl-mmc";
+			reg = <0x0 0xe0210000 0x0 0x4000>;
+			interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cmu CLK_SD0>;
+			dmas = <&dma 2>;
+			dma-names = "mmc";
+			bus-width = <4>;
+			status = "okay";
+		};
 	};
 };
 
-- 
2.7.4

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

* [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700
  2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
                   ` (3 preceding siblings ...)
  2020-12-13 10:08 ` [PATCH 4/6] ARM: dts: s700: add MMC/SD controller node Amit Singh Tomar
@ 2020-12-13 10:08 ` Amit Singh Tomar
  2020-12-14  0:49   ` Jaehoon Chung
  2020-12-14 13:56   ` André Przywara
  2020-12-13 10:08 ` [PATCH 6/6] configs: Enable mmc support Amit Singh Tomar
  5 siblings, 2 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

This commit adds support for MMC controllers found on Actions OWL
S700 SoC platform.

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
---
 drivers/mmc/Kconfig   |   7 +
 drivers/mmc/Makefile  |   1 +
 drivers/mmc/owl_mmc.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 412 insertions(+)
 create mode 100644 drivers/mmc/owl_mmc.c

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 14d7913..61f9c67 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -289,6 +289,13 @@ config MMC_MXC
 
 	  If unsure, say N.
 
+config MMC_OWL
+	bool "Actions OWL Multimedia Card Interface support"
+	depends on ARCH_OWL && DM_MMC && BLK
+	help
+	  This selects the OWL SD/MMC host controller found on board
+	  based on Actions S700 SoC.
+
 config MMC_MXS
 	bool "Freescale MXS Multimedia Card Interface support"
 	depends on MX23 || MX28 || MX6 || MX7
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 1c849cb..f270f6c 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
 obj-$(CONFIG_MMC_MXC)			+= mxcmmc.o
 obj-$(CONFIG_MMC_MXS)			+= mxsmmc.o
 obj-$(CONFIG_MMC_OCTEONTX)		+= octeontx_hsmmc.o
+obj-$(CONFIG_MMC_OWL)			+= owl_mmc.o
 obj-$(CONFIG_MMC_PCI)			+= pci_mmc.o
 obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
 obj-$(CONFIG_$(SPL_TPL_)SUPPORT_EMMC_RPMB) += rpmb.o
diff --git a/drivers/mmc/owl_mmc.c b/drivers/mmc/owl_mmc.c
new file mode 100644
index 0000000..aa0ccb7
--- /dev/null
+++ b/drivers/mmc/owl_mmc.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Amit Singh Tomar <amittomer25@gmail.com>
+ *
+ * Driver for SD/MMC controller present on Actions Semi S700 SoC, based
+ * on Linux Driver "drivers/mmc/host/owl-mmc.c".
+ *
+ * Though, there is a bit (BSEL, BUS or DMA Special Channel Selection) that
+ * controls the data transfer from SDx_DAT register either using CPU AHB Bus
+ * or DMA channel, but seems like, it only works correctly using external DMA
+ * channel, and those special bits used in this driver is picked from vendor
+ * source exclusively for MMC/SD.
+ */
+#include <common.h>
+#include <clk.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+/*
+ * SDC registers
+ */
+#define OWL_REG_SD_EN                   0x0000
+#define OWL_REG_SD_CTL                  0x0004
+#define OWL_REG_SD_STATE                0x0008
+#define OWL_REG_SD_CMD                  0x000c
+#define OWL_REG_SD_ARG                  0x0010
+#define OWL_REG_SD_RSPBUF0              0x0014
+#define OWL_REG_SD_RSPBUF1              0x0018
+#define OWL_REG_SD_RSPBUF2              0x001c
+#define OWL_REG_SD_RSPBUF3              0x0020
+#define OWL_REG_SD_RSPBUF4              0x0024
+#define OWL_REG_SD_DAT                  0x0028
+#define OWL_REG_SD_BLK_SIZE             0x002c
+#define OWL_REG_SD_BLK_NUM              0x0030
+#define OWL_REG_SD_BUF_SIZE             0x0034
+
+/* SD_EN Bits */
+#define OWL_SD_EN_RANE                  BIT(31)
+#define OWL_SD_EN_RESE                  BIT(10)
+#define OWL_SD_ENABLE                   BIT(7)
+#define OWL_SD_EN_BSEL                  BIT(6)
+#define OWL_SD_EN_DATAWID(x)            (((x) & 0x3) << 0)
+
+/* SD_CTL Bits */
+#define OWL_SD_CTL_TOUTEN               BIT(31)
+#define OWL_SD_CTL_DELAY_MSK            GENMASK(23, 16)
+#define OWL_SD_CTL_RDELAY(x)            (((x) & 0xf) << 20)
+#define OWL_SD_CTL_WDELAY(x)            (((x) & 0xf) << 16)
+#define OWL_SD_CTL_TS                   BIT(7)
+#define OWL_SD_CTL_LBE                  BIT(6)
+#define OWL_SD_CTL_TM(x)                (((x) & 0xf) << 0)
+
+#define OWL_SD_DELAY_LOW_CLK            0x0f
+#define OWL_SD_DELAY_MID_CLK            0x0a
+#define OWL_SD_RDELAY_HIGH		0x08
+#define OWL_SD_WDELAY_HIGH		0x09
+
+/* SD_STATE Bits */
+#define OWL_SD_STATE_CLNR               BIT(4)
+#define OWL_SD_STATE_CRC7ER             BIT(0)
+#define OWL_SD_STATE_DAT0S              BIT(7)
+
+#define OWL_MMC_OCR                     (MMC_VDD_32_33 | MMC_VDD_33_34 | \
+					 MMC_VDD_165_195)
+
+#define DATA_TRANSFER_TIMEOUT		(3 * (100 * 1000))
+
+/*
+ * Simple DMA transfer operations defines for MMC/SD card
+ */
+#define DMA0_CHANNEL_BASE(x)		(0xe0230100 + 0x100 * (x))
+
+#define DMA_MODE			0x0000
+#define DMA_SOURCE			0x0004
+#define DMA_DESTINATION			0x0008
+#define DMA_FRAME_LEN			0x000C
+#define DMA_FRAME_CNT			0x0010
+#define DMA_START			0x0024
+
+/* DMAx_MODE */
+#define DMA_MODE_ST(x)                  (((x) & 0x3) << 8)
+#define DMA_MODE_ST_DEV                 DMA_MODE_ST(0)
+#define DMA_MODE_DT(x)                  (((x) & 0x3) << 10)
+#define DMA_MODE_DT_DCU                 DMA_MODE_DT(2)
+#define DMA_MODE_SAM(x)                 (((x) & 0x3) << 16)
+#define DMA_MODE_SAM_CONST              DMA_MODE_SAM(0)
+#define DMA_MODE_DAM(x)                 (((x) & 0x3) << 18)
+#define DMA_MODE_DAM_INC                DMA_MODE_DAM(1)
+
+
+struct owl_mmc_plat {
+	struct mmc_config cfg;
+	struct mmc mmc;
+};
+
+struct owl_mmc_priv {
+	void *reg_base;
+	struct clk clk;
+	unsigned int clock;	  /* Current clock */
+	unsigned int dma_drq;	  /* Trigger Source */
+};
+
+static void owl_dma_config(void *dma_base, unsigned int drq,
+		unsigned int src, unsigned int dst,
+		unsigned int len)
+{
+	unsigned int mode = drq;
+	u8 frame_cnt = 0x1;
+
+	/* Set Source and Destination adderess mode */
+	mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST | DMA_MODE_DT_DCU |
+			DMA_MODE_DAM_INC);
+
+	writel(mode, dma_base + DMA_MODE);
+	writel(src, dma_base + DMA_SOURCE);
+	writel(dst, dma_base + DMA_DESTINATION);
+	writel(len, dma_base + DMA_FRAME_LEN);
+	writel(frame_cnt, dma_base + DMA_FRAME_CNT);
+}
+
+static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
+		struct mmc_data *data)
+{
+	unsigned int reg, total;
+	uint32_t buf = 0;
+
+	reg = readl(priv->reg_base + OWL_REG_SD_EN);
+	reg |= OWL_SD_EN_BSEL;
+	writel(reg, priv->reg_base + OWL_REG_SD_EN);
+
+	writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
+	writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
+	total = data->blocksize * data->blocks;
+
+	if (data->blocksize < 512)
+		writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
+	else
+		writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);
+
+	/* DMA STOP */
+	writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
+
+	if (data) {
+		if (data->flags == MMC_DATA_READ) {
+			buf = (ulong) (data->dest);
+			owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
+					priv->dma_drq,
+					(ulong) priv->reg_base +
+					OWL_REG_SD_DAT,
+					buf, total);
+			invalidate_dcache_range(buf, buf + total);
+		} else if (data->flags == MMC_DATA_WRITE) {
+			buf = (ulong) (data->src);
+			owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
+					priv->dma_drq,
+					buf,
+					(ulong) priv->reg_base +
+					OWL_REG_SD_DAT,
+					total);
+			flush_dcache_range(buf, buf + total);
+		}
+		/* DMA START */
+		writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
+	}
+}
+
+static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct owl_mmc_priv *priv = dev_get_priv(dev);
+	unsigned int cmd_rsp_mask, mode, reg;
+	int timeout = DATA_TRANSFER_TIMEOUT;
+
+	reg = readl(priv->reg_base + OWL_REG_SD_EN);
+	reg |= OWL_SD_ENABLE;
+	writel(reg, priv->reg_base + OWL_REG_SD_EN);
+
+	/* setup response */
+	switch (cmd->resp_type) {
+	case MMC_RSP_NONE:
+		break;
+	case MMC_RSP_R1:
+		if (data) {
+			if (data->flags == MMC_DATA_READ)
+				mode = OWL_SD_CTL_TM(4);
+			else
+				mode = OWL_SD_CTL_TM(5);
+		} else {
+			mode = OWL_SD_CTL_TM(1);
+		}
+		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
+		break;
+	case MMC_RSP_R1b:
+		mode = OWL_SD_CTL_TM(3);
+		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
+		break;
+	case MMC_RSP_R2:
+		mode = OWL_SD_CTL_TM(2);
+		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
+		break;
+	case MMC_RSP_R3:
+		cmd->cmdarg = 0x40ff8000;
+		mode = OWL_SD_CTL_TM(1);
+		cmd_rsp_mask = OWL_SD_STATE_CLNR;
+		break;
+	default:
+		printf("error: no math command RSP flag %x\n", cmd->cmdarg);
+		return -1;
+	}
+
+	mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
+
+	/* setup command */
+	writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
+	writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
+
+	/* Set LBE to send clk at the end of last read block */
+	if (data)
+		mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
+	else
+		mode |= OWL_SD_CTL_TS;
+
+	if (data)
+		owl_mmc_prepare_data(priv, data);
+
+	/* Start transfer */
+	writel(mode, priv->reg_base + OWL_REG_SD_CTL);
+
+	while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
+			&& timeout--)
+		udelay(20);
+
+	if (!timeout) {
+		printf("error: transferred data timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[3] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
+		cmd->response[2] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
+		cmd->response[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF2);
+		cmd->response[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF3);
+	} else {
+		u32 rsp[2];
+
+		rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
+		rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
+		cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
+		cmd->response[1] = rsp[1] >> 8;
+	}
+
+	if (data) {
+		/* DMA STOP */
+		writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
+		/* Transmission STOP */
+		while (readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
+			clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
+					OWL_SD_CTL_TS);
+	}
+
+	return 0;
+}
+
+static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
+{
+	u32 reg;
+
+	reg = readl(priv->reg_base + OWL_REG_SD_CTL);
+	reg &= ~OWL_SD_CTL_DELAY_MSK;
+
+	/* Set RDELAY and WDELAY based on the clock */
+	if (rate <= 1000000) {
+		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
+			OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
+			priv->reg_base + OWL_REG_SD_CTL);
+	} else if ((rate > 1000000) && (rate <= 26000000)) {
+		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
+			OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
+			priv->reg_base + OWL_REG_SD_CTL);
+	} else if ((rate > 26000000) && (rate <= 52000000)) {
+		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
+			OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
+			priv->reg_base + OWL_REG_SD_CTL);
+	} else {
+		printf("SD clock rate not supported\n");
+	}
+}
+
+static int owl_mmc_set_ios(struct udevice *dev)
+{
+	struct owl_mmc_priv *priv = dev_get_priv(dev);
+	struct owl_mmc_plat *plat = dev_get_platdata(dev);
+	struct mmc *mmc = &plat->mmc;
+	u32 reg, ret;
+
+	if (mmc->clock != priv->clock) {
+		priv->clock = mmc->clock;
+		owl_mmc_clk_set(priv, mmc->clock);
+	}
+
+	ret = clk_set_rate(&priv->clk, mmc->clock);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret)
+		return ret;
+
+	/* Set the Bus width */
+	reg = readl(priv->reg_base + OWL_REG_SD_EN);
+	reg &= ~0x03;
+	if (mmc->bus_width == 8)
+		reg |= OWL_SD_EN_DATAWID(2);
+	else if (mmc->bus_width == 4)
+		reg |= OWL_SD_EN_DATAWID(1);
+
+	writel(reg, priv->reg_base + OWL_REG_SD_EN);
+
+	return 0;
+}
+
+static const struct dm_mmc_ops owl_mmc_ops = {
+	.send_cmd       = owl_mmc_send_cmd,
+	.set_ios        = owl_mmc_set_ios,
+};
+
+static int owl_mmc_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct owl_mmc_plat *plat = dev_get_platdata(dev);
+	struct owl_mmc_priv *priv = dev_get_priv(dev);
+	struct mmc_config *cfg = &plat->cfg;
+	int bus_width, ret;
+	fdt_addr_t addr;
+
+	cfg->name = dev->name;
+	cfg->voltages = OWL_MMC_OCR;
+	cfg->f_min = 400000;
+	cfg->f_max = 52000000;
+	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+
+	bus_width = dev_read_u32_default(dev, "bus-width", 1);
+	switch (bus_width) {
+	case 8:
+		cfg->host_caps |= MMC_MODE_8BIT;
+		/* Hosts capable of 8-bit transfers can also do 4 bits */
+	case 4:
+		cfg->host_caps |= MMC_MODE_4BIT;
+		break;
+	case 1:
+		break;
+	default:
+		printf("Invalid bus-width value %u\n", bus_width);
+	}
+	cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
+
+	addr = dev_read_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->reg_base = (void *)addr;
+	priv->dma_drq  = 0x2;
+
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret) {
+		debug("clk_get_by_index() failed: %d\n", ret);
+		return ret;
+	}
+
+	upriv->mmc = &plat->mmc;
+
+	return 0;
+}
+
+static int owl_mmc_bind(struct udevice *dev)
+{
+	struct owl_mmc_plat *plat = dev_get_platdata(dev);
+
+	return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static const struct udevice_id owl_mmc_ids[] = {
+	{ .compatible = "actions,s700-mmc" },
+	{ .compatible = "actions,owl-mmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(owl_mmc_drv) = {
+	.name           = "owl_mmc",
+	.id             = UCLASS_MMC,
+	.of_match       = owl_mmc_ids,
+	.bind           = owl_mmc_bind,
+	.probe          = owl_mmc_probe,
+	.ops            = &owl_mmc_ops,
+	.platdata_auto_alloc_size = sizeof(struct owl_mmc_plat),
+	.priv_auto_alloc_size = sizeof(struct owl_mmc_priv),
+};
-- 
2.7.4

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

* [PATCH 6/6] configs: Enable mmc support
  2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
                   ` (4 preceding siblings ...)
  2020-12-13 10:08 ` [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700 Amit Singh Tomar
@ 2020-12-13 10:08 ` Amit Singh Tomar
  5 siblings, 0 replies; 12+ messages in thread
From: Amit Singh Tomar @ 2020-12-13 10:08 UTC (permalink / raw)
  To: u-boot

From: Amit Singh Tomar <amittomer25@gmail.com>

This commits enables mmc on the Actions Cubieboard7 board.

Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
---
 configs/cubieboard7_defconfig | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/configs/cubieboard7_defconfig b/configs/cubieboard7_defconfig
index 64dc593..d1ee862 100644
--- a/configs/cubieboard7_defconfig
+++ b/configs/cubieboard7_defconfig
@@ -14,3 +14,6 @@ CONFIG_PHY_REALTEK=y
 CONFIG_RTL8201F_PHY_S700_RMII_TIMINGS=y
 CONFIG_ETH_DESIGNWARE=y
 CONFIG_ETH_DESIGNWARE_S700=y
+CONFIG_DM_MMC=y
+CONFIG_MMC_OWL=y
+CONFIG_CMD_MMC=y
-- 
2.7.4

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

* [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700
  2020-12-13 10:08 ` [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700 Amit Singh Tomar
@ 2020-12-14  0:49   ` Jaehoon Chung
  2020-12-14 14:42     ` Amit Tomar
  2020-12-14 13:56   ` André Przywara
  1 sibling, 1 reply; 12+ messages in thread
From: Jaehoon Chung @ 2020-12-14  0:49 UTC (permalink / raw)
  To: u-boot

On 12/13/20 7:08 PM, Amit Singh Tomar wrote:
> From: Amit Singh Tomar <amittomer25@gmail.com>
> 
> This commit adds support for MMC controllers found on Actions OWL
> S700 SoC platform.
> 
> Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
> ---
>  drivers/mmc/Kconfig   |   7 +
>  drivers/mmc/Makefile  |   1 +
>  drivers/mmc/owl_mmc.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 412 insertions(+)
>  create mode 100644 drivers/mmc/owl_mmc.c
> 
> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index 14d7913..61f9c67 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -289,6 +289,13 @@ config MMC_MXC
>  
>  	  If unsure, say N.
>  
> +config MMC_OWL
> +	bool "Actions OWL Multimedia Card Interface support"
> +	depends on ARCH_OWL && DM_MMC && BLK
> +	help
> +	  This selects the OWL SD/MMC host controller found on board
> +	  based on Actions S700 SoC.
> +
>  config MMC_MXS
>  	bool "Freescale MXS Multimedia Card Interface support"
>  	depends on MX23 || MX28 || MX6 || MX7
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index 1c849cb..f270f6c 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
>  obj-$(CONFIG_MMC_MXC)			+= mxcmmc.o
>  obj-$(CONFIG_MMC_MXS)			+= mxsmmc.o
>  obj-$(CONFIG_MMC_OCTEONTX)		+= octeontx_hsmmc.o
> +obj-$(CONFIG_MMC_OWL)			+= owl_mmc.o
>  obj-$(CONFIG_MMC_PCI)			+= pci_mmc.o
>  obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
>  obj-$(CONFIG_$(SPL_TPL_)SUPPORT_EMMC_RPMB) += rpmb.o
> diff --git a/drivers/mmc/owl_mmc.c b/drivers/mmc/owl_mmc.c
> new file mode 100644
> index 0000000..aa0ccb7
> --- /dev/null
> +++ b/drivers/mmc/owl_mmc.c
> @@ -0,0 +1,404 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2020 Amit Singh Tomar <amittomer25@gmail.com>
> + *
> + * Driver for SD/MMC controller present on Actions Semi S700 SoC, based
> + * on Linux Driver "drivers/mmc/host/owl-mmc.c".
> + *
> + * Though, there is a bit (BSEL, BUS or DMA Special Channel Selection) that
> + * controls the data transfer from SDx_DAT register either using CPU AHB Bus
> + * or DMA channel, but seems like, it only works correctly using external DMA
> + * channel, and those special bits used in this driver is picked from vendor
> + * source exclusively for MMC/SD.
> + */
> +#include <common.h>
> +#include <clk.h>
> +#include <cpu_func.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <log.h>
> +#include <mmc.h>
> +#include <asm/io.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +
> +/*
> + * SDC registers
> + */
> +#define OWL_REG_SD_EN                   0x0000
> +#define OWL_REG_SD_CTL                  0x0004
> +#define OWL_REG_SD_STATE                0x0008
> +#define OWL_REG_SD_CMD                  0x000c
> +#define OWL_REG_SD_ARG                  0x0010
> +#define OWL_REG_SD_RSPBUF0              0x0014
> +#define OWL_REG_SD_RSPBUF1              0x0018
> +#define OWL_REG_SD_RSPBUF2              0x001c
> +#define OWL_REG_SD_RSPBUF3              0x0020
> +#define OWL_REG_SD_RSPBUF4              0x0024
> +#define OWL_REG_SD_DAT                  0x0028
> +#define OWL_REG_SD_BLK_SIZE             0x002c
> +#define OWL_REG_SD_BLK_NUM              0x0030
> +#define OWL_REG_SD_BUF_SIZE             0x0034
> +
> +/* SD_EN Bits */
> +#define OWL_SD_EN_RANE                  BIT(31)
> +#define OWL_SD_EN_RESE                  BIT(10)
> +#define OWL_SD_ENABLE                   BIT(7)
> +#define OWL_SD_EN_BSEL                  BIT(6)
> +#define OWL_SD_EN_DATAWID(x)            (((x) & 0x3) << 0)
> +
> +/* SD_CTL Bits */
> +#define OWL_SD_CTL_TOUTEN               BIT(31)
> +#define OWL_SD_CTL_DELAY_MSK            GENMASK(23, 16)
> +#define OWL_SD_CTL_RDELAY(x)            (((x) & 0xf) << 20)
> +#define OWL_SD_CTL_WDELAY(x)            (((x) & 0xf) << 16)
> +#define OWL_SD_CTL_TS                   BIT(7)
> +#define OWL_SD_CTL_LBE                  BIT(6)
> +#define OWL_SD_CTL_TM(x)                (((x) & 0xf) << 0)
> +
> +#define OWL_SD_DELAY_LOW_CLK            0x0f
> +#define OWL_SD_DELAY_MID_CLK            0x0a
> +#define OWL_SD_RDELAY_HIGH		0x08
> +#define OWL_SD_WDELAY_HIGH		0x09
> +
> +/* SD_STATE Bits */
> +#define OWL_SD_STATE_CLNR               BIT(4)
> +#define OWL_SD_STATE_CRC7ER             BIT(0)
> +#define OWL_SD_STATE_DAT0S              BIT(7)

Order to the bit number.

> +
> +#define OWL_MMC_OCR                     (MMC_VDD_32_33 | MMC_VDD_33_34 | \
> +					 MMC_VDD_165_195)
> +
> +#define DATA_TRANSFER_TIMEOUT		(3 * (100 * 1000))

Really need to set 300000?

> +
> +/*
> + * Simple DMA transfer operations defines for MMC/SD card
> + */
> +#define DMA0_CHANNEL_BASE(x)		(0xe0230100 + 0x100 * (x))

What is 0xe0230100?

> +
> +#define DMA_MODE			0x0000
> +#define DMA_SOURCE			0x0004
> +#define DMA_DESTINATION			0x0008
> +#define DMA_FRAME_LEN			0x000C
> +#define DMA_FRAME_CNT			0x0010
> +#define DMA_START			0x0024
> +
> +/* DMAx_MODE */
> +#define DMA_MODE_ST(x)                  (((x) & 0x3) << 8)
> +#define DMA_MODE_ST_DEV                 DMA_MODE_ST(0)
> +#define DMA_MODE_DT(x)                  (((x) & 0x3) << 10)
> +#define DMA_MODE_DT_DCU                 DMA_MODE_DT(2)
> +#define DMA_MODE_SAM(x)                 (((x) & 0x3) << 16)
> +#define DMA_MODE_SAM_CONST              DMA_MODE_SAM(0)
> +#define DMA_MODE_DAM(x)                 (((x) & 0x3) << 18)
> +#define DMA_MODE_DAM_INC                DMA_MODE_DAM(1)
> +
> +
> +struct owl_mmc_plat {
> +	struct mmc_config cfg;
> +	struct mmc mmc;
> +};
> +
> +struct owl_mmc_priv {
> +	void *reg_base;
> +	struct clk clk;
> +	unsigned int clock;	  /* Current clock */
> +	unsigned int dma_drq;	  /* Trigger Source */
> +};
> +
> +static void owl_dma_config(void *dma_base, unsigned int drq,
> +		unsigned int src, unsigned int dst,
> +		unsigned int len)
> +{
> +	unsigned int mode = drq;
> +	u8 frame_cnt = 0x1;

Does it need to use frame_cnt variable?

> +
> +	/* Set Source and Destination adderess mode */
> +	mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST | DMA_MODE_DT_DCU |
> +			DMA_MODE_DAM_INC);
> +
> +	writel(mode, dma_base + DMA_MODE);
> +	writel(src, dma_base + DMA_SOURCE);
> +	writel(dst, dma_base + DMA_DESTINATION);
> +	writel(len, dma_base + DMA_FRAME_LEN);
> +	writel(frame_cnt, dma_base + DMA_FRAME_CNT);
> +}
> +
> +static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
> +		struct mmc_data *data)
> +{
> +	unsigned int reg, total;
> +	uint32_t buf = 0;
> +
> +	reg = readl(priv->reg_base + OWL_REG_SD_EN);
> +	reg |= OWL_SD_EN_BSEL;
> +	writel(reg, priv->reg_base + OWL_REG_SD_EN);
> +
> +	writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
> +	writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
> +	total = data->blocksize * data->blocks;
> +
> +	if (data->blocksize < 512)
> +		writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
> +	else
> +		writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);

Is it right to set to 512?

> +
> +	/* DMA STOP */
> +	writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> +
> +	if (data) {
> +		if (data->flags == MMC_DATA_READ) {
> +			buf = (ulong) (data->dest);
> +			owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> +					priv->dma_drq,
> +					(ulong) priv->reg_base +
> +					OWL_REG_SD_DAT,
> +					buf, total);
> +			invalidate_dcache_range(buf, buf + total);
> +		} else if (data->flags == MMC_DATA_WRITE) {

Not need to use "else if", because it shoud be MMC_DATA_WRITE.

> +			buf = (ulong) (data->src);
> +			owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> +					priv->dma_drq,
> +					buf,
> +					(ulong) priv->reg_base +
> +					OWL_REG_SD_DAT,
> +					total);
> +			flush_dcache_range(buf, buf + total);
> +		}
> +		/* DMA START */
> +		writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
> +	}
> +}
> +
> +static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
> +		struct mmc_data *data)
> +{
> +	struct owl_mmc_priv *priv = dev_get_priv(dev);
> +	unsigned int cmd_rsp_mask, mode, reg;
> +	int timeout = DATA_TRANSFER_TIMEOUT;
> +
> +	reg = readl(priv->reg_base + OWL_REG_SD_EN);
> +	reg |= OWL_SD_ENABLE;
> +	writel(reg, priv->reg_base + OWL_REG_SD_EN);
> +
> +	/* setup response */
> +	switch (cmd->resp_type) {
> +	case MMC_RSP_NONE:
> +		break;
> +	case MMC_RSP_R1:
> +		if (data) {
> +			if (data->flags == MMC_DATA_READ)
> +				mode = OWL_SD_CTL_TM(4);
> +			else
> +				mode = OWL_SD_CTL_TM(5);
> +		} else {
> +			mode = OWL_SD_CTL_TM(1);
> +		}
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> +		break;
> +	case MMC_RSP_R1b:
> +		mode = OWL_SD_CTL_TM(3);
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> +		break;
> +	case MMC_RSP_R2:
> +		mode = OWL_SD_CTL_TM(2);
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> +		break;
> +	case MMC_RSP_R3:
> +		cmd->cmdarg = 0x40ff8000;

I don't know why set to cmdarg at here?

> +		mode = OWL_SD_CTL_TM(1);
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR;
> +		break;
> +	default:
> +		printf("error: no math command RSP flag %x\n", cmd->cmdarg);
> +		return -1;

Not use -1, instead use error number.

> +	}
> +
> +	mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
> +
> +	/* setup command */
> +	writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
> +	writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
> +
> +	/* Set LBE to send clk at the end of last read block */
> +	if (data)
> +		mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
> +	else
> +		mode |= OWL_SD_CTL_TS;
> +
> +	if (data)
> +		owl_mmc_prepare_data(priv, data);
> +
> +	/* Start transfer */
> +	writel(mode, priv->reg_base + OWL_REG_SD_CTL);
> +
> +	while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
> +			&& timeout--)
> +		udelay(20);

Use readl_poll_timeout()?

> +
> +	if (!timeout) {
> +		printf("error: transferred data timeout\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (cmd->resp_type & MMC_RSP_136) {
> +		cmd->response[3] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
> +		cmd->response[2] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
> +		cmd->response[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF2);
> +		cmd->response[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF3);
> +	} else {
> +		u32 rsp[2];
> +
> +		rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
> +		rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
> +		cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
> +		cmd->response[1] = rsp[1] >> 8;
> +	}
> +
> +	if (data) {
> +		/* DMA STOP */
> +		writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> +		/* Transmission STOP */
> +		while (readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
> +			clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
> +					OWL_SD_CTL_TS);
> +	}
> +
> +	return 0;
> +}
> +
> +static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
> +{
> +	u32 reg;
> +
> +	reg = readl(priv->reg_base + OWL_REG_SD_CTL);
> +	reg &= ~OWL_SD_CTL_DELAY_MSK;
> +
> +	/* Set RDELAY and WDELAY based on the clock */
> +	if (rate <= 1000000) {
> +		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
> +			OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
> +			priv->reg_base + OWL_REG_SD_CTL);
> +	} else if ((rate > 1000000) && (rate <= 26000000)) {
> +		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
> +			OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
> +			priv->reg_base + OWL_REG_SD_CTL);
> +	} else if ((rate > 26000000) && (rate <= 52000000)) {
> +		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
> +			OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
> +			priv->reg_base + OWL_REG_SD_CTL);
> +	} else {
> +		printf("SD clock rate not supported\n");
> +	}

I think that you can make more readable than now.

if (rate <= 1000000) {
	delay = OWL_SD_DELAY_LOW_CLK;
} else if ( ...) {
	delay = OWL_SD_DELAY_MID_CLK;
} else if (....) {
	delay = OWL_SD_DELAY_HIGH_CLK;
} 

writel(reg | OWL_SD_CTRL_RDELAY(delay) | OWL_SD_CTL_WDELAY(delay)...);



> +}
> +
> +static int owl_mmc_set_ios(struct udevice *dev)
> +{
> +	struct owl_mmc_priv *priv = dev_get_priv(dev);
> +	struct owl_mmc_plat *plat = dev_get_platdata(dev);
> +	struct mmc *mmc = &plat->mmc;
> +	u32 reg, ret;
> +
> +	if (mmc->clock != priv->clock) {
> +		priv->clock = mmc->clock;
> +		owl_mmc_clk_set(priv, mmc->clock);
> +	}
> +
> +	ret = clk_set_rate(&priv->clk, mmc->clock);
> +	if (IS_ERR_VALUE(ret))
> +		return ret;
> +
> +	ret = clk_enable(&priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	/* Set the Bus width */
> +	reg = readl(priv->reg_base + OWL_REG_SD_EN);
> +	reg &= ~0x03;

Use macro, not 0x03.

> +	if (mmc->bus_width == 8)
> +		reg |= OWL_SD_EN_DATAWID(2);
> +	else if (mmc->bus_width == 4)
> +		reg |= OWL_SD_EN_DATAWID(1);
> +
> +	writel(reg, priv->reg_base + OWL_REG_SD_EN);
> +
> +	return 0;
> +}
> +
> +static const struct dm_mmc_ops owl_mmc_ops = {
> +	.send_cmd       = owl_mmc_send_cmd,
> +	.set_ios        = owl_mmc_set_ios,
> +};
> +
> +static int owl_mmc_probe(struct udevice *dev)
> +{
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct owl_mmc_plat *plat = dev_get_platdata(dev);
> +	struct owl_mmc_priv *priv = dev_get_priv(dev);
> +	struct mmc_config *cfg = &plat->cfg;
> +	int bus_width, ret;
> +	fdt_addr_t addr;
> +
> +	cfg->name = dev->name;
> +	cfg->voltages = OWL_MMC_OCR;
> +	cfg->f_min = 400000;
> +	cfg->f_max = 52000000;
> +	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
> +
> +	bus_width = dev_read_u32_default(dev, "bus-width", 1);

Use mmc_of_parse(). It's generic parser.

Best Regards,
Jaehoon Chung

> +	switch (bus_width) {
> +	case 8:
> +		cfg->host_caps |= MMC_MODE_8BIT;
> +		/* Hosts capable of 8-bit transfers can also do 4 bits */
> +	case 4:
> +		cfg->host_caps |= MMC_MODE_4BIT;
> +		break;
> +	case 1:
> +		break;
> +	default:
> +		printf("Invalid bus-width value %u\n", bus_width);
> +	}
> +	cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
> +
> +	addr = dev_read_addr(dev);
> +	if (addr == FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	priv->reg_base = (void *)addr;
> +	priv->dma_drq  = 0x2;
> +
> +	ret = clk_get_by_index(dev, 0, &priv->clk);
> +	if (ret) {
> +		debug("clk_get_by_index() failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	upriv->mmc = &plat->mmc;
> +
> +	return 0;
> +}
> +
> +static int owl_mmc_bind(struct udevice *dev)
> +{
> +	struct owl_mmc_plat *plat = dev_get_platdata(dev);
> +
> +	return mmc_bind(dev, &plat->mmc, &plat->cfg);
> +}
> +
> +static const struct udevice_id owl_mmc_ids[] = {
> +	{ .compatible = "actions,s700-mmc" },
> +	{ .compatible = "actions,owl-mmc" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(owl_mmc_drv) = {
> +	.name           = "owl_mmc",
> +	.id             = UCLASS_MMC,
> +	.of_match       = owl_mmc_ids,
> +	.bind           = owl_mmc_bind,
> +	.probe          = owl_mmc_probe,
> +	.ops            = &owl_mmc_ops,
> +	.platdata_auto_alloc_size = sizeof(struct owl_mmc_plat),
> +	.priv_auto_alloc_size = sizeof(struct owl_mmc_priv),
> +};
> 

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

* [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700
  2020-12-13 10:08 ` [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700 Amit Singh Tomar
  2020-12-14  0:49   ` Jaehoon Chung
@ 2020-12-14 13:56   ` André Przywara
  2020-12-14 15:02     ` Amit Tomar
  1 sibling, 1 reply; 12+ messages in thread
From: André Przywara @ 2020-12-14 13:56 UTC (permalink / raw)
  To: u-boot

On 13/12/2020 10:08, Amit Singh Tomar wrote:
> From: Amit Singh Tomar <amittomer25@gmail.com>
> 
> This commit adds support for MMC controllers found on Actions OWL
> S700 SoC platform.
> 
> Signed-off-by: Amit Singh Tomar <amittomer25@gmail.com>
> ---
>  drivers/mmc/Kconfig   |   7 +
>  drivers/mmc/Makefile  |   1 +
>  drivers/mmc/owl_mmc.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 412 insertions(+)
>  create mode 100644 drivers/mmc/owl_mmc.c
> 
> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index 14d7913..61f9c67 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -289,6 +289,13 @@ config MMC_MXC
>  
>  	  If unsure, say N.
>  
> +config MMC_OWL
> +	bool "Actions OWL Multimedia Card Interface support"
> +	depends on ARCH_OWL && DM_MMC && BLK
> +	help
> +	  This selects the OWL SD/MMC host controller found on board
> +	  based on Actions S700 SoC.
> +
>  config MMC_MXS
>  	bool "Freescale MXS Multimedia Card Interface support"
>  	depends on MX23 || MX28 || MX6 || MX7
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index 1c849cb..f270f6c 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
>  obj-$(CONFIG_MMC_MXC)			+= mxcmmc.o
>  obj-$(CONFIG_MMC_MXS)			+= mxsmmc.o
>  obj-$(CONFIG_MMC_OCTEONTX)		+= octeontx_hsmmc.o
> +obj-$(CONFIG_MMC_OWL)			+= owl_mmc.o
>  obj-$(CONFIG_MMC_PCI)			+= pci_mmc.o
>  obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
>  obj-$(CONFIG_$(SPL_TPL_)SUPPORT_EMMC_RPMB) += rpmb.o
> diff --git a/drivers/mmc/owl_mmc.c b/drivers/mmc/owl_mmc.c
> new file mode 100644
> index 0000000..aa0ccb7
> --- /dev/null
> +++ b/drivers/mmc/owl_mmc.c
> @@ -0,0 +1,404 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2020 Amit Singh Tomar <amittomer25@gmail.com>
> + *
> + * Driver for SD/MMC controller present on Actions Semi S700 SoC, based
> + * on Linux Driver "drivers/mmc/host/owl-mmc.c".
> + *
> + * Though, there is a bit (BSEL, BUS or DMA Special Channel Selection) that
> + * controls the data transfer from SDx_DAT register either using CPU AHB Bus
> + * or DMA channel, but seems like, it only works correctly using external DMA
> + * channel, and those special bits used in this driver is picked from vendor
> + * source exclusively for MMC/SD.
> + */
> +#include <common.h>
> +#include <clk.h>
> +#include <cpu_func.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <log.h>
> +#include <mmc.h>
> +#include <asm/io.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +
> +/*
> + * SDC registers
> + */
> +#define OWL_REG_SD_EN                   0x0000
> +#define OWL_REG_SD_CTL                  0x0004
> +#define OWL_REG_SD_STATE                0x0008
> +#define OWL_REG_SD_CMD                  0x000c
> +#define OWL_REG_SD_ARG                  0x0010
> +#define OWL_REG_SD_RSPBUF0              0x0014
> +#define OWL_REG_SD_RSPBUF1              0x0018
> +#define OWL_REG_SD_RSPBUF2              0x001c
> +#define OWL_REG_SD_RSPBUF3              0x0020
> +#define OWL_REG_SD_RSPBUF4              0x0024
> +#define OWL_REG_SD_DAT                  0x0028
> +#define OWL_REG_SD_BLK_SIZE             0x002c
> +#define OWL_REG_SD_BLK_NUM              0x0030
> +#define OWL_REG_SD_BUF_SIZE             0x0034
> +
> +/* SD_EN Bits */
> +#define OWL_SD_EN_RANE                  BIT(31)
> +#define OWL_SD_EN_RESE                  BIT(10)
> +#define OWL_SD_ENABLE                   BIT(7)
> +#define OWL_SD_EN_BSEL                  BIT(6)
> +#define OWL_SD_EN_DATAWID(x)            (((x) & 0x3) << 0)
> +
> +/* SD_CTL Bits */
> +#define OWL_SD_CTL_TOUTEN               BIT(31)
> +#define OWL_SD_CTL_DELAY_MSK            GENMASK(23, 16)
> +#define OWL_SD_CTL_RDELAY(x)            (((x) & 0xf) << 20)
> +#define OWL_SD_CTL_WDELAY(x)            (((x) & 0xf) << 16)
> +#define OWL_SD_CTL_TS                   BIT(7)
> +#define OWL_SD_CTL_LBE                  BIT(6)
> +#define OWL_SD_CTL_TM(x)                (((x) & 0xf) << 0)
> +
> +#define OWL_SD_DELAY_LOW_CLK            0x0f
> +#define OWL_SD_DELAY_MID_CLK            0x0a
> +#define OWL_SD_RDELAY_HIGH		0x08
> +#define OWL_SD_WDELAY_HIGH		0x09
> +
> +/* SD_STATE Bits */
> +#define OWL_SD_STATE_CLNR               BIT(4)
> +#define OWL_SD_STATE_CRC7ER             BIT(0)
> +#define OWL_SD_STATE_DAT0S              BIT(7)
> +
> +#define OWL_MMC_OCR                     (MMC_VDD_32_33 | MMC_VDD_33_34 | \
> +					 MMC_VDD_165_195)
> +
> +#define DATA_TRANSFER_TIMEOUT		(3 * (100 * 1000))
> +
> +/*
> + * Simple DMA transfer operations defines for MMC/SD card
> + */
> +#define DMA0_CHANNEL_BASE(x)		(0xe0230100 + 0x100 * (x))
> +
> +#define DMA_MODE			0x0000
> +#define DMA_SOURCE			0x0004
> +#define DMA_DESTINATION			0x0008
> +#define DMA_FRAME_LEN			0x000C
> +#define DMA_FRAME_CNT			0x0010
> +#define DMA_START			0x0024
> +
> +/* DMAx_MODE */
> +#define DMA_MODE_ST(x)                  (((x) & 0x3) << 8)
> +#define DMA_MODE_ST_DEV                 DMA_MODE_ST(0)
> +#define DMA_MODE_DT(x)                  (((x) & 0x3) << 10)
> +#define DMA_MODE_DT_DCU                 DMA_MODE_DT(2)
> +#define DMA_MODE_SAM(x)                 (((x) & 0x3) << 16)
> +#define DMA_MODE_SAM_CONST              DMA_MODE_SAM(0)
> +#define DMA_MODE_DAM(x)                 (((x) & 0x3) << 18)
> +#define DMA_MODE_DAM_INC                DMA_MODE_DAM(1)
> +
> +
> +struct owl_mmc_plat {
> +	struct mmc_config cfg;
> +	struct mmc mmc;
> +};
> +
> +struct owl_mmc_priv {
> +	void *reg_base;
> +	struct clk clk;
> +	unsigned int clock;	  /* Current clock */
> +	unsigned int dma_drq;	  /* Trigger Source */
> +};
> +
> +static void owl_dma_config(void *dma_base, unsigned int drq,

You could pass a pointer to owl_mmc_priv here, which can hold both those
values. See below for more more on this.

> +		unsigned int src, unsigned int dst,
> +		unsigned int len)
> +{
> +	unsigned int mode = drq;
> +	u8 frame_cnt = 0x1;

Not needed, just use the 0x1 below directly.

> +
> +	/* Set Source and Destination adderess mode */
> +	mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST | DMA_MODE_DT_DCU |
> +			DMA_MODE_DAM_INC);
> +
> +	writel(mode, dma_base + DMA_MODE);
> +	writel(src, dma_base + DMA_SOURCE);
> +	writel(dst, dma_base + DMA_DESTINATION);
> +	writel(len, dma_base + DMA_FRAME_LEN);
> +	writel(frame_cnt, dma_base + DMA_FRAME_CNT);
> +}
> +
> +static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
> +		struct mmc_data *data)
> +{
> +	unsigned int reg, total;
> +	uint32_t buf = 0;
> +
> +	reg = readl(priv->reg_base + OWL_REG_SD_EN);
> +	reg |= OWL_SD_EN_BSEL;
> +	writel(reg, priv->reg_base + OWL_REG_SD_EN);
> +
> +	writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
> +	writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
> +	total = data->blocksize * data->blocks;
> +
> +	if (data->blocksize < 512)
> +		writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
> +	else
> +		writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);
> +
> +	/* DMA STOP */
> +	writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> +
> +	if (data) {
> +		if (data->flags == MMC_DATA_READ) {

Shouldn't that be used with '&', since it's one flag among others?

> +			buf = (ulong) (data->dest);
> +			owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> +					priv->dma_drq,
> +					(ulong) priv->reg_base +
> +					OWL_REG_SD_DAT,
> +					buf, total);
> +			invalidate_dcache_range(buf, buf + total);
> +		} else if (data->flags == MMC_DATA_WRITE) {
> +			buf = (ulong) (data->src);
> +			owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> +					priv->dma_drq,
> +					buf,
> +					(ulong) priv->reg_base +
> +					OWL_REG_SD_DAT,
> +					total);
> +			flush_dcache_range(buf, buf + total);
> +		}
> +		/* DMA START */
> +		writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
> +	}
> +}
> +
> +static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
> +		struct mmc_data *data)
> +{
> +	struct owl_mmc_priv *priv = dev_get_priv(dev);
> +	unsigned int cmd_rsp_mask, mode, reg;
> +	int timeout = DATA_TRANSFER_TIMEOUT;
> +
> +	reg = readl(priv->reg_base + OWL_REG_SD_EN);
> +	reg |= OWL_SD_ENABLE;
> +	writel(reg, priv->reg_base + OWL_REG_SD_EN);
> +
> +	/* setup response */
> +	switch (cmd->resp_type) {
> +	case MMC_RSP_NONE:
> +		break;
> +	case MMC_RSP_R1:
> +		if (data) {
> +			if (data->flags == MMC_DATA_READ)
> +				mode = OWL_SD_CTL_TM(4);
> +			else
> +				mode = OWL_SD_CTL_TM(5);
> +		} else {
> +			mode = OWL_SD_CTL_TM(1);
> +		}
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> +		break;
> +	case MMC_RSP_R1b:
> +		mode = OWL_SD_CTL_TM(3);
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> +		break;
> +	case MMC_RSP_R2:
> +		mode = OWL_SD_CTL_TM(2);
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> +		break;
> +	case MMC_RSP_R3:
> +		cmd->cmdarg = 0x40ff8000;
> +		mode = OWL_SD_CTL_TM(1);
> +		cmd_rsp_mask = OWL_SD_STATE_CLNR;
> +		break;
> +	default:
> +		printf("error: no math command RSP flag %x\n", cmd->cmdarg);
> +		return -1;
> +	}
> +
> +	mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
> +
> +	/* setup command */
> +	writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
> +	writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
> +
> +	/* Set LBE to send clk at the end of last read block */
> +	if (data)
> +		mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
> +	else
> +		mode |= OWL_SD_CTL_TS;
> +
> +	if (data)
> +		owl_mmc_prepare_data(priv, data);
> +
> +	/* Start transfer */
> +	writel(mode, priv->reg_base + OWL_REG_SD_CTL);
> +
> +	while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
> +			&& timeout--)
> +		udelay(20);
> +
> +	if (!timeout) {
> +		printf("error: transferred data timeout\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (cmd->resp_type & MMC_RSP_136) {
> +		cmd->response[3] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
> +		cmd->response[2] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
> +		cmd->response[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF2);
> +		cmd->response[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF3);
> +	} else {
> +		u32 rsp[2];
> +
> +		rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
> +		rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
> +		cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
> +		cmd->response[1] = rsp[1] >> 8;
> +	}
> +
> +	if (data) {
> +		/* DMA STOP */
> +		writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> +		/* Transmission STOP */
> +		while (readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
> +			clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
> +					OWL_SD_CTL_TS);
> +	}
> +
> +	return 0;
> +}
> +
> +static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
> +{
> +	u32 reg;
> +
> +	reg = readl(priv->reg_base + OWL_REG_SD_CTL);
> +	reg &= ~OWL_SD_CTL_DELAY_MSK;
> +
> +	/* Set RDELAY and WDELAY based on the clock */
> +	if (rate <= 1000000) {
> +		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
> +			OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
> +			priv->reg_base + OWL_REG_SD_CTL);
> +	} else if ((rate > 1000000) && (rate <= 26000000)) {
> +		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
> +			OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
> +			priv->reg_base + OWL_REG_SD_CTL);
> +	} else if ((rate > 26000000) && (rate <= 52000000)) {
> +		writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
> +			OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
> +			priv->reg_base + OWL_REG_SD_CTL);
> +	} else {
> +		printf("SD clock rate not supported\n");
> +	}
> +}
> +
> +static int owl_mmc_set_ios(struct udevice *dev)
> +{
> +	struct owl_mmc_priv *priv = dev_get_priv(dev);
> +	struct owl_mmc_plat *plat = dev_get_platdata(dev);
> +	struct mmc *mmc = &plat->mmc;
> +	u32 reg, ret;
> +
> +	if (mmc->clock != priv->clock) {
> +		priv->clock = mmc->clock;
> +		owl_mmc_clk_set(priv, mmc->clock);
> +	}
> +
> +	ret = clk_set_rate(&priv->clk, mmc->clock);
> +	if (IS_ERR_VALUE(ret))
> +		return ret;
> +
> +	ret = clk_enable(&priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	/* Set the Bus width */
> +	reg = readl(priv->reg_base + OWL_REG_SD_EN);
> +	reg &= ~0x03;
> +	if (mmc->bus_width == 8)
> +		reg |= OWL_SD_EN_DATAWID(2);
> +	else if (mmc->bus_width == 4)
> +		reg |= OWL_SD_EN_DATAWID(1);
> +
> +	writel(reg, priv->reg_base + OWL_REG_SD_EN);
> +
> +	return 0;
> +}
> +
> +static const struct dm_mmc_ops owl_mmc_ops = {
> +	.send_cmd       = owl_mmc_send_cmd,
> +	.set_ios        = owl_mmc_set_ios,
> +};
> +
> +static int owl_mmc_probe(struct udevice *dev)
> +{
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct owl_mmc_plat *plat = dev_get_platdata(dev);
> +	struct owl_mmc_priv *priv = dev_get_priv(dev);
> +	struct mmc_config *cfg = &plat->cfg;
> +	int bus_width, ret;
> +	fdt_addr_t addr;
> +
> +	cfg->name = dev->name;
> +	cfg->voltages = OWL_MMC_OCR;
> +	cfg->f_min = 400000;
> +	cfg->f_max = 52000000;
> +	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
> +
> +	bus_width = dev_read_u32_default(dev, "bus-width", 1);
> +	switch (bus_width) {
> +	case 8:
> +		cfg->host_caps |= MMC_MODE_8BIT;
> +		/* Hosts capable of 8-bit transfers can also do 4 bits */
> +	case 4:
> +		cfg->host_caps |= MMC_MODE_4BIT;
> +		break;
> +	case 1:
> +		break;
> +	default:
> +		printf("Invalid bus-width value %u\n", bus_width);
> +	}
> +	cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
> +
> +	addr = dev_read_addr(dev);
> +	if (addr == FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	priv->reg_base = (void *)addr;
> +	priv->dma_drq  = 0x2;
> +
> +	ret = clk_get_by_index(dev, 0, &priv->clk);
> +	if (ret) {
> +		debug("clk_get_by_index() failed: %d\n", ret);
> +		return ret;
> +	}

You should read the DMA parameters from the DT here. What you currently
hard code (channel 2) only works for the first MMC.
It should be fairly straight-forward to follow the phandle and get the
base address of the DMA device, and to read the second argument to learn
the DRQ number. Then put this into priv and use it in dma_config().

Cheers,
Andre

> +
> +	upriv->mmc = &plat->mmc;
> +
> +	return 0;
> +}
> +
> +static int owl_mmc_bind(struct udevice *dev)
> +{
> +	struct owl_mmc_plat *plat = dev_get_platdata(dev);
> +
> +	return mmc_bind(dev, &plat->mmc, &plat->cfg);
> +}
> +
> +static const struct udevice_id owl_mmc_ids[] = {
> +	{ .compatible = "actions,s700-mmc" },
> +	{ .compatible = "actions,owl-mmc" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(owl_mmc_drv) = {
> +	.name           = "owl_mmc",
> +	.id             = UCLASS_MMC,
> +	.of_match       = owl_mmc_ids,
> +	.bind           = owl_mmc_bind,
> +	.probe          = owl_mmc_probe,
> +	.ops            = &owl_mmc_ops,
> +	.platdata_auto_alloc_size = sizeof(struct owl_mmc_plat),
> +	.priv_auto_alloc_size = sizeof(struct owl_mmc_priv),
> +};
> 

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

* [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700
  2020-12-14  0:49   ` Jaehoon Chung
@ 2020-12-14 14:42     ` Amit Tomar
  0 siblings, 0 replies; 12+ messages in thread
From: Amit Tomar @ 2020-12-14 14:42 UTC (permalink / raw)
  To: u-boot

Hi Jaehoon

Thanks for having a look and providing the comments

Order to the bit number.

Ok, Sure

>
> > +
> > +#define OWL_MMC_OCR                     (MMC_VDD_32_33 | MMC_VDD_33_34
> | \
> > +                                      MMC_VDD_165_195)
> > +
> > +#define DATA_TRANSFER_TIMEOUT                (3 * (100 * 1000))
>
> Really need to set 300000?
>

I should have used the timeout value used in the Mainline Linux driver
which is 30000.
I would fix it to 30000.


>
> > +
> > +/*
> > + * Simple DMA transfer operations defines for MMC/SD card
> > + */
> > +#define DMA0_CHANNEL_BASE(x)         (0xe0230100 + 0x100 * (x))
>
> What is 0xe0230100?
>

This is the base address of external DMA channel 0 used for MMC/SD
controllers. Linux has
has separate full blown DMA driver for it but here we chose not to have
separate driver
instead use DMA bits only needed for MMC/SD.

>
> > +
> > +#define DMA_MODE                     0x0000
> > +#define DMA_SOURCE                   0x0004
> > +#define DMA_DESTINATION                      0x0008
> > +#define DMA_FRAME_LEN                        0x000C
> > +#define DMA_FRAME_CNT                        0x0010
> > +#define DMA_START                    0x0024
> > +
> > +/* DMAx_MODE */
> > +#define DMA_MODE_ST(x)                  (((x) & 0x3) << 8)
> > +#define DMA_MODE_ST_DEV                 DMA_MODE_ST(0)
> > +#define DMA_MODE_DT(x)                  (((x) & 0x3) << 10)
> > +#define DMA_MODE_DT_DCU                 DMA_MODE_DT(2)
> > +#define DMA_MODE_SAM(x)                 (((x) & 0x3) << 16)
> > +#define DMA_MODE_SAM_CONST              DMA_MODE_SAM(0)
> > +#define DMA_MODE_DAM(x)                 (((x) & 0x3) << 18)
> > +#define DMA_MODE_DAM_INC                DMA_MODE_DAM(1)
> > +
> > +
> > +struct owl_mmc_plat {
> > +     struct mmc_config cfg;
> > +     struct mmc mmc;
> > +};
> > +
> > +struct owl_mmc_priv {
> > +     void *reg_base;
> > +     struct clk clk;
> > +     unsigned int clock;       /* Current clock */
> > +     unsigned int dma_drq;     /* Trigger Source */
> > +};
> > +
> > +static void owl_dma_config(void *dma_base, unsigned int drq,
> > +             unsigned int src, unsigned int dst,
> > +             unsigned int len)
> > +{
> > +     unsigned int mode = drq;
> > +     u8 frame_cnt = 0x1;
>
> Does it need to use frame_cnt variable?
>


> Ok, would remove it and use fixed value 0x1 for it.
> > +
> > +     /* Set Source and Destination adderess mode */
> > +     mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST | DMA_MODE_DT_DCU |
> > +                     DMA_MODE_DAM_INC);
> > +
> > +     writel(mode, dma_base + DMA_MODE);
> > +     writel(src, dma_base + DMA_SOURCE);
> > +     writel(dst, dma_base + DMA_DESTINATION);
> > +     writel(len, dma_base + DMA_FRAME_LEN);
> > +     writel(frame_cnt, dma_base + DMA_FRAME_CNT);
> > +}
> > +
> > +static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
> > +             struct mmc_data *data)
> > +{
> > +     unsigned int reg, total;
> > +     uint32_t buf = 0;
> > +
> > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
> > +     reg |= OWL_SD_EN_BSEL;
> > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
> > +
> > +     writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
> > +     writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
> > +     total = data->blocksize * data->blocks;
> > +
> > +     if (data->blocksize < 512)
> > +             writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
> > +     else
> > +             writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);
>
> Is it right to set to 512?
>


> Actally, this is something picked from Linux driver as it.
>


> > +
> > +     /* DMA STOP */
> > +     writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> > +
> > +     if (data) {
> > +             if (data->flags == MMC_DATA_READ) {
> > +                     buf = (ulong) (data->dest);
> > +                     owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> > +                                     priv->dma_drq,
> > +                                     (ulong) priv->reg_base +
> > +                                     OWL_REG_SD_DAT,
> > +                                     buf, total);
> > +                     invalidate_dcache_range(buf, buf + total);
> > +             } else if (data->flags == MMC_DATA_WRITE) {
>
> Not need to use "else if", because it shoud be MMC_DATA_WRITE.
>

Ok, I would remove this "else if" statement.

>
> > +                     buf = (ulong) (data->src);
> > +                     owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> > +                                     priv->dma_drq,
> > +                                     buf,
> > +                                     (ulong) priv->reg_base +
> > +                                     OWL_REG_SD_DAT,
> > +                                     total);
> > +                     flush_dcache_range(buf, buf + total);
> > +             }
> > +             /* DMA START */
> > +             writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
> > +     }
> > +}
> > +
> > +static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
> > +             struct mmc_data *data)
> > +{
> > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
> > +     unsigned int cmd_rsp_mask, mode, reg;
> > +     int timeout = DATA_TRANSFER_TIMEOUT;
> > +
> > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
> > +     reg |= OWL_SD_ENABLE;
> > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
> > +
> > +     /* setup response */
> > +     switch (cmd->resp_type) {
> > +     case MMC_RSP_NONE:
> > +             break;
> > +     case MMC_RSP_R1:
> > +             if (data) {
> > +                     if (data->flags == MMC_DATA_READ)
> > +                             mode = OWL_SD_CTL_TM(4);
> > +                     else
> > +                             mode = OWL_SD_CTL_TM(5);
> > +             } else {
> > +                     mode = OWL_SD_CTL_TM(1);
> > +             }
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> > +             break;
> > +     case MMC_RSP_R1b:
> > +             mode = OWL_SD_CTL_TM(3);
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> > +             break;
> > +     case MMC_RSP_R2:
> > +             mode = OWL_SD_CTL_TM(2);
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> > +             break;
> > +     case MMC_RSP_R3:
> > +             cmd->cmdarg = 0x40ff8000;
>
> I don't know why set to cmdarg at here?
>

Sure, this is not needed and is a leftover from debugging.

>
> > +             mode = OWL_SD_CTL_TM(1);
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR;
> > +             break;
> > +     default:
> > +             printf("error: no math command RSP flag %x\n",
> cmd->cmdarg);
> > +             return -1;
>
> Not use -1, instead use error number.
>

Ok

>
> > +     }
> > +
> > +     mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
> > +
> > +     /* setup command */
> > +     writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
> > +     writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
> > +
> > +     /* Set LBE to send clk at the end of last read block */
> > +     if (data)
> > +             mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
> > +     else
> > +             mode |= OWL_SD_CTL_TS;
> > +
> > +     if (data)
> > +             owl_mmc_prepare_data(priv, data);
> > +
> > +     /* Start transfer */
> > +     writel(mode, priv->reg_base + OWL_REG_SD_CTL);
> > +
> > +     while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
> > +                     && timeout--)
> > +             udelay(20);
>
> Use readl_poll_timeout()?
>
> > +
> > +     if (!timeout) {
> > +             printf("error: transferred data timeout\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     if (cmd->resp_type & MMC_RSP_136) {
> > +             cmd->response[3] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF0);
> > +             cmd->response[2] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF1);
> > +             cmd->response[1] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF2);
> > +             cmd->response[0] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF3);
> > +     } else {
> > +             u32 rsp[2];
> > +
> > +             rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
> > +             rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
> > +             cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
> > +             cmd->response[1] = rsp[1] >> 8;
> > +     }
> > +
> > +     if (data) {
> > +             /* DMA STOP */
> > +             writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> > +             /* Transmission STOP */
> > +             while (readl(priv->reg_base + OWL_REG_SD_CTL) &
> OWL_SD_CTL_TS)
> > +                     clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
> > +                                     OWL_SD_CTL_TS);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
> > +{
> > +     u32 reg;
> > +
> > +     reg = readl(priv->reg_base + OWL_REG_SD_CTL);
> > +     reg &= ~OWL_SD_CTL_DELAY_MSK;
> > +
> > +     /* Set RDELAY and WDELAY based on the clock */
> > +     if (rate <= 1000000) {
> > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
> > +                     OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
> > +                     priv->reg_base + OWL_REG_SD_CTL);
> > +     } else if ((rate > 1000000) && (rate <= 26000000)) {
> > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
> > +                     OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
> > +                     priv->reg_base + OWL_REG_SD_CTL);
> > +     } else if ((rate > 26000000) && (rate <= 52000000)) {
> > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
> > +                     OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
> > +                     priv->reg_base + OWL_REG_SD_CTL);
> > +     } else {
> > +             printf("SD clock rate not supported\n");
> > +     }
>
> I think that you can make more readable than now.
>
> if (rate <= 1000000) {
>         delay = OWL_SD_DELAY_LOW_CLK;
> } else if ( ...) {
>         delay = OWL_SD_DELAY_MID_CLK;
> } else if (....) {
>         delay = OWL_SD_DELAY_HIGH_CLK;
> }
>
> writel(reg | OWL_SD_CTRL_RDELAY(delay) | OWL_SD_CTL_WDELAY(delay)...);
>

Sure, would make this change.

>
>
>
> > +}
> > +
> > +static int owl_mmc_set_ios(struct udevice *dev)
> > +{
> > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
> > +     struct owl_mmc_plat *plat = dev_get_platdata(dev);
> > +     struct mmc *mmc = &plat->mmc;
> > +     u32 reg, ret;
> > +
> > +     if (mmc->clock != priv->clock) {
> > +             priv->clock = mmc->clock;
> > +             owl_mmc_clk_set(priv, mmc->clock);
> > +     }
> > +
> > +     ret = clk_set_rate(&priv->clk, mmc->clock);
> > +     if (IS_ERR_VALUE(ret))
> > +             return ret;
> > +
> > +     ret = clk_enable(&priv->clk);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* Set the Bus width */
> > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
> > +     reg &= ~0x03;
>
> Use macro, not 0x03.
>
> > +     if (mmc->bus_width == 8)
> > +             reg |= OWL_SD_EN_DATAWID(2);
> > +     else if (mmc->bus_width == 4)
> > +             reg |= OWL_SD_EN_DATAWID(1);
> > +
> > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct dm_mmc_ops owl_mmc_ops = {
> > +     .send_cmd       = owl_mmc_send_cmd,
> > +     .set_ios        = owl_mmc_set_ios,
> > +};
> > +
> > +static int owl_mmc_probe(struct udevice *dev)
> > +{
> > +     struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> > +     struct owl_mmc_plat *plat = dev_get_platdata(dev);
> > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
> > +     struct mmc_config *cfg = &plat->cfg;
> > +     int bus_width, ret;
> > +     fdt_addr_t addr;
> > +
> > +     cfg->name = dev->name;
> > +     cfg->voltages = OWL_MMC_OCR;
> > +     cfg->f_min = 400000;
> > +     cfg->f_max = 52000000;
> > +     cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
> > +
> > +     bus_width = dev_read_u32_default(dev, "bus-width", 1);
>
> Use mmc_of_parse(). It's generic parser.
>

Ok, Sure.

>
> Best Regards,
> Jaehoon Chung
>
>
> Thanks,
Amit

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

* [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700
  2020-12-14 13:56   ` André Przywara
@ 2020-12-14 15:02     ` Amit Tomar
  2020-12-14 15:43       ` André Przywara
  0 siblings, 1 reply; 12+ messages in thread
From: Amit Tomar @ 2020-12-14 15:02 UTC (permalink / raw)
  To: u-boot

Hi,

You could pass a pointer to owl_mmc_priv here, which can hold both those

> values. See below for more more on this.
>
> Did you mean for argument 3rd and 4th but then in that case how one could
have differentiated
between source and destination.

for instance 3rd and 4th arguments are different for read and write flag.


> +             unsigned int src, unsigned int dst,
> > +             unsigned int len)
> > +{
> > +     unsigned int mode = drq;
> > +     u8 frame_cnt = 0x1;
>
> Not needed, just use the 0x1 below directly.
>

Ok.

>
> > +
> > +     /* Set Source and Destination adderess mode */
> > +     mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST | DMA_MODE_DT_DCU |
> > +                     DMA_MODE_DAM_INC);
> > +
> > +     writel(mode, dma_base + DMA_MODE);
> > +     writel(src, dma_base + DMA_SOURCE);
> > +     writel(dst, dma_base + DMA_DESTINATION);
> > +     writel(len, dma_base + DMA_FRAME_LEN);
> > +     writel(frame_cnt, dma_base + DMA_FRAME_CNT);
> > +}
> > +
> > +static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
> > +             struct mmc_data *data)
> > +{
> > +     unsigned int reg, total;
> > +     uint32_t buf = 0;
> > +
> > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
> > +     reg |= OWL_SD_EN_BSEL;
> > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
> > +
> > +     writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
> > +     writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
> > +     total = data->blocksize * data->blocks;
> > +
> > +     if (data->blocksize < 512)
> > +             writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
> > +     else
> > +             writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);
> > +
> > +     /* DMA STOP */
> > +     writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> > +
> > +     if (data) {
> > +             if (data->flags == MMC_DATA_READ) {
>
> Shouldn't that be used with '&', since it's one flag among others?
>

,Actually, I wasn't sure about some driver uses == and other one &, even
here in Mainline
U-boot.

>
> > +                     buf = (ulong) (data->dest);
> > +                     owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> > +                                     priv->dma_drq,
> > +                                     (ulong) priv->reg_base +
> > +                                     OWL_REG_SD_DAT,
> > +                                     buf, total);
> > +                     invalidate_dcache_range(buf, buf + total);
> > +             } else if (data->flags == MMC_DATA_WRITE) {
> > +                     buf = (ulong) (data->src);
> > +                     owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
> > +                                     priv->dma_drq,
> > +                                     buf,
> > +                                     (ulong) priv->reg_base +
> > +                                     OWL_REG_SD_DAT,
> > +                                     total);
> > +                     flush_dcache_range(buf, buf + total);
> > +             }
> > +             /* DMA START */
> > +             writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
> > +     }
> > +}
> > +
> > +static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
> > +             struct mmc_data *data)
> > +{
> > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
> > +     unsigned int cmd_rsp_mask, mode, reg;
> > +     int timeout = DATA_TRANSFER_TIMEOUT;
> > +
> > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
> > +     reg |= OWL_SD_ENABLE;
> > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
> > +
> > +     /* setup response */
> > +     switch (cmd->resp_type) {
> > +     case MMC_RSP_NONE:
> > +             break;
> > +     case MMC_RSP_R1:
> > +             if (data) {
> > +                     if (data->flags == MMC_DATA_READ)
> > +                             mode = OWL_SD_CTL_TM(4);
> > +                     else
> > +                             mode = OWL_SD_CTL_TM(5);
> > +             } else {
> > +                     mode = OWL_SD_CTL_TM(1);
> > +             }
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> > +             break;
> > +     case MMC_RSP_R1b:
> > +             mode = OWL_SD_CTL_TM(3);
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> > +             break;
> > +     case MMC_RSP_R2:
> > +             mode = OWL_SD_CTL_TM(2);
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
> > +             break;
> > +     case MMC_RSP_R3:
> > +             cmd->cmdarg = 0x40ff8000;
> > +             mode = OWL_SD_CTL_TM(1);
> > +             cmd_rsp_mask = OWL_SD_STATE_CLNR;
> > +             break;
> > +     default:
> > +             printf("error: no math command RSP flag %x\n",
> cmd->cmdarg);
> > +             return -1;
> > +     }
> > +
> > +     mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
> > +
> > +     /* setup command */
> > +     writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
> > +     writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
> > +
> > +     /* Set LBE to send clk at the end of last read block */
> > +     if (data)
> > +             mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
> > +     else
> > +             mode |= OWL_SD_CTL_TS;
> > +
> > +     if (data)
> > +             owl_mmc_prepare_data(priv, data);
> > +
> > +     /* Start transfer */
> > +     writel(mode, priv->reg_base + OWL_REG_SD_CTL);
> > +
> > +     while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
> > +                     && timeout--)
> > +             udelay(20);
> > +
> > +     if (!timeout) {
> > +             printf("error: transferred data timeout\n");
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     if (cmd->resp_type & MMC_RSP_136) {
> > +             cmd->response[3] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF0);
> > +             cmd->response[2] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF1);
> > +             cmd->response[1] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF2);
> > +             cmd->response[0] = readl(priv->reg_base +
> OWL_REG_SD_RSPBUF3);
> > +     } else {
> > +             u32 rsp[2];
> > +
> > +             rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
> > +             rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
> > +             cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
> > +             cmd->response[1] = rsp[1] >> 8;
> > +     }
> > +
> > +     if (data) {
> > +             /* DMA STOP */
> > +             writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
> > +             /* Transmission STOP */
> > +             while (readl(priv->reg_base + OWL_REG_SD_CTL) &
> OWL_SD_CTL_TS)
> > +                     clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
> > +                                     OWL_SD_CTL_TS);
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
> > +{
> > +     u32 reg;
> > +
> > +     reg = readl(priv->reg_base + OWL_REG_SD_CTL);
> > +     reg &= ~OWL_SD_CTL_DELAY_MSK;
> > +
> > +     /* Set RDELAY and WDELAY based on the clock */
> > +     if (rate <= 1000000) {
> > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
> > +                     OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
> > +                     priv->reg_base + OWL_REG_SD_CTL);
> > +     } else if ((rate > 1000000) && (rate <= 26000000)) {
> > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
> > +                     OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
> > +                     priv->reg_base + OWL_REG_SD_CTL);
> > +     } else if ((rate > 26000000) && (rate <= 52000000)) {
> > +             writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
> > +                     OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
> > +                     priv->reg_base + OWL_REG_SD_CTL);
> > +     } else {
> > +             printf("SD clock rate not supported\n");
> > +     }
> > +}
> > +
> > +static int owl_mmc_set_ios(struct udevice *dev)
> > +{
> > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
> > +     struct owl_mmc_plat *plat = dev_get_platdata(dev);
> > +     struct mmc *mmc = &plat->mmc;
> > +     u32 reg, ret;
> > +
> > +     if (mmc->clock != priv->clock) {
> > +             priv->clock = mmc->clock;
> > +             owl_mmc_clk_set(priv, mmc->clock);
> > +     }
> > +
> > +     ret = clk_set_rate(&priv->clk, mmc->clock);
> > +     if (IS_ERR_VALUE(ret))
> > +             return ret;
> > +
> > +     ret = clk_enable(&priv->clk);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* Set the Bus width */
> > +     reg = readl(priv->reg_base + OWL_REG_SD_EN);
> > +     reg &= ~0x03;
> > +     if (mmc->bus_width == 8)
> > +             reg |= OWL_SD_EN_DATAWID(2);
> > +     else if (mmc->bus_width == 4)
> > +             reg |= OWL_SD_EN_DATAWID(1);
> > +
> > +     writel(reg, priv->reg_base + OWL_REG_SD_EN);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct dm_mmc_ops owl_mmc_ops = {
> > +     .send_cmd       = owl_mmc_send_cmd,
> > +     .set_ios        = owl_mmc_set_ios,
> > +};
> > +
> > +static int owl_mmc_probe(struct udevice *dev)
> > +{
> > +     struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> > +     struct owl_mmc_plat *plat = dev_get_platdata(dev);
> > +     struct owl_mmc_priv *priv = dev_get_priv(dev);
> > +     struct mmc_config *cfg = &plat->cfg;
> > +     int bus_width, ret;
> > +     fdt_addr_t addr;
> > +
> > +     cfg->name = dev->name;
> > +     cfg->voltages = OWL_MMC_OCR;
> > +     cfg->f_min = 400000;
> > +     cfg->f_max = 52000000;
> > +     cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
> > +
> > +     bus_width = dev_read_u32_default(dev, "bus-width", 1);
> > +     switch (bus_width) {
> > +     case 8:
> > +             cfg->host_caps |= MMC_MODE_8BIT;
> > +             /* Hosts capable of 8-bit transfers can also do 4 bits */
> > +     case 4:
> > +             cfg->host_caps |= MMC_MODE_4BIT;
> > +             break;
> > +     case 1:
> > +             break;
> > +     default:
> > +             printf("Invalid bus-width value %u\n", bus_width);
> > +     }
> > +     cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
> > +
> > +     addr = dev_read_addr(dev);
> > +     if (addr == FDT_ADDR_T_NONE)
> > +             return -EINVAL;
> > +
> > +     priv->reg_base = (void *)addr;
> > +     priv->dma_drq  = 0x2;
> > +
> > +     ret = clk_get_by_index(dev, 0, &priv->clk);
> > +     if (ret) {
> > +             debug("clk_get_by_index() failed: %d\n", ret);
> > +             return ret;
> > +     }
>
> You should read the DMA parameters from the DT here. What you currently
> hard code (channel 2) only works for the first MMC.
> It should be fairly straight-forward to follow the phandle and get the
> base address of the DMA device, and to read the second argument to learn
> the DRQ number. Then put this into priv and use it in dma_config().
>
> Yeah, I initially thought about it but I wasn't sure that This device
> trigger value is exactly matching to
>
   DAM channel number, So I left the idea of having it.

>
> Thanks,
>
-Amit

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

* [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700
  2020-12-14 15:02     ` Amit Tomar
@ 2020-12-14 15:43       ` André Przywara
  0 siblings, 0 replies; 12+ messages in thread
From: André Przywara @ 2020-12-14 15:43 UTC (permalink / raw)
  To: u-boot

On 14/12/2020 15:02, Amit Tomar wrote:
> Hi,
> 
> You could pass a pointer to owl_mmc_priv here, which can hold both those
> 
>     values. See below for more more on this.
> 
> Did you mean for argument 3rd and 4th but then in that case how one
> could have differentiated
> between source and destination.
> 
> for instance 3rd and 4th arguments are different for read and write flag.

No, just the first two arguments (that's why I put the comment after
this line).

> 
> 
>     > +? ? ? ? ? ? ?unsigned int src, unsigned int dst,
>     > +? ? ? ? ? ? ?unsigned int len)
>     > +{
>     > +? ? ?unsigned int mode = drq;
>     > +? ? ?u8 frame_cnt = 0x1;
> 
>     Not needed, just use the 0x1 below directly.
> 
> 
> Ok.
> 
> 
>     > +
>     > +? ? ?/* Set Source and Destination adderess mode */
>     > +? ? ?mode |= (DMA_MODE_ST_DEV | DMA_MODE_SAM_CONST |
>     DMA_MODE_DT_DCU |
>     > +? ? ? ? ? ? ? ? ? ? ?DMA_MODE_DAM_INC);
>     > +
>     > +? ? ?writel(mode, dma_base + DMA_MODE);
>     > +? ? ?writel(src, dma_base + DMA_SOURCE);
>     > +? ? ?writel(dst, dma_base + DMA_DESTINATION);
>     > +? ? ?writel(len, dma_base + DMA_FRAME_LEN);
>     > +? ? ?writel(frame_cnt, dma_base + DMA_FRAME_CNT);
>     > +}
>     > +
>     > +static void owl_mmc_prepare_data(struct owl_mmc_priv *priv,
>     > +? ? ? ? ? ? ?struct mmc_data *data)
>     > +{
>     > +? ? ?unsigned int reg, total;
>     > +? ? ?uint32_t buf = 0;
>     > +
>     > +? ? ?reg = readl(priv->reg_base + OWL_REG_SD_EN);
>     > +? ? ?reg |= OWL_SD_EN_BSEL;
>     > +? ? ?writel(reg, priv->reg_base + OWL_REG_SD_EN);
>     > +
>     > +? ? ?writel(data->blocks, priv->reg_base + OWL_REG_SD_BLK_NUM);
>     > +? ? ?writel(data->blocksize, priv->reg_base + OWL_REG_SD_BLK_SIZE);
>     > +? ? ?total = data->blocksize * data->blocks;
>     > +
>     > +? ? ?if (data->blocksize < 512)
>     > +? ? ? ? ? ? ?writel(total, priv->reg_base + OWL_REG_SD_BUF_SIZE);
>     > +? ? ?else
>     > +? ? ? ? ? ? ?writel(512, priv->reg_base + OWL_REG_SD_BUF_SIZE);
>     > +
>     > +? ? ?/* DMA STOP */
>     > +? ? ?writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
>     > +
>     > +? ? ?if (data) {
>     > +? ? ? ? ? ? ?if (data->flags == MMC_DATA_READ) {
> 
>     Shouldn't that be used with '&', since it's one flag among others?
> 
> 
> ,Actually, I wasn't sure about some driver uses == and other one &, even
> here in Mainline
> U-boot.

Well, flags somewhat suggests it's a bitmap, but indeed mmc.c just
assigns a value, and "1" and "2" would work in both ways.

>     > +? ? ? ? ? ? ? ? ? ? ?buf = (ulong) (data->dest);
>     > +? ? ? ? ? ? ? ? ? ? ?owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?priv->dma_drq,
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(ulong) priv->reg_base +
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OWL_REG_SD_DAT,
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?buf, total);
>     > +? ? ? ? ? ? ? ? ? ? ?invalidate_dcache_range(buf, buf + total);
>     > +? ? ? ? ? ? ?} else if (data->flags == MMC_DATA_WRITE) {
>     > +? ? ? ? ? ? ? ? ? ? ?buf = (ulong) (data->src);
>     > +? ? ? ? ? ? ? ? ? ? ?owl_dma_config((void *) DMA0_CHANNEL_BASE(2),
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?priv->dma_drq,
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?buf,
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(ulong) priv->reg_base +
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OWL_REG_SD_DAT,
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?total);
>     > +? ? ? ? ? ? ? ? ? ? ?flush_dcache_range(buf, buf + total);
>     > +? ? ? ? ? ? ?}
>     > +? ? ? ? ? ? ?/* DMA START */
>     > +? ? ? ? ? ? ?writel(0x1, (DMA0_CHANNEL_BASE(2) + DMA_START));
>     > +? ? ?}
>     > +}
>     > +
>     > +static int owl_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>     > +? ? ? ? ? ? ?struct mmc_data *data)
>     > +{
>     > +? ? ?struct owl_mmc_priv *priv = dev_get_priv(dev);
>     > +? ? ?unsigned int cmd_rsp_mask, mode, reg;
>     > +? ? ?int timeout = DATA_TRANSFER_TIMEOUT;
>     > +
>     > +? ? ?reg = readl(priv->reg_base + OWL_REG_SD_EN);
>     > +? ? ?reg |= OWL_SD_ENABLE;
>     > +? ? ?writel(reg, priv->reg_base + OWL_REG_SD_EN);
>     > +
>     > +? ? ?/* setup response */
>     > +? ? ?switch (cmd->resp_type) {
>     > +? ? ?case MMC_RSP_NONE:
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?case MMC_RSP_R1:
>     > +? ? ? ? ? ? ?if (data) {
>     > +? ? ? ? ? ? ? ? ? ? ?if (data->flags == MMC_DATA_READ)
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ?mode = OWL_SD_CTL_TM(4);
>     > +? ? ? ? ? ? ? ? ? ? ?else
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ?mode = OWL_SD_CTL_TM(5);
>     > +? ? ? ? ? ? ?} else {
>     > +? ? ? ? ? ? ? ? ? ? ?mode = OWL_SD_CTL_TM(1);
>     > +? ? ? ? ? ? ?}
>     > +? ? ? ? ? ? ?cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?case MMC_RSP_R1b:
>     > +? ? ? ? ? ? ?mode = OWL_SD_CTL_TM(3);
>     > +? ? ? ? ? ? ?cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?case MMC_RSP_R2:
>     > +? ? ? ? ? ? ?mode = OWL_SD_CTL_TM(2);
>     > +? ? ? ? ? ? ?cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER;
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?case MMC_RSP_R3:
>     > +? ? ? ? ? ? ?cmd->cmdarg = 0x40ff8000;
>     > +? ? ? ? ? ? ?mode = OWL_SD_CTL_TM(1);
>     > +? ? ? ? ? ? ?cmd_rsp_mask = OWL_SD_STATE_CLNR;
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?default:
>     > +? ? ? ? ? ? ?printf("error: no math command RSP flag %x\n",
>     cmd->cmdarg);
>     > +? ? ? ? ? ? ?return -1;
>     > +? ? ?}
>     > +
>     > +? ? ?mode |= (readl(priv->reg_base + OWL_REG_SD_CTL) & (0xff << 16));
>     > +
>     > +? ? ?/* setup command */
>     > +? ? ?writel(cmd->cmdidx, priv->reg_base + OWL_REG_SD_CMD);
>     > +? ? ?writel(cmd->cmdarg, priv->reg_base + OWL_REG_SD_ARG);
>     > +
>     > +? ? ?/* Set LBE to send clk at the end of last read block */
>     > +? ? ?if (data)
>     > +? ? ? ? ? ? ?mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0xE4000000);
>     > +? ? ?else
>     > +? ? ? ? ? ? ?mode |= OWL_SD_CTL_TS;
>     > +
>     > +? ? ?if (data)
>     > +? ? ? ? ? ? ?owl_mmc_prepare_data(priv, data);
>     > +
>     > +? ? ?/* Start transfer */
>     > +? ? ?writel(mode, priv->reg_base + OWL_REG_SD_CTL);
>     > +
>     > +? ? ?while ((readl(priv->reg_base + OWL_REG_SD_CTL) & OWL_SD_CTL_TS)
>     > +? ? ? ? ? ? ? ? ? ? ?&& timeout--)
>     > +? ? ? ? ? ? ?udelay(20);
>     > +
>     > +? ? ?if (!timeout) {
>     > +? ? ? ? ? ? ?printf("error: transferred data timeout\n");
>     > +? ? ? ? ? ? ?return -ETIMEDOUT;
>     > +? ? ?}
>     > +
>     > +? ? ?if (cmd->resp_type & MMC_RSP_136) {
>     > +? ? ? ? ? ? ?cmd->response[3] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF0);
>     > +? ? ? ? ? ? ?cmd->response[2] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF1);
>     > +? ? ? ? ? ? ?cmd->response[1] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF2);
>     > +? ? ? ? ? ? ?cmd->response[0] = readl(priv->reg_base +
>     OWL_REG_SD_RSPBUF3);
>     > +? ? ?} else {
>     > +? ? ? ? ? ? ?u32 rsp[2];
>     > +
>     > +? ? ? ? ? ? ?rsp[0] = readl(priv->reg_base + OWL_REG_SD_RSPBUF0);
>     > +? ? ? ? ? ? ?rsp[1] = readl(priv->reg_base + OWL_REG_SD_RSPBUF1);
>     > +? ? ? ? ? ? ?cmd->response[0] = rsp[1] << 24 | rsp[0] >> 8;
>     > +? ? ? ? ? ? ?cmd->response[1] = rsp[1] >> 8;
>     > +? ? ?}
>     > +
>     > +? ? ?if (data) {
>     > +? ? ? ? ? ? ?/* DMA STOP */
>     > +? ? ? ? ? ? ?writel(0x0, (DMA0_CHANNEL_BASE(2) + DMA_START));
>     > +? ? ? ? ? ? ?/* Transmission STOP */
>     > +? ? ? ? ? ? ?while (readl(priv->reg_base + OWL_REG_SD_CTL) &
>     OWL_SD_CTL_TS)
>     > +? ? ? ? ? ? ? ? ? ? ?clrbits_le32(priv->reg_base + OWL_REG_SD_CTL,
>     > +? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OWL_SD_CTL_TS);
>     > +? ? ?}
>     > +
>     > +? ? ?return 0;
>     > +}
>     > +
>     > +static void owl_mmc_clk_set(struct owl_mmc_priv *priv, int rate)
>     > +{
>     > +? ? ?u32 reg;
>     > +
>     > +? ? ?reg = readl(priv->reg_base + OWL_REG_SD_CTL);
>     > +? ? ?reg &= ~OWL_SD_CTL_DELAY_MSK;
>     > +
>     > +? ? ?/* Set RDELAY and WDELAY based on the clock */
>     > +? ? ?if (rate <= 1000000) {
>     > +? ? ? ? ? ? ?writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) |
>     > +? ? ? ? ? ? ? ? ? ? ?OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK),
>     > +? ? ? ? ? ? ? ? ? ? ?priv->reg_base + OWL_REG_SD_CTL);
>     > +? ? ?} else if ((rate > 1000000) && (rate <= 26000000)) {
>     > +? ? ? ? ? ? ?writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) |
>     > +? ? ? ? ? ? ? ? ? ? ?OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK),
>     > +? ? ? ? ? ? ? ? ? ? ?priv->reg_base + OWL_REG_SD_CTL);
>     > +? ? ?} else if ((rate > 26000000) && (rate <= 52000000)) {
>     > +? ? ? ? ? ? ?writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_HIGH) |
>     > +? ? ? ? ? ? ? ? ? ? ?OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_HIGH),
>     > +? ? ? ? ? ? ? ? ? ? ?priv->reg_base + OWL_REG_SD_CTL);
>     > +? ? ?} else {
>     > +? ? ? ? ? ? ?printf("SD clock rate not supported\n");
>     > +? ? ?}
>     > +}
>     > +
>     > +static int owl_mmc_set_ios(struct udevice *dev)
>     > +{
>     > +? ? ?struct owl_mmc_priv *priv = dev_get_priv(dev);
>     > +? ? ?struct owl_mmc_plat *plat = dev_get_platdata(dev);
>     > +? ? ?struct mmc *mmc = &plat->mmc;
>     > +? ? ?u32 reg, ret;
>     > +
>     > +? ? ?if (mmc->clock != priv->clock) {
>     > +? ? ? ? ? ? ?priv->clock = mmc->clock;
>     > +? ? ? ? ? ? ?owl_mmc_clk_set(priv, mmc->clock);
>     > +? ? ?}
>     > +
>     > +? ? ?ret = clk_set_rate(&priv->clk, mmc->clock);
>     > +? ? ?if (IS_ERR_VALUE(ret))
>     > +? ? ? ? ? ? ?return ret;
>     > +
>     > +? ? ?ret = clk_enable(&priv->clk);
>     > +? ? ?if (ret)
>     > +? ? ? ? ? ? ?return ret;
>     > +
>     > +? ? ?/* Set the Bus width */
>     > +? ? ?reg = readl(priv->reg_base + OWL_REG_SD_EN);
>     > +? ? ?reg &= ~0x03;
>     > +? ? ?if (mmc->bus_width == 8)
>     > +? ? ? ? ? ? ?reg |= OWL_SD_EN_DATAWID(2);
>     > +? ? ?else if (mmc->bus_width == 4)
>     > +? ? ? ? ? ? ?reg |= OWL_SD_EN_DATAWID(1);
>     > +
>     > +? ? ?writel(reg, priv->reg_base + OWL_REG_SD_EN);
>     > +
>     > +? ? ?return 0;
>     > +}
>     > +
>     > +static const struct dm_mmc_ops owl_mmc_ops = {
>     > +? ? ?.send_cmd? ? ? ?= owl_mmc_send_cmd,
>     > +? ? ?.set_ios? ? ? ? = owl_mmc_set_ios,
>     > +};
>     > +
>     > +static int owl_mmc_probe(struct udevice *dev)
>     > +{
>     > +? ? ?struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>     > +? ? ?struct owl_mmc_plat *plat = dev_get_platdata(dev);
>     > +? ? ?struct owl_mmc_priv *priv = dev_get_priv(dev);
>     > +? ? ?struct mmc_config *cfg = &plat->cfg;
>     > +? ? ?int bus_width, ret;
>     > +? ? ?fdt_addr_t addr;
>     > +
>     > +? ? ?cfg->name = dev->name;
>     > +? ? ?cfg->voltages = OWL_MMC_OCR;
>     > +? ? ?cfg->f_min = 400000;
>     > +? ? ?cfg->f_max = 52000000;
>     > +? ? ?cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>     > +
>     > +? ? ?bus_width = dev_read_u32_default(dev, "bus-width", 1);
>     > +? ? ?switch (bus_width) {
>     > +? ? ?case 8:
>     > +? ? ? ? ? ? ?cfg->host_caps |= MMC_MODE_8BIT;
>     > +? ? ? ? ? ? ?/* Hosts capable of 8-bit transfers can also do 4
>     bits */
>     > +? ? ?case 4:
>     > +? ? ? ? ? ? ?cfg->host_caps |= MMC_MODE_4BIT;
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?case 1:
>     > +? ? ? ? ? ? ?break;
>     > +? ? ?default:
>     > +? ? ? ? ? ? ?printf("Invalid bus-width value %u\n", bus_width);
>     > +? ? ?}
>     > +? ? ?cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
>     > +
>     > +? ? ?addr = dev_read_addr(dev);
>     > +? ? ?if (addr == FDT_ADDR_T_NONE)
>     > +? ? ? ? ? ? ?return -EINVAL;
>     > +
>     > +? ? ?priv->reg_base = (void *)addr;
>     > +? ? ?priv->dma_drq? = 0x2;
>     > +
>     > +? ? ?ret = clk_get_by_index(dev, 0, &priv->clk);
>     > +? ? ?if (ret) {
>     > +? ? ? ? ? ? ?debug("clk_get_by_index() failed: %d\n", ret);
>     > +? ? ? ? ? ? ?return ret;
>     > +? ? ?}
> 
>     You should read the DMA parameters from the DT here. What you currently
>     hard code (channel 2) only works for the first MMC.
>     It should be fairly straight-forward to follow the phandle and get the
>     base address of the DMA device, and to read the second argument to learn
>     the DRQ number. Then put this into priv and use it in dma_config().
> 
>     Yeah, I initially thought about it but I wasn't sure that This
>     device trigger value is exactly matching to
> 
> ?? DAM channel number, So I left the idea of having it.

You should be able to verify this in in the Linux code.

Cheers,
Andre

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

end of thread, other threads:[~2020-12-14 15:43 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-13 10:08 [PATCH 0/6] Add MMC/SD support for S700 Amit Singh Tomar
2020-12-13 10:08 ` [PATCH 1/6] clk: actions: Introduce dummy get/set_rate callbacks Amit Singh Tomar
2020-12-13 10:08 ` [PATCH 2/6] clk: actions: Add SD/MMC clocks Amit Singh Tomar
2020-12-13 10:08 ` [PATCH 3/6] ARM: dts: sync Actions Semi S700 DT from Linux 5.10-rc7 Amit Singh Tomar
2020-12-13 10:08 ` [PATCH 4/6] ARM: dts: s700: add MMC/SD controller node Amit Singh Tomar
2020-12-13 10:08 ` [PATCH 5/6] mmc: actions: add MMC driver for Actions OWL S700 Amit Singh Tomar
2020-12-14  0:49   ` Jaehoon Chung
2020-12-14 14:42     ` Amit Tomar
2020-12-14 13:56   ` André Przywara
2020-12-14 15:02     ` Amit Tomar
2020-12-14 15:43       ` André Przywara
2020-12-13 10:08 ` [PATCH 6/6] configs: Enable mmc support Amit Singh Tomar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.