u-boot-brain/drivers/mmc/uniphier-sd.c
Stephen Warren 135aa95002 clk: convert API to match reset/mailbox style
The following changes are made to the clock API:
* The concept of "clocks" and "peripheral clocks" are unified; each clock
  provider now implements a single set of clocks. This provides a simpler
  conceptual interface to clients, and better aligns with device tree
  clock bindings.
* Clocks are now identified with a single "struct clk", rather than
  requiring clients to store the clock provider device and clock identity
  values separately. For simple clock consumers, this isolates clients
  from internal details of the clock API.
* clk.h is split so it only contains the client/consumer API, whereas
  clk-uclass.h contains the provider API. This aligns with the recently
  added reset and mailbox APIs.
* clk_ops .of_xlate(), .request(), and .free() are added so providers
  can customize these operations if needed. This also aligns with the
  recently added reset and mailbox APIs.
* clk_disable() is added.
* All users of the current clock APIs are updated.
* Sandbox clock tests are updated to exercise clock lookup via DT, and
  clock enable/disable.
* rkclk_get_clk() is removed and replaced with standard APIs.

Buildman shows no clock-related errors for any board for which buildman
can download a toolchain.

test/py passes for sandbox (which invokes the dm clk test amongst
others).

Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Simon Glass <sjg@chromium.org>
2016-06-19 17:05:55 -06:00

757 lines
21 KiB
C

/*
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <clk.h>
#include <fdtdec.h>
#include <mapmem.h>
#include <mmc.h>
#include <dm/device.h>
#include <linux/compat.h>
#include <linux/io.h>
#include <linux/sizes.h>
#include <asm/unaligned.h>
#include <asm/dma-mapping.h>
DECLARE_GLOBAL_DATA_PTR;
#define UNIPHIER_SD_CMD 0x000 /* command */
#define UNIPHIER_SD_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */
#define UNIPHIER_SD_CMD_MULTI BIT(13) /* multiple block transfer */
#define UNIPHIER_SD_CMD_RD BIT(12) /* 1: read, 0: write */
#define UNIPHIER_SD_CMD_DATA BIT(11) /* data transfer */
#define UNIPHIER_SD_CMD_APP BIT(6) /* ACMD preceded by CMD55 */
#define UNIPHIER_SD_CMD_NORMAL (0 << 8)/* auto-detect of resp-type */
#define UNIPHIER_SD_CMD_RSP_NONE (3 << 8)/* response: none */
#define UNIPHIER_SD_CMD_RSP_R1 (4 << 8)/* response: R1, R5, R6, R7 */
#define UNIPHIER_SD_CMD_RSP_R1B (5 << 8)/* response: R1b, R5b */
#define UNIPHIER_SD_CMD_RSP_R2 (6 << 8)/* response: R2 */
#define UNIPHIER_SD_CMD_RSP_R3 (7 << 8)/* response: R3, R4 */
#define UNIPHIER_SD_ARG 0x008 /* command argument */
#define UNIPHIER_SD_STOP 0x010 /* stop action control */
#define UNIPHIER_SD_STOP_SEC BIT(8) /* use sector count */
#define UNIPHIER_SD_STOP_STP BIT(0) /* issue CMD12 */
#define UNIPHIER_SD_SECCNT 0x014 /* sector counter */
#define UNIPHIER_SD_RSP10 0x018 /* response[39:8] */
#define UNIPHIER_SD_RSP32 0x020 /* response[71:40] */
#define UNIPHIER_SD_RSP54 0x028 /* response[103:72] */
#define UNIPHIER_SD_RSP76 0x030 /* response[127:104] */
#define UNIPHIER_SD_INFO1 0x038 /* IRQ status 1 */
#define UNIPHIER_SD_INFO1_CD BIT(5) /* state of card detect */
#define UNIPHIER_SD_INFO1_INSERT BIT(4) /* card inserted */
#define UNIPHIER_SD_INFO1_REMOVE BIT(3) /* card removed */
#define UNIPHIER_SD_INFO1_CMP BIT(2) /* data complete */
#define UNIPHIER_SD_INFO1_RSP BIT(0) /* response complete */
#define UNIPHIER_SD_INFO2 0x03c /* IRQ status 2 */
#define UNIPHIER_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */
#define UNIPHIER_SD_INFO2_CBSY BIT(14) /* command busy */
#define UNIPHIER_SD_INFO2_BWE BIT(9) /* write buffer ready */
#define UNIPHIER_SD_INFO2_BRE BIT(8) /* read buffer ready */
#define UNIPHIER_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */
#define UNIPHIER_SD_INFO2_ERR_RTO BIT(6) /* response time out */
#define UNIPHIER_SD_INFO2_ERR_ILR BIT(5) /* illegal read err */
#define UNIPHIER_SD_INFO2_ERR_ILW BIT(4) /* illegal write err */
#define UNIPHIER_SD_INFO2_ERR_TO BIT(3) /* time out error */
#define UNIPHIER_SD_INFO2_ERR_END BIT(2) /* END bit error */
#define UNIPHIER_SD_INFO2_ERR_CRC BIT(1) /* CRC error */
#define UNIPHIER_SD_INFO2_ERR_IDX BIT(0) /* cmd index error */
#define UNIPHIER_SD_INFO1_MASK 0x040
#define UNIPHIER_SD_INFO2_MASK 0x044
#define UNIPHIER_SD_CLKCTL 0x048 /* clock divisor */
#define UNIPHIER_SD_CLKCTL_DIV_MASK 0x104ff
#define UNIPHIER_SD_CLKCTL_DIV1024 BIT(16) /* SDCLK = CLK / 1024 */
#define UNIPHIER_SD_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */
#define UNIPHIER_SD_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */
#define UNIPHIER_SD_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */
#define UNIPHIER_SD_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */
#define UNIPHIER_SD_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */
#define UNIPHIER_SD_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */
#define UNIPHIER_SD_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */
#define UNIPHIER_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */
#define UNIPHIER_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */
#define UNIPHIER_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */
#define UNIPHIER_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */
#define UNIPHIER_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */
#define UNIPHIER_SD_SIZE 0x04c /* block size */
#define UNIPHIER_SD_OPTION 0x050
#define UNIPHIER_SD_OPTION_WIDTH_MASK (5 << 13)
#define UNIPHIER_SD_OPTION_WIDTH_1 (4 << 13)
#define UNIPHIER_SD_OPTION_WIDTH_4 (0 << 13)
#define UNIPHIER_SD_OPTION_WIDTH_8 (1 << 13)
#define UNIPHIER_SD_BUF 0x060 /* read/write buffer */
#define UNIPHIER_SD_EXTMODE 0x1b0
#define UNIPHIER_SD_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */
#define UNIPHIER_SD_SOFT_RST 0x1c0
#define UNIPHIER_SD_SOFT_RST_RSTX BIT(0) /* reset deassert */
#define UNIPHIER_SD_VERSION 0x1c4 /* version register */
#define UNIPHIER_SD_VERSION_IP 0xff /* IP version */
#define UNIPHIER_SD_HOST_MODE 0x1c8
#define UNIPHIER_SD_IF_MODE 0x1cc
#define UNIPHIER_SD_IF_MODE_DDR BIT(0) /* DDR mode */
#define UNIPHIER_SD_VOLT 0x1e4 /* voltage switch */
#define UNIPHIER_SD_VOLT_MASK (3 << 0)
#define UNIPHIER_SD_VOLT_OFF (0 << 0)
#define UNIPHIER_SD_VOLT_330 (1 << 0)/* 3.3V signal */
#define UNIPHIER_SD_VOLT_180 (2 << 0)/* 1.8V signal */
#define UNIPHIER_SD_DMA_MODE 0x410
#define UNIPHIER_SD_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */
#define UNIPHIER_SD_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */
#define UNIPHIER_SD_DMA_CTL 0x414
#define UNIPHIER_SD_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */
#define UNIPHIER_SD_DMA_RST 0x418
#define UNIPHIER_SD_DMA_RST_RD BIT(9)
#define UNIPHIER_SD_DMA_RST_WR BIT(8)
#define UNIPHIER_SD_DMA_INFO1 0x420
#define UNIPHIER_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete*/
#define UNIPHIER_SD_DMA_INFO1_END_RD BIT(17) /* Don't use! Hardware bug */
#define UNIPHIER_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */
#define UNIPHIER_SD_DMA_INFO1_MASK 0x424
#define UNIPHIER_SD_DMA_INFO2 0x428
#define UNIPHIER_SD_DMA_INFO2_ERR_RD BIT(17)
#define UNIPHIER_SD_DMA_INFO2_ERR_WR BIT(16)
#define UNIPHIER_SD_DMA_INFO2_MASK 0x42c
#define UNIPHIER_SD_DMA_ADDR_L 0x440
#define UNIPHIER_SD_DMA_ADDR_H 0x444
/* alignment required by the DMA engine of this controller */
#define UNIPHIER_SD_DMA_MINALIGN 0x10
struct uniphier_sd_priv {
struct mmc_config cfg;
struct mmc *mmc;
struct udevice *dev;
void __iomem *regbase;
unsigned long mclk;
unsigned int version;
u32 caps;
#define UNIPHIER_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */
#define UNIPHIER_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */
#define UNIPHIER_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */
};
static dma_addr_t __dma_map_single(void *ptr, size_t size,
enum dma_data_direction dir)
{
unsigned long addr = (unsigned long)ptr;
if (dir == DMA_FROM_DEVICE)
invalidate_dcache_range(addr, addr + size);
else
flush_dcache_range(addr, addr + size);
return addr;
}
static void __dma_unmap_single(dma_addr_t addr, size_t size,
enum dma_data_direction dir)
{
if (dir != DMA_TO_DEVICE)
invalidate_dcache_range(addr, addr + size);
}
static int uniphier_sd_check_error(struct uniphier_sd_priv *priv)
{
u32 info2 = readl(priv->regbase + UNIPHIER_SD_INFO2);
if (info2 & UNIPHIER_SD_INFO2_ERR_RTO) {
/*
* TIMEOUT must be returned for unsupported command. Do not
* display error log since this might be a part of sequence to
* distinguish between SD and MMC.
*/
return TIMEOUT;
}
if (info2 & UNIPHIER_SD_INFO2_ERR_TO) {
dev_err(priv->dev, "timeout error\n");
return -ETIMEDOUT;
}
if (info2 & (UNIPHIER_SD_INFO2_ERR_END | UNIPHIER_SD_INFO2_ERR_CRC |
UNIPHIER_SD_INFO2_ERR_IDX)) {
dev_err(priv->dev, "communication out of sync\n");
return -EILSEQ;
}
if (info2 & (UNIPHIER_SD_INFO2_ERR_ILA | UNIPHIER_SD_INFO2_ERR_ILR |
UNIPHIER_SD_INFO2_ERR_ILW)) {
dev_err(priv->dev, "illegal access\n");
return -EIO;
}
return 0;
}
static int uniphier_sd_wait_for_irq(struct uniphier_sd_priv *priv,
unsigned int reg, u32 flag)
{
long wait = 1000000;
int ret;
while (!(readl(priv->regbase + reg) & flag)) {
if (wait-- < 0) {
dev_err(priv->dev, "timeout\n");
return -ETIMEDOUT;
}
ret = uniphier_sd_check_error(priv);
if (ret)
return ret;
udelay(1);
}
return 0;
}
static int uniphier_sd_pio_read_one_block(struct mmc *mmc, u32 **pbuf,
uint blocksize)
{
struct uniphier_sd_priv *priv = mmc->priv;
int i, ret;
/* wait until the buffer is filled with data */
ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO2,
UNIPHIER_SD_INFO2_BRE);
if (ret)
return ret;
/*
* Clear the status flag _before_ read the buffer out because
* UNIPHIER_SD_INFO2_BRE is edge-triggered, not level-triggered.
*/
writel(0, priv->regbase + UNIPHIER_SD_INFO2);
if (likely(IS_ALIGNED((unsigned long)*pbuf, 4))) {
for (i = 0; i < blocksize / 4; i++)
*(*pbuf)++ = readl(priv->regbase + UNIPHIER_SD_BUF);
} else {
for (i = 0; i < blocksize / 4; i++)
put_unaligned(readl(priv->regbase + UNIPHIER_SD_BUF),
(*pbuf)++);
}
return 0;
}
static int uniphier_sd_pio_write_one_block(struct mmc *mmc, const u32 **pbuf,
uint blocksize)
{
struct uniphier_sd_priv *priv = mmc->priv;
int i, ret;
/* wait until the buffer becomes empty */
ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO2,
UNIPHIER_SD_INFO2_BWE);
if (ret)
return ret;
writel(0, priv->regbase + UNIPHIER_SD_INFO2);
if (likely(IS_ALIGNED((unsigned long)*pbuf, 4))) {
for (i = 0; i < blocksize / 4; i++)
writel(*(*pbuf)++, priv->regbase + UNIPHIER_SD_BUF);
} else {
for (i = 0; i < blocksize / 4; i++)
writel(get_unaligned((*pbuf)++),
priv->regbase + UNIPHIER_SD_BUF);
}
return 0;
}
static int uniphier_sd_pio_xfer(struct mmc *mmc, struct mmc_data *data)
{
u32 *dest = (u32 *)data->dest;
const u32 *src = (const u32 *)data->src;
int i, ret;
for (i = 0; i < data->blocks; i++) {
if (data->flags & MMC_DATA_READ)
ret = uniphier_sd_pio_read_one_block(mmc, &dest,
data->blocksize);
else
ret = uniphier_sd_pio_write_one_block(mmc, &src,
data->blocksize);
if (ret)
return ret;
}
return 0;
}
static void uniphier_sd_dma_start(struct uniphier_sd_priv *priv,
dma_addr_t dma_addr)
{
u32 tmp;
writel(0, priv->regbase + UNIPHIER_SD_DMA_INFO1);
writel(0, priv->regbase + UNIPHIER_SD_DMA_INFO2);
/* enable DMA */
tmp = readl(priv->regbase + UNIPHIER_SD_EXTMODE);
tmp |= UNIPHIER_SD_EXTMODE_DMA_EN;
writel(tmp, priv->regbase + UNIPHIER_SD_EXTMODE);
writel(dma_addr & U32_MAX, priv->regbase + UNIPHIER_SD_DMA_ADDR_L);
/* suppress the warning "right shift count >= width of type" */
dma_addr >>= min_t(int, 32, 8 * sizeof(dma_addr));
writel(dma_addr & U32_MAX, priv->regbase + UNIPHIER_SD_DMA_ADDR_H);
writel(UNIPHIER_SD_DMA_CTL_START, priv->regbase + UNIPHIER_SD_DMA_CTL);
}
static int uniphier_sd_dma_wait_for_irq(struct uniphier_sd_priv *priv, u32 flag,
unsigned int blocks)
{
long wait = 1000000 + 10 * blocks;
while (!(readl(priv->regbase + UNIPHIER_SD_DMA_INFO1) & flag)) {
if (wait-- < 0) {
dev_err(priv->dev, "timeout during DMA\n");
return -ETIMEDOUT;
}
udelay(10);
}
if (readl(priv->regbase + UNIPHIER_SD_DMA_INFO2)) {
dev_err(priv->dev, "error during DMA\n");
return -EIO;
}
return 0;
}
static int uniphier_sd_dma_xfer(struct mmc *mmc, struct mmc_data *data)
{
struct uniphier_sd_priv *priv = mmc->priv;
size_t len = data->blocks * data->blocksize;
void *buf;
enum dma_data_direction dir;
dma_addr_t dma_addr;
u32 poll_flag, tmp;
int ret;
tmp = readl(priv->regbase + UNIPHIER_SD_DMA_MODE);
if (data->flags & MMC_DATA_READ) {
buf = data->dest;
dir = DMA_FROM_DEVICE;
poll_flag = UNIPHIER_SD_DMA_INFO1_END_RD2;
tmp |= UNIPHIER_SD_DMA_MODE_DIR_RD;
} else {
buf = (void *)data->src;
dir = DMA_TO_DEVICE;
poll_flag = UNIPHIER_SD_DMA_INFO1_END_WR;
tmp &= ~UNIPHIER_SD_DMA_MODE_DIR_RD;
}
writel(tmp, priv->regbase + UNIPHIER_SD_DMA_MODE);
dma_addr = __dma_map_single(buf, len, dir);
uniphier_sd_dma_start(priv, dma_addr);
ret = uniphier_sd_dma_wait_for_irq(priv, poll_flag, data->blocks);
__dma_unmap_single(dma_addr, len, dir);
return ret;
}
/* check if the address is DMA'able */
static bool uniphier_sd_addr_is_dmaable(unsigned long addr)
{
if (!IS_ALIGNED(addr, UNIPHIER_SD_DMA_MINALIGN))
return false;
#if defined(CONFIG_ARCH_UNIPHIER) && !defined(CONFIG_ARM64) && \
defined(CONFIG_SPL_BUILD)
/*
* For UniPhier ARMv7 SoCs, the stack is allocated in the locked ways
* of L2, which is unreachable from the DMA engine.
*/
if (addr < CONFIG_SPL_STACK)
return false;
#endif
return true;
}
static int uniphier_sd_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct uniphier_sd_priv *priv = mmc->priv;
int ret;
u32 tmp;
if (readl(priv->regbase + UNIPHIER_SD_INFO2) & UNIPHIER_SD_INFO2_CBSY) {
dev_err(priv->dev, "command busy\n");
return -EBUSY;
}
/* clear all status flags */
writel(0, priv->regbase + UNIPHIER_SD_INFO1);
writel(0, priv->regbase + UNIPHIER_SD_INFO2);
/* disable DMA once */
tmp = readl(priv->regbase + UNIPHIER_SD_EXTMODE);
tmp &= ~UNIPHIER_SD_EXTMODE_DMA_EN;
writel(tmp, priv->regbase + UNIPHIER_SD_EXTMODE);
writel(cmd->cmdarg, priv->regbase + UNIPHIER_SD_ARG);
tmp = cmd->cmdidx;
if (data) {
writel(data->blocksize, priv->regbase + UNIPHIER_SD_SIZE);
writel(data->blocks, priv->regbase + UNIPHIER_SD_SECCNT);
/* Do not send CMD12 automatically */
tmp |= UNIPHIER_SD_CMD_NOSTOP | UNIPHIER_SD_CMD_DATA;
if (data->blocks > 1)
tmp |= UNIPHIER_SD_CMD_MULTI;
if (data->flags & MMC_DATA_READ)
tmp |= UNIPHIER_SD_CMD_RD;
}
/*
* Do not use the response type auto-detection on this hardware.
* CMD8, for example, has different response types on SD and eMMC,
* while this controller always assumes the response type for SD.
* Set the response type manually.
*/
switch (cmd->resp_type) {
case MMC_RSP_NONE:
tmp |= UNIPHIER_SD_CMD_RSP_NONE;
break;
case MMC_RSP_R1:
tmp |= UNIPHIER_SD_CMD_RSP_R1;
break;
case MMC_RSP_R1b:
tmp |= UNIPHIER_SD_CMD_RSP_R1B;
break;
case MMC_RSP_R2:
tmp |= UNIPHIER_SD_CMD_RSP_R2;
break;
case MMC_RSP_R3:
tmp |= UNIPHIER_SD_CMD_RSP_R3;
break;
default:
dev_err(priv->dev, "unknown response type\n");
return -EINVAL;
}
dev_dbg(priv->dev, "sending CMD%d (SD_CMD=%08x, SD_ARG=%08x)\n",
cmd->cmdidx, tmp, cmd->cmdarg);
writel(tmp, priv->regbase + UNIPHIER_SD_CMD);
ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO1,
UNIPHIER_SD_INFO1_RSP);
if (ret)
return ret;
if (cmd->resp_type & MMC_RSP_136) {
u32 rsp_127_104 = readl(priv->regbase + UNIPHIER_SD_RSP76);
u32 rsp_103_72 = readl(priv->regbase + UNIPHIER_SD_RSP54);
u32 rsp_71_40 = readl(priv->regbase + UNIPHIER_SD_RSP32);
u32 rsp_39_8 = readl(priv->regbase + UNIPHIER_SD_RSP10);
cmd->response[0] = (rsp_127_104 & 0xffffff) << 8 |
(rsp_103_72 & 0xff);
cmd->response[1] = (rsp_103_72 & 0xffffff) << 8 |
(rsp_71_40 & 0xff);
cmd->response[2] = (rsp_71_40 & 0xffffff) << 8 |
(rsp_39_8 & 0xff);
cmd->response[3] = (rsp_39_8 & 0xffffff) << 8;
} else {
/* bit 39-8 */
cmd->response[0] = readl(priv->regbase + UNIPHIER_SD_RSP10);
}
if (data) {
/* use DMA if the HW supports it and the buffer is aligned */
if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL &&
uniphier_sd_addr_is_dmaable((long)data->src))
ret = uniphier_sd_dma_xfer(mmc, data);
else
ret = uniphier_sd_pio_xfer(mmc, data);
ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO1,
UNIPHIER_SD_INFO1_CMP);
if (ret)
return ret;
}
return ret;
}
static void uniphier_sd_set_bus_width(struct uniphier_sd_priv *priv,
struct mmc *mmc)
{
u32 val, tmp;
switch (mmc->bus_width) {
case 1:
val = UNIPHIER_SD_OPTION_WIDTH_1;
break;
case 4:
val = UNIPHIER_SD_OPTION_WIDTH_4;
break;
case 8:
val = UNIPHIER_SD_OPTION_WIDTH_8;
break;
default:
BUG();
break;
}
tmp = readl(priv->regbase + UNIPHIER_SD_OPTION);
tmp &= ~UNIPHIER_SD_OPTION_WIDTH_MASK;
tmp |= val;
writel(tmp, priv->regbase + UNIPHIER_SD_OPTION);
}
static void uniphier_sd_set_ddr_mode(struct uniphier_sd_priv *priv,
struct mmc *mmc)
{
u32 tmp;
tmp = readl(priv->regbase + UNIPHIER_SD_IF_MODE);
if (mmc->ddr_mode)
tmp |= UNIPHIER_SD_IF_MODE_DDR;
else
tmp &= ~UNIPHIER_SD_IF_MODE_DDR;
writel(tmp, priv->regbase + UNIPHIER_SD_IF_MODE);
}
static void uniphier_sd_set_clk_rate(struct uniphier_sd_priv *priv,
struct mmc *mmc)
{
unsigned int divisor;
u32 val, tmp;
if (!mmc->clock)
return;
divisor = DIV_ROUND_UP(priv->mclk, mmc->clock);
if (divisor <= 1)
val = UNIPHIER_SD_CLKCTL_DIV1;
else if (divisor <= 2)
val = UNIPHIER_SD_CLKCTL_DIV2;
else if (divisor <= 4)
val = UNIPHIER_SD_CLKCTL_DIV4;
else if (divisor <= 8)
val = UNIPHIER_SD_CLKCTL_DIV8;
else if (divisor <= 16)
val = UNIPHIER_SD_CLKCTL_DIV16;
else if (divisor <= 32)
val = UNIPHIER_SD_CLKCTL_DIV32;
else if (divisor <= 64)
val = UNIPHIER_SD_CLKCTL_DIV64;
else if (divisor <= 128)
val = UNIPHIER_SD_CLKCTL_DIV128;
else if (divisor <= 256)
val = UNIPHIER_SD_CLKCTL_DIV256;
else if (divisor <= 512 || !(priv->caps & UNIPHIER_SD_CAP_DIV1024))
val = UNIPHIER_SD_CLKCTL_DIV512;
else
val = UNIPHIER_SD_CLKCTL_DIV1024;
tmp = readl(priv->regbase + UNIPHIER_SD_CLKCTL);
/* stop the clock before changing its rate to avoid a glitch signal */
tmp &= ~UNIPHIER_SD_CLKCTL_SCLKEN;
writel(tmp, priv->regbase + UNIPHIER_SD_CLKCTL);
tmp &= ~UNIPHIER_SD_CLKCTL_DIV_MASK;
tmp |= val | UNIPHIER_SD_CLKCTL_OFFEN;
writel(tmp, priv->regbase + UNIPHIER_SD_CLKCTL);
tmp |= UNIPHIER_SD_CLKCTL_SCLKEN;
writel(tmp, priv->regbase + UNIPHIER_SD_CLKCTL);
}
static void uniphier_sd_set_ios(struct mmc *mmc)
{
struct uniphier_sd_priv *priv = mmc->priv;
dev_dbg(priv->dev, "clock %uHz, DDRmode %d, width %u\n",
mmc->clock, mmc->ddr_mode, mmc->bus_width);
uniphier_sd_set_bus_width(priv, mmc);
uniphier_sd_set_ddr_mode(priv, mmc);
uniphier_sd_set_clk_rate(priv, mmc);
udelay(1000);
}
static int uniphier_sd_init(struct mmc *mmc)
{
struct uniphier_sd_priv *priv = mmc->priv;
u32 tmp;
/* soft reset of the host */
tmp = readl(priv->regbase + UNIPHIER_SD_SOFT_RST);
tmp &= ~UNIPHIER_SD_SOFT_RST_RSTX;
writel(tmp, priv->regbase + UNIPHIER_SD_SOFT_RST);
tmp |= UNIPHIER_SD_SOFT_RST_RSTX;
writel(tmp, priv->regbase + UNIPHIER_SD_SOFT_RST);
/* FIXME: implement eMMC hw_reset */
writel(UNIPHIER_SD_STOP_SEC, priv->regbase + UNIPHIER_SD_STOP);
/*
* Connected to 32bit AXI.
* This register dropped backward compatibility at version 0x10.
* Write an appropriate value depending on the IP version.
*/
writel(priv->version >= 0x10 ? 0x00000101 : 0x00000000,
priv->regbase + UNIPHIER_SD_HOST_MODE);
if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL) {
tmp = readl(priv->regbase + UNIPHIER_SD_DMA_MODE);
tmp |= UNIPHIER_SD_DMA_MODE_ADDR_INC;
writel(tmp, priv->regbase + UNIPHIER_SD_DMA_MODE);
}
return 0;
}
static int uniphier_sd_getcd(struct mmc *mmc)
{
struct uniphier_sd_priv *priv = mmc->priv;
if (priv->caps & UNIPHIER_SD_CAP_NONREMOVABLE)
return 1;
return !!(readl(priv->regbase + UNIPHIER_SD_INFO1) &
UNIPHIER_SD_INFO1_CD);
}
static const struct mmc_ops uniphier_sd_ops = {
.send_cmd = uniphier_sd_send_cmd,
.set_ios = uniphier_sd_set_ios,
.init = uniphier_sd_init,
.getcd = uniphier_sd_getcd,
};
int uniphier_sd_probe(struct udevice *dev)
{
struct uniphier_sd_priv *priv = dev_get_priv(dev);
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
fdt_addr_t base;
struct clk clk;
int ret;
priv->dev = dev;
base = dev_get_addr(dev);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regbase = map_sysmem(base, SZ_2K);
if (!priv->regbase)
return -ENOMEM;
ret = clk_get_by_index(dev, 0, &clk);
if (ret < 0) {
dev_err(dev, "failed to get host clock\n");
return ret;
}
/* set to max rate */
priv->mclk = clk_set_rate(&clk, ULONG_MAX);
if (IS_ERR_VALUE(priv->mclk)) {
dev_err(dev, "failed to set rate for host clock\n");
clk_free(&clk);
return priv->mclk;
}
ret = clk_enable(&clk);
clk_free(&clk);
if (ret) {
dev_err(dev, "failed to enable host clock\n");
return ret;
}
priv->cfg.name = dev->name;
priv->cfg.ops = &uniphier_sd_ops;
priv->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
switch (fdtdec_get_int(gd->fdt_blob, dev->of_offset, "bus-width", 1)) {
case 8:
priv->cfg.host_caps |= MMC_MODE_8BIT;
break;
case 4:
priv->cfg.host_caps |= MMC_MODE_4BIT;
break;
case 1:
break;
default:
dev_err(dev, "Invalid \"bus-width\" value\n");
return -EINVAL;
}
if (fdt_get_property(gd->fdt_blob, dev->of_offset, "non-removable",
NULL))
priv->caps |= UNIPHIER_SD_CAP_NONREMOVABLE;
priv->version = readl(priv->regbase + UNIPHIER_SD_VERSION) &
UNIPHIER_SD_VERSION_IP;
dev_dbg(dev, "version %x\n", priv->version);
if (priv->version >= 0x10) {
priv->caps |= UNIPHIER_SD_CAP_DMA_INTERNAL;
priv->caps |= UNIPHIER_SD_CAP_DIV1024;
}
priv->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
priv->cfg.f_min = priv->mclk /
(priv->caps & UNIPHIER_SD_CAP_DIV1024 ? 1024 : 512);
priv->cfg.f_max = priv->mclk;
priv->cfg.b_max = U32_MAX; /* max value of UNIPHIER_SD_SECCNT */
priv->mmc = mmc_create(&priv->cfg, priv);
if (!priv->mmc)
return -EIO;
upriv->mmc = priv->mmc;
priv->mmc->dev = dev;
return 0;
}
int uniphier_sd_remove(struct udevice *dev)
{
struct uniphier_sd_priv *priv = dev_get_priv(dev);
unmap_sysmem(priv->regbase);
mmc_destroy(priv->mmc);
return 0;
}
static const struct udevice_id uniphier_sd_match[] = {
{ .compatible = "socionext,uniphier-sdhc" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(uniphier_mmc) = {
.name = "uniphier-mmc",
.id = UCLASS_MMC,
.of_match = uniphier_sd_match,
.probe = uniphier_sd_probe,
.remove = uniphier_sd_remove,
.priv_auto_alloc_size = sizeof(struct uniphier_sd_priv),
};