From patchwork Thu May 12 12:41:41 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Carlo Caione X-Patchwork-Id: 621488 X-Patchwork-Delegate: jh80.chung@samsung.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3r5Ckl039Nz9sD5 for ; Thu, 12 May 2016 22:59:59 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=vrr0alqY; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id ECB96A75F8; Thu, 12 May 2016 14:59:52 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cpEJn3dYkYQK; Thu, 12 May 2016 14:59:52 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5D67BA7603; Thu, 12 May 2016 14:59:43 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D7B55A74BA for ; Thu, 12 May 2016 14:41:54 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ccIiBCD1F21o for ; Thu, 12 May 2016 14:41:54 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com [74.125.82.68]) by theia.denx.de (Postfix) with ESMTPS id 8F5ABA74A8 for ; Thu, 12 May 2016 14:41:50 +0200 (CEST) Received: by mail-wm0-f68.google.com with SMTP id e201so15884881wme.2 for ; Thu, 12 May 2016 05:41:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=WYyPjswyz8+ZSUkHzySeYeopRORZgrUzP+6du/w0sec=; b=vrr0alqYkb2Og9CqjSsknSi/9ctheOScR3s0Px2WeoOAC/ZBevkAULDA81tkIE2kDM s2d8E+WO6cF1jfUoMJg1TOTBVH3T+68TZxNXwQlcRMRq3RBUQr6zdeasRIhDrBHx6VnS 6pOwPIFD/d5bD+D+jeLhJzKePO7vg1BW+a5ogDMLaol3vkhO5+Cx9AfW0hQyyxWbDQOo XDCe1MiOuwFxz7XHeKhNOJJxX56NlVrO9QjrcM0BtfUOHs1IJnviAQfaI9FQPAaEJBnl YL7xeZUAT56lB00R19zNzLS/C741jX59qzL9arSdLIeMsQH/UKGFeNBgmMR5v8VUyAnr w9ZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=WYyPjswyz8+ZSUkHzySeYeopRORZgrUzP+6du/w0sec=; b=Ch65/D+18nryHe3O8xlxf7mfHuHITJUc+Hecw/re/sX133TR5IwIdNlb5lZCZt0h7n xwvji1CuEb3ebMD++LhirJCJ37OhSTRcg7IQIrkFBu3wtKfI+pzqg+DxPv8K1fP1FIHg rQwEdw4UClZTe4pZF58dW3QqjpPVCmXPZPUvQq9sf7+fJkHrE+L6yEA9Ztuof2Szd4EP wkkZEx1mKjpYyy5+uQu3T1HxCYIc0xrE6BwdrXAiP9p8ycRv4F85fajyCWWDoVNcJ5P1 Z40N8zc3R5Ygt1UKDVx1eW84dl1b8hFDnOVvXIvbJc3zK+RxqoBTFNrL2HjGwH7THmDY SMBA== X-Gm-Message-State: AOPr4FWZFFajQQzZNeaLBKveF66hy4yw6Jb0pcecioWet2ZRmOkHXwQNjzLDUzxwCVOSKg== X-Received: by 10.194.116.9 with SMTP id js9mr11024289wjb.112.1463056909920; Thu, 12 May 2016 05:41:49 -0700 (PDT) Received: from localhost.localdomain (2-238-57-164.ip242.fastwebnet.it. [2.238.57.164]) by smtp.gmail.com with ESMTPSA id q125sm13709933wmd.19.2016.05.12.05.41.48 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 12 May 2016 05:41:48 -0700 (PDT) From: Carlo Caione To: u-boot@lists.denx.de, b.galvani@gmail.com, albert.u.boot@aribaud.net, panto@antoniou-consulting.com, khilman@baylibre.com, linux@endlessm.com, linux-amlogic@lists.infradead.org Date: Thu, 12 May 2016 14:41:41 +0200 Message-Id: <1463056902-8533-2-git-send-email-carlo@caione.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1463056902-8533-1-git-send-email-carlo@caione.org> References: <1463056902-8533-1-git-send-email-carlo@caione.org> X-Mailman-Approved-At: Thu, 12 May 2016 14:59:37 +0200 Cc: Carlo Caione Subject: [U-Boot] [PATCH 1/2] mmc: Add Amlogic Meson driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Carlo Caione This is a port / rewrite of the Amlogic driver shipped whithin the Amlogic SDK for the Meson GXBaby (S905) SoC. Signed-off-by: Carlo Caione --- 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 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..6781dca --- /dev/null +++ b/arch/arm/include/asm/arch-meson/sd_emmc.h @@ -0,0 +1,109 @@ +/* + * (C) Copyright 2016 Carlo Caione + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SD_EMMC_H__ +#define __SD_EMMC_H__ + +#include + +#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 diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 585aaf3..08ac9ba 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -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 diff --git a/drivers/mmc/meson_mmc.c b/drivers/mmc/meson_mmc.c new file mode 100644 index 0000000..af224ec --- /dev/null +++ b/drivers/mmc/meson_mmc.c @@ -0,0 +1,305 @@ +/* + * (C) Copyright 2016 Carlo Caione + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +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; + } 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; + + /* Keep the clock always on */ + meson_mmc_clk |= (1 << CLK_ALWAYS_ON); + + /* 180 phase */ + meson_mmc_clk |= (2 << CLK_CO_PHASE); + + /* clock settings */ + meson_mmc_clk |= (clk_src << CLK_SRC); + meson_mmc_clk |= (clk_div << CLK_DIV); + + 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 */ + 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); + + writel(meson_mmc_cfg, &meson_mmc_reg->gcfg); + + return; +} + +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) +{ + 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; + + 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) { + 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) { + 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); + 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; + } + + 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; + + 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; + 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), +};