Skip to contentSkip to conversation

Add support for Amlogic Meson MMC controller

PATCH New Carlo Caione 5 participants 14 messages May 12, 2016 – Aug 7, 2016

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover Add support for Amlogic Meson MMC controller

From: Carlo Caione <[email protected]>

This patchset is a port / rewrite of the Amlogic driver shipped in the SDK for the Meson GXBB (S905) platform. It has been tested on the Hardkernel Odroid-C2 board.

The driver is enabled using U_BOOT_DEVICE in the board file because there is no MMC driver yet in the linux kernel.

This patchset depends on the basic support for Amlogic S905 SoCs written by Beniamino[1]

[1] https://www.mail-archive.com/u-boot at lists.denx.de/msg212188.html

Carlo Caione (2):
mmc: Add Amlogic Meson driver
arm: amlogic: Enable MMC driver on Odroid-C2

 arch/arm/include/asm/arch-meson/sd_emmc.h | 109 +++++++++++
 board/hardkernel/odroid-c2/odroid-c2.c    |  42 ++++
 configs/odroid-c2_defconfig               |   5 +
 drivers/mmc/Makefile                      |   1 +
 drivers/mmc/meson_mmc.c                   | 305 ++++++++++++++++++++++++++++++
 include/configs/odroid-c2.h               |   7 +
 6 files changed, 469 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
 create mode 100644 drivers/mmc/meson_mmc.c
Signature · 1 line
2.7.4
#1 lore raw

PATCH 1/2 mmc: Add Amlogic Meson driver

From: Carlo Caione <[email protected]>

This is a port / rewrite of the Amlogic driver shipped whithin the Amlogic SDK for the Meson GXBaby (S905) SoC.

---
 arch/arm/include/asm/arch-meson/sd_emmc.h | 109 +++++++++++
 drivers/mmc/Makefile                      |   1 +
 drivers/mmc/meson_mmc.c                   | 305 ++++++++++++++++++++++++++++++
 3 files changed, 415 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
 create mode 100644 drivers/mmc/meson_mmc.c
arch/arm/include/asm/arch-meson/sd_emmc.h new file +109 -0
new file mode 100644
index 0000000..6781dca
@@ -0,0 +1,109 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __SD_EMMC_H__
+#define __SD_EMMC_H__
+
+#include <mmc.h>
+
+#define SDIO_PORT_A			0
+#define SDIO_PORT_B			1
+#define SDIO_PORT_C			2
+
+#define SD_EMMC_BASE_A			0xd0070000
+#define SD_EMMC_BASE_B			0xd0072000
+#define SD_EMMC_BASE_C			0xd0074000
+
+#define SD_IRQ_ALL			0x3fff
+
+#define SD_EMMC_CLKSRC_24M		24000000
+#define SD_EMMC_CLKSRC_DIV2		1000000000
+
+#define CLK_DIV				0
+#define CLK_SRC				6
+#define CLK_CO_PHASE			8
+#define CLK_ALWAYS_ON			24
+
+#define ADDR_USE_PING_BUF		BIT(1)
+
+#define SD_EMMC_RXD_ERROR		BIT(0)
+#define SD_EMMC_TXD_ERROR		BIT(1)
+#define SD_EMMC_DESC_ERROR		BIT(2)
+#define SD_EMMC_RESP_CRC_ERROR		BIT(3)
+#define SD_EMMC_RESP_TIMEOUT_ERROR	BIT(4)
+#define SD_EMMC_DESC_TIMEOUT_ERROR	BIT(5)
+
+#define CFG_BUS_WIDTH			0
+#define CFG_BUS_WIDTH_MASK		(0x3 << 0)
+#define CFG_BL_LEN			4
+#define CFG_BL_LEN_MASK			(0xf << 4)
+#define CFG_RESP_TIMEOUT		8
+#define CFG_RESP_TIMEOUT_MASK		(0xf << 8)
+#define CFG_RC_CC			12
+#define CFG_RC_CC_MASK			(0xf << 12)
+
+#define STATUS_RXD_ERR_MASK		0xff
+#define STATUS_TXD_ERR			BIT(8)
+#define STATUS_DESC_ERR			BIT(9)
+#define STATUS_RESP_ERR			BIT(10)
+#define STATUS_RESP_TIMEOUT		BIT(11)
+#define STATUS_DESC_TIMEOUT		BIT(12)
+#define STATUS_END_OF_CHAIN		BIT(13)
+
+#define CMD_CFG_LENGTH_MASK		0x1ff
+#define CMD_CFG_CMD_INDEX		24
+#define CMD_CFG_BLOCK_MODE		BIT(9)
+#define CMD_CFG_R1B			BIT(10)
+#define CMD_CFG_END_OF_CHAIN		BIT(11)
+#define CMD_CFG_NO_RESP			BIT(16)
+#define CMD_CFG_DATA_IO			BIT(18)
+#define CMD_CFG_DATA_WR			BIT(19)
+#define CMD_CFG_RESP_NOCRC		BIT(20)
+#define CMD_CFG_RESP_128		BIT(21)
+#define CMD_CFG_OWNER			BIT(31)
+
+struct meson_mmc_regs {
+	uint32_t gclock;
+	uint32_t gdelay;
+	uint32_t gadjust;
+	uint32_t reserved_0c;
+	uint32_t gcalout;
+	uint32_t reserved_14[11];
+	uint32_t gstart;
+	uint32_t gcfg;
+	uint32_t gstatus;
+	uint32_t girq_en;
+	uint32_t gcmd_cfg;
+	uint32_t gcmd_arg;
+	uint32_t gcmd_dat;
+	uint32_t gcmd_rsp0;
+	uint32_t gcmd_rsp1;
+	uint32_t gcmd_rsp2;
+	uint32_t gcmd_rsp3;
+	uint32_t reserved_6c;
+	uint32_t gcurr_cfg;
+	uint32_t gcurr_arg;
+	uint32_t gcurr_dat;
+	uint32_t gcurr_rsp;
+	uint32_t gnext_cfg;
+	uint32_t gnext_arg;
+	uint32_t gnext_dat;
+	uint32_t gnext_rsp;
+	uint32_t grxd;
+	uint32_t gtxd;
+	uint32_t reserved_98[90];
+	uint32_t gdesc[128];
+	uint32_t gping[128];
+	uint32_t gpong[128];
+};
+
+struct meson_mmc_platdata {
+	struct mmc_config cfg;
+	struct meson_mmc_regs *sd_emmc_reg;
+	char *w_buf;
+};
+
+#endif
drivers/mmc/Makefile +1 -0
index 585aaf3..08ac9ba 100644
@@ -21,6 +21,7 @@ obj-$(CONFIG_FTSDC021) += ftsdc021_sdhci.o
 obj-$(CONFIG_GENERIC_MMC) += mmc.o
 obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
 obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o
+obj-$(CONFIG_MMC_MESON) += meson_mmc.o
 obj-$(CONFIG_MMC_SPI) += mmc_spi.o
 obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o
 obj-$(CONFIG_MV_SDHCI) += mv_sdhci.o
drivers/mmc/meson_mmc.c new file +305 -0

305 lines not on this page · raw message

Signature · 1 line
2.7.4
maintainer
#2 lore raw

PATCH 1/2 mmc: Add Amlogic Meson driver

Quoted 6 lines · Carlo Caione

From: Carlo Caione <[email protected]>

This is a port / rewrite of the Amlogic driver shipped whithin the Amlogic SDK for the Meson GXBaby (S905) SoC.

[...]

drivers/mmc/meson_mmc.c line 295

Quoted 3 lines
drivers/mmc/meson_mmc.c new file +3 -0
+static const struct udevice_id meson_mmc_match[] = {
+	{ .compatible = "amlogic,meson-mmc" },
+	{ /* sentinel */ }

IIUC, this controller is different between meson8* and gxbb. If that's the case, this compatible should probably be more specific.

Kevin

#3 lore raw

PATCH 1/2 mmc: Add Amlogic Meson driver

Quoted 9 lines · Kevin Hilman
Quoted 6 lines · Carlo Caione

From: Carlo Caione <[email protected]>

This is a port / rewrite of the Amlogic driver shipped whithin the Amlogic SDK for the Meson GXBaby (S905) SoC.

Quoted 8 lines

[...]

drivers/mmc/meson_mmc.c line 295

Quoted 3 lines
drivers/mmc/meson_mmc.c new file +3 -0
+static const struct udevice_id meson_mmc_match[] = {
+     { .compatible = "amlogic,meson-mmc" },
+     { /* sentinel */ }

IIUC, this controller is different between meson8* and gxbb. If that's the case, this compatible should probably be more specific.

Yes, this is a point I raised also on lkml when I submitted the MMC driver for the Meson8b.
Since the registers mapping is different I assume that this is the case. Just to be sure, given that you are the only one with a datasheet for the GXBB, can you confirm that?

Actually for the same reason I was in doubt if CONFIG_MMC_MESON was a right naming choice here, but since it is really unlikely that we will ever see a U-Boot port for the Meson8b I stuck with that.

Cheers,

Signature · 1 line
Carlo Caione
maintainer
#4 lore raw

PATCH 1/2 mmc: Add Amlogic Meson driver

Quoted 16 lines · Carlo Caione
Quoted 15 lines · Kevin Hilman
Quoted 6 lines · Carlo Caione

From: Carlo Caione <[email protected]>

This is a port / rewrite of the Amlogic driver shipped whithin the Amlogic SDK for the Meson GXBaby (S905) SoC.

[...]

Quoted 3 lines
drivers/mmc/meson_mmc.c new file +3 -0
+static const struct udevice_id meson_mmc_match[] = {
+     { .compatible = "amlogic,meson-mmc" },
+     { /* sentinel */ }
Quoted 8 lines
Quoted 2 lines

IIUC, this controller is different between meson8* and gxbb. If that's the case, this compatible should probably be more specific.

Yes, this is a point I raised also on lkml when I submitted the MMC driver for the Meson8b.
Since the registers mapping is different I assume that this is the case. Just to be sure, given that you are the only one with a datasheet for the GXBB, can you confirm that?

I took a quick look and just compared the register layouts between s805 and s905 and indeed they are quite different.

Kevin

#5 lore raw

PATCH 1/2 mmc: Add Amlogic Meson driver

In reply to Carlo Caione

Am 13.05.2016 um 15:53 schrieb Carlo Caione:

Quoted 2 lines

[...] but since it is really unlikely that we will ever see a U-Boot port for the Meson8b I stuck with that.

Why do you consider that so unlikely? Please don't make such assumptions for design decisions.

Regards,
Andreas

Signature · 3 lines
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
#6 lore raw

PATCH 1/2 mmc: Add Amlogic Meson driver

In reply to Carlo Caione

Hi Carlo

Quoted 4 lines · Carlo Caione

From: Carlo Caione <[email protected]>

This is a port / rewrite of the Amlogic driver shipped whithin the Amlogic SDK for the Meson GXBaby (S905) SoC.

s/whithin/within

And If you want to apply this patch, could you resend the patch on lates u-boot?

Quoted 29 lines
---
 arch/arm/include/asm/arch-meson/sd_emmc.h | 109 +++++++++++
 drivers/mmc/Makefile                      |   1 +
 drivers/mmc/meson_mmc.c                   | 305 ++++++++++++++++++++++++++++++
 3 files changed, 415 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
 create mode 100644 drivers/mmc/meson_mmc.c
arch/arm/include/asm/arch-meson/sd_emmc.h new file +13 -0
new file mode 100644
index 0000000..6781dca
@@ -0,0 +1,109 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __SD_EMMC_H__
+#define __SD_EMMC_H__
+
+#include <mmc.h>
+
+#define SDIO_PORT_A			0
+#define SDIO_PORT_B			1

arch/arm/include/asm/arch-meson/sd_emmc.h line 23

Quoted 10 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +10 -0
+#define SDIO_PORT_C			2
+
+#define SD_EMMC_BASE_A			0xd0070000
+#define SD_EMMC_BASE_B			0xd0072000
+#define SD_EMMC_BASE_C			0xd0074000
+
+#define SD_IRQ_ALL			0x3fff
+
+#define SD_EMMC_CLKSRC_24M		24000000
+#define SD_EMMC_CLKSRC_DIV2		1000000000

Is it right? 1GHz?

arch/arm/include/asm/arch-meson/sd_emmc.h line 28

Quoted 5 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +5 -0
+
+#define CLK_DIV				0
+#define CLK_SRC				6
+#define CLK_CO_PHASE			8
+#define CLK_ALWAYS_ON			24

Are these for shifting?

Quoted 28 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +28 -0
+
+#define ADDR_USE_PING_BUF		BIT(1)
+
+#define SD_EMMC_RXD_ERROR		BIT(0)
+#define SD_EMMC_TXD_ERROR		BIT(1)
+#define SD_EMMC_DESC_ERROR		BIT(2)
+#define SD_EMMC_RESP_CRC_ERROR		BIT(3)
+#define SD_EMMC_RESP_TIMEOUT_ERROR	BIT(4)
+#define SD_EMMC_DESC_TIMEOUT_ERROR	BIT(5)
+
+#define CFG_BUS_WIDTH			0
+#define CFG_BUS_WIDTH_MASK		(0x3 << 0)
+#define CFG_BL_LEN			4
+#define CFG_BL_LEN_MASK			(0xf << 4)
+#define CFG_RESP_TIMEOUT		8
+#define CFG_RESP_TIMEOUT_MASK		(0xf << 8)
+#define CFG_RC_CC			12
+#define CFG_RC_CC_MASK			(0xf << 12)
+
+#define STATUS_RXD_ERR_MASK		0xff
+#define STATUS_TXD_ERR			BIT(8)
+#define STATUS_DESC_ERR			BIT(9)
+#define STATUS_RESP_ERR			BIT(10)
+#define STATUS_RESP_TIMEOUT		BIT(11)
+#define STATUS_DESC_TIMEOUT		BIT(12)
+#define STATUS_END_OF_CHAIN		BIT(13)
+
+#define CMD_CFG_LENGTH_MASK		0x1ff

arch/arm/include/asm/arch-meson/sd_emmc.h line 66

Quoted 10 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +10 -0
+#define CMD_CFG_CMD_INDEX		24
+#define CMD_CFG_BLOCK_MODE		BIT(9)
+#define CMD_CFG_R1B			BIT(10)
+#define CMD_CFG_END_OF_CHAIN		BIT(11)
+#define CMD_CFG_NO_RESP			BIT(16)
+#define CMD_CFG_DATA_IO			BIT(18)
+#define CMD_CFG_DATA_WR			BIT(19)
+#define CMD_CFG_RESP_NOCRC		BIT(20)
+#define CMD_CFG_RESP_128		BIT(21)
+#define CMD_CFG_OWNER			BIT(31)

Add the description for which register.
e,g
/* GCLOCK register */
#define ...

Quoted 25 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +25 -0
+
+struct meson_mmc_regs {
+	uint32_t gclock;
+	uint32_t gdelay;
+	uint32_t gadjust;
+	uint32_t reserved_0c;
+	uint32_t gcalout;
+	uint32_t reserved_14[11];
+	uint32_t gstart;
+	uint32_t gcfg;
+	uint32_t gstatus;
+	uint32_t girq_en;
+	uint32_t gcmd_cfg;
+	uint32_t gcmd_arg;
+	uint32_t gcmd_dat;
+	uint32_t gcmd_rsp0;
+	uint32_t gcmd_rsp1;
+	uint32_t gcmd_rsp2;
+	uint32_t gcmd_rsp3;
+	uint32_t reserved_6c;
+	uint32_t gcurr_cfg;
+	uint32_t gcurr_arg;
+	uint32_t gcurr_dat;
+	uint32_t gcurr_rsp;
+	uint32_t gnext_cfg;

arch/arm/include/asm/arch-meson/sd_emmc.h line 101

Quoted 10 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +10 -0
+	uint32_t gnext_arg;
+	uint32_t gnext_dat;
+	uint32_t gnext_rsp;
+	uint32_t grxd;
+	uint32_t gtxd;
+	uint32_t reserved_98[90];
+	uint32_t gdesc[128];
+	uint32_t gping[128];
+	uint32_t gpong[128];
+};

Don't use this style..Use the define.
It's difficult to debug..devloper don't know which offset is used before calculating. And add the descriptions..

Quoted 49 lines
arch/arm/include/asm/arch-meson/sd_emmc.h new file +8 -0
+
+struct meson_mmc_platdata {
+	struct mmc_config cfg;
+	struct meson_mmc_regs *sd_emmc_reg;
+	char *w_buf;
+};
+
+#endif
drivers/mmc/Makefile +1 -0
index 585aaf3..08ac9ba 100644
@@ -21,6 +21,7 @@ obj-$(CONFIG_FTSDC021) += ftsdc021_sdhci.o
 obj-$(CONFIG_GENERIC_MMC) += mmc.o
 obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
 obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o
+obj-$(CONFIG_MMC_MESON) += meson_mmc.o
 obj-$(CONFIG_MMC_SPI) += mmc_spi.o
 obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o
 obj-$(CONFIG_MV_SDHCI) += mv_sdhci.o
drivers/mmc/meson_mmc.c new file +23 -0
new file mode 100644
index 0000000..af224ec
@@ -0,0 +1,305 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <asm/arch/sd_emmc.h>
+#include <dm/device.h>
+
+static void meson_mmc_config_clock(struct mmc *mmc,
+				   struct meson_mmc_regs *meson_mmc_reg)
+{
+	uint32_t meson_mmc_clk = 0;
+	unsigned int clk, clk_src, clk_div;
+
+	if (mmc->clock > 12000000) {
+		clk = SD_EMMC_CLKSRC_DIV2;
+		clk_src = 1;

drivers/mmc/meson_mmc.c line 33

Quoted 10 lines
drivers/mmc/meson_mmc.c new file +10 -0
+	} else {
+		clk = SD_EMMC_CLKSRC_24M;
+		clk_src = 0;
+	}
+	clk_div = clk / mmc->clock;
+
+	if (mmc->clock < mmc->cfg->f_min)
+		mmc->clock = mmc->cfg->f_min;
+	if (mmc->clock > mmc->cfg->f_max)
+		mmc->clock = mmc->cfg->f_max;

In mmc_set_clock(), mmc->clock is already assigned.. Doesn't need this.

drivers/mmc/meson_mmc.c line 39

Quoted 6 lines
drivers/mmc/meson_mmc.c new file +6 -0
+
+	/* Keep the clock always on */
+	meson_mmc_clk |= (1 << CLK_ALWAYS_ON);
+
+	/* 180 phase */
+	meson_mmc_clk |= (2 << CLK_CO_PHASE);

Don't use the magic number, what do 1 and 2 mean?

drivers/mmc/meson_mmc.c line 43

Quoted 4 lines
drivers/mmc/meson_mmc.c new file +4 -0
+
+	/* clock settings */
+	meson_mmc_clk |= (clk_src << CLK_SRC);
+	meson_mmc_clk |= (clk_div << CLK_DIV);

How many bits do use for clk_div? bit[0:11]?

Quoted 28 lines
drivers/mmc/meson_mmc.c new file +28 -0
+
+	writel(meson_mmc_clk, &meson_mmc_reg->gclock);
+}
+
+static void meson_mmc_set_ios(struct mmc *mmc)
+{
+	struct meson_mmc_platdata *pdata;
+	struct meson_mmc_regs *meson_mmc_reg;
+	unsigned int bus_width;
+	uint32_t meson_mmc_cfg = 0;
+
+	pdata = mmc->priv;
+	meson_mmc_reg = pdata->sd_emmc_reg;
+
+	meson_mmc_config_clock(mmc, meson_mmc_reg);
+
+	meson_mmc_cfg = readl(&meson_mmc_reg->gcfg);
+
+	if (mmc->bus_width == 1)
+		bus_width = 0;
+	else
+		bus_width = mmc->bus_width / 4;
+
+	/* 1-bit mode */
+	meson_mmc_cfg &= ~CFG_BUS_WIDTH_MASK;
+	meson_mmc_cfg |= (bus_width << CFG_BUS_WIDTH);
+
+	/* 512 bytes block lenght */

drivers/mmc/meson_mmc.c line 81

Quoted 10 lines
drivers/mmc/meson_mmc.c new file +10 -0
+	meson_mmc_cfg &= ~CFG_BL_LEN_MASK;
+	meson_mmc_cfg |= (9 << CFG_BL_LEN);
+
+	/* Response timeout 256 clk */
+	meson_mmc_cfg &= ~CFG_RESP_TIMEOUT_MASK;
+	meson_mmc_cfg |= (7 << CFG_RESP_TIMEOUT);
+
+	/* Command-command gap 1024 clk */
+	meson_mmc_cfg &= ~CFG_RC_CC_MASK;
+	meson_mmc_cfg |= (4 << CFG_RC_CC);

Ditto...what are 9, 7, 4?
Don't use the magic number in entire your codes.

drivers/mmc/meson_mmc.c line 85

Quoted 4 lines
drivers/mmc/meson_mmc.c new file +4 -0
+
+	writel(meson_mmc_cfg, &meson_mmc_reg->gcfg);
+
+	return;

not needs "return"

Quoted 13 lines
drivers/mmc/meson_mmc.c new file +13 -0
+}
+
+static int meson_mmc_init(struct mmc *mmc)
+{
+	mmc_set_clock(mmc, 400000);
+
+	return 0;
+}
+
+static void meson_mmc_setup_cmd(struct mmc *mmc, struct mmc_data *data,
+				struct mmc_cmd *cmd,
+				struct meson_mmc_regs *meson_mmc_reg)
+{

drivers/mmc/meson_mmc.c line 108

Quoted 10 lines
drivers/mmc/meson_mmc.c new file +10 -0
+	uint32_t meson_mmc_cmd = 0;
+
+	meson_mmc_cmd = ((0x80 | cmd->cmdidx) << CMD_CFG_CMD_INDEX);
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136)
+			meson_mmc_cmd |= CMD_CFG_RESP_128;
+
+		if (cmd->resp_type & MMC_RSP_BUSY)
+			meson_mmc_cmd |= CMD_CFG_R1B;

There is no case with MMC_RSP_136 and MMC_RSP_BUSY together.

Quoted 43 lines
drivers/mmc/meson_mmc.c new file +43 -0
+
+		if (!(cmd->resp_type & MMC_RSP_CRC))
+			meson_mmc_cmd |= CMD_CFG_RESP_NOCRC;
+	} else {
+		meson_mmc_cmd |= CMD_CFG_NO_RESP;
+	}
+
+	if (data) {
+		meson_mmc_cmd |= CMD_CFG_DATA_IO;
+
+		if (data->flags == MMC_DATA_WRITE)
+			meson_mmc_cmd |= CMD_CFG_DATA_WR;
+
+		if (data->blocks > 1) {
+			meson_mmc_cmd |= CMD_CFG_BLOCK_MODE;
+			meson_mmc_cmd |= data->blocks;
+		} else {
+			meson_mmc_cmd |= (data->blocksize & CMD_CFG_LENGTH_MASK);
+		}
+	}
+
+	meson_mmc_cmd |= CMD_CFG_OWNER;
+	meson_mmc_cmd |= CMD_CFG_END_OF_CHAIN;
+
+	writel(meson_mmc_cmd, &meson_mmc_reg->gcmd_cfg);
+
+	return;
+}
+
+static void meson_mmc_setup_addr(struct mmc *mmc, struct mmc_data *data,
+				 struct meson_mmc_regs *meson_mmc_reg)
+{
+	struct meson_mmc_platdata *pdata;
+	unsigned int data_size = 0;
+	uint32_t meson_mmc_data_addr = 0;
+
+	pdata = mmc->priv;
+
+	if (data) {
+		data_size = data->blocks * data->blocksize;
+
+		if (data->flags == MMC_DATA_READ) {
+			if (data_size < 0x200) {

drivers/mmc/meson_mmc.c line 161

Quoted 10 lines
drivers/mmc/meson_mmc.c new file +10 -0
+				meson_mmc_data_addr = (ulong) meson_mmc_reg->gping;
+				meson_mmc_data_addr |= ADDR_USE_PING_BUF;
+			} else {
+				invalidate_dcache_range((ulong) data->dest,
+							(ulong) (data->dest + data_size));
+				meson_mmc_data_addr = (ulong) data->dest;
+			}
+		}
+
+		if (data->flags == MMC_DATA_WRITE) {

data->flags is only two..MMC_DATA_WRITE ..otherwise MMC_DATA_READ.

Quoted 47 lines
drivers/mmc/meson_mmc.c new file +47 -0
+			pdata->w_buf = calloc(data_size, sizeof(char));
+			memcpy(pdata->w_buf, data->src, data_size);
+			flush_dcache_range((ulong) pdata->w_buf,
+					   (ulong) (pdata->w_buf + data_size));
+			meson_mmc_data_addr = (ulong) pdata->w_buf;
+		}
+	}
+
+	writel(meson_mmc_data_addr, &meson_mmc_reg->gcmd_dat);
+
+	return;
+}
+
+static void meson_mmc_read_response(struct mmc *mmc, struct mmc_data *data,
+				    struct mmc_cmd *cmd,
+				    struct meson_mmc_regs *meson_mmc_reg)
+{
+	unsigned int data_size = 0;
+
+	if (data) {
+		data_size = data->blocks * data->blocksize;
+		if ((data_size < 0x200) && (data->flags == MMC_DATA_READ))
+			memcpy(data->dest, (const void *)meson_mmc_reg->gping, data_size);
+	}
+
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[0] = readl(&meson_mmc_reg->gcmd_rsp3);
+		cmd->response[1] = readl(&meson_mmc_reg->gcmd_rsp2);
+		cmd->response[2] = readl(&meson_mmc_reg->gcmd_rsp1);
+		cmd->response[3] = readl(&meson_mmc_reg->gcmd_rsp0);
+	} else {
+		cmd->response[0] = readl(&meson_mmc_reg->gcmd_rsp0);
+	}
+}
+
+static int meson_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+			      struct mmc_data *data)
+{
+	struct meson_mmc_platdata *pdata;
+	struct meson_mmc_regs *meson_mmc_reg;
+	uint32_t meson_mmc_irq = 0;
+	int ret = 0;
+
+	pdata = mmc->priv;
+	meson_mmc_reg = pdata->sd_emmc_reg;
+
+	meson_mmc_setup_cmd(mmc, data, cmd, meson_mmc_reg);

drivers/mmc/meson_mmc.c line 218

Quoted 10 lines
drivers/mmc/meson_mmc.c new file +10 -0
+	meson_mmc_setup_addr(mmc, data, meson_mmc_reg);
+
+	writel(SD_IRQ_ALL, &meson_mmc_reg->gstatus);
+	writel(cmd->cmdarg, &meson_mmc_reg->gcmd_arg);
+
+	while (1) {
+		meson_mmc_irq = readl(&meson_mmc_reg->gstatus);
+		if (meson_mmc_irq & STATUS_END_OF_CHAIN)
+			break;
+	}

I don't like this style...potential infinte loop.

drivers/mmc/meson_mmc.c line 231

Quoted 13 lines
drivers/mmc/meson_mmc.c new file +13 -0
+
+	if (meson_mmc_irq & STATUS_RXD_ERR_MASK)
+		ret |= SD_EMMC_RXD_ERROR;
+	if (meson_mmc_irq & STATUS_TXD_ERR)
+		ret |= SD_EMMC_TXD_ERROR;
+	if (meson_mmc_irq & STATUS_DESC_ERR)
+		ret |= SD_EMMC_DESC_ERROR;
+	if (meson_mmc_irq & STATUS_RESP_ERR)
+		ret |= SD_EMMC_RESP_CRC_ERROR;
+	if (meson_mmc_irq & STATUS_RESP_TIMEOUT)
+		ret |= SD_EMMC_RESP_TIMEOUT_ERROR;
+	if (meson_mmc_irq & STATUS_DESC_TIMEOUT)
+		ret |= SD_EMMC_DESC_TIMEOUT_ERROR;

What are these ret values? Do you use the your specific error values?

drivers/mmc/meson_mmc.c line 240

Quoted 9 lines
drivers/mmc/meson_mmc.c new file +9 -0
+
+	meson_mmc_read_response(mmc, data, cmd, meson_mmc_reg);
+
+	if (data && data->flags == MMC_DATA_WRITE)
+		free(pdata->w_buf);
+
+	if (ret) {
+		if (meson_mmc_irq & STATUS_RESP_TIMEOUT)
+			return TIMEOUT;

Strange this..why return at here...not above?

Best Regards,
Jaehoon Chung

Quoted 66 lines
drivers/mmc/meson_mmc.c new file +65 -0
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct mmc_ops meson_mmc_ops = {
+	.send_cmd	= meson_mmc_send_cmd,
+	.set_ios	= meson_mmc_set_ios,
+	.init		= meson_mmc_init,
+};
+
+static int meson_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev->platdata;
+	fdt_addr_t addr;
+
+	addr = dev_get_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	pdata->sd_emmc_reg = (struct meson_mmc_regs *)addr;
+
+	return 0;
+}
+
+static int meson_mmc_probe(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev->platdata;
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct mmc *mmc;
+	struct mmc_config *cfg;
+
+	cfg = &pdata->cfg;
+	cfg->ops = &meson_mmc_ops;
+
+	cfg->voltages = MMC_VDD_33_34 | MMC_VDD_32_33 |
+			MMC_VDD_31_32 | MMC_VDD_165_195;
+	cfg->host_caps = MMC_MODE_8BIT | MMC_MODE_4BIT |
+			 MMC_MODE_HS_52MHz | MMC_MODE_HS;
+	cfg->f_min = 400000;
+	cfg->f_max = 50000000;
+	cfg->b_max = 256;
+
+	mmc = mmc_create(cfg, pdata);
+	if (!mmc)
+		return -ENOMEM;
+
+	upriv->mmc = mmc;
+	return 0;
+}
+
+static const struct udevice_id meson_mmc_match[] = {
+	{ .compatible = "amlogic,meson-mmc" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(meson_mmc) = {
+	.name = "meson_mmc",
+	.id = UCLASS_MMC,
+	.of_match = meson_mmc_match,
+	.probe = meson_mmc_probe,
+	.ofdata_to_platdata = meson_mmc_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct meson_mmc_platdata),
+};
#7 lore raw

PATCH 2/2 arm: amlogic: Enable MMC driver on Odroid-C2

In reply to Carlo Caione

From: Carlo Caione <[email protected]>

Add support for the MMC on the Hardkernel Odroid-C2 board.

---
 board/hardkernel/odroid-c2/odroid-c2.c | 42 ++++++++++++++++++++++++++++++++++
 configs/odroid-c2_defconfig            |  5 ++++
 include/configs/odroid-c2.h            |  7 ++++++
 3 files changed, 54 insertions(+)
board/hardkernel/odroid-c2/odroid-c2.c +42 -0
index bd72100..34b9a95 100644
@@ -8,6 +8,7 @@
 #include <asm/io.h>
 #include <asm/arch/gxbb.h>
 #include <asm/arch/sm.h>
+#include <asm/arch/sd_emmc.h>
 #include <dm/platdata.h>
 #include <phy.h>
 
@@ -65,3 +66,44 @@ int misc_init_r(void)
 
 	return 0;
 }
+
+#ifdef CONFIG_GENERIC_MMC
+
+static const struct meson_mmc_platdata gxbb_sd_platdata[] = {
+	{ .sd_emmc_reg = (struct meson_mmc_regs *)SD_EMMC_BASE_A },
+	{ .sd_emmc_reg = (struct meson_mmc_regs *)SD_EMMC_BASE_B },
+	{ .sd_emmc_reg = (struct meson_mmc_regs *)SD_EMMC_BASE_C },
+};
+
+U_BOOT_DEVICE(meson_mmc) = {
+	.name = "meson_mmc",
+	.platdata = &gxbb_sd_platdata[CONFIG_MMC_MESON_SD_PORT],
+};
+
+static void meson_mmc_pinmux_setup(unsigned int port)
+{
+	switch (port) {
+	case SDIO_PORT_A:
+		setbits_le32(GXBB_PINMUX(8), 0x3f);
+		break;
+	case SDIO_PORT_B:
+		setbits_le32(GXBB_PINMUX(2), 0x3f << 10);
+		break;
+	case SDIO_PORT_C:
+		clrbits_le32(GXBB_PINMUX(2), 0x1f << 22);
+		setbits_le32(GXBB_PINMUX(4), (0x3 << 18) | (3 << 30));
+		break;
+	default:
+		printf("meson: invalid MMC port %d for pinmux setup\n", port);
+		break;
+	}
+}
+
+int board_mmc_init(bd_t *bis)
+{
+	meson_mmc_pinmux_setup(CONFIG_MMC_MESON_SD_PORT);
+
+	return 0;
+}
+
+#endif
configs/odroid-c2_defconfig +5 -0
index a771b20..a1ffe73 100644
@@ -7,11 +7,16 @@ CONFIG_DEFAULT_DEVICE_TREE="meson-gxbb-odroidc2"
 # CONFIG_CMD_IMI is not set
 # CONFIG_CMD_IMLS is not set
 # CONFIG_CMD_LOADS is not set
+CONFIG_CMD_MMC=y
 # CONFIG_CMD_FPGA is not set
 # CONFIG_CMD_SOURCE is not set
 # CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
 CONFIG_OF_CONTROL=y
 CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_DM_MMC=y
 CONFIG_DM_ETH=y
 CONFIG_ETH_DESIGNWARE=y
 CONFIG_DEBUG_UART=y
include/configs/odroid-c2.h +7 -0
index 37a5671..4af7cc6 100644
@@ -46,6 +46,13 @@
 #define CONFIG_SYS_LONGHELP
 #define CONFIG_CMDLINE_EDITING
 
+#ifdef CONFIG_DM_MMC
+#define CONFIG_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_MMC_MESON
+#define CONFIG_MMC_MESON_SD_PORT	1
+#endif
+
 #include <config_distro_defaults.h>
 
 #endif /* __CONFIG_H */
Signature · 1 line
2.7.4
#8 lore raw

cover Add support for Amlogic Meson MMC controller

In reply to Carlo Caione

Applied this on my Odroid C2, and it worked OK when installed on the SDcard, but failed when installed on the eMMC module:

=> version
U-Boot 2016.05-rc3 (May 14 2016 - 21:06:28 -0700) odroid-c2 aarch64-linux-gnu-gcc (GCC) 5.3.1 20160212 (Red Hat Cross 5.3.1-2) GNU ld version 2.26.20160125

=> mmc info
Card did not respond to voltage select!

Robert Gadsdon.

#9 lore raw

cover Add support for Amlogic Meson MMC controller

Quoted 10 lines · Robert Gadsdon

Applied this on my Odroid C2, and it worked OK when installed on the SDcard, but failed when installed on the eMMC module:

=> version
U-Boot 2016.05-rc3 (May 14 2016 - 21:06:28 -0700) odroid-c2 aarch64-linux-gnu-gcc (GCC) 5.3.1 20160212 (Red Hat Cross 5.3.1-2) GNU ld version 2.26.20160125

=> mmc info
Card did not respond to voltage select!

Yes, this is expected since the muxing in the board file is only enabling the SDcard. Could you try to set the correct muxing also for the eMMC and check again?

Unfortunately I don't have an eMMC for the Odroid-C2 to check this out. I'll try to prepare a board file also for the P20x so I can test this better.

Thank you for testing this BTW.

Cheers,

Signature · 1 line
Carlo Caione
#10 lore raw

cover Add support for Amlogic Meson MMC controller

Quoted 3 lines · Carlo Caione
Quoted 2 lines · Robert Gadsdon

Applied this on my Odroid C2, and it worked OK when installed on the SDcard, but failed when installed on the eMMC module:

Oh, reading this again you meant that you put u-boot directly on the eMMC? Ok then, something is wrong when using the driver with the eMMC.

I'll try to boot it on the P20x and check what's wrong with it.

Thanks,

Signature · 1 line
Carlo Caione
#11 lore raw

cover Add support for Amlogic Meson MMC controller

Quoted 8 lines · Carlo Caione
Quoted 3 lines · Carlo Caione
Quoted 2 lines · Robert Gadsdon

Applied this on my Odroid C2, and it worked OK when installed on the SDcard, but failed when installed on the eMMC module:

Oh, reading this again you meant that you put u-boot directly on the eMMC? Ok then, something is wrong when using the driver with the eMMC.

Yes, I installed it directly on the eMMC, using the micro-sd adapter, and following the same instructions as for the SDcard..

Robert Gadsdon.

#12 lore raw

cover Add support for Amlogic Meson MMC controller

Quoted 6 lines · Robert Gadsdon
Quoted 5 lines · Carlo Caione
Quoted 3 lines · Carlo Caione
Quoted 2 lines · Robert Gadsdon

Applied this on my Odroid C2, and it worked OK when installed on the SDcard, but failed when installed on the eMMC module:

Quoted 6 lines
Quoted 3 lines

Oh, reading this again you meant that you put u-boot directly on the eMMC? Ok then, something is wrong when using the driver with the eMMC.

Yes, I installed it directly on the eMMC, using the micro-sd adapter, and following the same instructions as for the SDcard..

Could you try with this patch?

(quoted diff) +0 -0

b/board/hardkernel/odroid-c2/odroid-c2.c
index 34b9a95..ab78328 100644

board/hardkernel/odroid-c2/odroid-c2.c +0 -0
@@ -80,6 +80,11 @@ U_BOOT_DEVICE(meson_mmc) = {
        .platdata = &gxbb_sd_platdata[CONFIG_MMC_MESON_SD_PORT],
	 };
	  
	  +U_BOOT_DEVICE(meson_emmc) = {
	  +       .name = "meson_mmc",
	  +       .platdata = &gxbb_sd_platdata[2],
	  +};
	  +
	   static void meson_mmc_pinmux_setup(unsigned int port)
	    {
	            switch (port) {

Thanks,

Signature · 1 line
Carlo Caione
#13 lore raw

cover Add support for Amlogic Meson MMC controller

Quoted 31 lines · Carlo Caione
Quoted 10 lines · Robert Gadsdon
Quoted 9 lines · Carlo Caione
Quoted 3 lines · Carlo Caione
Quoted 2 lines · Robert Gadsdon

Applied this on my Odroid C2, and it worked OK when installed on the SDcard, but failed when installed on the eMMC module:

Oh, reading this again you meant that you put u-boot directly on the eMMC? Ok then, something is wrong when using the driver with the eMMC.

Yes, I installed it directly on the eMMC, using the micro-sd adapter, and following the same instructions as for the SDcard..

Could you try with this patch?

board/hardkernel/odroid-c2/odroid-c2.c +0 -0
index 34b9a95..ab78328 100644
@@ -80,6 +80,11 @@ U_BOOT_DEVICE(meson_mmc) = {
        .platdata = &gxbb_sd_platdata[CONFIG_MMC_MESON_SD_PORT],
 	 };
 
 	  +U_BOOT_DEVICE(meson_emmc) = {
 	  +       .name = "meson_mmc",
 	  +       .platdata = &gxbb_sd_platdata[2],
	  +};
	  +
	   static void meson_mmc_pinmux_setup(unsigned int port)
	    {
	            switch (port) {
Quoted 1 line

Thanks,

Patch applied, and now the eMMC is detected OK:

=> mmc info
Device: <NULL>
Manufacturer ID: 15
OEM: 100
Name: BGND3
Tran Speed: 52000000
Rd Block Len: 512
MMC version 5.0
High Capacity: Yes
Capacity: 29.1 GiB
Bus Width: 8-bit
Erase Group Size: 512 KiB
HC WP Group Size: 8 MiB
User Capacity: 29.1 GiB WRREL
Boot Capacity: 4 MiB ENH
RPMB Capacity: 4 MiB ENH

... And I can load/boot the Kernel (as far as the /dev/mmc... prompt..)

Thanks!

Robert Gadsdon.