All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905)
       [not found] <5bb641b5-36d4-654a-2e4a-ce8215053540@gmail.com>
@ 2017-02-02 19:03 ` Carlo Caione
  2017-02-19 22:00   ` Jaehoon Chung
  2017-02-02 19:04 ` [U-Boot] [PATCH v4 3/3] odroid-c2: enable new Meson GX MMC driver in board defconfig Heiner Kallweit
  2017-02-02 19:37 ` [U-Boot] [PATCH v5 2/3] mmc: meson: add MMC driver for Meson GX (S905) Heiner Kallweit
  2 siblings, 1 reply; 5+ messages in thread
From: Carlo Caione @ 2017-02-02 19:03 UTC (permalink / raw)
  To: u-boot

This driver implements MMC support on Meson GX (S905) based systems.
It's based on Carlo Caione's work, changes:
- BLK support added
- general refactoring

Signed-off-by: Carlo Caione <carlo@caione.org>
Signed-off-by: Andreas F?rber <afaerber@suse.de>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v4:
- addressed review comments
- general refactoring
- remove config symbol MMC_MESON_GX_SD_PORT
- changed From: to Carlo Caione
- changed copyright year to 2016
---
 arch/arm/include/asm/arch-meson/sd_emmc.h |  89 +++++++++
 drivers/mmc/Kconfig                       |   6 +
 drivers/mmc/Makefile                      |   1 +
 drivers/mmc/meson_gx_mmc.c                | 291 ++++++++++++++++++++++++++++++
 4 files changed, 387 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
 create mode 100644 drivers/mmc/meson_gx_mmc.c

diff --git a/arch/arm/include/asm/arch-meson/sd_emmc.h b/arch/arm/include/asm/arch-meson/sd_emmc.h
new file mode 100644
index 0000000..a09e034
--- /dev/null
+++ b/arch/arm/include/asm/arch-meson/sd_emmc.h
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
+ *
+ * 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_CLKSRC_24M		24000000	/* 24 MHz */
+#define SD_EMMC_CLKSRC_DIV2		1000000000	/* 1 GHz */
+
+#define MESON_SD_EMMC_CLOCK		0x00
+#define   CLK_MAX_DIV			63
+#define   CLK_SRC_24M			(0 << 6)
+#define   CLK_SRC_DIV2			(1 << 6)
+#define   CLK_CO_PHASE_000		(0 << 8)
+#define   CLK_CO_PHASE_090		(1 << 8)
+#define   CLK_CO_PHASE_180		(2 << 8)
+#define   CLK_CO_PHASE_270		(3 << 8)
+#define   CLK_TX_PHASE_000		(0 << 10)
+#define   CLK_TX_PHASE_090		(1 << 10)
+#define   CLK_TX_PHASE_180		(2 << 10)
+#define   CLK_TX_PHASE_270		(3 << 10)
+#define   CLK_ALWAYS_ON			BIT(24)
+
+#define MESON_SD_EMMC_CFG		0x44
+#define   CFG_BUS_WIDTH_MASK		GENMASK(1, 0)
+#define   CFG_BUS_WIDTH_1		0
+#define   CFG_BUS_WIDTH_4		1
+#define   CFG_BUS_WIDTH_8		2
+#define   CFG_BL_LEN_MASK		GENMASK(7, 4)
+#define   CFG_BL_LEN_SHIFT		4
+#define   CFG_BL_LEN_512		(9 << 4)
+#define   CFG_RESP_TIMEOUT_MASK		GENMASK(11, 8)
+#define   CFG_RESP_TIMEOUT_256		(8 << 8)
+#define   CFG_RC_CC_MASK		GENMASK(15, 12)
+#define   CFG_RC_CC_16			(4 << 12)
+#define   CFG_SDCLK_ALWAYS_ON		BIT(18)
+#define   CFG_AUTO_CLK			BIT(23)
+
+#define MESON_SD_EMMC_STATUS		0x48
+#define   STATUS_MASK			GENMASK(15, 0)
+#define   STATUS_ERR_MASK		GENMASK(12, 0)
+#define   STATUS_RXD_ERR_MASK		GENMASK(7, 0)
+#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 MESON_SD_EMMC_IRQ_EN		0x4c
+
+#define MESON_SD_EMMC_CMD_CFG		0x50
+#define   CMD_CFG_LENGTH_MASK		GENMASK(8, 0)
+#define   CMD_CFG_BLOCK_MODE		BIT(9)
+#define   CMD_CFG_R1B			BIT(10)
+#define   CMD_CFG_END_OF_CHAIN		BIT(11)
+#define   CMD_CFG_TIMEOUT_4S		(12 << 12)
+#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_CMD_INDEX_SHIFT	24
+#define   CMD_CFG_OWNER			BIT(31)
+
+#define MESON_SD_EMMC_CMD_ARG		0x54
+#define MESON_SD_EMMC_CMD_DAT		0x58
+#define MESON_SD_EMMC_CMD_RSP		0x5c
+#define MESON_SD_EMMC_CMD_RSP1		0x60
+#define MESON_SD_EMMC_CMD_RSP2		0x64
+#define MESON_SD_EMMC_CMD_RSP3		0x68
+
+struct meson_mmc_platdata {
+	struct mmc_config cfg;
+	struct mmc mmc;
+	void *regbase;
+	void *w_buf;
+};
+
+#endif
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index df4913b..bc647c8 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -172,6 +172,12 @@ config ZYNQ_SDHCI
 	help
 	  Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
 
+config MMC_MESON_GX
+	bool "Meson GX EMMC controller support"
+	depends on DM_MMC && BLK && DM_MMC_OPS && ARCH_MESON
+	help
+	  Support for EMMC host controller on Meson GX ARM SoCs platform (S905)
+
 config ROCKCHIP_SDHCI
 	bool "Arasan SDHCI controller for Rockchip support"
 	depends on DM_MMC && BLK && DM_MMC_OPS
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6af7f79..baa6531 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -31,6 +31,7 @@ ifdef CONFIG_SUPPORT_EMMC_BOOT
 obj-$(CONFIG_GENERIC_MMC) += mmc_boot.o
 endif
 obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
+obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o
 obj-$(CONFIG_MMC_SPI) += mmc_spi.o
 obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
 obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c
new file mode 100644
index 0000000..12d2047
--- /dev/null
+++ b/drivers/mmc/meson_gx_mmc.c
@@ -0,0 +1,291 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
+ *
+ * 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>
+#include <linux/log2.h>
+
+static inline void *get_regbase(const struct mmc *mmc)
+{
+	struct meson_mmc_platdata *pdata = mmc->priv;
+
+	return pdata->regbase;
+}
+
+static inline uint32_t meson_read(struct mmc *mmc, int offset)
+{
+	return readl(get_regbase(mmc) + offset);
+}
+
+static inline void meson_write(struct mmc *mmc, uint32_t val, int offset)
+{
+	writel(val, get_regbase(mmc) + offset);
+}
+
+static void meson_mmc_config_clock(struct mmc *mmc)
+{
+	uint32_t meson_mmc_clk = 0;
+	unsigned int clk, clk_src, clk_div;
+
+	/* 1GHz / CLK_MAX_DIV = 15,9 MHz */
+	if (mmc->clock > 16000000) {
+		clk = SD_EMMC_CLKSRC_DIV2;
+		clk_src = CLK_SRC_DIV2;
+	} else {
+		clk = SD_EMMC_CLKSRC_24M;
+		clk_src = CLK_SRC_24M;
+	}
+	clk_div = DIV_ROUND_UP(clk, mmc->clock);
+
+	/* core clock phase */
+	meson_mmc_clk |= CLK_CO_PHASE_180;
+
+	/* tx clock phase */
+	meson_mmc_clk |= CLK_TX_PHASE_000;
+
+	/* clock settings */
+	meson_mmc_clk |= clk_src;
+	meson_mmc_clk |= clk_div;
+
+	meson_write(mmc, meson_mmc_clk, MESON_SD_EMMC_CLOCK);
+}
+
+static int meson_dm_mmc_set_ios(struct udevice *dev)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+	uint32_t meson_mmc_cfg;
+
+	meson_mmc_config_clock(mmc);
+
+	meson_mmc_cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
+
+	meson_mmc_cfg &= ~CFG_BUS_WIDTH_MASK;
+	if (mmc->bus_width == 1)
+		meson_mmc_cfg |= CFG_BUS_WIDTH_1;
+	else if (mmc->bus_width == 4)
+		meson_mmc_cfg |= CFG_BUS_WIDTH_4;
+	else if (mmc->bus_width == 8)
+		meson_mmc_cfg |= CFG_BUS_WIDTH_8;
+	else
+		return -EINVAL;
+
+	/* 512 bytes block length */
+	meson_mmc_cfg &= ~CFG_BL_LEN_MASK;
+	meson_mmc_cfg |= CFG_BL_LEN_512;
+
+	/* Response timeout 256 clk */
+	meson_mmc_cfg &= ~CFG_RESP_TIMEOUT_MASK;
+	meson_mmc_cfg |= CFG_RESP_TIMEOUT_256;
+
+	/* Command-command gap 16 clk */
+	meson_mmc_cfg &= ~CFG_RC_CC_MASK;
+	meson_mmc_cfg |= CFG_RC_CC_16;
+
+	meson_write(mmc, meson_mmc_cfg, MESON_SD_EMMC_CFG);
+
+	return 0;
+}
+
+static void meson_mmc_setup_cmd(struct mmc *mmc, struct mmc_data *data,
+				struct mmc_cmd *cmd)
+{
+	uint32_t meson_mmc_cmd = 0, cfg;
+
+	meson_mmc_cmd |= cmd->cmdidx << CMD_CFG_CMD_INDEX_SHIFT;
+
+	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;
+
+		if (!(cmd->resp_type & MMC_RSP_CRC))
+			meson_mmc_cmd |= CMD_CFG_RESP_NOCRC;
+	} else {
+		meson_mmc_cmd |= CMD_CFG_NO_RESP;
+	}
+
+	if (data) {
+		cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
+		cfg &= ~CFG_BL_LEN_MASK;
+		cfg |= ilog2(data->blocksize) << CFG_BL_LEN_SHIFT;
+		meson_write(mmc, cfg, MESON_SD_EMMC_CFG);
+
+		if (data->flags == MMC_DATA_WRITE)
+			meson_mmc_cmd |= CMD_CFG_DATA_WR;
+
+		cfg = CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data->blocks;
+		meson_mmc_cmd |= cfg;
+	}
+
+	cfg = CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN;
+	meson_mmc_cmd |= cfg;
+
+	meson_write(mmc, meson_mmc_cmd, MESON_SD_EMMC_CMD_CFG);
+}
+
+static void meson_mmc_setup_addr(struct mmc *mmc, struct mmc_data *data)
+{
+	struct meson_mmc_platdata *pdata = mmc->priv;
+	unsigned int data_size;
+	uint32_t data_addr = 0;
+
+	if (data) {
+		data_size = data->blocks * data->blocksize;
+
+		if (data->flags == MMC_DATA_READ) {
+			data_addr = (ulong) data->dest;
+			invalidate_dcache_range(data_addr,
+						data_addr + data_size);
+		} else if (data->flags == MMC_DATA_WRITE) {
+			pdata->w_buf = calloc(data_size, sizeof(char));
+			data_addr = (ulong) pdata->w_buf;
+			memcpy(pdata->w_buf, data->src, data_size);
+			flush_dcache_range(data_addr, data_addr + data_size);
+		}
+	}
+
+	meson_write(mmc, data_addr, MESON_SD_EMMC_CMD_DAT);
+}
+
+static void meson_mmc_read_response(struct mmc *mmc, struct mmc_cmd *cmd)
+{
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP3);
+		cmd->response[1] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP2);
+		cmd->response[2] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP1);
+		cmd->response[3] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
+	} else {
+		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
+	}
+}
+
+static int meson_dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+				 struct mmc_data *data)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+	struct meson_mmc_platdata *pdata = mmc->priv;
+	uint32_t status;
+	ulong start;
+	int ret = 0;
+
+	/* max block size supported by chip is 512 byte */
+	if (data && data->blocksize > 512)
+		return -EINVAL;
+
+	meson_mmc_setup_cmd(mmc, data, cmd);
+	meson_mmc_setup_addr(mmc, data);
+
+	meson_write(mmc, cmd->cmdarg, MESON_SD_EMMC_CMD_ARG);
+
+	/* use 10s timeout */
+	start = get_timer(0);
+	do {
+		status = meson_read(mmc, MESON_SD_EMMC_STATUS);
+	} while(!(status & STATUS_END_OF_CHAIN) && get_timer(start) < 10000);
+
+	if (!(status & STATUS_END_OF_CHAIN))
+		ret = -ETIMEDOUT;
+	else if (status & STATUS_RESP_TIMEOUT)
+		ret = -ETIMEDOUT;
+	else if (status & STATUS_ERR_MASK)
+		ret = -EIO;
+
+	meson_mmc_read_response(mmc, cmd);
+
+	if (data && data->flags == MMC_DATA_WRITE)
+		free(pdata->w_buf);
+
+	/* reset status bits */
+	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
+
+	return ret;
+}
+
+static const struct dm_mmc_ops meson_dm_mmc_ops = {
+	.send_cmd = meson_dm_mmc_send_cmd,
+	.set_ios = meson_dm_mmc_set_ios,
+};
+
+static int meson_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
+	fdt_addr_t addr;
+
+	addr = dev_get_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	pdata->regbase = (void *)addr;
+
+	return 0;
+}
+
+static int meson_mmc_probe(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct mmc *mmc = &pdata->mmc;
+	struct mmc_config *cfg = &pdata->cfg;
+	uint32_t val;
+
+	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 = DIV_ROUND_UP(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV);
+	cfg->f_max = 100000000; /* 100 MHz */
+	cfg->b_max = 256; /* max 256 blocks */
+	cfg->name = dev->name;
+
+	mmc->priv = pdata;
+	upriv->mmc = mmc;
+
+	mmc_set_clock(mmc, cfg->f_min);
+
+	/* reset all status bits */
+	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
+
+	/* disable interrupts */
+	meson_write(mmc, 0, MESON_SD_EMMC_IRQ_EN);
+
+	/* enable auto clock mode */
+	val = meson_read(mmc, MESON_SD_EMMC_CFG);
+	val &= ~CFG_SDCLK_ALWAYS_ON;
+	val |= CFG_AUTO_CLK;
+	meson_write(mmc, val, MESON_SD_EMMC_CFG);
+
+	return 0;
+}
+
+int meson_mmc_bind(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
+
+	return mmc_bind(dev, &pdata->mmc, &pdata->cfg);
+}
+
+static const struct udevice_id meson_mmc_match[] = {
+	{ .compatible = "amlogic,meson-gx-mmc" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(meson_mmc) = {
+	.name = "meson_gx_mmc",
+	.id = UCLASS_MMC,
+	.of_match = meson_mmc_match,
+	.ops = &meson_dm_mmc_ops,
+	.probe = meson_mmc_probe,
+	.bind = meson_mmc_bind,
+	.ofdata_to_platdata = meson_mmc_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct meson_mmc_platdata),
+};
-- 
2.11.0

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

* [U-Boot] [PATCH v4 3/3] odroid-c2: enable new Meson GX MMC driver in board defconfig
       [not found] <5bb641b5-36d4-654a-2e4a-ce8215053540@gmail.com>
  2017-02-02 19:03 ` [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905) Carlo Caione
@ 2017-02-02 19:04 ` Heiner Kallweit
  2017-02-02 19:37 ` [U-Boot] [PATCH v5 2/3] mmc: meson: add MMC driver for Meson GX (S905) Heiner Kallweit
  2 siblings, 0 replies; 5+ messages in thread
From: Heiner Kallweit @ 2017-02-02 19:04 UTC (permalink / raw)
  To: u-boot

Enable new Meson GX MMC driver in Odroid C2 defconfig.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v4:
- move GXBB_PINMUX definition from patch 3 to this one
- remove pinmux configuration in board init and use
  pinctrl driver instead
- rebased
---
 configs/odroid-c2_defconfig | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/configs/odroid-c2_defconfig b/configs/odroid-c2_defconfig
index 62b7627..6de712a 100644
--- a/configs/odroid-c2_defconfig
+++ b/configs/odroid-c2_defconfig
@@ -3,7 +3,9 @@ CONFIG_ARCH_MESON=y
 CONFIG_MESON_GXBB=y
 CONFIG_TARGET_ODROID_C2=y
 CONFIG_IDENT_STRING=" odroid-c2"
-# CONFIG_MMC is not set
+CONFIG_MMC=y
+CONFIG_DM_MMC=y
+CONFIG_MMC_MESON_GX=y
 CONFIG_DEFAULT_DEVICE_TREE="meson-gxbb-odroidc2"
 # CONFIG_DISPLAY_CPUINFO is not set
 # CONFIG_DISPLAY_BOARDINFO is not set
@@ -14,6 +16,7 @@ CONFIG_HUSH_PARSER=y
 # CONFIG_CMD_LOADS is not set
 # CONFIG_CMD_FPGA is not set
 # CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_MMC=y
 CONFIG_DOS_PARTITION=y
 CONFIG_ISO_PARTITION=y
 CONFIG_EFI_PARTITION=y
-- 
2.11.0

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

* [U-Boot] [PATCH v5 2/3] mmc: meson: add MMC driver for Meson GX (S905)
       [not found] <5bb641b5-36d4-654a-2e4a-ce8215053540@gmail.com>
  2017-02-02 19:03 ` [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905) Carlo Caione
  2017-02-02 19:04 ` [U-Boot] [PATCH v4 3/3] odroid-c2: enable new Meson GX MMC driver in board defconfig Heiner Kallweit
@ 2017-02-02 19:37 ` Heiner Kallweit
  2 siblings, 0 replies; 5+ messages in thread
From: Heiner Kallweit @ 2017-02-02 19:37 UTC (permalink / raw)
  To: u-boot

From: Carlo Caione <carlo@caione.org>
This driver implements MMC support on Meson GX (S905) based systems.
It's based on Carlo Caione's work, changes:
- BLK support added
- general refactoring

Signed-off-by: Carlo Caione <carlo@caione.org>
Signed-off-by: Andreas F?rber <afaerber@suse.de>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v5:
- addressed review comments
- general refactoring
- remove config symbol MMC_MESON_GX_SD_PORT
- set From: to Carlo Caione
- changed copyright year to 2016
---
 arch/arm/include/asm/arch-meson/sd_emmc.h |  89 +++++++++
 drivers/mmc/Kconfig                       |   6 +
 drivers/mmc/Makefile                      |   1 +
 drivers/mmc/meson_gx_mmc.c                | 291 ++++++++++++++++++++++++++++++
 4 files changed, 387 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
 create mode 100644 drivers/mmc/meson_gx_mmc.c

diff --git a/arch/arm/include/asm/arch-meson/sd_emmc.h b/arch/arm/include/asm/arch-meson/sd_emmc.h
new file mode 100644
index 0000000..a09e034
--- /dev/null
+++ b/arch/arm/include/asm/arch-meson/sd_emmc.h
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
+ *
+ * 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_CLKSRC_24M		24000000	/* 24 MHz */
+#define SD_EMMC_CLKSRC_DIV2		1000000000	/* 1 GHz */
+
+#define MESON_SD_EMMC_CLOCK		0x00
+#define   CLK_MAX_DIV			63
+#define   CLK_SRC_24M			(0 << 6)
+#define   CLK_SRC_DIV2			(1 << 6)
+#define   CLK_CO_PHASE_000		(0 << 8)
+#define   CLK_CO_PHASE_090		(1 << 8)
+#define   CLK_CO_PHASE_180		(2 << 8)
+#define   CLK_CO_PHASE_270		(3 << 8)
+#define   CLK_TX_PHASE_000		(0 << 10)
+#define   CLK_TX_PHASE_090		(1 << 10)
+#define   CLK_TX_PHASE_180		(2 << 10)
+#define   CLK_TX_PHASE_270		(3 << 10)
+#define   CLK_ALWAYS_ON			BIT(24)
+
+#define MESON_SD_EMMC_CFG		0x44
+#define   CFG_BUS_WIDTH_MASK		GENMASK(1, 0)
+#define   CFG_BUS_WIDTH_1		0
+#define   CFG_BUS_WIDTH_4		1
+#define   CFG_BUS_WIDTH_8		2
+#define   CFG_BL_LEN_MASK		GENMASK(7, 4)
+#define   CFG_BL_LEN_SHIFT		4
+#define   CFG_BL_LEN_512		(9 << 4)
+#define   CFG_RESP_TIMEOUT_MASK		GENMASK(11, 8)
+#define   CFG_RESP_TIMEOUT_256		(8 << 8)
+#define   CFG_RC_CC_MASK		GENMASK(15, 12)
+#define   CFG_RC_CC_16			(4 << 12)
+#define   CFG_SDCLK_ALWAYS_ON		BIT(18)
+#define   CFG_AUTO_CLK			BIT(23)
+
+#define MESON_SD_EMMC_STATUS		0x48
+#define   STATUS_MASK			GENMASK(15, 0)
+#define   STATUS_ERR_MASK		GENMASK(12, 0)
+#define   STATUS_RXD_ERR_MASK		GENMASK(7, 0)
+#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 MESON_SD_EMMC_IRQ_EN		0x4c
+
+#define MESON_SD_EMMC_CMD_CFG		0x50
+#define   CMD_CFG_LENGTH_MASK		GENMASK(8, 0)
+#define   CMD_CFG_BLOCK_MODE		BIT(9)
+#define   CMD_CFG_R1B			BIT(10)
+#define   CMD_CFG_END_OF_CHAIN		BIT(11)
+#define   CMD_CFG_TIMEOUT_4S		(12 << 12)
+#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_CMD_INDEX_SHIFT	24
+#define   CMD_CFG_OWNER			BIT(31)
+
+#define MESON_SD_EMMC_CMD_ARG		0x54
+#define MESON_SD_EMMC_CMD_DAT		0x58
+#define MESON_SD_EMMC_CMD_RSP		0x5c
+#define MESON_SD_EMMC_CMD_RSP1		0x60
+#define MESON_SD_EMMC_CMD_RSP2		0x64
+#define MESON_SD_EMMC_CMD_RSP3		0x68
+
+struct meson_mmc_platdata {
+	struct mmc_config cfg;
+	struct mmc mmc;
+	void *regbase;
+	void *w_buf;
+};
+
+#endif
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index df4913b..bc647c8 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -172,6 +172,12 @@ config ZYNQ_SDHCI
 	help
 	  Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
 
+config MMC_MESON_GX
+	bool "Meson GX EMMC controller support"
+	depends on DM_MMC && BLK && DM_MMC_OPS && ARCH_MESON
+	help
+	  Support for EMMC host controller on Meson GX ARM SoCs platform (S905)
+
 config ROCKCHIP_SDHCI
 	bool "Arasan SDHCI controller for Rockchip support"
 	depends on DM_MMC && BLK && DM_MMC_OPS
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6af7f79..baa6531 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -31,6 +31,7 @@ ifdef CONFIG_SUPPORT_EMMC_BOOT
 obj-$(CONFIG_GENERIC_MMC) += mmc_boot.o
 endif
 obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
+obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o
 obj-$(CONFIG_MMC_SPI) += mmc_spi.o
 obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
 obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c
new file mode 100644
index 0000000..12d2047
--- /dev/null
+++ b/drivers/mmc/meson_gx_mmc.c
@@ -0,0 +1,291 @@
+/*
+ * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
+ *
+ * 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>
+#include <linux/log2.h>
+
+static inline void *get_regbase(const struct mmc *mmc)
+{
+	struct meson_mmc_platdata *pdata = mmc->priv;
+
+	return pdata->regbase;
+}
+
+static inline uint32_t meson_read(struct mmc *mmc, int offset)
+{
+	return readl(get_regbase(mmc) + offset);
+}
+
+static inline void meson_write(struct mmc *mmc, uint32_t val, int offset)
+{
+	writel(val, get_regbase(mmc) + offset);
+}
+
+static void meson_mmc_config_clock(struct mmc *mmc)
+{
+	uint32_t meson_mmc_clk = 0;
+	unsigned int clk, clk_src, clk_div;
+
+	/* 1GHz / CLK_MAX_DIV = 15,9 MHz */
+	if (mmc->clock > 16000000) {
+		clk = SD_EMMC_CLKSRC_DIV2;
+		clk_src = CLK_SRC_DIV2;
+	} else {
+		clk = SD_EMMC_CLKSRC_24M;
+		clk_src = CLK_SRC_24M;
+	}
+	clk_div = DIV_ROUND_UP(clk, mmc->clock);
+
+	/* core clock phase */
+	meson_mmc_clk |= CLK_CO_PHASE_180;
+
+	/* tx clock phase */
+	meson_mmc_clk |= CLK_TX_PHASE_000;
+
+	/* clock settings */
+	meson_mmc_clk |= clk_src;
+	meson_mmc_clk |= clk_div;
+
+	meson_write(mmc, meson_mmc_clk, MESON_SD_EMMC_CLOCK);
+}
+
+static int meson_dm_mmc_set_ios(struct udevice *dev)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+	uint32_t meson_mmc_cfg;
+
+	meson_mmc_config_clock(mmc);
+
+	meson_mmc_cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
+
+	meson_mmc_cfg &= ~CFG_BUS_WIDTH_MASK;
+	if (mmc->bus_width == 1)
+		meson_mmc_cfg |= CFG_BUS_WIDTH_1;
+	else if (mmc->bus_width == 4)
+		meson_mmc_cfg |= CFG_BUS_WIDTH_4;
+	else if (mmc->bus_width == 8)
+		meson_mmc_cfg |= CFG_BUS_WIDTH_8;
+	else
+		return -EINVAL;
+
+	/* 512 bytes block length */
+	meson_mmc_cfg &= ~CFG_BL_LEN_MASK;
+	meson_mmc_cfg |= CFG_BL_LEN_512;
+
+	/* Response timeout 256 clk */
+	meson_mmc_cfg &= ~CFG_RESP_TIMEOUT_MASK;
+	meson_mmc_cfg |= CFG_RESP_TIMEOUT_256;
+
+	/* Command-command gap 16 clk */
+	meson_mmc_cfg &= ~CFG_RC_CC_MASK;
+	meson_mmc_cfg |= CFG_RC_CC_16;
+
+	meson_write(mmc, meson_mmc_cfg, MESON_SD_EMMC_CFG);
+
+	return 0;
+}
+
+static void meson_mmc_setup_cmd(struct mmc *mmc, struct mmc_data *data,
+				struct mmc_cmd *cmd)
+{
+	uint32_t meson_mmc_cmd = 0, cfg;
+
+	meson_mmc_cmd |= cmd->cmdidx << CMD_CFG_CMD_INDEX_SHIFT;
+
+	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;
+
+		if (!(cmd->resp_type & MMC_RSP_CRC))
+			meson_mmc_cmd |= CMD_CFG_RESP_NOCRC;
+	} else {
+		meson_mmc_cmd |= CMD_CFG_NO_RESP;
+	}
+
+	if (data) {
+		cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
+		cfg &= ~CFG_BL_LEN_MASK;
+		cfg |= ilog2(data->blocksize) << CFG_BL_LEN_SHIFT;
+		meson_write(mmc, cfg, MESON_SD_EMMC_CFG);
+
+		if (data->flags == MMC_DATA_WRITE)
+			meson_mmc_cmd |= CMD_CFG_DATA_WR;
+
+		cfg = CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data->blocks;
+		meson_mmc_cmd |= cfg;
+	}
+
+	cfg = CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN;
+	meson_mmc_cmd |= cfg;
+
+	meson_write(mmc, meson_mmc_cmd, MESON_SD_EMMC_CMD_CFG);
+}
+
+static void meson_mmc_setup_addr(struct mmc *mmc, struct mmc_data *data)
+{
+	struct meson_mmc_platdata *pdata = mmc->priv;
+	unsigned int data_size;
+	uint32_t data_addr = 0;
+
+	if (data) {
+		data_size = data->blocks * data->blocksize;
+
+		if (data->flags == MMC_DATA_READ) {
+			data_addr = (ulong) data->dest;
+			invalidate_dcache_range(data_addr,
+						data_addr + data_size);
+		} else if (data->flags == MMC_DATA_WRITE) {
+			pdata->w_buf = calloc(data_size, sizeof(char));
+			data_addr = (ulong) pdata->w_buf;
+			memcpy(pdata->w_buf, data->src, data_size);
+			flush_dcache_range(data_addr, data_addr + data_size);
+		}
+	}
+
+	meson_write(mmc, data_addr, MESON_SD_EMMC_CMD_DAT);
+}
+
+static void meson_mmc_read_response(struct mmc *mmc, struct mmc_cmd *cmd)
+{
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP3);
+		cmd->response[1] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP2);
+		cmd->response[2] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP1);
+		cmd->response[3] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
+	} else {
+		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
+	}
+}
+
+static int meson_dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+				 struct mmc_data *data)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+	struct meson_mmc_platdata *pdata = mmc->priv;
+	uint32_t status;
+	ulong start;
+	int ret = 0;
+
+	/* max block size supported by chip is 512 byte */
+	if (data && data->blocksize > 512)
+		return -EINVAL;
+
+	meson_mmc_setup_cmd(mmc, data, cmd);
+	meson_mmc_setup_addr(mmc, data);
+
+	meson_write(mmc, cmd->cmdarg, MESON_SD_EMMC_CMD_ARG);
+
+	/* use 10s timeout */
+	start = get_timer(0);
+	do {
+		status = meson_read(mmc, MESON_SD_EMMC_STATUS);
+	} while(!(status & STATUS_END_OF_CHAIN) && get_timer(start) < 10000);
+
+	if (!(status & STATUS_END_OF_CHAIN))
+		ret = -ETIMEDOUT;
+	else if (status & STATUS_RESP_TIMEOUT)
+		ret = -ETIMEDOUT;
+	else if (status & STATUS_ERR_MASK)
+		ret = -EIO;
+
+	meson_mmc_read_response(mmc, cmd);
+
+	if (data && data->flags == MMC_DATA_WRITE)
+		free(pdata->w_buf);
+
+	/* reset status bits */
+	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
+
+	return ret;
+}
+
+static const struct dm_mmc_ops meson_dm_mmc_ops = {
+	.send_cmd = meson_dm_mmc_send_cmd,
+	.set_ios = meson_dm_mmc_set_ios,
+};
+
+static int meson_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
+	fdt_addr_t addr;
+
+	addr = dev_get_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	pdata->regbase = (void *)addr;
+
+	return 0;
+}
+
+static int meson_mmc_probe(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct mmc *mmc = &pdata->mmc;
+	struct mmc_config *cfg = &pdata->cfg;
+	uint32_t val;
+
+	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 = DIV_ROUND_UP(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV);
+	cfg->f_max = 100000000; /* 100 MHz */
+	cfg->b_max = 256; /* max 256 blocks */
+	cfg->name = dev->name;
+
+	mmc->priv = pdata;
+	upriv->mmc = mmc;
+
+	mmc_set_clock(mmc, cfg->f_min);
+
+	/* reset all status bits */
+	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
+
+	/* disable interrupts */
+	meson_write(mmc, 0, MESON_SD_EMMC_IRQ_EN);
+
+	/* enable auto clock mode */
+	val = meson_read(mmc, MESON_SD_EMMC_CFG);
+	val &= ~CFG_SDCLK_ALWAYS_ON;
+	val |= CFG_AUTO_CLK;
+	meson_write(mmc, val, MESON_SD_EMMC_CFG);
+
+	return 0;
+}
+
+int meson_mmc_bind(struct udevice *dev)
+{
+	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
+
+	return mmc_bind(dev, &pdata->mmc, &pdata->cfg);
+}
+
+static const struct udevice_id meson_mmc_match[] = {
+	{ .compatible = "amlogic,meson-gx-mmc" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(meson_mmc) = {
+	.name = "meson_gx_mmc",
+	.id = UCLASS_MMC,
+	.of_match = meson_mmc_match,
+	.ops = &meson_dm_mmc_ops,
+	.probe = meson_mmc_probe,
+	.bind = meson_mmc_bind,
+	.ofdata_to_platdata = meson_mmc_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct meson_mmc_platdata),
+};
-- 
2.11.0

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

* [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905)
  2017-02-02 19:03 ` [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905) Carlo Caione
@ 2017-02-19 22:00   ` Jaehoon Chung
  2017-02-20  6:52     ` Heiner Kallweit
  0 siblings, 1 reply; 5+ messages in thread
From: Jaehoon Chung @ 2017-02-19 22:00 UTC (permalink / raw)
  To: u-boot

On 02/03/2017 04:03 AM, Carlo Caione wrote:
> This driver implements MMC support on Meson GX (S905) based systems.
> It's based on Carlo Caione's work, changes:
> - BLK support added
> - general refactoring
> 
> Signed-off-by: Carlo Caione <carlo@caione.org>
> Signed-off-by: Andreas F?rber <afaerber@suse.de>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> v4:
> - addressed review comments
> - general refactoring
> - remove config symbol MMC_MESON_GX_SD_PORT
> - changed From: to Carlo Caione
> - changed copyright year to 2016
> ---
>  arch/arm/include/asm/arch-meson/sd_emmc.h |  89 +++++++++
>  drivers/mmc/Kconfig                       |   6 +
>  drivers/mmc/Makefile                      |   1 +
>  drivers/mmc/meson_gx_mmc.c                | 291 ++++++++++++++++++++++++++++++
>  4 files changed, 387 insertions(+)
>  create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
>  create mode 100644 drivers/mmc/meson_gx_mmc.c
> 
> diff --git a/arch/arm/include/asm/arch-meson/sd_emmc.h b/arch/arm/include/asm/arch-meson/sd_emmc.h
> new file mode 100644
> index 0000000..a09e034
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-meson/sd_emmc.h
> @@ -0,0 +1,89 @@
> +/*
> + * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
> + *
> + * 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_CLKSRC_24M		24000000	/* 24 MHz */
> +#define SD_EMMC_CLKSRC_DIV2		1000000000	/* 1 GHz */
> +
> +#define MESON_SD_EMMC_CLOCK		0x00
> +#define   CLK_MAX_DIV			63
> +#define   CLK_SRC_24M			(0 << 6)
> +#define   CLK_SRC_DIV2			(1 << 6)
> +#define   CLK_CO_PHASE_000		(0 << 8)
> +#define   CLK_CO_PHASE_090		(1 << 8)
> +#define   CLK_CO_PHASE_180		(2 << 8)
> +#define   CLK_CO_PHASE_270		(3 << 8)
> +#define   CLK_TX_PHASE_000		(0 << 10)
> +#define   CLK_TX_PHASE_090		(1 << 10)
> +#define   CLK_TX_PHASE_180		(2 << 10)
> +#define   CLK_TX_PHASE_270		(3 << 10)
> +#define   CLK_ALWAYS_ON			BIT(24)
> +
> +#define MESON_SD_EMMC_CFG		0x44
> +#define   CFG_BUS_WIDTH_MASK		GENMASK(1, 0)
> +#define   CFG_BUS_WIDTH_1		0
> +#define   CFG_BUS_WIDTH_4		1
> +#define   CFG_BUS_WIDTH_8		2
> +#define   CFG_BL_LEN_MASK		GENMASK(7, 4)
> +#define   CFG_BL_LEN_SHIFT		4
> +#define   CFG_BL_LEN_512		(9 << 4)
> +#define   CFG_RESP_TIMEOUT_MASK		GENMASK(11, 8)
> +#define   CFG_RESP_TIMEOUT_256		(8 << 8)
> +#define   CFG_RC_CC_MASK		GENMASK(15, 12)
> +#define   CFG_RC_CC_16			(4 << 12)
> +#define   CFG_SDCLK_ALWAYS_ON		BIT(18)
> +#define   CFG_AUTO_CLK			BIT(23)
> +
> +#define MESON_SD_EMMC_STATUS		0x48
> +#define   STATUS_MASK			GENMASK(15, 0)
> +#define   STATUS_ERR_MASK		GENMASK(12, 0)
> +#define   STATUS_RXD_ERR_MASK		GENMASK(7, 0)
> +#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 MESON_SD_EMMC_IRQ_EN		0x4c
> +
> +#define MESON_SD_EMMC_CMD_CFG		0x50
> +#define   CMD_CFG_LENGTH_MASK		GENMASK(8, 0)
> +#define   CMD_CFG_BLOCK_MODE		BIT(9)
> +#define   CMD_CFG_R1B			BIT(10)
> +#define   CMD_CFG_END_OF_CHAIN		BIT(11)
> +#define   CMD_CFG_TIMEOUT_4S		(12 << 12)
> +#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_CMD_INDEX_SHIFT	24
> +#define   CMD_CFG_OWNER			BIT(31)
> +
> +#define MESON_SD_EMMC_CMD_ARG		0x54
> +#define MESON_SD_EMMC_CMD_DAT		0x58
> +#define MESON_SD_EMMC_CMD_RSP		0x5c
> +#define MESON_SD_EMMC_CMD_RSP1		0x60
> +#define MESON_SD_EMMC_CMD_RSP2		0x64
> +#define MESON_SD_EMMC_CMD_RSP3		0x68
> +
> +struct meson_mmc_platdata {
> +	struct mmc_config cfg;
> +	struct mmc mmc;
> +	void *regbase;
> +	void *w_buf;
> +};
> +
> +#endif
> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index df4913b..bc647c8 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -172,6 +172,12 @@ config ZYNQ_SDHCI
>  	help
>  	  Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
>  
> +config MMC_MESON_GX
> +	bool "Meson GX EMMC controller support"
> +	depends on DM_MMC && BLK && DM_MMC_OPS && ARCH_MESON

Not put DM_MMC_OPS.

> +	help
> +	  Support for EMMC host controller on Meson GX ARM SoCs platform (S905)
> +
>  config ROCKCHIP_SDHCI
>  	bool "Arasan SDHCI controller for Rockchip support"
>  	depends on DM_MMC && BLK && DM_MMC_OPS
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index 6af7f79..baa6531 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -31,6 +31,7 @@ ifdef CONFIG_SUPPORT_EMMC_BOOT
>  obj-$(CONFIG_GENERIC_MMC) += mmc_boot.o
>  endif
>  obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
> +obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o
>  obj-$(CONFIG_MMC_SPI) += mmc_spi.o
>  obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
>  obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
> diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c
> new file mode 100644
> index 0000000..12d2047
> --- /dev/null
> +++ b/drivers/mmc/meson_gx_mmc.c
> @@ -0,0 +1,291 @@
> +/*
> + * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
> + *
> + * 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>
> +#include <linux/log2.h>
> +
> +static inline void *get_regbase(const struct mmc *mmc)
> +{
> +	struct meson_mmc_platdata *pdata = mmc->priv;
> +
> +	return pdata->regbase;
> +}
> +
> +static inline uint32_t meson_read(struct mmc *mmc, int offset)
> +{
> +	return readl(get_regbase(mmc) + offset);
> +}
> +
> +static inline void meson_write(struct mmc *mmc, uint32_t val, int offset)
> +{
> +	writel(val, get_regbase(mmc) + offset);
> +}
> +
> +static void meson_mmc_config_clock(struct mmc *mmc)
> +{
> +	uint32_t meson_mmc_clk = 0;
> +	unsigned int clk, clk_src, clk_div;
> +
> +	/* 1GHz / CLK_MAX_DIV = 15,9 MHz */
> +	if (mmc->clock > 16000000) {
> +		clk = SD_EMMC_CLKSRC_DIV2;
> +		clk_src = CLK_SRC_DIV2;
> +	} else {
> +		clk = SD_EMMC_CLKSRC_24M;
> +		clk_src = CLK_SRC_24M;
> +	}
> +	clk_div = DIV_ROUND_UP(clk, mmc->clock);
> +
> +	/* core clock phase */
> +	meson_mmc_clk |= CLK_CO_PHASE_180;
> +
> +	/* tx clock phase */
> +	meson_mmc_clk |= CLK_TX_PHASE_000;
> +
> +	/* clock settings */
> +	meson_mmc_clk |= clk_src;
> +	meson_mmc_clk |= clk_div;
> +
> +	meson_write(mmc, meson_mmc_clk, MESON_SD_EMMC_CLOCK);
> +}
> +
> +static int meson_dm_mmc_set_ios(struct udevice *dev)
> +{
> +	struct mmc *mmc = mmc_get_mmc_dev(dev);
> +	uint32_t meson_mmc_cfg;
> +
> +	meson_mmc_config_clock(mmc);
> +
> +	meson_mmc_cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
> +
> +	meson_mmc_cfg &= ~CFG_BUS_WIDTH_MASK;
> +	if (mmc->bus_width == 1)
> +		meson_mmc_cfg |= CFG_BUS_WIDTH_1;
> +	else if (mmc->bus_width == 4)
> +		meson_mmc_cfg |= CFG_BUS_WIDTH_4;
> +	else if (mmc->bus_width == 8)
> +		meson_mmc_cfg |= CFG_BUS_WIDTH_8;
> +	else
> +		return -EINVAL;
> +
> +	/* 512 bytes block length */
> +	meson_mmc_cfg &= ~CFG_BL_LEN_MASK;
> +	meson_mmc_cfg |= CFG_BL_LEN_512;
> +
> +	/* Response timeout 256 clk */
> +	meson_mmc_cfg &= ~CFG_RESP_TIMEOUT_MASK;
> +	meson_mmc_cfg |= CFG_RESP_TIMEOUT_256;
> +
> +	/* Command-command gap 16 clk */
> +	meson_mmc_cfg &= ~CFG_RC_CC_MASK;
> +	meson_mmc_cfg |= CFG_RC_CC_16;
> +
> +	meson_write(mmc, meson_mmc_cfg, MESON_SD_EMMC_CFG);
> +
> +	return 0;
> +}
> +
> +static void meson_mmc_setup_cmd(struct mmc *mmc, struct mmc_data *data,
> +				struct mmc_cmd *cmd)
> +{
> +	uint32_t meson_mmc_cmd = 0, cfg;
> +
> +	meson_mmc_cmd |= cmd->cmdidx << CMD_CFG_CMD_INDEX_SHIFT;
> +
> +	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;
> +
> +		if (!(cmd->resp_type & MMC_RSP_CRC))
> +			meson_mmc_cmd |= CMD_CFG_RESP_NOCRC;
> +	} else {
> +		meson_mmc_cmd |= CMD_CFG_NO_RESP;
> +	}
> +
> +	if (data) {
> +		cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
> +		cfg &= ~CFG_BL_LEN_MASK;
> +		cfg |= ilog2(data->blocksize) << CFG_BL_LEN_SHIFT;
> +		meson_write(mmc, cfg, MESON_SD_EMMC_CFG);
> +
> +		if (data->flags == MMC_DATA_WRITE)
> +			meson_mmc_cmd |= CMD_CFG_DATA_WR;
> +
> +		cfg = CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data->blocks;
> +		meson_mmc_cmd |= cfg;

This code doesn't need. You can use the below code.
cfg |= CMD_CFG_TIMEOUT_4S | ...;

> +	}
> +
> +	cfg = CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN;
> +	meson_mmc_cmd |= cfg;
> +
> +	meson_write(mmc, meson_mmc_cmd, MESON_SD_EMMC_CMD_CFG);
> +}
> +
> +static void meson_mmc_setup_addr(struct mmc *mmc, struct mmc_data *data)
> +{
> +	struct meson_mmc_platdata *pdata = mmc->priv;
> +	unsigned int data_size;
> +	uint32_t data_addr = 0;
> +
> +	if (data) {
> +		data_size = data->blocks * data->blocksize;
> +
> +		if (data->flags == MMC_DATA_READ) {
> +			data_addr = (ulong) data->dest;
> +			invalidate_dcache_range(data_addr,
> +						data_addr + data_size);
> +		} else if (data->flags == MMC_DATA_WRITE) {

not need to use "else if", use "else"

> +			pdata->w_buf = calloc(data_size, sizeof(char));
> +			data_addr = (ulong) pdata->w_buf;
> +			memcpy(pdata->w_buf, data->src, data_size);
> +			flush_dcache_range(data_addr, data_addr + data_size);
> +		}
> +	}
> +
> +	meson_write(mmc, data_addr, MESON_SD_EMMC_CMD_DAT);
> +}
> +
> +static void meson_mmc_read_response(struct mmc *mmc, struct mmc_cmd *cmd)
> +{
> +	if (cmd->resp_type & MMC_RSP_136) {
> +		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP3);
> +		cmd->response[1] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP2);
> +		cmd->response[2] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP1);
> +		cmd->response[3] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
> +	} else {
> +		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
> +	}
> +}
> +
> +static int meson_dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
> +				 struct mmc_data *data)
> +{
> +	struct mmc *mmc = mmc_get_mmc_dev(dev);
> +	struct meson_mmc_platdata *pdata = mmc->priv;
> +	uint32_t status;
> +	ulong start;
> +	int ret = 0;
> +
> +	/* max block size supported by chip is 512 byte */
> +	if (data && data->blocksize > 512)
> +		return -EINVAL;
> +
> +	meson_mmc_setup_cmd(mmc, data, cmd);
> +	meson_mmc_setup_addr(mmc, data);
> +
> +	meson_write(mmc, cmd->cmdarg, MESON_SD_EMMC_CMD_ARG);
> +
> +	/* use 10s timeout */
> +	start = get_timer(0);
> +	do {
> +		status = meson_read(mmc, MESON_SD_EMMC_STATUS);
> +	} while(!(status & STATUS_END_OF_CHAIN) && get_timer(start) < 10000);
> +
> +	if (!(status & STATUS_END_OF_CHAIN))
> +		ret = -ETIMEDOUT;
> +	else if (status & STATUS_RESP_TIMEOUT)
> +		ret = -ETIMEDOUT;
> +	else if (status & STATUS_ERR_MASK)
> +		ret = -EIO;
> +
> +	meson_mmc_read_response(mmc, cmd);
> +
> +	if (data && data->flags == MMC_DATA_WRITE)
> +		free(pdata->w_buf);
> +
> +	/* reset status bits */
> +	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
> +
> +	return ret;
> +}
> +
> +static const struct dm_mmc_ops meson_dm_mmc_ops = {
> +	.send_cmd = meson_dm_mmc_send_cmd,
> +	.set_ios = meson_dm_mmc_set_ios,
> +};
> +
> +static int meson_mmc_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
> +	fdt_addr_t addr;
> +
> +	addr = dev_get_addr(dev);
> +	if (addr == FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	pdata->regbase = (void *)addr;
> +
> +	return 0;
> +}
> +
> +static int meson_mmc_probe(struct udevice *dev)
> +{
> +	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct mmc *mmc = &pdata->mmc;
> +	struct mmc_config *cfg = &pdata->cfg;
> +	uint32_t val;
> +
> +	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 = DIV_ROUND_UP(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV);
> +	cfg->f_max = 100000000; /* 100 MHz */
> +	cfg->b_max = 256; /* max 256 blocks */
> +	cfg->name = dev->name;
> +
> +	mmc->priv = pdata;
> +	upriv->mmc = mmc;
> +
> +	mmc_set_clock(mmc, cfg->f_min);
> +
> +	/* reset all status bits */
> +	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
> +
> +	/* disable interrupts */
> +	meson_write(mmc, 0, MESON_SD_EMMC_IRQ_EN);
> +
> +	/* enable auto clock mode */
> +	val = meson_read(mmc, MESON_SD_EMMC_CFG);
> +	val &= ~CFG_SDCLK_ALWAYS_ON;
> +	val |= CFG_AUTO_CLK;
> +	meson_write(mmc, val, MESON_SD_EMMC_CFG);
> +
> +	return 0;
> +}
> +
> +int meson_mmc_bind(struct udevice *dev)
> +{
> +	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
> +
> +	return mmc_bind(dev, &pdata->mmc, &pdata->cfg);
> +}
> +
> +static const struct udevice_id meson_mmc_match[] = {
> +	{ .compatible = "amlogic,meson-gx-mmc" },
> +	{ /* sentinel */ }
> +};
> +
> +U_BOOT_DRIVER(meson_mmc) = {
> +	.name = "meson_gx_mmc",
> +	.id = UCLASS_MMC,
> +	.of_match = meson_mmc_match,
> +	.ops = &meson_dm_mmc_ops,
> +	.probe = meson_mmc_probe,
> +	.bind = meson_mmc_bind,
> +	.ofdata_to_platdata = meson_mmc_ofdata_to_platdata,
> +	.platdata_auto_alloc_size = sizeof(struct meson_mmc_platdata),
> +};
> 

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

* [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905)
  2017-02-19 22:00   ` Jaehoon Chung
@ 2017-02-20  6:52     ` Heiner Kallweit
  0 siblings, 0 replies; 5+ messages in thread
From: Heiner Kallweit @ 2017-02-20  6:52 UTC (permalink / raw)
  To: u-boot

Am 19.02.2017 um 23:00 schrieb Jaehoon Chung:
> On 02/03/2017 04:03 AM, Carlo Caione wrote:
>> This driver implements MMC support on Meson GX (S905) based systems.
>> It's based on Carlo Caione's work, changes:
>> - BLK support added
>> - general refactoring
>>
>> Signed-off-by: Carlo Caione <carlo@caione.org>
>> Signed-off-by: Andreas F?rber <afaerber@suse.de>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> v4:
>> - addressed review comments
>> - general refactoring
>> - remove config symbol MMC_MESON_GX_SD_PORT
>> - changed From: to Carlo Caione
>> - changed copyright year to 2016
>> ---
>>  arch/arm/include/asm/arch-meson/sd_emmc.h |  89 +++++++++
>>  drivers/mmc/Kconfig                       |   6 +
>>  drivers/mmc/Makefile                      |   1 +
>>  drivers/mmc/meson_gx_mmc.c                | 291 ++++++++++++++++++++++++++++++
>>  4 files changed, 387 insertions(+)
>>  create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h
>>  create mode 100644 drivers/mmc/meson_gx_mmc.c
>>
>> diff --git a/arch/arm/include/asm/arch-meson/sd_emmc.h b/arch/arm/include/asm/arch-meson/sd_emmc.h
>> new file mode 100644
>> index 0000000..a09e034
>> --- /dev/null
>> +++ b/arch/arm/include/asm/arch-meson/sd_emmc.h
>> @@ -0,0 +1,89 @@
>> +/*
>> + * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
>> + *
>> + * 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_CLKSRC_24M		24000000	/* 24 MHz */
>> +#define SD_EMMC_CLKSRC_DIV2		1000000000	/* 1 GHz */
>> +
>> +#define MESON_SD_EMMC_CLOCK		0x00
>> +#define   CLK_MAX_DIV			63
>> +#define   CLK_SRC_24M			(0 << 6)
>> +#define   CLK_SRC_DIV2			(1 << 6)
>> +#define   CLK_CO_PHASE_000		(0 << 8)
>> +#define   CLK_CO_PHASE_090		(1 << 8)
>> +#define   CLK_CO_PHASE_180		(2 << 8)
>> +#define   CLK_CO_PHASE_270		(3 << 8)
>> +#define   CLK_TX_PHASE_000		(0 << 10)
>> +#define   CLK_TX_PHASE_090		(1 << 10)
>> +#define   CLK_TX_PHASE_180		(2 << 10)
>> +#define   CLK_TX_PHASE_270		(3 << 10)
>> +#define   CLK_ALWAYS_ON			BIT(24)
>> +
>> +#define MESON_SD_EMMC_CFG		0x44
>> +#define   CFG_BUS_WIDTH_MASK		GENMASK(1, 0)
>> +#define   CFG_BUS_WIDTH_1		0
>> +#define   CFG_BUS_WIDTH_4		1
>> +#define   CFG_BUS_WIDTH_8		2
>> +#define   CFG_BL_LEN_MASK		GENMASK(7, 4)
>> +#define   CFG_BL_LEN_SHIFT		4
>> +#define   CFG_BL_LEN_512		(9 << 4)
>> +#define   CFG_RESP_TIMEOUT_MASK		GENMASK(11, 8)
>> +#define   CFG_RESP_TIMEOUT_256		(8 << 8)
>> +#define   CFG_RC_CC_MASK		GENMASK(15, 12)
>> +#define   CFG_RC_CC_16			(4 << 12)
>> +#define   CFG_SDCLK_ALWAYS_ON		BIT(18)
>> +#define   CFG_AUTO_CLK			BIT(23)
>> +
>> +#define MESON_SD_EMMC_STATUS		0x48
>> +#define   STATUS_MASK			GENMASK(15, 0)
>> +#define   STATUS_ERR_MASK		GENMASK(12, 0)
>> +#define   STATUS_RXD_ERR_MASK		GENMASK(7, 0)
>> +#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 MESON_SD_EMMC_IRQ_EN		0x4c
>> +
>> +#define MESON_SD_EMMC_CMD_CFG		0x50
>> +#define   CMD_CFG_LENGTH_MASK		GENMASK(8, 0)
>> +#define   CMD_CFG_BLOCK_MODE		BIT(9)
>> +#define   CMD_CFG_R1B			BIT(10)
>> +#define   CMD_CFG_END_OF_CHAIN		BIT(11)
>> +#define   CMD_CFG_TIMEOUT_4S		(12 << 12)
>> +#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_CMD_INDEX_SHIFT	24
>> +#define   CMD_CFG_OWNER			BIT(31)
>> +
>> +#define MESON_SD_EMMC_CMD_ARG		0x54
>> +#define MESON_SD_EMMC_CMD_DAT		0x58
>> +#define MESON_SD_EMMC_CMD_RSP		0x5c
>> +#define MESON_SD_EMMC_CMD_RSP1		0x60
>> +#define MESON_SD_EMMC_CMD_RSP2		0x64
>> +#define MESON_SD_EMMC_CMD_RSP3		0x68
>> +
>> +struct meson_mmc_platdata {
>> +	struct mmc_config cfg;
>> +	struct mmc mmc;
>> +	void *regbase;
>> +	void *w_buf;
>> +};
>> +
>> +#endif
>> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
>> index df4913b..bc647c8 100644
>> --- a/drivers/mmc/Kconfig
>> +++ b/drivers/mmc/Kconfig
>> @@ -172,6 +172,12 @@ config ZYNQ_SDHCI
>>  	help
>>  	  Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
>>  
>> +config MMC_MESON_GX
>> +	bool "Meson GX EMMC controller support"
>> +	depends on DM_MMC && BLK && DM_MMC_OPS && ARCH_MESON
> 
> Not put DM_MMC_OPS.
> 
>> +	help
>> +	  Support for EMMC host controller on Meson GX ARM SoCs platform (S905)
>> +
>>  config ROCKCHIP_SDHCI
>>  	bool "Arasan SDHCI controller for Rockchip support"
>>  	depends on DM_MMC && BLK && DM_MMC_OPS
>> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
>> index 6af7f79..baa6531 100644
>> --- a/drivers/mmc/Makefile
>> +++ b/drivers/mmc/Makefile
>> @@ -31,6 +31,7 @@ ifdef CONFIG_SUPPORT_EMMC_BOOT
>>  obj-$(CONFIG_GENERIC_MMC) += mmc_boot.o
>>  endif
>>  obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
>> +obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o
>>  obj-$(CONFIG_MMC_SPI) += mmc_spi.o
>>  obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
>>  obj-$(CONFIG_MMC_OMAP_HS)		+= omap_hsmmc.o
>> diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c
>> new file mode 100644
>> index 0000000..12d2047
>> --- /dev/null
>> +++ b/drivers/mmc/meson_gx_mmc.c
>> @@ -0,0 +1,291 @@
>> +/*
>> + * (C) Copyright 2016 Carlo Caione <carlo@caione.org>
>> + *
>> + * 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>
>> +#include <linux/log2.h>
>> +
>> +static inline void *get_regbase(const struct mmc *mmc)
>> +{
>> +	struct meson_mmc_platdata *pdata = mmc->priv;
>> +
>> +	return pdata->regbase;
>> +}
>> +
>> +static inline uint32_t meson_read(struct mmc *mmc, int offset)
>> +{
>> +	return readl(get_regbase(mmc) + offset);
>> +}
>> +
>> +static inline void meson_write(struct mmc *mmc, uint32_t val, int offset)
>> +{
>> +	writel(val, get_regbase(mmc) + offset);
>> +}
>> +
>> +static void meson_mmc_config_clock(struct mmc *mmc)
>> +{
>> +	uint32_t meson_mmc_clk = 0;
>> +	unsigned int clk, clk_src, clk_div;
>> +
>> +	/* 1GHz / CLK_MAX_DIV = 15,9 MHz */
>> +	if (mmc->clock > 16000000) {
>> +		clk = SD_EMMC_CLKSRC_DIV2;
>> +		clk_src = CLK_SRC_DIV2;
>> +	} else {
>> +		clk = SD_EMMC_CLKSRC_24M;
>> +		clk_src = CLK_SRC_24M;
>> +	}
>> +	clk_div = DIV_ROUND_UP(clk, mmc->clock);
>> +
>> +	/* core clock phase */
>> +	meson_mmc_clk |= CLK_CO_PHASE_180;
>> +
>> +	/* tx clock phase */
>> +	meson_mmc_clk |= CLK_TX_PHASE_000;
>> +
>> +	/* clock settings */
>> +	meson_mmc_clk |= clk_src;
>> +	meson_mmc_clk |= clk_div;
>> +
>> +	meson_write(mmc, meson_mmc_clk, MESON_SD_EMMC_CLOCK);
>> +}
>> +
>> +static int meson_dm_mmc_set_ios(struct udevice *dev)
>> +{
>> +	struct mmc *mmc = mmc_get_mmc_dev(dev);
>> +	uint32_t meson_mmc_cfg;
>> +
>> +	meson_mmc_config_clock(mmc);
>> +
>> +	meson_mmc_cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
>> +
>> +	meson_mmc_cfg &= ~CFG_BUS_WIDTH_MASK;
>> +	if (mmc->bus_width == 1)
>> +		meson_mmc_cfg |= CFG_BUS_WIDTH_1;
>> +	else if (mmc->bus_width == 4)
>> +		meson_mmc_cfg |= CFG_BUS_WIDTH_4;
>> +	else if (mmc->bus_width == 8)
>> +		meson_mmc_cfg |= CFG_BUS_WIDTH_8;
>> +	else
>> +		return -EINVAL;
>> +
>> +	/* 512 bytes block length */
>> +	meson_mmc_cfg &= ~CFG_BL_LEN_MASK;
>> +	meson_mmc_cfg |= CFG_BL_LEN_512;
>> +
>> +	/* Response timeout 256 clk */
>> +	meson_mmc_cfg &= ~CFG_RESP_TIMEOUT_MASK;
>> +	meson_mmc_cfg |= CFG_RESP_TIMEOUT_256;
>> +
>> +	/* Command-command gap 16 clk */
>> +	meson_mmc_cfg &= ~CFG_RC_CC_MASK;
>> +	meson_mmc_cfg |= CFG_RC_CC_16;
>> +
>> +	meson_write(mmc, meson_mmc_cfg, MESON_SD_EMMC_CFG);
>> +
>> +	return 0;
>> +}
>> +
>> +static void meson_mmc_setup_cmd(struct mmc *mmc, struct mmc_data *data,
>> +				struct mmc_cmd *cmd)
>> +{
>> +	uint32_t meson_mmc_cmd = 0, cfg;
>> +
>> +	meson_mmc_cmd |= cmd->cmdidx << CMD_CFG_CMD_INDEX_SHIFT;
>> +
>> +	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;
>> +
>> +		if (!(cmd->resp_type & MMC_RSP_CRC))
>> +			meson_mmc_cmd |= CMD_CFG_RESP_NOCRC;
>> +	} else {
>> +		meson_mmc_cmd |= CMD_CFG_NO_RESP;
>> +	}
>> +
>> +	if (data) {
>> +		cfg = meson_read(mmc, MESON_SD_EMMC_CFG);
>> +		cfg &= ~CFG_BL_LEN_MASK;
>> +		cfg |= ilog2(data->blocksize) << CFG_BL_LEN_SHIFT;
>> +		meson_write(mmc, cfg, MESON_SD_EMMC_CFG);
>> +
>> +		if (data->flags == MMC_DATA_WRITE)
>> +			meson_mmc_cmd |= CMD_CFG_DATA_WR;
>> +
>> +		cfg = CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data->blocks;
>> +		meson_mmc_cmd |= cfg;
> 
> This code doesn't need. You can use the below code.
> cfg |= CMD_CFG_TIMEOUT_4S | ...;
> 
OK, will send a v5 of the patch series.

>> +	}
>> +
>> +	cfg = CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN;
>> +	meson_mmc_cmd |= cfg;
>> +
>> +	meson_write(mmc, meson_mmc_cmd, MESON_SD_EMMC_CMD_CFG);
>> +}
>> +
>> +static void meson_mmc_setup_addr(struct mmc *mmc, struct mmc_data *data)
>> +{
>> +	struct meson_mmc_platdata *pdata = mmc->priv;
>> +	unsigned int data_size;
>> +	uint32_t data_addr = 0;
>> +
>> +	if (data) {
>> +		data_size = data->blocks * data->blocksize;
>> +
>> +		if (data->flags == MMC_DATA_READ) {
>> +			data_addr = (ulong) data->dest;
>> +			invalidate_dcache_range(data_addr,
>> +						data_addr + data_size);
>> +		} else if (data->flags == MMC_DATA_WRITE) {
> 
> not need to use "else if", use "else"
> 
OK

>> +			pdata->w_buf = calloc(data_size, sizeof(char));
>> +			data_addr = (ulong) pdata->w_buf;
>> +			memcpy(pdata->w_buf, data->src, data_size);
>> +			flush_dcache_range(data_addr, data_addr + data_size);
>> +		}
>> +	}
>> +
>> +	meson_write(mmc, data_addr, MESON_SD_EMMC_CMD_DAT);
>> +}
>> +
>> +static void meson_mmc_read_response(struct mmc *mmc, struct mmc_cmd *cmd)
>> +{
>> +	if (cmd->resp_type & MMC_RSP_136) {
>> +		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP3);
>> +		cmd->response[1] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP2);
>> +		cmd->response[2] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP1);
>> +		cmd->response[3] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
>> +	} else {
>> +		cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP);
>> +	}
>> +}
>> +
>> +static int meson_dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>> +				 struct mmc_data *data)
>> +{
>> +	struct mmc *mmc = mmc_get_mmc_dev(dev);
>> +	struct meson_mmc_platdata *pdata = mmc->priv;
>> +	uint32_t status;
>> +	ulong start;
>> +	int ret = 0;
>> +
>> +	/* max block size supported by chip is 512 byte */
>> +	if (data && data->blocksize > 512)
>> +		return -EINVAL;
>> +
>> +	meson_mmc_setup_cmd(mmc, data, cmd);
>> +	meson_mmc_setup_addr(mmc, data);
>> +
>> +	meson_write(mmc, cmd->cmdarg, MESON_SD_EMMC_CMD_ARG);
>> +
>> +	/* use 10s timeout */
>> +	start = get_timer(0);
>> +	do {
>> +		status = meson_read(mmc, MESON_SD_EMMC_STATUS);
>> +	} while(!(status & STATUS_END_OF_CHAIN) && get_timer(start) < 10000);
>> +
>> +	if (!(status & STATUS_END_OF_CHAIN))
>> +		ret = -ETIMEDOUT;
>> +	else if (status & STATUS_RESP_TIMEOUT)
>> +		ret = -ETIMEDOUT;
>> +	else if (status & STATUS_ERR_MASK)
>> +		ret = -EIO;
>> +
>> +	meson_mmc_read_response(mmc, cmd);
>> +
>> +	if (data && data->flags == MMC_DATA_WRITE)
>> +		free(pdata->w_buf);
>> +
>> +	/* reset status bits */
>> +	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct dm_mmc_ops meson_dm_mmc_ops = {
>> +	.send_cmd = meson_dm_mmc_send_cmd,
>> +	.set_ios = meson_dm_mmc_set_ios,
>> +};
>> +
>> +static int meson_mmc_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
>> +	fdt_addr_t addr;
>> +
>> +	addr = dev_get_addr(dev);
>> +	if (addr == FDT_ADDR_T_NONE)
>> +		return -EINVAL;
>> +
>> +	pdata->regbase = (void *)addr;
>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_mmc_probe(struct udevice *dev)
>> +{
>> +	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
>> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>> +	struct mmc *mmc = &pdata->mmc;
>> +	struct mmc_config *cfg = &pdata->cfg;
>> +	uint32_t val;
>> +
>> +	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 = DIV_ROUND_UP(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV);
>> +	cfg->f_max = 100000000; /* 100 MHz */
>> +	cfg->b_max = 256; /* max 256 blocks */
>> +	cfg->name = dev->name;
>> +
>> +	mmc->priv = pdata;
>> +	upriv->mmc = mmc;
>> +
>> +	mmc_set_clock(mmc, cfg->f_min);
>> +
>> +	/* reset all status bits */
>> +	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
>> +
>> +	/* disable interrupts */
>> +	meson_write(mmc, 0, MESON_SD_EMMC_IRQ_EN);
>> +
>> +	/* enable auto clock mode */
>> +	val = meson_read(mmc, MESON_SD_EMMC_CFG);
>> +	val &= ~CFG_SDCLK_ALWAYS_ON;
>> +	val |= CFG_AUTO_CLK;
>> +	meson_write(mmc, val, MESON_SD_EMMC_CFG);
>> +
>> +	return 0;
>> +}
>> +
>> +int meson_mmc_bind(struct udevice *dev)
>> +{
>> +	struct meson_mmc_platdata *pdata = dev_get_platdata(dev);
>> +
>> +	return mmc_bind(dev, &pdata->mmc, &pdata->cfg);
>> +}
>> +
>> +static const struct udevice_id meson_mmc_match[] = {
>> +	{ .compatible = "amlogic,meson-gx-mmc" },
>> +	{ /* sentinel */ }
>> +};
>> +
>> +U_BOOT_DRIVER(meson_mmc) = {
>> +	.name = "meson_gx_mmc",
>> +	.id = UCLASS_MMC,
>> +	.of_match = meson_mmc_match,
>> +	.ops = &meson_dm_mmc_ops,
>> +	.probe = meson_mmc_probe,
>> +	.bind = meson_mmc_bind,
>> +	.ofdata_to_platdata = meson_mmc_ofdata_to_platdata,
>> +	.platdata_auto_alloc_size = sizeof(struct meson_mmc_platdata),
>> +};
>>
> 
> 

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

end of thread, other threads:[~2017-02-20  6:52 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <5bb641b5-36d4-654a-2e4a-ce8215053540@gmail.com>
2017-02-02 19:03 ` [U-Boot] [PATCH v4 2/3] mmc: meson: add MMC driver for Meson GX (S905) Carlo Caione
2017-02-19 22:00   ` Jaehoon Chung
2017-02-20  6:52     ` Heiner Kallweit
2017-02-02 19:04 ` [U-Boot] [PATCH v4 3/3] odroid-c2: enable new Meson GX MMC driver in board defconfig Heiner Kallweit
2017-02-02 19:37 ` [U-Boot] [PATCH v5 2/3] mmc: meson: add MMC driver for Meson GX (S905) Heiner Kallweit

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.