avionic design with actual uboot and tooling

submodule of avionic design uboot bootloader and with included tools to
get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
menu "MMC Host controller Support"
config MMC
bool "Enable MMC support"
depends on ARCH_SUNXI || SANDBOX
help
TODO: Move all architectures to use this option
config DM_MMC
bool "Enable MMC controllers using Driver Model"
depends on DM
help
This enables the MultiMediaCard (MMC) uclass which suports MMC and
Secure Digital I/O (SDIO) cards. Both removable (SD, micro-SD, etc.)
and non-removable (e.g. eMMC chip) devices are supported. These
appear as block devices in U-Boot and can support filesystems such
as EXT4 and FAT.
config MSM_SDHCI
bool "Qualcomm SDHCI controller"
depends on DM_MMC
help
Enables support for SDHCI 2.0 controller present on some Qualcomm
Snapdragon devices. This device is compatible with eMMC v4.5 and
SD 3.0 specifications. Both SD and eMMC devices are supported.
Card-detect gpios are not supported.
config ROCKCHIP_DWMMC
bool "Rockchip SD/MMC controller support"
depends on DM_MMC && OF_CONTROL
help
This enables support for the Rockchip SD/MMM controller, which is
based on Designware IP. The device is compatible with at least
SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well
as removeable SD and micro-SD cards.
config SH_SDHI
bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
depends on RMOBILE
help
Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform
config PIC32_SDHCI
bool "Microchip PIC32 on-chip SDHCI support"
depends on DM_MMC && MACH_PIC32
help
Support for Microchip PIC32 SDHCI controller.
config ZYNQ_SDHCI
bool "Arasan SDHCI controller support"
depends on DM_MMC && OF_CONTROL
help
Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
config MMC_UNIPHIER
bool "UniPhier SD/MMC Host Controller support"
depends on ARCH_UNIPHIER
help
This selects support for the SD/MMC Host Controller on UniPhier SoCs.
config SANDBOX_MMC
bool "Sandbox MMC support"
depends on MMC && SANDBOX
help
This select a dummy sandbox MMC driver. At present this does nothing
other than allow sandbox to be build with MMC support. This
improves build coverage for sandbox and makes it easier to detect
MMC build errors with sandbox.
endmenu

View File

@@ -0,0 +1,63 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# SPDX-License-Identifier: GPL-2.0+
#
ifdef CONFIG_DM_MMC
obj-$(CONFIG_GENERIC_MMC) += mmc-uclass.o
endif
ifndef CONFIG_BLK
obj-$(CONFIG_GENERIC_MMC) += mmc_legacy.o
endif
obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o
obj-$(CONFIG_ATMEL_SDHCI) += atmel_sdhci.o
obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o
obj-$(CONFIG_BFIN_SDH) += bfin_sdh.o
obj-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
obj-$(CONFIG_DWMMC) += dw_mmc.o
obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
obj-$(CONFIG_HIKEY_DWMMC) += hi6220_dw_mmc.o
obj-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
obj-$(CONFIG_FTSDC010) += ftsdc010_mci.o
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_SPI) += mmc_spi.o
obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o
obj-$(CONFIG_MV_SDHCI) += mv_sdhci.o
obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
obj-$(CONFIG_MXC_MMC) += mxcmmc.o
obj-$(CONFIG_MXS_MMC) += mxsmmc.o
obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_X86) += pci_mmc.o
obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
obj-$(CONFIG_ROCKCHIP_DWMMC) += rockchip_dw_mmc.o
obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
ifdef CONFIG_BLK
ifdef CONFIG_GENERIC_MMC
obj-$(CONFIG_SANDBOX) += sandbox_mmc.o
endif
endif
obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_SH_SDHI) += sh_sdhi.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o
obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o
obj-$(CONFIG_MMC_UNIPHIER) += uniphier-sd.o
obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
obj-$(CONFIG_GENERIC_MMC) += mmc_write.o
endif
obj-$(CONFIG_PIC32_SDHCI) += pic32_sdhci.o
obj-$(CONFIG_MSM_SDHCI) += msm_sdhci.o

View File

@@ -0,0 +1,381 @@
/*
* ARM PrimeCell MultiMedia Card Interface - PL180
*
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Ulf Hansson <ulf.hansson@stericsson.com>
* Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
* Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* #define DEBUG */
#include <asm/io.h>
#include "common.h"
#include <errno.h>
#include <mmc.h>
#include "arm_pl180_mmci.h"
#include <malloc.h>
static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
{
u32 hoststatus, statusmask;
struct pl180_mmc_host *host = dev->priv;
statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL;
if ((cmd->resp_type & MMC_RSP_PRESENT))
statusmask |= SDI_STA_CMDREND;
else
statusmask |= SDI_STA_CMDSENT;
do
hoststatus = readl(&host->base->status) & statusmask;
while (!hoststatus);
writel(statusmask, &host->base->status_clear);
if (hoststatus & SDI_STA_CTIMEOUT) {
debug("CMD%d time out\n", cmd->cmdidx);
return TIMEOUT;
} else if ((hoststatus & SDI_STA_CCRCFAIL) &&
(cmd->resp_type & MMC_RSP_CRC)) {
printf("CMD%d CRC error\n", cmd->cmdidx);
return -EILSEQ;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
cmd->response[0] = readl(&host->base->response0);
cmd->response[1] = readl(&host->base->response1);
cmd->response[2] = readl(&host->base->response2);
cmd->response[3] = readl(&host->base->response3);
debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, "
"response[2]:0x%08X, response[3]:0x%08X\n",
cmd->cmdidx, cmd->response[0], cmd->response[1],
cmd->response[2], cmd->response[3]);
}
return 0;
}
/* send command to the mmc card and wait for results */
static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
{
int result;
u32 sdi_cmd = 0;
struct pl180_mmc_host *host = dev->priv;
sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN);
if (cmd->resp_type) {
sdi_cmd |= SDI_CMD_WAITRESP;
if (cmd->resp_type & MMC_RSP_136)
sdi_cmd |= SDI_CMD_LONGRESP;
}
writel((u32)cmd->cmdarg, &host->base->argument);
udelay(COMMAND_REG_DELAY);
writel(sdi_cmd, &host->base->command);
result = wait_for_command_end(dev, cmd);
/* After CMD2 set RCA to a none zero value. */
if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID))
dev->rca = 10;
/* After CMD3 open drain is switched off and push pull is used. */
if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) {
u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD;
writel(sdi_pwr, &host->base->power);
}
return result;
}
static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)
{
u32 *tempbuff = dest;
u64 xfercount = blkcount * blksize;
struct pl180_mmc_host *host = dev->priv;
u32 status, status_err;
debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
status = readl(&host->base->status);
status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT |
SDI_STA_RXOVERR);
while ((!status_err) && (xfercount >= sizeof(u32))) {
if (status & SDI_STA_RXDAVL) {
*(tempbuff) = readl(&host->base->fifo);
tempbuff++;
xfercount -= sizeof(u32);
}
status = readl(&host->base->status);
status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT |
SDI_STA_RXOVERR);
}
status_err = status &
(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND |
SDI_STA_RXOVERR);
while (!status_err) {
status = readl(&host->base->status);
status_err = status &
(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND |
SDI_STA_RXOVERR);
}
if (status & SDI_STA_DTIMEOUT) {
printf("Read data timed out, xfercount: %llu, status: 0x%08X\n",
xfercount, status);
return -ETIMEDOUT;
} else if (status & SDI_STA_DCRCFAIL) {
printf("Read data bytes CRC error: 0x%x\n", status);
return -EILSEQ;
} else if (status & SDI_STA_RXOVERR) {
printf("Read data RX overflow error\n");
return -EIO;
}
writel(SDI_ICR_MASK, &host->base->status_clear);
if (xfercount) {
printf("Read data error, xfercount: %llu\n", xfercount);
return -ENOBUFS;
}
return 0;
}
static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize)
{
u32 *tempbuff = src;
int i;
u64 xfercount = blkcount * blksize;
struct pl180_mmc_host *host = dev->priv;
u32 status, status_err;
debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
status = readl(&host->base->status);
status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
while (!status_err && xfercount) {
if (status & SDI_STA_TXFIFOBW) {
if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) {
for (i = 0; i < SDI_FIFO_BURST_SIZE; i++)
writel(*(tempbuff + i),
&host->base->fifo);
tempbuff += SDI_FIFO_BURST_SIZE;
xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32);
} else {
while (xfercount >= sizeof(u32)) {
writel(*(tempbuff), &host->base->fifo);
tempbuff++;
xfercount -= sizeof(u32);
}
}
}
status = readl(&host->base->status);
status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
}
status_err = status &
(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
while (!status_err) {
status = readl(&host->base->status);
status_err = status &
(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
}
if (status & SDI_STA_DTIMEOUT) {
printf("Write data timed out, xfercount:%llu,status:0x%08X\n",
xfercount, status);
return -ETIMEDOUT;
} else if (status & SDI_STA_DCRCFAIL) {
printf("Write data CRC error\n");
return -EILSEQ;
}
writel(SDI_ICR_MASK, &host->base->status_clear);
if (xfercount) {
printf("Write data error, xfercount:%llu", xfercount);
return -ENOBUFS;
}
return 0;
}
static int do_data_transfer(struct mmc *dev,
struct mmc_cmd *cmd,
struct mmc_data *data)
{
int error = -ETIMEDOUT;
struct pl180_mmc_host *host = dev->priv;
u32 blksz = 0;
u32 data_ctrl = 0;
u32 data_len = (u32) (data->blocks * data->blocksize);
if (!host->version2) {
blksz = (ffs(data->blocksize) - 1);
data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK);
} else {
blksz = data->blocksize;
data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT);
}
data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE;
writel(SDI_DTIMER_DEFAULT, &host->base->datatimer);
writel(data_len, &host->base->datalength);
udelay(DATA_REG_DELAY);
if (data->flags & MMC_DATA_READ) {
data_ctrl |= SDI_DCTRL_DTDIR_IN;
writel(data_ctrl, &host->base->datactrl);
error = do_command(dev, cmd);
if (error)
return error;
error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks,
(u32)data->blocksize);
} else if (data->flags & MMC_DATA_WRITE) {
error = do_command(dev, cmd);
if (error)
return error;
writel(data_ctrl, &host->base->datactrl);
error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks,
(u32)data->blocksize);
}
return error;
}
static int host_request(struct mmc *dev,
struct mmc_cmd *cmd,
struct mmc_data *data)
{
int result;
if (data)
result = do_data_transfer(dev, cmd, data);
else
result = do_command(dev, cmd);
return result;
}
/* MMC uses open drain drivers in the enumeration phase */
static int mmc_host_reset(struct mmc *dev)
{
struct pl180_mmc_host *host = dev->priv;
writel(host->pwr_init, &host->base->power);
return 0;
}
static void host_set_ios(struct mmc *dev)
{
struct pl180_mmc_host *host = dev->priv;
u32 sdi_clkcr;
sdi_clkcr = readl(&host->base->clock);
/* Ramp up the clock rate */
if (dev->clock) {
u32 clkdiv = 0;
u32 tmp_clock;
if (dev->clock >= dev->cfg->f_max) {
clkdiv = 0;
dev->clock = dev->cfg->f_max;
} else {
clkdiv = (host->clock_in / dev->clock) - 2;
}
tmp_clock = host->clock_in / (clkdiv + 2);
while (tmp_clock > dev->clock) {
clkdiv++;
tmp_clock = host->clock_in / (clkdiv + 2);
}
if (clkdiv > SDI_CLKCR_CLKDIV_MASK)
clkdiv = SDI_CLKCR_CLKDIV_MASK;
tmp_clock = host->clock_in / (clkdiv + 2);
dev->clock = tmp_clock;
sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK);
sdi_clkcr |= clkdiv;
}
/* Set the bus width */
if (dev->bus_width) {
u32 buswidth = 0;
switch (dev->bus_width) {
case 1:
buswidth |= SDI_CLKCR_WIDBUS_1;
break;
case 4:
buswidth |= SDI_CLKCR_WIDBUS_4;
break;
case 8:
buswidth |= SDI_CLKCR_WIDBUS_8;
break;
default:
printf("Invalid bus width: %d\n", dev->bus_width);
break;
}
sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK);
sdi_clkcr |= buswidth;
}
writel(sdi_clkcr, &host->base->clock);
udelay(CLK_CHANGE_DELAY);
}
static const struct mmc_ops arm_pl180_mmci_ops = {
.send_cmd = host_request,
.set_ios = host_set_ios,
.init = mmc_host_reset,
};
/*
* mmc_host_init - initialize the mmc controller.
* Set initial clock and power for mmc slot.
* Initialize mmc struct and register with mmc framework.
*/
int arm_pl180_mmci_init(struct pl180_mmc_host *host)
{
struct mmc *mmc;
u32 sdi_u32;
writel(host->pwr_init, &host->base->power);
writel(host->clkdiv_init, &host->base->clock);
udelay(CLK_CHANGE_DELAY);
/* Disable mmc interrupts */
sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;
writel(sdi_u32, &host->base->mask0);
host->cfg.name = host->name;
host->cfg.ops = &arm_pl180_mmci_ops;
/* TODO remove the duplicates */
host->cfg.host_caps = host->caps;
host->cfg.voltages = host->voltages;
host->cfg.f_min = host->clock_min;
host->cfg.f_max = host->clock_max;
if (host->b_max != 0)
host->cfg.b_max = host->b_max;
else
host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
mmc = mmc_create(&host->cfg, host);
if (mmc == NULL)
return -1;
debug("registered mmc interface number is:%d\n", mmc->block_dev.devnum);
return 0;
}

View File

@@ -0,0 +1,195 @@
/*
* ARM PrimeCell MultiMedia Card Interface - PL180
*
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Ulf Hansson <ulf.hansson@stericsson.com>
* Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
* Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __ARM_PL180_MMCI_H__
#define __ARM_PL180_MMCI_H__
/* need definition of struct mmc_config */
#include <mmc.h>
#define COMMAND_REG_DELAY 300
#define DATA_REG_DELAY 1000
#define CLK_CHANGE_DELAY 2000
#define INIT_PWR 0xBF /* Power on, full power, not open drain */
#define ARM_MCLK (100*1000*1000)
/* SDI Power Control register bits */
#define SDI_PWR_PWRCTRL_MASK 0x00000003
#define SDI_PWR_PWRCTRL_ON 0x00000003
#define SDI_PWR_PWRCTRL_OFF 0x00000000
#define SDI_PWR_DAT2DIREN 0x00000004
#define SDI_PWR_CMDDIREN 0x00000008
#define SDI_PWR_DAT0DIREN 0x00000010
#define SDI_PWR_DAT31DIREN 0x00000020
#define SDI_PWR_OPD 0x00000040
#define SDI_PWR_FBCLKEN 0x00000080
#define SDI_PWR_DAT74DIREN 0x00000100
#define SDI_PWR_RSTEN 0x00000200
#define VOLTAGE_WINDOW_MMC 0x00FF8080
#define VOLTAGE_WINDOW_SD 0x80010000
/* SDI clock control register bits */
#define SDI_CLKCR_CLKDIV_MASK 0x000000FF
#define SDI_CLKCR_CLKEN 0x00000100
#define SDI_CLKCR_PWRSAV 0x00000200
#define SDI_CLKCR_BYPASS 0x00000400
#define SDI_CLKCR_WIDBUS_MASK 0x00001800
#define SDI_CLKCR_WIDBUS_1 0x00000000
#define SDI_CLKCR_WIDBUS_4 0x00000800
/* V2 only */
#define SDI_CLKCR_WIDBUS_8 0x00001000
#define SDI_CLKCR_NEDGE 0x00002000
#define SDI_CLKCR_HWFC_EN 0x00004000
#define SDI_CLKCR_CLKDIV_INIT_V1 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */
#define SDI_CLKCR_CLKDIV_INIT_V2 0x000000FD
/* SDI command register bits */
#define SDI_CMD_CMDINDEX_MASK 0x000000FF
#define SDI_CMD_WAITRESP 0x00000040
#define SDI_CMD_LONGRESP 0x00000080
#define SDI_CMD_WAITINT 0x00000100
#define SDI_CMD_WAITPEND 0x00000200
#define SDI_CMD_CPSMEN 0x00000400
#define SDI_CMD_SDIOSUSPEND 0x00000800
#define SDI_CMD_ENDCMDCOMPL 0x00001000
#define SDI_CMD_NIEN 0x00002000
#define SDI_CMD_CE_ATACMD 0x00004000
#define SDI_CMD_CBOOTMODEEN 0x00008000
#define SDI_DTIMER_DEFAULT 0xFFFF0000
/* SDI Status register bits */
#define SDI_STA_CCRCFAIL 0x00000001
#define SDI_STA_DCRCFAIL 0x00000002
#define SDI_STA_CTIMEOUT 0x00000004
#define SDI_STA_DTIMEOUT 0x00000008
#define SDI_STA_TXUNDERR 0x00000010
#define SDI_STA_RXOVERR 0x00000020
#define SDI_STA_CMDREND 0x00000040
#define SDI_STA_CMDSENT 0x00000080
#define SDI_STA_DATAEND 0x00000100
#define SDI_STA_STBITERR 0x00000200
#define SDI_STA_DBCKEND 0x00000400
#define SDI_STA_CMDACT 0x00000800
#define SDI_STA_TXACT 0x00001000
#define SDI_STA_RXACT 0x00002000
#define SDI_STA_TXFIFOBW 0x00004000
#define SDI_STA_RXFIFOBR 0x00008000
#define SDI_STA_TXFIFOF 0x00010000
#define SDI_STA_RXFIFOF 0x00020000
#define SDI_STA_TXFIFOE 0x00040000
#define SDI_STA_RXFIFOE 0x00080000
#define SDI_STA_TXDAVL 0x00100000
#define SDI_STA_RXDAVL 0x00200000
#define SDI_STA_SDIOIT 0x00400000
#define SDI_STA_CEATAEND 0x00800000
#define SDI_STA_CARDBUSY 0x01000000
#define SDI_STA_BOOTMODE 0x02000000
#define SDI_STA_BOOTACKERR 0x04000000
#define SDI_STA_BOOTACKTIMEOUT 0x08000000
#define SDI_STA_RSTNEND 0x10000000
/* SDI Interrupt Clear register bits */
#define SDI_ICR_MASK 0x1DC007FF
#define SDI_ICR_CCRCFAILC 0x00000001
#define SDI_ICR_DCRCFAILC 0x00000002
#define SDI_ICR_CTIMEOUTC 0x00000004
#define SDI_ICR_DTIMEOUTC 0x00000008
#define SDI_ICR_TXUNDERRC 0x00000010
#define SDI_ICR_RXOVERRC 0x00000020
#define SDI_ICR_CMDRENDC 0x00000040
#define SDI_ICR_CMDSENTC 0x00000080
#define SDI_ICR_DATAENDC 0x00000100
#define SDI_ICR_STBITERRC 0x00000200
#define SDI_ICR_DBCKENDC 0x00000400
#define SDI_ICR_SDIOITC 0x00400000
#define SDI_ICR_CEATAENDC 0x00800000
#define SDI_ICR_BUSYENDC 0x01000000
#define SDI_ICR_BOOTACKERRC 0x04000000
#define SDI_ICR_BOOTACKTIMEOUTC 0x08000000
#define SDI_ICR_RSTNENDC 0x10000000
#define SDI_MASK0_MASK 0x1FFFFFFF
/* SDI Data control register bits */
#define SDI_DCTRL_DTEN 0x00000001
#define SDI_DCTRL_DTDIR_IN 0x00000002
#define SDI_DCTRL_DTMODE_STREAM 0x00000004
#define SDI_DCTRL_DMAEN 0x00000008
#define SDI_DCTRL_DBLKSIZE_MASK 0x000000F0
#define SDI_DCTRL_RWSTART 0x00000100
#define SDI_DCTRL_RWSTOP 0x00000200
#define SDI_DCTRL_RWMOD 0x00000200
#define SDI_DCTRL_SDIOEN 0x00000800
#define SDI_DCTRL_DMAREQCTL 0x00001000
#define SDI_DCTRL_DBOOTMODEEN 0x00002000
#define SDI_DCTRL_BUSYMODE 0x00004000
#define SDI_DCTRL_DDR_MODE 0x00008000
#define SDI_DCTRL_DBLOCKSIZE_V2_MASK 0x7fff0000
#define SDI_DCTRL_DBLOCKSIZE_V2_SHIFT 16
#define SDI_FIFO_BURST_SIZE 8
struct sdi_registers {
u32 power; /* 0x00*/
u32 clock; /* 0x04*/
u32 argument; /* 0x08*/
u32 command; /* 0x0c*/
u32 respcommand; /* 0x10*/
u32 response0; /* 0x14*/
u32 response1; /* 0x18*/
u32 response2; /* 0x1c*/
u32 response3; /* 0x20*/
u32 datatimer; /* 0x24*/
u32 datalength; /* 0x28*/
u32 datactrl; /* 0x2c*/
u32 datacount; /* 0x30*/
u32 status; /* 0x34*/
u32 status_clear; /* 0x38*/
u32 mask0; /* 0x3c*/
u32 mask1; /* 0x40*/
u32 card_select; /* 0x44*/
u32 fifo_count; /* 0x48*/
u32 padding1[(0x80-0x4C)>>2];
u32 fifo; /* 0x80*/
u32 padding2[(0xFE0-0x84)>>2];
u32 periph_id0; /* 0xFE0 mmc Peripheral Identifcation Register*/
u32 periph_id1; /* 0xFE4*/
u32 periph_id2; /* 0xFE8*/
u32 periph_id3; /* 0xFEC*/
u32 pcell_id0; /* 0xFF0*/
u32 pcell_id1; /* 0xFF4*/
u32 pcell_id2; /* 0xFF8*/
u32 pcell_id3; /* 0xFFC*/
};
struct pl180_mmc_host {
struct sdi_registers *base;
char name[32];
unsigned int b_max;
unsigned int voltages;
unsigned int caps;
unsigned int clock_in;
unsigned int clock_min;
unsigned int clock_max;
unsigned int clkdiv_init;
unsigned int pwr_init;
int version2;
struct mmc_config cfg;
};
int arm_pl180_mmci_init(struct pl180_mmc_host *);
#endif

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2015 Atmel Corporation
* Wenyou.Yang <wenyou.yang@atmel.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#include <asm/arch/clk.h>
#define ATMEL_SDHC_MIN_FREQ 400000
int atmel_sdhci_init(void *regbase, u32 id)
{
struct sdhci_host *host;
u32 max_clk, min_clk = ATMEL_SDHC_MIN_FREQ;
host = (struct sdhci_host *)calloc(1, sizeof(struct sdhci_host));
if (!host) {
printf("%s: sdhci_host calloc failed\n", __func__);
return -ENOMEM;
}
host->name = "atmel_sdhci";
host->ioaddr = regbase;
host->quirks = 0;
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
max_clk = at91_get_periph_generated_clk(id);
if (!max_clk) {
printf("%s: Failed to get the proper clock\n", __func__);
free(host);
return -ENODEV;
}
add_sdhci(host, max_clk, min_clk);
return 0;
}

View File

@@ -0,0 +1,191 @@
/*
* This code was extracted from:
* git://github.com/gonzoua/u-boot-pi.git master
* and hence presumably (C) 2012 Oleksandr Tymoshenko
*
* Tweaks for U-Boot upstreaming
* (C) 2012 Stephen Warren
*
* Portions (e.g. read/write macros, concepts for back-to-back register write
* timing workarounds) obviously extracted from the Linux kernel at:
* https://github.com/raspberrypi/linux.git rpi-3.6.y
*
* The Linux kernel code has the following (c) and license, which is hence
* propagated to Oleksandr's tree and here:
*
* Support for SDHCI device on 2835
* Based on sdhci-bcm2708.c (c) 2010 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Supports:
* SDHCI platform device - Arasan SD controller in BCM2708
*
* Inspired by sdhci-pci.c, by Pierre Ossman
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#include <mach/timer.h>
#include <mach/sdhci.h>
/* 400KHz is max freq for card ID etc. Use that as min */
#define MIN_FREQ 400000
struct bcm2835_sdhci_host {
struct sdhci_host host;
uint twoticks_delay;
ulong last_write;
};
static inline struct bcm2835_sdhci_host *to_bcm(struct sdhci_host *host)
{
return (struct bcm2835_sdhci_host *)host;
}
static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val,
int reg)
{
struct bcm2835_sdhci_host *bcm_host = to_bcm(host);
/*
* The Arasan has a bugette whereby it may lose the content of
* successive writes to registers that are within two SD-card clock
* cycles of each other (a clock domain crossing problem).
* It seems, however, that the data register does not have this problem.
* (Which is just as well - otherwise we'd have to nobble the DMA engine
* too)
*/
while (timer_get_us() - bcm_host->last_write < bcm_host->twoticks_delay)
;
writel(val, host->ioaddr + reg);
bcm_host->last_write = timer_get_us();
}
static inline u32 bcm2835_sdhci_raw_readl(struct sdhci_host *host, int reg)
{
return readl(host->ioaddr + reg);
}
static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
bcm2835_sdhci_raw_writel(host, val, reg);
}
static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
static u32 shadow;
u32 oldval = (reg == SDHCI_COMMAND) ? shadow :
bcm2835_sdhci_raw_readl(host, reg & ~3);
u32 word_num = (reg >> 1) & 1;
u32 word_shift = word_num * 16;
u32 mask = 0xffff << word_shift;
u32 newval = (oldval & ~mask) | (val << word_shift);
if (reg == SDHCI_TRANSFER_MODE)
shadow = newval;
else
bcm2835_sdhci_raw_writel(host, newval, reg & ~3);
}
static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
u32 oldval = bcm2835_sdhci_raw_readl(host, reg & ~3);
u32 byte_num = reg & 3;
u32 byte_shift = byte_num * 8;
u32 mask = 0xff << byte_shift;
u32 newval = (oldval & ~mask) | (val << byte_shift);
bcm2835_sdhci_raw_writel(host, newval, reg & ~3);
}
static u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg)
{
u32 val = bcm2835_sdhci_raw_readl(host, reg);
return val;
}
static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg)
{
u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3));
u32 word_num = (reg >> 1) & 1;
u32 word_shift = word_num * 16;
u32 word = (val >> word_shift) & 0xffff;
return word;
}
static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg)
{
u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3));
u32 byte_num = reg & 3;
u32 byte_shift = byte_num * 8;
u32 byte = (val >> byte_shift) & 0xff;
return byte;
}
static const struct sdhci_ops bcm2835_ops = {
.write_l = bcm2835_sdhci_writel,
.write_w = bcm2835_sdhci_writew,
.write_b = bcm2835_sdhci_writeb,
.read_l = bcm2835_sdhci_readl,
.read_w = bcm2835_sdhci_readw,
.read_b = bcm2835_sdhci_readb,
};
int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq)
{
struct bcm2835_sdhci_host *bcm_host;
struct sdhci_host *host;
bcm_host = calloc(1, sizeof(*bcm_host));
if (!bcm_host) {
printf("sdhci_host calloc fail!\n");
return 1;
}
/*
* See the comments in bcm2835_sdhci_raw_writel().
*
* This should probably be dynamically calculated based on the actual
* frequency. However, this is the longest we'll have to wait, and
* doesn't seem to slow access down too much, so the added complexity
* doesn't seem worth it for now.
*
* 1/MIN_FREQ is (max) time per tick of eMMC clock.
* 2/MIN_FREQ is time for two ticks.
* Multiply by 1000000 to get uS per two ticks.
* +1 for hack rounding.
*/
bcm_host->twoticks_delay = ((2 * 1000000) / MIN_FREQ) + 1;
bcm_host->last_write = 0;
host = &bcm_host->host;
host->name = "bcm2835_sdhci";
host->ioaddr = (void *)(unsigned long)regbase;
host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B |
SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_NO_HISPD_BIT;
host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
host->ops = &bcm2835_ops;
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
add_sdhci(host, emmc_freq, MIN_FREQ);
return 0;
}

View File

@@ -0,0 +1,304 @@
/*
* Driver for Blackfin on-chip SDH controller
*
* Copyright (c) 2008-2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <malloc.h>
#include <part.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/byteorder.h>
#include <asm/blackfin.h>
#include <asm/clock.h>
#include <asm/portmux.h>
#include <asm/mach-common/bits/sdh.h>
#include <asm/mach-common/bits/dma.h>
#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) || defined(__ADSPBF60x__)
# define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CONTROL
# define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CONTROL
# define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT
# define bfin_write_SDH_COMMAND bfin_write_RSI_COMMAND
# define bfin_read_SDH_RESPONSE0 bfin_read_RSI_RESPONSE0
# define bfin_read_SDH_RESPONSE1 bfin_read_RSI_RESPONSE1
# define bfin_read_SDH_RESPONSE2 bfin_read_RSI_RESPONSE2
# define bfin_read_SDH_RESPONSE3 bfin_read_RSI_RESPONSE3
# define bfin_write_SDH_DATA_TIMER bfin_write_RSI_DATA_TIMER
# define bfin_write_SDH_DATA_LGTH bfin_write_RSI_DATA_LGTH
# define bfin_read_SDH_DATA_CTL bfin_read_RSI_DATA_CONTROL
# define bfin_write_SDH_DATA_CTL bfin_write_RSI_DATA_CONTROL
# define bfin_read_SDH_STATUS bfin_read_RSI_STATUS
# define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUSCL
# define bfin_read_SDH_CFG bfin_read_RSI_CONFIG
# define bfin_write_SDH_CFG bfin_write_RSI_CONFIG
# if defined(__ADSPBF60x__)
# define bfin_read_SDH_BLK_SIZE bfin_read_RSI_BLKSZ
# define bfin_write_SDH_BLK_SIZE bfin_write_RSI_BLKSZ
# define bfin_write_DMA_START_ADDR bfin_write_DMA10_START_ADDR
# define bfin_write_DMA_X_COUNT bfin_write_DMA10_X_COUNT
# define bfin_write_DMA_X_MODIFY bfin_write_DMA10_X_MODIFY
# define bfin_write_DMA_CONFIG bfin_write_DMA10_CONFIG
# else
# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CONTROL
# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CONTROL
# define bfin_write_DMA_START_ADDR bfin_write_DMA4_START_ADDR
# define bfin_write_DMA_X_COUNT bfin_write_DMA4_X_COUNT
# define bfin_write_DMA_X_MODIFY bfin_write_DMA4_X_MODIFY
# define bfin_write_DMA_CONFIG bfin_write_DMA4_CONFIG
# endif
# define PORTMUX_PINS \
{ P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3, P_RSI_CMD, P_RSI_CLK, 0 }
#elif defined(__ADSPBF54x__)
# define bfin_write_DMA_START_ADDR bfin_write_DMA22_START_ADDR
# define bfin_write_DMA_X_COUNT bfin_write_DMA22_X_COUNT
# define bfin_write_DMA_X_MODIFY bfin_write_DMA22_X_MODIFY
# define bfin_write_DMA_CONFIG bfin_write_DMA22_CONFIG
# define PORTMUX_PINS \
{ P_SD_D0, P_SD_D1, P_SD_D2, P_SD_D3, P_SD_CLK, P_SD_CMD, 0 }
#else
# error no support for this proc yet
#endif
static int
sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
{
unsigned int status, timeout;
int cmd = mmc_cmd->cmdidx;
int flags = mmc_cmd->resp_type;
int arg = mmc_cmd->cmdarg;
int ret;
u16 sdh_cmd;
sdh_cmd = cmd | CMD_E;
if (flags & MMC_RSP_PRESENT)
sdh_cmd |= CMD_RSP;
if (flags & MMC_RSP_136)
sdh_cmd |= CMD_L_RSP;
#ifdef RSI_BLKSZ
sdh_cmd |= CMD_DATA0_BUSY;
#endif
bfin_write_SDH_ARGUMENT(arg);
bfin_write_SDH_COMMAND(sdh_cmd);
/* wait for a while */
timeout = 0;
do {
if (++timeout > 1000000) {
status = CMD_TIME_OUT;
break;
}
udelay(1);
status = bfin_read_SDH_STATUS();
} while (!(status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT |
CMD_CRC_FAIL)));
if (flags & MMC_RSP_PRESENT) {
mmc_cmd->response[0] = bfin_read_SDH_RESPONSE0();
if (flags & MMC_RSP_136) {
mmc_cmd->response[1] = bfin_read_SDH_RESPONSE1();
mmc_cmd->response[2] = bfin_read_SDH_RESPONSE2();
mmc_cmd->response[3] = bfin_read_SDH_RESPONSE3();
}
}
if (status & CMD_TIME_OUT)
ret = TIMEOUT;
else if (status & CMD_CRC_FAIL && flags & MMC_RSP_CRC)
ret = COMM_ERR;
else
ret = 0;
bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT |
CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
#ifdef RSI_BLKSZ
/* wait till card ready */
while (!(bfin_read_RSI_ESTAT() & SD_CARD_READY))
continue;
bfin_write_RSI_ESTAT(SD_CARD_READY);
#endif
return ret;
}
/* set data for single block transfer */
static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data)
{
u16 data_ctl = 0;
u16 dma_cfg = 0;
unsigned long data_size = data->blocksize * data->blocks;
/* Don't support write yet. */
if (data->flags & MMC_DATA_WRITE)
return UNUSABLE_ERR;
#ifndef RSI_BLKSZ
data_ctl |= ((ffs(data->blocksize) - 1) << 4);
#else
bfin_write_SDH_BLK_SIZE(data->blocksize);
#endif
data_ctl |= DTX_DIR;
bfin_write_SDH_DATA_CTL(data_ctl);
dma_cfg = WDSIZE_32 | PSIZE_32 | RESTART | WNR | DMAEN;
bfin_write_SDH_DATA_TIMER(-1);
blackfin_dcache_flush_invalidate_range(data->dest,
data->dest + data_size);
/* configure DMA */
bfin_write_DMA_START_ADDR(data->dest);
bfin_write_DMA_X_COUNT(data_size / 4);
bfin_write_DMA_X_MODIFY(4);
bfin_write_DMA_CONFIG(dma_cfg);
bfin_write_SDH_DATA_LGTH(data_size);
/* kick off transfer */
bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
return 0;
}
static int bfin_sdh_request(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
u32 status;
int ret = 0;
if (data) {
ret = sdh_setup_data(mmc, data);
if (ret)
return ret;
}
ret = sdh_send_cmd(mmc, cmd);
if (ret) {
bfin_write_SDH_COMMAND(0);
bfin_write_DMA_CONFIG(0);
bfin_write_SDH_DATA_CTL(0);
SSYNC();
printf("sending CMD%d failed\n", cmd->cmdidx);
return ret;
}
if (data) {
do {
udelay(1);
status = bfin_read_SDH_STATUS();
} while (!(status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL |
RX_OVERRUN)));
if (status & DAT_TIME_OUT) {
bfin_write_SDH_STATUS_CLR(DAT_TIMEOUT_STAT);
ret |= TIMEOUT;
} else if (status & (DAT_CRC_FAIL | RX_OVERRUN)) {
bfin_write_SDH_STATUS_CLR(DAT_CRC_FAIL_STAT | RX_OVERRUN_STAT);
ret |= COMM_ERR;
} else
bfin_write_SDH_STATUS_CLR(DAT_BLK_END_STAT | DAT_END_STAT);
if (ret) {
printf("tranfering data failed\n");
return ret;
}
}
return 0;
}
static void sdh_set_clk(unsigned long clk)
{
unsigned long sys_clk;
unsigned long clk_div;
u16 clk_ctl = 0;
clk_ctl = bfin_read_SDH_CLK_CTL();
if (clk) {
/* setting SD_CLK */
sys_clk = get_sclk();
bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E);
if (sys_clk % (2 * clk) == 0)
clk_div = sys_clk / (2 * clk) - 1;
else
clk_div = sys_clk / (2 * clk);
if (clk_div > 0xff)
clk_div = 0xff;
clk_ctl |= (clk_div & 0xff);
clk_ctl |= CLK_E;
bfin_write_SDH_CLK_CTL(clk_ctl);
} else
bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E);
}
static void bfin_sdh_set_ios(struct mmc *mmc)
{
u16 cfg = 0;
u16 clk_ctl = 0;
if (mmc->bus_width == 4) {
cfg = bfin_read_SDH_CFG();
#ifndef RSI_BLKSZ
cfg &= ~PD_SDDAT3;
#endif
cfg |= PUP_SDDAT3;
bfin_write_SDH_CFG(cfg);
clk_ctl |= WIDE_BUS_4;
}
bfin_write_SDH_CLK_CTL(clk_ctl);
sdh_set_clk(mmc->clock);
}
static int bfin_sdh_init(struct mmc *mmc)
{
const unsigned short pins[] = PORTMUX_PINS;
int ret;
/* Initialize sdh controller */
ret = peripheral_request_list(pins, "bfin_sdh");
if (ret < 0)
return ret;
#if defined(__ADSPBF54x__)
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
#endif
bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
/* Disable card detect pin */
bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | 0x60);
#ifndef RSI_BLKSZ
bfin_write_SDH_PWR_CTL(PWR_ON | ROD_CTL);
#else
bfin_write_SDH_CFG(bfin_read_SDH_CFG() | PWR_ON);
#endif
return 0;
}
static const struct mmc_ops bfin_mmc_ops = {
.send_cmd = bfin_sdh_request,
.set_ios = bfin_sdh_set_ios,
.init = bfin_sdh_init,
};
static struct mmc_config bfin_mmc_cfg = {
.name = "Blackfin SDH",
.ops = &bfin_mmc_ops,
.host_caps = MMC_MODE_4BIT,
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
int bfin_mmc_init(bd_t *bis)
{
struct mmc *mmc;
bfin_mmc_cfg.f_max = get_sclk();
bfin_mmc_cfg.f_min = bfin_mmc_cfg.f_max >> 9;
mmc = mmc_create(&bfin_mmc_cfg, NULL);
if (mmc == NULL)
return -1;
return 0;
}

View File

@@ -0,0 +1,389 @@
/*
* Davinci MMC Controller Driver
*
* Copyright (C) 2010 Texas Instruments Incorporated
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <command.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/sdmmc_defs.h>
#define DAVINCI_MAX_BLOCKS (32)
#define WATCHDOG_COUNT (100000)
#define get_val(addr) REG(addr)
#define set_val(addr, val) REG(addr) = (val)
#define set_bit(addr, val) set_val((addr), (get_val(addr) | (val)))
#define clear_bit(addr, val) set_val((addr), (get_val(addr) & ~(val)))
/* Set davinci clock prescalar value based on the required clock in HZ */
static void dmmc_set_clock(struct mmc *mmc, uint clock)
{
struct davinci_mmc *host = mmc->priv;
struct davinci_mmc_regs *regs = host->reg_base;
uint clkrt, sysclk2, act_clock;
if (clock < mmc->cfg->f_min)
clock = mmc->cfg->f_min;
if (clock > mmc->cfg->f_max)
clock = mmc->cfg->f_max;
set_val(&regs->mmcclk, 0);
sysclk2 = host->input_clk;
clkrt = (sysclk2 / (2 * clock)) - 1;
/* Calculate the actual clock for the divider used */
act_clock = (sysclk2 / (2 * (clkrt + 1)));
/* Adjust divider if actual clock exceeds the required clock */
if (act_clock > clock)
clkrt++;
/* check clock divider boundary and correct it */
if (clkrt > 0xFF)
clkrt = 0xFF;
set_val(&regs->mmcclk, (clkrt | MMCCLK_CLKEN));
}
/* Status bit wait loop for MMCST1 */
static int
dmmc_wait_fifo_status(volatile struct davinci_mmc_regs *regs, uint status)
{
uint wdog = WATCHDOG_COUNT;
while (--wdog && ((get_val(&regs->mmcst1) & status) != status))
udelay(10);
if (!(get_val(&regs->mmcctl) & MMCCTL_WIDTH_4_BIT))
udelay(100);
if (wdog == 0)
return COMM_ERR;
return 0;
}
/* Busy bit wait loop for MMCST1 */
static int dmmc_busy_wait(volatile struct davinci_mmc_regs *regs)
{
uint wdog = WATCHDOG_COUNT;
while (--wdog && (get_val(&regs->mmcst1) & MMCST1_BUSY))
udelay(10);
if (wdog == 0)
return COMM_ERR;
return 0;
}
/* Status bit wait loop for MMCST0 - Checks for error bits as well */
static int dmmc_check_status(volatile struct davinci_mmc_regs *regs,
uint *cur_st, uint st_ready, uint st_error)
{
uint wdog = WATCHDOG_COUNT;
uint mmcstatus = *cur_st;
while (wdog--) {
if (mmcstatus & st_ready) {
*cur_st = mmcstatus;
mmcstatus = get_val(&regs->mmcst1);
return 0;
} else if (mmcstatus & st_error) {
if (mmcstatus & MMCST0_TOUTRS)
return TIMEOUT;
printf("[ ST0 ERROR %x]\n", mmcstatus);
/*
* Ignore CRC errors as some MMC cards fail to
* initialize on DM365-EVM on the SD1 slot
*/
if (mmcstatus & MMCST0_CRCRS)
return 0;
return COMM_ERR;
}
udelay(10);
mmcstatus = get_val(&regs->mmcst0);
}
printf("Status %x Timeout ST0:%x ST1:%x\n", st_ready, mmcstatus,
get_val(&regs->mmcst1));
return COMM_ERR;
}
/*
* Sends a command out on the bus. Takes the mmc pointer,
* a command pointer, and an optional data pointer.
*/
static int
dmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
struct davinci_mmc *host = mmc->priv;
volatile struct davinci_mmc_regs *regs = host->reg_base;
uint mmcstatus, status_rdy, status_err;
uint i, cmddata, bytes_left = 0;
int fifo_words, fifo_bytes, err;
char *data_buf = NULL;
/* Clear status registers */
mmcstatus = get_val(&regs->mmcst0);
fifo_words = (host->version == MMC_CTLR_VERSION_2) ? 16 : 8;
fifo_bytes = fifo_words << 2;
/* Wait for any previous busy signal to be cleared */
dmmc_busy_wait(regs);
cmddata = cmd->cmdidx;
cmddata |= MMCCMD_PPLEN;
/* Send init clock for CMD0 */
if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE)
cmddata |= MMCCMD_INITCK;
switch (cmd->resp_type) {
case MMC_RSP_R1b:
cmddata |= MMCCMD_BSYEXP;
/* Fall-through */
case MMC_RSP_R1: /* R1, R1b, R5, R6, R7 */
cmddata |= MMCCMD_RSPFMT_R1567;
break;
case MMC_RSP_R2:
cmddata |= MMCCMD_RSPFMT_R2;
break;
case MMC_RSP_R3: /* R3, R4 */
cmddata |= MMCCMD_RSPFMT_R3;
break;
}
set_val(&regs->mmcim, 0);
if (data) {
/* clear previous data transfer if any and set new one */
bytes_left = (data->blocksize * data->blocks);
/* Reset FIFO - Always use 32 byte fifo threshold */
set_val(&regs->mmcfifoctl,
(MMCFIFOCTL_FIFOLEV | MMCFIFOCTL_FIFORST));
if (host->version == MMC_CTLR_VERSION_2)
cmddata |= MMCCMD_DMATRIG;
cmddata |= MMCCMD_WDATX;
if (data->flags == MMC_DATA_READ) {
set_val(&regs->mmcfifoctl, MMCFIFOCTL_FIFOLEV);
} else if (data->flags == MMC_DATA_WRITE) {
set_val(&regs->mmcfifoctl,
(MMCFIFOCTL_FIFOLEV |
MMCFIFOCTL_FIFODIR));
cmddata |= MMCCMD_DTRW;
}
set_val(&regs->mmctod, 0xFFFF);
set_val(&regs->mmcnblk, (data->blocks & MMCNBLK_NBLK_MASK));
set_val(&regs->mmcblen, (data->blocksize & MMCBLEN_BLEN_MASK));
if (data->flags == MMC_DATA_WRITE) {
uint val;
data_buf = (char *)data->src;
/* For write, fill FIFO with data before issue of CMD */
for (i = 0; (i < fifo_words) && bytes_left; i++) {
memcpy((char *)&val, data_buf, 4);
set_val(&regs->mmcdxr, val);
data_buf += 4;
bytes_left -= 4;
}
}
} else {
set_val(&regs->mmcblen, 0);
set_val(&regs->mmcnblk, 0);
}
set_val(&regs->mmctor, 0x1FFF);
/* Send the command */
set_val(&regs->mmcarghl, cmd->cmdarg);
set_val(&regs->mmccmd, cmddata);
status_rdy = MMCST0_RSPDNE;
status_err = (MMCST0_TOUTRS | MMCST0_TOUTRD |
MMCST0_CRCWR | MMCST0_CRCRD);
if (cmd->resp_type & MMC_RSP_CRC)
status_err |= MMCST0_CRCRS;
mmcstatus = get_val(&regs->mmcst0);
err = dmmc_check_status(regs, &mmcstatus, status_rdy, status_err);
if (err)
return err;
/* For R1b wait for busy done */
if (cmd->resp_type == MMC_RSP_R1b)
dmmc_busy_wait(regs);
/* Collect response from controller for specific commands */
if (mmcstatus & MMCST0_RSPDNE) {
/* Copy the response to the response buffer */
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = get_val(&regs->mmcrsp67);
cmd->response[1] = get_val(&regs->mmcrsp45);
cmd->response[2] = get_val(&regs->mmcrsp23);
cmd->response[3] = get_val(&regs->mmcrsp01);
} else if (cmd->resp_type & MMC_RSP_PRESENT) {
cmd->response[0] = get_val(&regs->mmcrsp67);
}
}
if (data == NULL)
return 0;
if (data->flags == MMC_DATA_READ) {
/* check for DATDNE along with DRRDY as the controller might
* set the DATDNE without DRRDY for smaller transfers with
* less than FIFO threshold bytes
*/
status_rdy = MMCST0_DRRDY | MMCST0_DATDNE;
status_err = MMCST0_TOUTRD | MMCST0_CRCRD;
data_buf = data->dest;
} else {
status_rdy = MMCST0_DXRDY | MMCST0_DATDNE;
status_err = MMCST0_CRCWR;
}
/* Wait until all of the blocks are transferred */
while (bytes_left) {
err = dmmc_check_status(regs, &mmcstatus, status_rdy,
status_err);
if (err)
return err;
if (data->flags == MMC_DATA_READ) {
/*
* MMC controller sets the Data receive ready bit
* (DRRDY) in MMCST0 even before the entire FIFO is
* full. This results in erratic behavior if we start
* reading the FIFO soon after DRRDY. Wait for the
* FIFO full bit in MMCST1 for proper FIFO clearing.
*/
if (bytes_left > fifo_bytes)
dmmc_wait_fifo_status(regs, 0x4a);
else if (bytes_left == fifo_bytes) {
dmmc_wait_fifo_status(regs, 0x40);
if (cmd->cmdidx == MMC_CMD_SEND_EXT_CSD)
udelay(600);
}
for (i = 0; bytes_left && (i < fifo_words); i++) {
cmddata = get_val(&regs->mmcdrr);
memcpy(data_buf, (char *)&cmddata, 4);
data_buf += 4;
bytes_left -= 4;
}
} else {
/*
* MMC controller sets the Data transmit ready bit
* (DXRDY) in MMCST0 even before the entire FIFO is
* empty. This results in erratic behavior if we start
* writing the FIFO soon after DXRDY. Wait for the
* FIFO empty bit in MMCST1 for proper FIFO clearing.
*/
dmmc_wait_fifo_status(regs, MMCST1_FIFOEMP);
for (i = 0; bytes_left && (i < fifo_words); i++) {
memcpy((char *)&cmddata, data_buf, 4);
set_val(&regs->mmcdxr, cmddata);
data_buf += 4;
bytes_left -= 4;
}
dmmc_busy_wait(regs);
}
}
err = dmmc_check_status(regs, &mmcstatus, MMCST0_DATDNE, status_err);
if (err)
return err;
return 0;
}
/* Initialize Davinci MMC controller */
static int dmmc_init(struct mmc *mmc)
{
struct davinci_mmc *host = mmc->priv;
struct davinci_mmc_regs *regs = host->reg_base;
/* Clear status registers explicitly - soft reset doesn't clear it
* If Uboot is invoked from UBL with SDMMC Support, the status
* registers can have uncleared bits
*/
get_val(&regs->mmcst0);
get_val(&regs->mmcst1);
/* Hold software reset */
set_bit(&regs->mmcctl, MMCCTL_DATRST);
set_bit(&regs->mmcctl, MMCCTL_CMDRST);
udelay(10);
set_val(&regs->mmcclk, 0x0);
set_val(&regs->mmctor, 0x1FFF);
set_val(&regs->mmctod, 0xFFFF);
/* Clear software reset */
clear_bit(&regs->mmcctl, MMCCTL_DATRST);
clear_bit(&regs->mmcctl, MMCCTL_CMDRST);
udelay(10);
/* Reset FIFO - Always use the maximum fifo threshold */
set_val(&regs->mmcfifoctl, (MMCFIFOCTL_FIFOLEV | MMCFIFOCTL_FIFORST));
set_val(&regs->mmcfifoctl, MMCFIFOCTL_FIFOLEV);
return 0;
}
/* Set buswidth or clock as indicated by the GENERIC_MMC framework */
static void dmmc_set_ios(struct mmc *mmc)
{
struct davinci_mmc *host = mmc->priv;
struct davinci_mmc_regs *regs = host->reg_base;
/* Set the bus width */
if (mmc->bus_width == 4)
set_bit(&regs->mmcctl, MMCCTL_WIDTH_4_BIT);
else
clear_bit(&regs->mmcctl, MMCCTL_WIDTH_4_BIT);
/* Set clock speed */
if (mmc->clock)
dmmc_set_clock(mmc, mmc->clock);
}
static const struct mmc_ops dmmc_ops = {
.send_cmd = dmmc_send_cmd,
.set_ios = dmmc_set_ios,
.init = dmmc_init,
};
/* Called from board_mmc_init during startup. Can be called multiple times
* depending on the number of slots available on board and controller
*/
int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host)
{
host->cfg.name = "davinci";
host->cfg.ops = &dmmc_ops;
host->cfg.f_min = 200000;
host->cfg.f_max = 25000000;
host->cfg.voltages = host->voltages;
host->cfg.host_caps = host->host_caps;
host->cfg.b_max = DAVINCI_MAX_BLOCKS;
mmc_create(&host->cfg, host);
return 0;
}

498
u-boot/drivers/mmc/dw_mmc.c Normal file
View File

@@ -0,0 +1,498 @@
/*
* (C) Copyright 2012 SAMSUNG Electronics
* Jaehoon Chung <jh80.chung@samsung.com>
* Rajeshawari Shinde <rajeshwari.s@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <bouncebuf.h>
#include <common.h>
#include <errno.h>
#include <malloc.h>
#include <memalign.h>
#include <mmc.h>
#include <dwmmc.h>
#include <asm-generic/errno.h>
#define PAGE_SIZE 4096
static int dwmci_wait_reset(struct dwmci_host *host, u32 value)
{
unsigned long timeout = 1000;
u32 ctrl;
dwmci_writel(host, DWMCI_CTRL, value);
while (timeout--) {
ctrl = dwmci_readl(host, DWMCI_CTRL);
if (!(ctrl & DWMCI_RESET_ALL))
return 1;
}
return 0;
}
static void dwmci_set_idma_desc(struct dwmci_idmac *idmac,
u32 desc0, u32 desc1, u32 desc2)
{
struct dwmci_idmac *desc = idmac;
desc->flags = desc0;
desc->cnt = desc1;
desc->addr = desc2;
desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac);
}
static void dwmci_prepare_data(struct dwmci_host *host,
struct mmc_data *data,
struct dwmci_idmac *cur_idmac,
void *bounce_buffer)
{
unsigned long ctrl;
unsigned int i = 0, flags, cnt, blk_cnt;
ulong data_start, data_end;
blk_cnt = data->blocks;
dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
data_start = (ulong)cur_idmac;
dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac);
do {
flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ;
flags |= (i == 0) ? DWMCI_IDMAC_FS : 0;
if (blk_cnt <= 8) {
flags |= DWMCI_IDMAC_LD;
cnt = data->blocksize * blk_cnt;
} else
cnt = data->blocksize * 8;
dwmci_set_idma_desc(cur_idmac, flags, cnt,
(ulong)bounce_buffer + (i * PAGE_SIZE));
if (blk_cnt <= 8)
break;
blk_cnt -= 8;
cur_idmac++;
i++;
} while(1);
data_end = (ulong)cur_idmac;
flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN);
ctrl = dwmci_readl(host, DWMCI_CTRL);
ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN;
dwmci_writel(host, DWMCI_CTRL, ctrl);
ctrl = dwmci_readl(host, DWMCI_BMOD);
ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN;
dwmci_writel(host, DWMCI_BMOD, ctrl);
dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize);
dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks);
}
static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
{
int ret = 0;
u32 timeout = 240000;
u32 mask, size, i, len = 0;
u32 *buf = NULL;
ulong start = get_timer(0);
u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >>
RX_WMARK_SHIFT) + 1) * 2;
size = data->blocksize * data->blocks / 4;
if (data->flags == MMC_DATA_READ)
buf = (unsigned int *)data->dest;
else
buf = (unsigned int *)data->src;
for (;;) {
mask = dwmci_readl(host, DWMCI_RINTSTS);
/* Error during data transfer. */
if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) {
debug("%s: DATA ERROR!\n", __func__);
ret = -EINVAL;
break;
}
if (host->fifo_mode && size) {
if (data->flags == MMC_DATA_READ) {
if ((dwmci_readl(host, DWMCI_RINTSTS) &
DWMCI_INTMSK_RXDR)) {
len = dwmci_readl(host, DWMCI_STATUS);
len = (len >> DWMCI_FIFO_SHIFT) &
DWMCI_FIFO_MASK;
for (i = 0; i < len; i++)
*buf++ =
dwmci_readl(host, DWMCI_DATA);
dwmci_writel(host, DWMCI_RINTSTS,
DWMCI_INTMSK_RXDR);
}
} else {
if ((dwmci_readl(host, DWMCI_RINTSTS) &
DWMCI_INTMSK_TXDR)) {
len = dwmci_readl(host, DWMCI_STATUS);
len = fifo_depth - ((len >>
DWMCI_FIFO_SHIFT) &
DWMCI_FIFO_MASK);
for (i = 0; i < len; i++)
dwmci_writel(host, DWMCI_DATA,
*buf++);
dwmci_writel(host, DWMCI_RINTSTS,
DWMCI_INTMSK_TXDR);
}
}
size = size > len ? (size - len) : 0;
}
/* Data arrived correctly. */
if (mask & DWMCI_INTMSK_DTO) {
ret = 0;
break;
}
/* Check for timeout. */
if (get_timer(start) > timeout) {
debug("%s: Timeout waiting for data!\n",
__func__);
ret = TIMEOUT;
break;
}
}
dwmci_writel(host, DWMCI_RINTSTS, mask);
return ret;
}
static int dwmci_set_transfer_mode(struct dwmci_host *host,
struct mmc_data *data)
{
unsigned long mode;
mode = DWMCI_CMD_DATA_EXP;
if (data->flags & MMC_DATA_WRITE)
mode |= DWMCI_CMD_RW;
return mode;
}
static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct dwmci_host *host = mmc->priv;
ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac,
data ? DIV_ROUND_UP(data->blocks, 8) : 0);
int ret = 0, flags = 0, i;
unsigned int timeout = 100000;
u32 retry = 100000;
u32 mask, ctrl;
ulong start = get_timer(0);
struct bounce_buffer bbstate;
while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) {
if (get_timer(start) > timeout) {
debug("%s: Timeout on data busy\n", __func__);
return TIMEOUT;
}
}
dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
if (data) {
if (host->fifo_mode) {
dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize);
dwmci_writel(host, DWMCI_BYTCNT,
data->blocksize * data->blocks);
dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
} else {
if (data->flags == MMC_DATA_READ) {
bounce_buffer_start(&bbstate, (void*)data->dest,
data->blocksize *
data->blocks, GEN_BB_WRITE);
} else {
bounce_buffer_start(&bbstate, (void*)data->src,
data->blocksize *
data->blocks, GEN_BB_READ);
}
dwmci_prepare_data(host, data, cur_idmac,
bbstate.bounce_buffer);
}
}
dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg);
if (data)
flags = dwmci_set_transfer_mode(host, data);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
flags |= DWMCI_CMD_ABORT_STOP;
else
flags |= DWMCI_CMD_PRV_DAT_WAIT;
if (cmd->resp_type & MMC_RSP_PRESENT) {
flags |= DWMCI_CMD_RESP_EXP;
if (cmd->resp_type & MMC_RSP_136)
flags |= DWMCI_CMD_RESP_LENGTH;
}
if (cmd->resp_type & MMC_RSP_CRC)
flags |= DWMCI_CMD_CHECK_CRC;
flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG);
debug("Sending CMD%d\n",cmd->cmdidx);
dwmci_writel(host, DWMCI_CMD, flags);
for (i = 0; i < retry; i++) {
mask = dwmci_readl(host, DWMCI_RINTSTS);
if (mask & DWMCI_INTMSK_CDONE) {
if (!data)
dwmci_writel(host, DWMCI_RINTSTS, mask);
break;
}
}
if (i == retry) {
debug("%s: Timeout.\n", __func__);
return TIMEOUT;
}
if (mask & DWMCI_INTMSK_RTO) {
/*
* Timeout here is not necessarily fatal. (e)MMC cards
* will splat here when they receive CMD55 as they do
* not support this command and that is exactly the way
* to tell them apart from SD cards. Thus, this output
* below shall be debug(). eMMC cards also do not favor
* CMD8, please keep that in mind.
*/
debug("%s: Response Timeout.\n", __func__);
return TIMEOUT;
} else if (mask & DWMCI_INTMSK_RE) {
debug("%s: Response Error.\n", __func__);
return -EIO;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = dwmci_readl(host, DWMCI_RESP3);
cmd->response[1] = dwmci_readl(host, DWMCI_RESP2);
cmd->response[2] = dwmci_readl(host, DWMCI_RESP1);
cmd->response[3] = dwmci_readl(host, DWMCI_RESP0);
} else {
cmd->response[0] = dwmci_readl(host, DWMCI_RESP0);
}
}
if (data) {
ret = dwmci_data_transfer(host, data);
/* only dma mode need it */
if (!host->fifo_mode) {
ctrl = dwmci_readl(host, DWMCI_CTRL);
ctrl &= ~(DWMCI_DMA_EN);
dwmci_writel(host, DWMCI_CTRL, ctrl);
bounce_buffer_stop(&bbstate);
}
}
udelay(100);
return ret;
}
static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
{
u32 div, status;
int timeout = 10000;
unsigned long sclk;
if ((freq == host->clock) || (freq == 0))
return 0;
/*
* If host->get_mmc_clk isn't defined,
* then assume that host->bus_hz is source clock value.
* host->bus_hz should be set by user.
*/
if (host->get_mmc_clk)
sclk = host->get_mmc_clk(host, freq);
else if (host->bus_hz)
sclk = host->bus_hz;
else {
debug("%s: Didn't get source clock value.\n", __func__);
return -EINVAL;
}
if (sclk == freq)
div = 0; /* bypass mode */
else
div = DIV_ROUND_UP(sclk, 2 * freq);
dwmci_writel(host, DWMCI_CLKENA, 0);
dwmci_writel(host, DWMCI_CLKSRC, 0);
dwmci_writel(host, DWMCI_CLKDIV, div);
dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
do {
status = dwmci_readl(host, DWMCI_CMD);
if (timeout-- < 0) {
debug("%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
} while (status & DWMCI_CMD_START);
dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE |
DWMCI_CLKEN_LOW_PWR);
dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
timeout = 10000;
do {
status = dwmci_readl(host, DWMCI_CMD);
if (timeout-- < 0) {
debug("%s: Timeout!\n", __func__);
return -ETIMEDOUT;
}
} while (status & DWMCI_CMD_START);
host->clock = freq;
return 0;
}
static void dwmci_set_ios(struct mmc *mmc)
{
struct dwmci_host *host = (struct dwmci_host *)mmc->priv;
u32 ctype, regs;
debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock);
dwmci_setup_bus(host, mmc->clock);
switch (mmc->bus_width) {
case 8:
ctype = DWMCI_CTYPE_8BIT;
break;
case 4:
ctype = DWMCI_CTYPE_4BIT;
break;
default:
ctype = DWMCI_CTYPE_1BIT;
break;
}
dwmci_writel(host, DWMCI_CTYPE, ctype);
regs = dwmci_readl(host, DWMCI_UHS_REG);
if (mmc->ddr_mode)
regs |= DWMCI_DDR_MODE;
else
regs &= ~DWMCI_DDR_MODE;
dwmci_writel(host, DWMCI_UHS_REG, regs);
if (host->clksel)
host->clksel(host);
}
static int dwmci_init(struct mmc *mmc)
{
struct dwmci_host *host = mmc->priv;
if (host->board_init)
host->board_init(host);
dwmci_writel(host, DWMCI_PWREN, 1);
if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) {
debug("%s[%d] Fail-reset!!\n", __func__, __LINE__);
return -EIO;
}
/* Enumerate at 400KHz */
dwmci_setup_bus(host, mmc->cfg->f_min);
dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF);
dwmci_writel(host, DWMCI_INTMASK, 0);
dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF);
dwmci_writel(host, DWMCI_IDINTEN, 0);
dwmci_writel(host, DWMCI_BMOD, 1);
if (!host->fifoth_val) {
uint32_t fifo_size;
fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
TX_WMARK(fifo_size / 2);
}
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
dwmci_writel(host, DWMCI_CLKENA, 0);
dwmci_writel(host, DWMCI_CLKSRC, 0);
return 0;
}
static const struct mmc_ops dwmci_ops = {
.send_cmd = dwmci_send_cmd,
.set_ios = dwmci_set_ios,
.init = dwmci_init,
};
void dwmci_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth,
uint caps, u32 max_clk, u32 min_clk)
{
cfg->name = name;
cfg->ops = &dwmci_ops;
cfg->f_min = min_clk;
cfg->f_max = max_clk;
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
cfg->host_caps = caps;
if (buswidth == 8) {
cfg->host_caps |= MMC_MODE_8BIT;
cfg->host_caps &= ~MMC_MODE_4BIT;
} else {
cfg->host_caps |= MMC_MODE_4BIT;
cfg->host_caps &= ~MMC_MODE_8BIT;
}
cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
}
#ifdef CONFIG_BLK
int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg)
{
return mmc_bind(dev, mmc, cfg);
}
#else
int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk)
{
dwmci_setup_cfg(&host->cfg, host->name, host->buswidth, host->caps,
max_clk, min_clk);
host->mmc = mmc_create(&host->cfg, host);
if (host->mmc == NULL)
return -1;
return 0;
}
#endif

View File

@@ -0,0 +1,289 @@
/*
* (C) Copyright 2012 SAMSUNG Electronics
* Jaehoon Chung <jh80.chung@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dwmmc.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <malloc.h>
#include <asm/arch/dwmmc.h>
#include <asm/arch/clk.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/power.h>
#include <asm/gpio.h>
#include <asm-generic/errno.h>
#define DWMMC_MAX_CH_NUM 4
#define DWMMC_MAX_FREQ 52000000
#define DWMMC_MIN_FREQ 400000
#define DWMMC_MMC0_SDR_TIMING_VAL 0x03030001
#define DWMMC_MMC2_SDR_TIMING_VAL 0x03020001
/* Exynos implmentation specific drver private data */
struct dwmci_exynos_priv_data {
u32 sdr_timing;
};
/*
* Function used as callback function to initialise the
* CLKSEL register for every mmc channel.
*/
static void exynos_dwmci_clksel(struct dwmci_host *host)
{
struct dwmci_exynos_priv_data *priv = host->priv;
dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing);
}
unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
{
unsigned long sclk;
int8_t clk_div;
/*
* Since SDCLKIN is divided inside controller by the DIVRATIO
* value set in the CLKSEL register, we need to use the same output
* clock value to calculate the CLKDIV value.
* as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1)
*/
clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT)
& DWMCI_DIVRATIO_MASK) + 1;
sclk = get_mmc_clk(host->dev_index);
/*
* Assume to know divider value.
* When clock unit is broken, need to set "host->div"
*/
return sclk / clk_div / (host->div + 1);
}
static void exynos_dwmci_board_init(struct dwmci_host *host)
{
struct dwmci_exynos_priv_data *priv = host->priv;
if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) {
dwmci_writel(host, EMMCP_MPSBEGIN0, 0);
dwmci_writel(host, EMMCP_SEND0, 0);
dwmci_writel(host, EMMCP_CTRL0,
MPSCTRL_SECURE_READ_BIT |
MPSCTRL_SECURE_WRITE_BIT |
MPSCTRL_NON_SECURE_READ_BIT |
MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID);
}
/* Set to timing value at initial time */
if (priv->sdr_timing)
exynos_dwmci_clksel(host);
}
static int exynos_dwmci_core_init(struct dwmci_host *host, int index)
{
unsigned int div;
unsigned long freq, sclk;
struct dwmci_exynos_priv_data *priv = host->priv;
if (host->bus_hz)
freq = host->bus_hz;
else
freq = DWMMC_MAX_FREQ;
/* request mmc clock vlaue of 52MHz. */
sclk = get_mmc_clk(index);
div = DIV_ROUND_UP(sclk, freq);
/* set the clock divisor for mmc */
set_mmc_clk(index, div);
host->name = "EXYNOS DWMMC";
#ifdef CONFIG_EXYNOS5420
host->quirks = DWMCI_QUIRK_DISABLE_SMU;
#endif
host->board_init = exynos_dwmci_board_init;
if (!priv->sdr_timing) {
if (index == 0)
priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL;
else if (index == 2)
priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL;
}
host->caps = MMC_MODE_DDR_52MHz;
host->clksel = exynos_dwmci_clksel;
host->dev_index = index;
host->get_mmc_clk = exynos_dwmci_get_clk;
/* Add the mmc channel to be registered with mmc core */
if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) {
printf("DWMMC%d registration failed\n", index);
return -1;
}
return 0;
}
/*
* This function adds the mmc channel to be registered with mmc core.
* index - mmc channel number.
* regbase - register base address of mmc channel specified in 'index'.
* bus_width - operating bus width of mmc channel specified in 'index'.
* clksel - value to be written into CLKSEL register in case of FDT.
* NULL in case od non-FDT.
*/
int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
{
struct dwmci_host *host = NULL;
struct dwmci_exynos_priv_data *priv;
host = malloc(sizeof(struct dwmci_host));
if (!host) {
error("dwmci_host malloc fail!\n");
return -ENOMEM;
}
priv = malloc(sizeof(struct dwmci_exynos_priv_data));
if (!priv) {
error("dwmci_exynos_priv_data malloc fail!\n");
return -ENOMEM;
}
host->ioaddr = (void *)regbase;
host->buswidth = bus_width;
if (clksel)
priv->sdr_timing = clksel;
host->priv = priv;
return exynos_dwmci_core_init(host, index);
}
#if CONFIG_IS_ENABLED(OF_CONTROL)
static struct dwmci_host dwmci_host[DWMMC_MAX_CH_NUM];
static int do_dwmci_init(struct dwmci_host *host)
{
int index, flag, err;
index = host->dev_index;
flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
err = exynos_pinmux_config(host->dev_id, flag);
if (err) {
printf("DWMMC%d not configure\n", index);
return err;
}
return exynos_dwmci_core_init(host, index);
}
static int exynos_dwmci_get_config(const void *blob, int node,
struct dwmci_host *host)
{
int err = 0;
u32 base, timing[3];
struct dwmci_exynos_priv_data *priv;
priv = malloc(sizeof(struct dwmci_exynos_priv_data));
if (!priv) {
error("dwmci_exynos_priv_data malloc fail!\n");
return -ENOMEM;
}
/* Extract device id for each mmc channel */
host->dev_id = pinmux_decode_periph_id(blob, node);
host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id);
if (host->dev_index == host->dev_id)
host->dev_index = host->dev_id - PERIPH_ID_SDMMC0;
/* Get the bus width from the device node */
host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
if (host->buswidth <= 0) {
printf("DWMMC%d: Can't get bus-width\n", host->dev_index);
return -EINVAL;
}
/* Set the base address from the device node */
base = fdtdec_get_addr(blob, node, "reg");
if (!base) {
printf("DWMMC%d: Can't get base address\n", host->dev_index);
return -EINVAL;
}
host->ioaddr = (void *)base;
/* Extract the timing info from the node */
err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3);
if (err) {
printf("DWMMC%d: Can't get sdr-timings for devider\n",
host->dev_index);
return -EINVAL;
}
priv->sdr_timing = (DWMCI_SET_SAMPLE_CLK(timing[0]) |
DWMCI_SET_DRV_CLK(timing[1]) |
DWMCI_SET_DIV_RATIO(timing[2]));
/* sdr_timing didn't assigned anything, use the default value */
if (!priv->sdr_timing) {
if (host->dev_index == 0)
priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL;
else if (host->dev_index == 2)
priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL;
}
host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0);
host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0);
host->div = fdtdec_get_int(blob, node, "div", 0);
host->priv = priv;
return 0;
}
static int exynos_dwmci_process_node(const void *blob,
int node_list[], int count)
{
struct dwmci_host *host;
int i, node, err;
for (i = 0; i < count; i++) {
node = node_list[i];
if (node <= 0)
continue;
host = &dwmci_host[i];
err = exynos_dwmci_get_config(blob, node, host);
if (err) {
printf("%s: failed to decode dev %d\n", __func__, i);
return err;
}
do_dwmci_init(host);
}
return 0;
}
int exynos_dwmmc_init(const void *blob)
{
int compat_id;
int node_list[DWMMC_MAX_CH_NUM];
int boot_dev_node;
int err = 0, count;
compat_id = COMPAT_SAMSUNG_EXYNOS_DWMMC;
count = fdtdec_find_aliases_for_id(blob, "mmc",
compat_id, node_list, DWMMC_MAX_CH_NUM);
/* For DWMMC always set boot device as mmc 0 */
if (count >= 3 && get_boot_mode() == BOOT_MODE_SD) {
boot_dev_node = node_list[2];
node_list[2] = node_list[0];
node_list[0] = boot_dev_node;
}
err = exynos_dwmci_process_node(blob, node_list, count);
return err;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
#include <malloc.h>
/*
* The environment variables are written to just after the u-boot image
* on SDCard, so we must read the MBR to get the start address and code
* length of the u-boot image, then calculate the address of the env.
*/
#define ESDHC_BOOT_IMAGE_SIZE 0x48
#define ESDHC_BOOT_IMAGE_ADDR 0x50
#define MBRDBR_BOOT_SIG_55 0x1fe
#define MBRDBR_BOOT_SIG_AA 0x1ff
#define CONFIG_CFG_DATA_SECTOR 0
void mmc_spl_load_image(uint32_t offs, unsigned int size, void *vdst)
{
uint blk_start, blk_cnt, err;
struct mmc *mmc = find_mmc_device(0);
if (!mmc) {
puts("spl: mmc device not found!!\n");
hang();
}
if (mmc_init(mmc)) {
puts("MMC init failed\n");
return;
}
blk_start = ALIGN(offs, mmc->read_bl_len) / mmc->read_bl_len;
blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
err = mmc->block_dev.block_read(&mmc->block_dev, blk_start, blk_cnt,
vdst);
if (err != blk_cnt) {
puts("spl: mmc read failed!!\n");
hang();
}
}
/*
* The main entry for mmc booting. It's necessary that SDRAM is already
* configured and available since this code loads the main U-Boot image
* from mmc into SDRAM and starts it from there.
*/
void __noreturn mmc_boot(void)
{
__attribute__((noreturn)) void (*uboot)(void);
uint blk_start, blk_cnt, err;
#ifndef CONFIG_FSL_CORENET
uchar *tmp_buf;
u32 blklen;
uchar val;
uint i, byte_num;
#endif
u32 offset, code_len;
struct mmc *mmc;
mmc = find_mmc_device(0);
if (!mmc) {
puts("spl: mmc device not found!!\n");
hang();
}
#ifdef CONFIG_FSL_CORENET
offset = CONFIG_SYS_MMC_U_BOOT_OFFS;
code_len = CONFIG_SYS_MMC_U_BOOT_SIZE;
#else
blklen = mmc->read_bl_len;
tmp_buf = malloc(blklen);
if (!tmp_buf) {
puts("spl: malloc memory failed!!\n");
hang();
}
memset(tmp_buf, 0, blklen);
/*
* Read source addr from sd card
*/
err = mmc->block_dev.block_read(&mmc->block_dev,
CONFIG_CFG_DATA_SECTOR, 1, tmp_buf);
if (err != 1) {
puts("spl: mmc read failed!!\n");
free(tmp_buf);
hang();
}
val = *(tmp_buf + MBRDBR_BOOT_SIG_55);
if (0x55 != val) {
puts("spl: mmc signature is not valid!!\n");
free(tmp_buf);
hang();
}
val = *(tmp_buf + MBRDBR_BOOT_SIG_AA);
if (0xAA != val) {
puts("spl: mmc signature is not valid!!\n");
free(tmp_buf);
hang();
}
byte_num = 4;
offset = 0;
for (i = 0; i < byte_num; i++) {
val = *(tmp_buf + ESDHC_BOOT_IMAGE_ADDR + i);
offset = (offset << 8) + val;
}
offset += CONFIG_SYS_MMC_U_BOOT_OFFS;
/* Get the code size from offset 0x48 */
byte_num = 4;
code_len = 0;
for (i = 0; i < byte_num; i++) {
val = *(tmp_buf + ESDHC_BOOT_IMAGE_SIZE + i);
code_len = (code_len << 8) + val;
}
code_len -= CONFIG_SYS_MMC_U_BOOT_OFFS;
/*
* Load U-Boot image from mmc into RAM
*/
#endif
blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
blk_cnt = ALIGN(code_len, mmc->read_bl_len) / mmc->read_bl_len;
err = mmc->block_dev.block_read(&mmc->block_dev, blk_start, blk_cnt,
(uchar *)CONFIG_SYS_MMC_U_BOOT_DST);
if (err != blk_cnt) {
puts("spl: mmc read failed!!\n");
#ifndef CONFIG_FSL_CORENET
free(tmp_buf);
#endif
hang();
}
/*
* Clean d-cache and invalidate i-cache, to
* make sure that no stale data is executed.
*/
flush_cache(CONFIG_SYS_MMC_U_BOOT_DST, CONFIG_SYS_MMC_U_BOOT_SIZE);
/*
* Jump to U-Boot image
*/
uboot = (void *)CONFIG_SYS_MMC_U_BOOT_START;
(*uboot)();
}

View File

@@ -0,0 +1,373 @@
/*
* Faraday MMC/SD Host Controller
*
* (C) Copyright 2010 Faraday Technology
* Dante Su <dantesu@faraday-tech.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <part.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/byteorder.h>
#include <faraday/ftsdc010.h>
#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */
#define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */
struct ftsdc010_chip {
void __iomem *regs;
uint32_t wprot; /* write protected (locked) */
uint32_t rate; /* actual SD clock in Hz */
uint32_t sclk; /* FTSDC010 source clock in Hz */
uint32_t fifo; /* fifo depth in bytes */
uint32_t acmd;
struct mmc_config cfg; /* mmc configuration */
};
static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
{
struct ftsdc010_chip *chip = mmc->priv;
struct ftsdc010_mmc __iomem *regs = chip->regs;
int ret = TIMEOUT;
uint32_t ts, st;
uint32_t cmd = FTSDC010_CMD_IDX(mmc_cmd->cmdidx);
uint32_t arg = mmc_cmd->cmdarg;
uint32_t flags = mmc_cmd->resp_type;
cmd |= FTSDC010_CMD_CMD_EN;
if (chip->acmd) {
cmd |= FTSDC010_CMD_APP_CMD;
chip->acmd = 0;
}
if (flags & MMC_RSP_PRESENT)
cmd |= FTSDC010_CMD_NEED_RSP;
if (flags & MMC_RSP_136)
cmd |= FTSDC010_CMD_LONG_RSP;
writel(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND,
&regs->clr);
writel(arg, &regs->argu);
writel(cmd, &regs->cmd);
if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) {
for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
if (readl(&regs->status) & FTSDC010_STATUS_CMD_SEND) {
writel(FTSDC010_STATUS_CMD_SEND, &regs->clr);
ret = 0;
break;
}
}
} else {
st = 0;
for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
st = readl(&regs->status);
writel(st & FTSDC010_STATUS_RSP_MASK, &regs->clr);
if (st & FTSDC010_STATUS_RSP_MASK)
break;
}
if (st & FTSDC010_STATUS_RSP_CRC_OK) {
if (flags & MMC_RSP_136) {
mmc_cmd->response[0] = readl(&regs->rsp3);
mmc_cmd->response[1] = readl(&regs->rsp2);
mmc_cmd->response[2] = readl(&regs->rsp1);
mmc_cmd->response[3] = readl(&regs->rsp0);
} else {
mmc_cmd->response[0] = readl(&regs->rsp0);
}
ret = 0;
} else {
debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n",
mmc_cmd->cmdidx, st);
}
}
if (ret) {
debug("ftsdc010: cmd timeout (op code=%d)\n",
mmc_cmd->cmdidx);
} else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) {
chip->acmd = 1;
}
return ret;
}
static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate)
{
struct ftsdc010_chip *chip = mmc->priv;
struct ftsdc010_mmc __iomem *regs = chip->regs;
uint32_t div;
for (div = 0; div < 0x7f; ++div) {
if (rate >= chip->sclk / (2 * (div + 1)))
break;
}
chip->rate = chip->sclk / (2 * (div + 1));
writel(FTSDC010_CCR_CLK_DIV(div), &regs->ccr);
if (IS_SD(mmc)) {
setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_SD);
if (chip->rate > 25000000)
setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
else
clrbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
}
}
static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
{
int ret = TIMEOUT;
uint32_t st, ts;
for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
st = readl(&regs->status);
if (!(st & mask))
continue;
writel(st & mask, &regs->clr);
ret = 0;
break;
}
if (ret)
debug("ftsdc010: wait st(0x%x) timeout\n", mask);
return ret;
}
/*
* u-boot mmc api
*/
static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
int ret = UNUSABLE_ERR;
uint32_t len = 0;
struct ftsdc010_chip *chip = mmc->priv;
struct ftsdc010_mmc __iomem *regs = chip->regs;
if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) {
printf("ftsdc010: the card is write protected!\n");
return ret;
}
if (data) {
uint32_t dcr;
len = data->blocksize * data->blocks;
/* 1. data disable + fifo reset */
dcr = 0;
#ifdef CONFIG_FTSDC010_SDIO
dcr |= FTSDC010_DCR_FIFO_RST;
#endif
writel(dcr, &regs->dcr);
/* 2. clear status register */
writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN
| FTSDC010_STATUS_FIFO_ORUN, &regs->clr);
/* 3. data timeout (1 sec) */
writel(chip->rate, &regs->dtr);
/* 4. data length (bytes) */
writel(len, &regs->dlr);
/* 5. data enable */
dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN;
if (data->flags & MMC_DATA_WRITE)
dcr |= FTSDC010_DCR_DATA_WRITE;
writel(dcr, &regs->dcr);
}
ret = ftsdc010_send_cmd(mmc, cmd);
if (ret) {
printf("ftsdc010: CMD%d failed\n", cmd->cmdidx);
return ret;
}
if (!data)
return ret;
if (data->flags & MMC_DATA_WRITE) {
const uint8_t *buf = (const uint8_t *)data->src;
while (len > 0) {
int wlen;
/* wait for tx ready */
ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_URUN);
if (ret)
break;
/* write bytes to ftsdc010 */
for (wlen = 0; wlen < len && wlen < chip->fifo; ) {
writel(*(uint32_t *)buf, &regs->dwr);
buf += 4;
wlen += 4;
}
len -= wlen;
}
} else {
uint8_t *buf = (uint8_t *)data->dest;
while (len > 0) {
int rlen;
/* wait for rx ready */
ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_ORUN);
if (ret)
break;
/* fetch bytes from ftsdc010 */
for (rlen = 0; rlen < len && rlen < chip->fifo; ) {
*(uint32_t *)buf = readl(&regs->dwr);
buf += 4;
rlen += 4;
}
len -= rlen;
}
}
if (!ret) {
ret = ftsdc010_wait(regs,
FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_ERROR);
}
return ret;
}
static void ftsdc010_set_ios(struct mmc *mmc)
{
struct ftsdc010_chip *chip = mmc->priv;
struct ftsdc010_mmc __iomem *regs = chip->regs;
ftsdc010_clkset(mmc, mmc->clock);
clrbits_le32(&regs->bwr, FTSDC010_BWR_MODE_MASK);
switch (mmc->bus_width) {
case 4:
setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_4BIT);
break;
case 8:
setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_8BIT);
break;
default:
setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_1BIT);
break;
}
}
static int ftsdc010_init(struct mmc *mmc)
{
struct ftsdc010_chip *chip = mmc->priv;
struct ftsdc010_mmc __iomem *regs = chip->regs;
uint32_t ts;
if (readl(&regs->status) & FTSDC010_STATUS_CARD_DETECT)
return NO_CARD_ERR;
if (readl(&regs->status) & FTSDC010_STATUS_WRITE_PROT) {
printf("ftsdc010: write protected\n");
chip->wprot = 1;
}
chip->fifo = (readl(&regs->feature) & 0xff) << 2;
/* 1. chip reset */
writel(FTSDC010_CMD_SDC_RST, &regs->cmd);
for (ts = get_timer(0); get_timer(ts) < CFG_RST_TIMEOUT; ) {
if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST)
continue;
break;
}
if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST) {
printf("ftsdc010: reset failed\n");
return UNUSABLE_ERR;
}
/* 2. enter low speed mode (400k card detection) */
ftsdc010_clkset(mmc, 400000);
/* 3. interrupt disabled */
writel(0, &regs->int_mask);
return 0;
}
static const struct mmc_ops ftsdc010_ops = {
.send_cmd = ftsdc010_request,
.set_ios = ftsdc010_set_ios,
.init = ftsdc010_init,
};
int ftsdc010_mmc_init(int devid)
{
struct mmc *mmc;
struct ftsdc010_chip *chip;
struct ftsdc010_mmc __iomem *regs;
#ifdef CONFIG_FTSDC010_BASE_LIST
uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST;
if (devid < 0 || devid >= ARRAY_SIZE(base_list))
return -1;
regs = (void __iomem *)base_list[devid];
#else
regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20));
#endif
chip = malloc(sizeof(struct ftsdc010_chip));
if (!chip)
return -ENOMEM;
memset(chip, 0, sizeof(struct ftsdc010_chip));
chip->regs = regs;
#ifdef CONFIG_SYS_CLK_FREQ
chip->sclk = CONFIG_SYS_CLK_FREQ;
#else
chip->sclk = clk_get_rate("SDC");
#endif
chip->cfg.name = "ftsdc010";
chip->cfg.ops = &ftsdc010_ops;
chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz;
switch (readl(&regs->bwr) & FTSDC010_BWR_CAPS_MASK) {
case FTSDC010_BWR_CAPS_4BIT:
chip->cfg.host_caps |= MMC_MODE_4BIT;
break;
case FTSDC010_BWR_CAPS_8BIT:
chip->cfg.host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
break;
default:
break;
}
chip->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
chip->cfg.f_max = chip->sclk / 2;
chip->cfg.f_min = chip->sclk / 0x100;
chip->cfg.part_type = PART_TYPE_DOS;
chip->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
mmc = mmc_create(&chip->cfg, chip);
if (mmc == NULL) {
free(chip);
return -ENOMEM;
}
return 0;
}

View File

@@ -0,0 +1,33 @@
/*
* (C) Copyright 2013 Faraday Technology
* Kuo-Jung Su <dantesu@faraday-tech.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#ifndef CONFIG_FTSDC021_CLOCK
#define CONFIG_FTSDC021_CLOCK clk_get_rate("MMC")
#endif
int ftsdc021_sdhci_init(u32 regbase)
{
struct sdhci_host *host = NULL;
uint32_t freq = CONFIG_FTSDC021_CLOCK;
host = calloc(1, sizeof(struct sdhci_host));
if (!host) {
puts("sdh_host malloc fail!\n");
return 1;
}
host->name = "FTSDC021";
host->ioaddr = (void __iomem *)regbase;
host->quirks = 0;
add_sdhci(host, freq, 0);
return 0;
}

View File

@@ -0,0 +1,456 @@
/*
* Copyright (C) 2010
* Rob Emanuele <rob@emanuele.us>
* Reinhard Meyer, EMK Elektronik <reinhard.meyer@emk-elektronik.de>
*
* Original Driver:
* Copyright (C) 2004-2006 Atmel Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/byteorder.h>
#include <asm/arch/clk.h>
#include <asm/arch/hardware.h>
#include "atmel_mci.h"
#ifndef CONFIG_SYS_MMC_CLK_OD
# define CONFIG_SYS_MMC_CLK_OD 150000
#endif
#define MMC_DEFAULT_BLKLEN 512
#if defined(CONFIG_ATMEL_MCI_PORTB)
# define MCI_BUS 1
#else
# define MCI_BUS 0
#endif
struct atmel_mci_priv {
struct mmc_config cfg;
struct atmel_mci *mci;
unsigned int initialized:1;
unsigned int curr_clk;
};
/* Read Atmel MCI IP version */
static unsigned int atmel_mci_get_version(struct atmel_mci *mci)
{
return readl(&mci->version) & 0x00000fff;
}
/*
* Print command and status:
*
* - always when DEBUG is defined
* - on command errors
*/
static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg)
{
debug("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n",
cmdr, cmdr & 0x3F, arg, status, msg);
}
/* Setup for MCI Clock and Block Size */
static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen)
{
struct atmel_mci_priv *priv = mmc->priv;
atmel_mci_t *mci = priv->mci;
u32 bus_hz = get_mci_clk_rate();
u32 clkdiv = 255;
unsigned int version = atmel_mci_get_version(mci);
u32 clkodd = 0;
u32 mr;
debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n",
bus_hz, hz, blklen);
if (hz > 0) {
if (version >= 0x500) {
clkdiv = DIV_ROUND_UP(bus_hz, hz) - 2;
if (clkdiv > 511)
clkdiv = 511;
clkodd = clkdiv & 1;
clkdiv >>= 1;
debug("mci: setting clock %u Hz, block size %u\n",
bus_hz / (clkdiv * 2 + clkodd + 2), blklen);
} else {
/* find clkdiv yielding a rate <= than requested */
for (clkdiv = 0; clkdiv < 255; clkdiv++) {
if ((bus_hz / (clkdiv + 1) / 2) <= hz)
break;
}
debug("mci: setting clock %u Hz, block size %u\n",
(bus_hz / (clkdiv + 1)) / 2, blklen);
}
}
if (version >= 0x500)
priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2);
else
priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2;
blklen &= 0xfffc;
mr = MMCI_BF(CLKDIV, clkdiv);
/* MCI IP version >= 0x200 has R/WPROOF */
if (version >= 0x200)
mr |= MMCI_BIT(RDPROOF) | MMCI_BIT(WRPROOF);
/*
* MCI IP version >= 0x500 use bit 16 as clkodd.
* MCI IP version < 0x500 use upper 16 bits for blklen.
*/
if (version >= 0x500)
mr |= MMCI_BF(CLKODD, clkodd);
else
mr |= MMCI_BF(BLKLEN, blklen);
writel(mr, &mci->mr);
/* MCI IP version >= 0x200 has blkr */
if (version >= 0x200)
writel(MMCI_BF(BLKLEN, blklen), &mci->blkr);
if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS)
writel(MMCI_BIT(HSMODE), &mci->cfg);
priv->initialized = 1;
}
/* Return the CMDR with flags for a given command and data packet */
static u32 mci_encode_cmd(
struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags)
{
u32 cmdr = 0;
/* Default Flags for Errors */
*error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) |
MMCI_BIT(RINDE) | MMCI_BIT(RTOE));
/* Default Flags for the Command */
cmdr |= MMCI_BIT(MAXLAT);
if (data) {
cmdr |= MMCI_BF(TRCMD, 1);
if (data->blocks > 1)
cmdr |= MMCI_BF(TRTYP, 1);
if (data->flags & MMC_DATA_READ)
cmdr |= MMCI_BIT(TRDIR);
}
if (cmd->resp_type & MMC_RSP_CRC)
*error_flags |= MMCI_BIT(RCRCE);
if (cmd->resp_type & MMC_RSP_136)
cmdr |= MMCI_BF(RSPTYP, 2);
else if (cmd->resp_type & MMC_RSP_BUSY)
cmdr |= MMCI_BF(RSPTYP, 3);
else if (cmd->resp_type & MMC_RSP_PRESENT)
cmdr |= MMCI_BF(RSPTYP, 1);
return cmdr | MMCI_BF(CMDNB, cmd->cmdidx);
}
/* Entered into function pointer in mci_send_cmd */
static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags)
{
u32 status;
do {
status = readl(&mci->sr);
if (status & (error_flags | MMCI_BIT(OVRE)))
goto io_fail;
} while (!(status & MMCI_BIT(RXRDY)));
if (status & MMCI_BIT(RXRDY)) {
*data = readl(&mci->rdr);
status = 0;
}
io_fail:
return status;
}
/* Entered into function pointer in mci_send_cmd */
static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags)
{
u32 status;
do {
status = readl(&mci->sr);
if (status & (error_flags | MMCI_BIT(UNRE)))
goto io_fail;
} while (!(status & MMCI_BIT(TXRDY)));
if (status & MMCI_BIT(TXRDY)) {
writel(*data, &mci->tdr);
status = 0;
}
io_fail:
return status;
}
/*
* Entered into mmc structure during driver init
*
* Sends a command out on the bus and deals with the block data.
* Takes the mmc pointer, a command pointer, and an optional data pointer.
*/
static int
mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
struct atmel_mci_priv *priv = mmc->priv;
atmel_mci_t *mci = priv->mci;
u32 cmdr;
u32 error_flags = 0;
u32 status;
if (!priv->initialized) {
puts ("MCI not initialized!\n");
return COMM_ERR;
}
/* Figure out the transfer arguments */
cmdr = mci_encode_cmd(cmd, data, &error_flags);
/* For multi blocks read/write, set the block register */
if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK)
|| (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK))
writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len),
&mci->blkr);
/* Send the command */
writel(cmd->cmdarg, &mci->argr);
writel(cmdr, &mci->cmdr);
#ifdef DEBUG
dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG");
#endif
/* Wait for the command to complete */
while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY)));
if ((status & error_flags) & MMCI_BIT(RTOE)) {
dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out");
return TIMEOUT;
} else if (status & error_flags) {
dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed");
return COMM_ERR;
}
/* Copy the response to the response buffer */
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = readl(&mci->rspr);
cmd->response[1] = readl(&mci->rspr1);
cmd->response[2] = readl(&mci->rspr2);
cmd->response[3] = readl(&mci->rspr3);
} else
cmd->response[0] = readl(&mci->rspr);
/* transfer all of the blocks */
if (data) {
u32 word_count, block_count;
u32* ioptr;
u32 sys_blocksize, dummy, i;
u32 (*mci_data_op)
(atmel_mci_t *mci, u32* data, u32 error_flags);
if (data->flags & MMC_DATA_READ) {
mci_data_op = mci_data_read;
sys_blocksize = mmc->read_bl_len;
ioptr = (u32*)data->dest;
} else {
mci_data_op = mci_data_write;
sys_blocksize = mmc->write_bl_len;
ioptr = (u32*)data->src;
}
status = 0;
for (block_count = 0;
block_count < data->blocks && !status;
block_count++) {
word_count = 0;
do {
status = mci_data_op(mci, ioptr, error_flags);
word_count++;
ioptr++;
} while (!status && word_count < (data->blocksize/4));
#ifdef DEBUG
if (data->flags & MMC_DATA_READ)
{
u32 cnt = word_count * 4;
printf("Read Data:\n");
print_buffer(0, data->dest + cnt * block_count,
1, cnt, 0);
}
#endif
#ifdef DEBUG
if (!status && word_count < (sys_blocksize / 4))
printf("filling rest of block...\n");
#endif
/* fill the rest of a full block */
while (!status && word_count < (sys_blocksize / 4)) {
status = mci_data_op(mci, &dummy,
error_flags);
word_count++;
}
if (status) {
dump_cmd(cmdr, cmd->cmdarg, status,
"Data Transfer Failed");
return COMM_ERR;
}
}
/* Wait for Transfer End */
i = 0;
do {
status = readl(&mci->sr);
if (status & error_flags) {
dump_cmd(cmdr, cmd->cmdarg, status,
"DTIP Wait Failed");
return COMM_ERR;
}
i++;
} while ((status & MMCI_BIT(DTIP)) && i < 10000);
if (status & MMCI_BIT(DTIP)) {
dump_cmd(cmdr, cmd->cmdarg, status,
"XFER DTIP never unset, ignoring");
}
}
/*
* After the switch command, wait for 8 clocks before the next
* command
*/
if (cmd->cmdidx == MMC_CMD_SWITCH)
udelay(8*1000000 / priv->curr_clk); /* 8 clk in us */
return 0;
}
/* Entered into mmc structure during driver init */
static void mci_set_ios(struct mmc *mmc)
{
struct atmel_mci_priv *priv = mmc->priv;
atmel_mci_t *mci = priv->mci;
int bus_width = mmc->bus_width;
unsigned int version = atmel_mci_get_version(mci);
int busw;
/* Set the clock speed */
mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN);
/*
* set the bus width and select slot for this interface
* there is no capability for multiple slots on the same interface yet
*/
if ((version & 0xf00) >= 0x300) {
switch (bus_width) {
case 8:
busw = 3;
break;
case 4:
busw = 2;
break;
default:
busw = 0;
break;
}
writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
} else {
busw = (bus_width == 4) ? 1 : 0;
writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
}
}
/* Entered into mmc structure during driver init */
static int mci_init(struct mmc *mmc)
{
struct atmel_mci_priv *priv = mmc->priv;
atmel_mci_t *mci = priv->mci;
/* Initialize controller */
writel(MMCI_BIT(SWRST), &mci->cr); /* soft reset */
writel(MMCI_BIT(PWSDIS), &mci->cr); /* disable power save */
writel(MMCI_BIT(MCIEN), &mci->cr); /* enable mci */
writel(MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); /* select port */
/* This delay can be optimized, but stick with max value */
writel(0x7f, &mci->dtor);
/* Disable Interrupts */
writel(~0UL, &mci->idr);
/* Set default clocks and blocklen */
mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
return 0;
}
static const struct mmc_ops atmel_mci_ops = {
.send_cmd = mci_send_cmd,
.set_ios = mci_set_ios,
.init = mci_init,
};
/*
* This is the only exported function
*
* Call it with the MCI register base address
*/
int atmel_mci_init(void *regs)
{
struct mmc *mmc;
struct mmc_config *cfg;
struct atmel_mci_priv *priv;
unsigned int version;
priv = calloc(1, sizeof(*priv));
if (!priv)
return -ENOMEM;
cfg = &priv->cfg;
cfg->name = "mci";
cfg->ops = &atmel_mci_ops;
priv->mci = (struct atmel_mci *)regs;
priv->initialized = 0;
/* need to be able to pass these in on a board by board basis */
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
version = atmel_mci_get_version(priv->mci);
if ((version & 0xf00) >= 0x300) {
cfg->host_caps = MMC_MODE_8BIT;
cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
}
cfg->host_caps |= MMC_MODE_4BIT;
/*
* min and max frequencies determined by
* max and min of clock divider
*/
cfg->f_min = get_mci_clk_rate() / (2*256);
cfg->f_max = get_mci_clk_rate() / (2*1);
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
mmc = mmc_create(cfg, priv);
if (mmc == NULL) {
free(priv);
return -ENODEV;
}
/* NOTE: possibly leaking the priv structure */
return 0;
}

View File

@@ -0,0 +1,56 @@
/*
* (C) Copyright 2015 Linaro
* peter.griffin <peter.griffin@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dwmmc.h>
#include <malloc.h>
#include <asm-generic/errno.h>
#define DWMMC_MAX_CH_NUM 4
#define DWMMC_MAX_FREQ 50000000
#define DWMMC_MIN_FREQ 400000
/* Source clock is configured to 100MHz by ATF bl1*/
#define MMC0_DEFAULT_FREQ 100000000
static int hi6220_dwmci_core_init(struct dwmci_host *host, int index)
{
host->name = "HiKey DWMMC";
host->dev_index = index;
/* Add the mmc channel to be registered with mmc core */
if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) {
printf("DWMMC%d registration failed\n", index);
return -1;
}
return 0;
}
/*
* This function adds the mmc channel to be registered with mmc core.
* index - mmc channel number.
* regbase - register base address of mmc channel specified in 'index'.
* bus_width - operating bus width of mmc channel specified in 'index'.
*/
int hi6220_dwmci_add_port(int index, u32 regbase, int bus_width)
{
struct dwmci_host *host = NULL;
host = calloc(1, sizeof(struct dwmci_host));
if (!host) {
error("dwmci_host calloc failed!\n");
return -ENOMEM;
}
host->ioaddr = (void *)(ulong)regbase;
host->buswidth = bus_width;
host->bus_hz = MMC0_DEFAULT_FREQ;
return hi6220_dwmci_core_init(host, index);
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright 2013 Broadcom Corporation.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#include <asm/errno.h>
#include <asm/kona-common/clk.h>
#define SDHCI_CORECTRL_OFFSET 0x00008000
#define SDHCI_CORECTRL_EN 0x01
#define SDHCI_CORECTRL_RESET 0x02
#define SDHCI_CORESTAT_OFFSET 0x00008004
#define SDHCI_CORESTAT_CD_SW 0x01
#define SDHCI_COREIMR_OFFSET 0x00008008
#define SDHCI_COREIMR_IP 0x01
static int init_kona_mmc_core(struct sdhci_host *host)
{
unsigned int mask;
unsigned int timeout;
if (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL) {
printf("%s: sd host controller reset error\n", __func__);
return 1;
}
/* For kona a hardware reset before anything else. */
mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET) | SDHCI_CORECTRL_RESET;
sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET);
/* Wait max 100 ms */
timeout = 1000;
do {
if (timeout == 0) {
printf("%s: reset timeout error\n", __func__);
return 1;
}
timeout--;
udelay(100);
} while (0 ==
(sdhci_readl(host, SDHCI_CORECTRL_OFFSET) &
SDHCI_CORECTRL_RESET));
/* Clear the reset bit. */
mask = mask & ~SDHCI_CORECTRL_RESET;
sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET);
/* Enable AHB clock */
mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET);
sdhci_writel(host, mask | SDHCI_CORECTRL_EN, SDHCI_CORECTRL_OFFSET);
/* Enable interrupts */
sdhci_writel(host, SDHCI_COREIMR_IP, SDHCI_COREIMR_OFFSET);
/* Make sure Card is detected in controller */
mask = sdhci_readl(host, SDHCI_CORESTAT_OFFSET);
sdhci_writel(host, mask | SDHCI_CORESTAT_CD_SW, SDHCI_CORESTAT_OFFSET);
/* Wait max 100 ms */
timeout = 1000;
while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
if (timeout == 0) {
printf("%s: CARD DETECT timeout error\n", __func__);
return 1;
}
timeout--;
udelay(100);
}
return 0;
}
int kona_sdhci_init(int dev_index, u32 min_clk, u32 quirks)
{
int ret = 0;
u32 max_clk;
void *reg_base;
struct sdhci_host *host = NULL;
host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
if (!host) {
printf("%s: sdhci host malloc fail!\n", __func__);
return -ENOMEM;
}
switch (dev_index) {
case 0:
reg_base = (void *)CONFIG_SYS_SDIO_BASE0;
ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO0_MAX_CLK,
&max_clk);
break;
case 1:
reg_base = (void *)CONFIG_SYS_SDIO_BASE1;
ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO1_MAX_CLK,
&max_clk);
break;
case 2:
reg_base = (void *)CONFIG_SYS_SDIO_BASE2;
ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO2_MAX_CLK,
&max_clk);
break;
case 3:
reg_base = (void *)CONFIG_SYS_SDIO_BASE3;
ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO3_MAX_CLK,
&max_clk);
break;
default:
printf("%s: sdio dev index %d not supported\n",
__func__, dev_index);
ret = -EINVAL;
}
if (ret) {
free(host);
return ret;
}
host->name = "kona-sdhci";
host->ioaddr = reg_base;
host->quirks = quirks;
if (init_kona_mmc_core(host)) {
free(host);
return -EINVAL;
}
if (quirks & SDHCI_QUIRK_REG32_RW)
host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
else
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
add_sdhci(host, max_clk, min_clk);
return ret;
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
#include <dm.h>
#include <dm/lists.h>
#include <dm/root.h>
struct mmc *mmc_get_mmc_dev(struct udevice *dev)
{
struct mmc_uclass_priv *upriv;
if (!device_active(dev))
return NULL;
upriv = dev_get_uclass_priv(dev);
return upriv->mmc;
}
#ifdef CONFIG_BLK
struct mmc *find_mmc_device(int dev_num)
{
struct udevice *dev, *mmc_dev;
int ret;
ret = blk_get_device(IF_TYPE_MMC, dev_num, &dev);
if (ret) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC Device %d not found\n", dev_num);
#endif
return NULL;
}
mmc_dev = dev_get_parent(dev);
return mmc_get_mmc_dev(mmc_dev);
}
int get_mmc_num(void)
{
return max(blk_find_max_devnum(IF_TYPE_MMC), 0);
}
int mmc_get_next_devnum(void)
{
int ret;
ret = get_mmc_num();
if (ret < 0)
return ret;
return ret + 1;
}
struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
{
struct blk_desc *desc;
struct udevice *dev;
device_find_first_child(mmc->dev, &dev);
if (!dev)
return NULL;
desc = dev_get_uclass_platdata(dev);
return desc;
}
void mmc_do_preinit(void)
{
struct udevice *dev;
struct uclass *uc;
int ret;
ret = uclass_get(UCLASS_MMC, &uc);
if (ret)
return;
uclass_foreach_dev(dev, uc) {
struct mmc *m = mmc_get_mmc_dev(dev);
if (!m)
continue;
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
mmc_set_preinit(m, 1);
#endif
if (m->preinit)
mmc_start_init(m);
}
}
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
void print_mmc_devices(char separator)
{
struct udevice *dev;
char *mmc_type;
bool first = true;
for (uclass_first_device(UCLASS_MMC, &dev);
dev;
uclass_next_device(&dev)) {
struct mmc *m = mmc_get_mmc_dev(dev);
if (!first) {
printf("%c", separator);
if (separator != '\n')
puts(" ");
}
if (m->has_init)
mmc_type = IS_SD(m) ? "SD" : "eMMC";
else
mmc_type = NULL;
printf("%s: %d", m->cfg->name, mmc_get_blk_desc(m)->devnum);
if (mmc_type)
printf(" (%s)", mmc_type);
}
printf("\n");
}
#else
void print_mmc_devices(char separator) { }
#endif
#endif /* CONFIG_BLK */
U_BOOT_DRIVER(mmc) = {
.name = "mmc",
.id = UCLASS_MMC,
};
UCLASS_DRIVER(mmc) = {
.id = UCLASS_MMC,
.name = "mmc",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct mmc_uclass_priv),
};

1986
u-boot/drivers/mmc/mmc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
static struct list_head mmc_devices;
static int cur_dev_num = -1;
struct mmc *find_mmc_device(int dev_num)
{
struct mmc *m;
struct list_head *entry;
list_for_each(entry, &mmc_devices) {
m = list_entry(entry, struct mmc, link);
if (m->block_dev.devnum == dev_num)
return m;
}
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC Device %d not found\n", dev_num);
#endif
return NULL;
}
int mmc_get_next_devnum(void)
{
return cur_dev_num++;
}
struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
{
return &mmc->block_dev;
}
int get_mmc_num(void)
{
return cur_dev_num;
}
void mmc_do_preinit(void)
{
struct mmc *m;
struct list_head *entry;
list_for_each(entry, &mmc_devices) {
m = list_entry(entry, struct mmc, link);
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
mmc_set_preinit(m, 1);
#endif
if (m->preinit)
mmc_start_init(m);
}
}
void mmc_list_init(void)
{
INIT_LIST_HEAD(&mmc_devices);
cur_dev_num = 0;
}
void mmc_list_add(struct mmc *mmc)
{
INIT_LIST_HEAD(&mmc->link);
list_add_tail(&mmc->link, &mmc_devices);
}
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
void print_mmc_devices(char separator)
{
struct mmc *m;
struct list_head *entry;
char *mmc_type;
list_for_each(entry, &mmc_devices) {
m = list_entry(entry, struct mmc, link);
if (m->has_init)
mmc_type = IS_SD(m) ? "SD" : "eMMC";
else
mmc_type = NULL;
printf("%s: %d", m->cfg->name, m->block_dev.devnum);
if (mmc_type)
printf(" (%s)", mmc_type);
if (entry->next != &mmc_devices) {
printf("%c", separator);
if (separator != '\n')
puts(" ");
}
}
printf("\n");
}
#else
void print_mmc_devices(char separator) { }
#endif

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2008,2010 Freescale Semiconductor, Inc
* Andy Fleming
*
* Based (loosely) on the Linux code
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _MMC_PRIVATE_H_
#define _MMC_PRIVATE_H_
#include <mmc.h>
extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data);
extern int mmc_send_status(struct mmc *mmc, int timeout);
extern int mmc_set_blocklen(struct mmc *mmc, int len);
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
void mmc_adapter_card_type_ident(void);
#endif
#ifndef CONFIG_SPL_BUILD
unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt);
#ifdef CONFIG_BLK
ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
const void *src);
#else
ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
const void *src);
#endif
#else /* CONFIG_SPL_BUILD */
/* SPL will never write or erase, declare dummies to reduce code size. */
#ifdef CONFIG_BLK
static inline unsigned long mmc_berase(struct udevice *dev,
lbaint_t start, lbaint_t blkcnt)
{
return 0;
}
static inline ulong mmc_bwrite(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt, const void *src)
{
return 0;
}
#else
static inline unsigned long mmc_berase(struct blk_desc *block_dev,
lbaint_t start, lbaint_t blkcnt)
{
return 0;
}
static inline ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *src)
{
return 0;
}
#endif
#endif /* CONFIG_SPL_BUILD */
/**
* mmc_get_next_devnum() - Get the next available MMC device number
*
* @return next available device number (0 = first), or -ve on error
*/
int mmc_get_next_devnum(void);
/**
* mmc_do_preinit() - Get an MMC device ready for use
*/
void mmc_do_preinit(void);
/**
* mmc_list_init() - Set up the list of MMC devices
*/
void mmc_list_init(void);
/**
* mmc_list_add() - Add a new MMC device to the list of devices
*
* @mmc: Device to add
*/
void mmc_list_add(struct mmc *mmc);
#endif /* _MMC_PRIVATE_H_ */

View File

@@ -0,0 +1,291 @@
/*
* generic mmc spi driver
*
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <malloc.h>
#include <part.h>
#include <mmc.h>
#include <spi.h>
#include <crc.h>
#include <linux/crc7.h>
#include <asm/byteorder.h>
/* MMC/SD in SPI mode reports R1 status always */
#define R1_SPI_IDLE (1 << 0)
#define R1_SPI_ERASE_RESET (1 << 1)
#define R1_SPI_ILLEGAL_COMMAND (1 << 2)
#define R1_SPI_COM_CRC (1 << 3)
#define R1_SPI_ERASE_SEQ (1 << 4)
#define R1_SPI_ADDRESS (1 << 5)
#define R1_SPI_PARAMETER (1 << 6)
/* R1 bit 7 is always zero, reuse this bit for error */
#define R1_SPI_ERROR (1 << 7)
/* Response tokens used to ack each block written: */
#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f)
#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1)
#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1)
#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1)
/* Read and write blocks start with these tokens and end with crc;
* on error, read tokens act like a subset of R2_SPI_* values.
*/
#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */
#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */
#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */
/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f))
/* bus capability */
#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34)
#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */
/* timeout value */
#define CTOUT 8
#define RTOUT 3000000 /* 1 sec */
#define WTOUT 3000000 /* 1 sec */
static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg)
{
struct spi_slave *spi = mmc->priv;
u8 cmdo[7];
u8 r1;
int i;
cmdo[0] = 0xff;
cmdo[1] = MMC_SPI_CMD(cmdidx);
cmdo[2] = cmdarg >> 24;
cmdo[3] = cmdarg >> 16;
cmdo[4] = cmdarg >> 8;
cmdo[5] = cmdarg;
cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0);
for (i = 0; i < CTOUT; i++) {
spi_xfer(spi, 1 * 8, NULL, &r1, 0);
if (i && (r1 & 0x80) == 0) /* r1 response */
break;
}
debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
return r1;
}
static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf,
u32 bcnt, u32 bsize)
{
struct spi_slave *spi = mmc->priv;
u8 *buf = xbuf;
u8 r1;
u16 crc;
int i;
while (bcnt--) {
for (i = 0; i < RTOUT; i++) {
spi_xfer(spi, 1 * 8, NULL, &r1, 0);
if (r1 != 0xff) /* data token */
break;
}
debug("%s:tok%d %x\n", __func__, i, r1);
if (r1 == SPI_TOKEN_SINGLE) {
spi_xfer(spi, bsize * 8, NULL, buf, 0);
spi_xfer(spi, 2 * 8, NULL, &crc, 0);
#ifdef CONFIG_MMC_SPI_CRC_ON
if (be_to_cpu16(crc16_ccitt(0, buf, bsize)) != crc) {
debug("%s: CRC error\n", mmc->cfg->name);
r1 = R1_SPI_COM_CRC;
break;
}
#endif
r1 = 0;
} else {
r1 = R1_SPI_ERROR;
break;
}
buf += bsize;
}
return r1;
}
static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf,
u32 bcnt, u32 bsize, int multi)
{
struct spi_slave *spi = mmc->priv;
const u8 *buf = xbuf;
u8 r1;
u16 crc;
u8 tok[2];
int i;
tok[0] = 0xff;
tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE;
while (bcnt--) {
#ifdef CONFIG_MMC_SPI_CRC_ON
crc = cpu_to_be16(crc16_ccitt(0, (u8 *)buf, bsize));
#endif
spi_xfer(spi, 2 * 8, tok, NULL, 0);
spi_xfer(spi, bsize * 8, buf, NULL, 0);
spi_xfer(spi, 2 * 8, &crc, NULL, 0);
for (i = 0; i < CTOUT; i++) {
spi_xfer(spi, 1 * 8, NULL, &r1, 0);
if ((r1 & 0x10) == 0) /* response token */
break;
}
debug("%s:tok%d %x\n", __func__, i, r1);
if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
for (i = 0; i < WTOUT; i++) { /* wait busy */
spi_xfer(spi, 1 * 8, NULL, &r1, 0);
if (i && r1 == 0xff) {
r1 = 0;
break;
}
}
if (i == WTOUT) {
debug("%s:wtout %x\n", __func__, r1);
r1 = R1_SPI_ERROR;
break;
}
} else {
debug("%s: err %x\n", __func__, r1);
r1 = R1_SPI_COM_CRC;
break;
}
buf += bsize;
}
if (multi && bcnt == -1) { /* stop multi write */
tok[1] = SPI_TOKEN_STOP_TRAN;
spi_xfer(spi, 2 * 8, tok, NULL, 0);
for (i = 0; i < WTOUT; i++) { /* wait busy */
spi_xfer(spi, 1 * 8, NULL, &r1, 0);
if (i && r1 == 0xff) {
r1 = 0;
break;
}
}
if (i == WTOUT) {
debug("%s:wstop %x\n", __func__, r1);
r1 = R1_SPI_ERROR;
}
}
return r1;
}
static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct spi_slave *spi = mmc->priv;
u8 r1;
int i;
int ret = 0;
debug("%s:cmd%d %x %x\n", __func__,
cmd->cmdidx, cmd->resp_type, cmd->cmdarg);
spi_claim_bus(spi);
spi_cs_activate(spi);
r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg);
if (r1 == 0xff) { /* no response */
ret = NO_CARD_ERR;
goto done;
} else if (r1 & R1_SPI_COM_CRC) {
ret = COMM_ERR;
goto done;
} else if (r1 & ~R1_SPI_IDLE) { /* other errors */
ret = TIMEOUT;
goto done;
} else if (cmd->resp_type == MMC_RSP_R2) {
r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16);
for (i = 0; i < 4; i++)
cmd->response[i] = be32_to_cpu(cmd->response[i]);
debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
cmd->response[2], cmd->response[3]);
} else if (!data) {
switch (cmd->cmdidx) {
case SD_CMD_APP_SEND_OP_COND:
case MMC_CMD_SEND_OP_COND:
cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY;
break;
case SD_CMD_SEND_IF_COND:
case MMC_CMD_SPI_READ_OCR:
spi_xfer(spi, 4 * 8, NULL, cmd->response, 0);
cmd->response[0] = be32_to_cpu(cmd->response[0]);
debug("r32 %x\n", cmd->response[0]);
break;
case MMC_CMD_SEND_STATUS:
spi_xfer(spi, 1 * 8, NULL, cmd->response, 0);
cmd->response[0] = (cmd->response[0] & 0xff) ?
MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA;
break;
}
} else {
debug("%s:data %x %x %x\n", __func__,
data->flags, data->blocks, data->blocksize);
if (data->flags == MMC_DATA_READ)
r1 = mmc_spi_readdata(mmc, data->dest,
data->blocks, data->blocksize);
else if (data->flags == MMC_DATA_WRITE)
r1 = mmc_spi_writedata(mmc, data->src,
data->blocks, data->blocksize,
(cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK));
if (r1 & R1_SPI_COM_CRC)
ret = COMM_ERR;
else if (r1) /* other errors */
ret = TIMEOUT;
}
done:
spi_cs_deactivate(spi);
spi_release_bus(spi);
return ret;
}
static void mmc_spi_set_ios(struct mmc *mmc)
{
struct spi_slave *spi = mmc->priv;
debug("%s: clock %u\n", __func__, mmc->clock);
if (mmc->clock)
spi_set_speed(spi, mmc->clock);
}
static int mmc_spi_init_p(struct mmc *mmc)
{
struct spi_slave *spi = mmc->priv;
spi_set_speed(spi, MMC_SPI_MIN_CLOCK);
spi_claim_bus(spi);
/* cs deactivated for 100+ clock */
spi_xfer(spi, 18 * 8, NULL, NULL, 0);
spi_release_bus(spi);
return 0;
}
static const struct mmc_ops mmc_spi_ops = {
.send_cmd = mmc_spi_request,
.set_ios = mmc_spi_set_ios,
.init = mmc_spi_init_p,
};
static struct mmc_config mmc_spi_cfg = {
.name = "MMC_SPI",
.ops = &mmc_spi_ops,
.host_caps = MMC_MODE_SPI,
.voltages = MMC_SPI_VOLTAGE,
.f_min = MMC_SPI_MIN_CLOCK,
.part_type = PART_TYPE_DOS,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
{
struct mmc *mmc;
struct spi_slave *spi;
spi = spi_setup_slave(bus, cs, speed, mode);
if (spi == NULL)
return NULL;
mmc_spi_cfg.f_max = speed;
mmc = mmc_create(&mmc_spi_cfg, spi);
if (mmc == NULL) {
spi_free_slave(spi);
return NULL;
}
return mmc;
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright 2008, Freescale Semiconductor, Inc
* Andy Fleming
*
* Based vaguely on the Linux code
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <dm.h>
#include <part.h>
#include <div64.h>
#include <linux/math64.h>
#include "mmc_private.h"
static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
{
struct mmc_cmd cmd;
ulong end;
int err, start_cmd, end_cmd;
if (mmc->high_capacity) {
end = start + blkcnt - 1;
} else {
end = (start + blkcnt - 1) * mmc->write_bl_len;
start *= mmc->write_bl_len;
}
if (IS_SD(mmc)) {
start_cmd = SD_CMD_ERASE_WR_BLK_START;
end_cmd = SD_CMD_ERASE_WR_BLK_END;
} else {
start_cmd = MMC_CMD_ERASE_GROUP_START;
end_cmd = MMC_CMD_ERASE_GROUP_END;
}
cmd.cmdidx = start_cmd;
cmd.cmdarg = start;
cmd.resp_type = MMC_RSP_R1;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
goto err_out;
cmd.cmdidx = end_cmd;
cmd.cmdarg = end;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
goto err_out;
cmd.cmdidx = MMC_CMD_ERASE;
cmd.cmdarg = MMC_ERASE_ARG;
cmd.resp_type = MMC_RSP_R1b;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
goto err_out;
return 0;
err_out:
puts("mmc erase failed\n");
return err;
}
unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt)
{
int dev_num = block_dev->devnum;
int err = 0;
u32 start_rem, blkcnt_rem;
struct mmc *mmc = find_mmc_device(dev_num);
lbaint_t blk = 0, blk_r = 0;
int timeout = 1000;
if (!mmc)
return -1;
err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num,
block_dev->hwpart);
if (err < 0)
return -1;
/*
* We want to see if the requested start or total block count are
* unaligned. We discard the whole numbers and only care about the
* remainder.
*/
err = div_u64_rem(start, mmc->erase_grp_size, &start_rem);
err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem);
if (start_rem || blkcnt_rem)
printf("\n\nCaution! Your devices Erase group is 0x%x\n"
"The erase range would be change to "
"0x" LBAF "~0x" LBAF "\n\n",
mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
((start + blkcnt + mmc->erase_grp_size)
& ~(mmc->erase_grp_size - 1)) - 1);
while (blk < blkcnt) {
blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
mmc->erase_grp_size : (blkcnt - blk);
err = mmc_erase_t(mmc, start + blk, blk_r);
if (err)
break;
blk += blk_r;
/* Waiting for the ready status */
if (mmc_send_status(mmc, timeout))
return 0;
}
return blk;
}
static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
lbaint_t blkcnt, const void *src)
{
struct mmc_cmd cmd;
struct mmc_data data;
int timeout = 1000;
if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) {
printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
start + blkcnt, mmc_get_blk_desc(mmc)->lba);
return 0;
}
if (blkcnt == 0)
return 0;
else if (blkcnt == 1)
cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
if (mmc->high_capacity)
cmd.cmdarg = start;
else
cmd.cmdarg = start * mmc->write_bl_len;
cmd.resp_type = MMC_RSP_R1;
data.src = src;
data.blocks = blkcnt;
data.blocksize = mmc->write_bl_len;
data.flags = MMC_DATA_WRITE;
if (mmc_send_cmd(mmc, &cmd, &data)) {
printf("mmc write failed\n");
return 0;
}
/* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
*/
if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
if (mmc_send_cmd(mmc, &cmd, NULL)) {
printf("mmc fail to send stop cmd\n");
return 0;
}
}
/* Waiting for the ready status */
if (mmc_send_status(mmc, timeout))
return 0;
return blkcnt;
}
#ifdef CONFIG_BLK
ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
const void *src)
#else
ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
const void *src)
#endif
{
#ifdef CONFIG_BLK
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
#endif
int dev_num = block_dev->devnum;
lbaint_t cur, blocks_todo = blkcnt;
int err;
struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
return 0;
err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart);
if (err < 0)
return 0;
if (mmc_set_blocklen(mmc, mmc->write_bl_len))
return 0;
do {
cur = (blocks_todo > mmc->cfg->b_max) ?
mmc->cfg->b_max : blocks_todo;
if (mmc_write_blocks(mmc, start, cur, src) != cur)
return 0;
blocks_todo -= cur;
start += cur;
src += cur * mmc->write_bl_len;
} while (blocks_todo > 0);
return blkcnt;
}

View File

@@ -0,0 +1,192 @@
/*
* Qualcomm SDHCI driver - SD/eMMC controller
*
* (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
*
* Based on Linux driver
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <sdhci.h>
#include <wait_bit.h>
#include <asm/io.h>
#include <linux/bitops.h>
/* Non-standard registers needed for SDHCI startup */
#define SDCC_MCI_POWER 0x0
#define SDCC_MCI_POWER_SW_RST BIT(7)
/* This is undocumented register */
#define SDCC_MCI_VERSION 0x50
#define SDCC_MCI_VERSION_MAJOR_SHIFT 28
#define SDCC_MCI_VERSION_MAJOR_MASK (0xf << SDCC_MCI_VERSION_MAJOR_SHIFT)
#define SDCC_MCI_VERSION_MINOR_MASK 0xff
#define SDCC_MCI_STATUS2 0x6C
#define SDCC_MCI_STATUS2_MCI_ACT 0x1
#define SDCC_MCI_HC_MODE 0x78
/* Offset to SDHCI registers */
#define SDCC_SDHCI_OFFSET 0x900
/* Non standard (?) SDHCI register */
#define SDHCI_VENDOR_SPEC_CAPABILITIES0 0x11c
struct msm_sdhc {
struct sdhci_host host;
void *base;
};
DECLARE_GLOBAL_DATA_PTR;
static int msm_sdc_clk_init(struct udevice *dev)
{
uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev->of_offset,
"clock-frequency", 400000);
uint clkd[2]; /* clk_id and clk_no */
int clk_offset;
struct udevice *clk_dev;
struct clk clk;
int ret;
ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd,
2);
if (ret)
return ret;
clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]);
if (clk_offset < 0)
return clk_offset;
ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk_dev);
if (ret)
return ret;
clk.id = clkd[1];
ret = clk_request(clk_dev, &clk);
if (ret < 0)
return ret;
ret = clk_set_rate(&clk, clk_rate);
clk_free(&clk);
if (ret < 0)
return ret;
return 0;
}
static int msm_sdc_probe(struct udevice *dev)
{
struct msm_sdhc *prv = dev_get_priv(dev);
struct sdhci_host *host = &prv->host;
u32 core_version, core_minor, core_major;
int ret;
host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B;
/* Init clocks */
ret = msm_sdc_clk_init(dev);
if (ret)
return ret;
/* Reset the core and Enable SDHC mode */
writel(readl(prv->base + SDCC_MCI_POWER) | SDCC_MCI_POWER_SW_RST,
prv->base + SDCC_MCI_POWER);
/* Wait for reset to be written to register */
if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2,
SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) {
printf("msm_sdhci: reset request failed\n");
return -EIO;
}
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER,
SDCC_MCI_POWER_SW_RST, false, 2, false)) {
printf("msm_sdhci: stuck in reset\n");
return -ETIMEDOUT;
}
/* Enable host-controller mode */
writel(1, prv->base + SDCC_MCI_HC_MODE);
core_version = readl(prv->base + SDCC_MCI_VERSION);
core_major = (core_version & SDCC_MCI_VERSION_MAJOR_MASK);
core_major >>= SDCC_MCI_VERSION_MAJOR_SHIFT;
core_minor = core_version & SDCC_MCI_VERSION_MINOR_MASK;
/*
* Support for some capabilities is not advertised by newer
* controller versions and must be explicitly enabled.
*/
if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
u32 caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
writel(caps, host->ioaddr + SDHCI_VENDOR_SPEC_CAPABILITIES0);
}
/* Set host controller version */
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
/* automatically detect max and min speed */
ret = add_sdhci(host, 0, 0);
if (ret)
return ret;
host->mmc->dev = dev;
return 0;
}
static int msm_sdc_remove(struct udevice *dev)
{
struct msm_sdhc *priv = dev_get_priv(dev);
/* Disable host-controller mode */
writel(0, priv->base + SDCC_MCI_HC_MODE);
return 0;
}
static int msm_ofdata_to_platdata(struct udevice *dev)
{
struct udevice *parent = dev->parent;
struct msm_sdhc *priv = dev_get_priv(dev);
struct sdhci_host *host = &priv->host;
host->name = strdup(dev->name);
host->ioaddr = (void *)dev_get_addr(dev);
host->bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"bus-width", 4);
host->index = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, "index", 0);
priv->base = (void *)fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
parent->of_offset,
dev->of_offset,
"reg", 1, NULL);
if (priv->base == (void *)FDT_ADDR_T_NONE ||
host->ioaddr == (void *)FDT_ADDR_T_NONE)
return -EINVAL;
return 0;
}
static const struct udevice_id msm_mmc_ids[] = {
{ .compatible = "qcom,sdhci-msm-v4" },
{ }
};
U_BOOT_DRIVER(msm_sdc_drv) = {
.name = "msm_sdc",
.id = UCLASS_MMC,
.of_match = msm_mmc_ids,
.ofdata_to_platdata = msm_ofdata_to_platdata,
.probe = msm_sdc_probe,
.remove = msm_sdc_remove,
.priv_auto_alloc_size = sizeof(struct msm_sdhc),
};

View File

@@ -0,0 +1,96 @@
/*
* Marvell SD Host Controller Interface
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#include <linux/mbus.h>
#define SDHCI_WINDOW_CTRL(win) (0x4080 + ((win) << 4))
#define SDHCI_WINDOW_BASE(win) (0x4084 + ((win) << 4))
static void sdhci_mvebu_mbus_config(void __iomem *base)
{
const struct mbus_dram_target_info *dram;
int i;
dram = mvebu_mbus_dram_info();
for (i = 0; i < 4; i++) {
writel(0, base + SDHCI_WINDOW_CTRL(i));
writel(0, base + SDHCI_WINDOW_BASE(i));
}
for (i = 0; i < dram->num_cs; i++) {
const struct mbus_dram_window *cs = dram->cs + i;
/* Write size, attributes and target id to control register */
writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
base + SDHCI_WINDOW_CTRL(i));
/* Write base address to base register */
writel(cs->base, base + SDHCI_WINDOW_BASE(i));
}
}
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
static struct sdhci_ops mv_ops;
#if defined(CONFIG_SHEEVA_88SV331xV5)
#define SD_CE_ATA_2 0xEA
#define MMC_CARD 0x1000
#define MMC_WIDTH 0x0100
static inline void mv_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
struct mmc *mmc = host->mmc;
u32 ata = (unsigned long)host->ioaddr + SD_CE_ATA_2;
if (!IS_SD(mmc) && reg == SDHCI_HOST_CONTROL) {
if (mmc->bus_width == 8)
writew(readw(ata) | (MMC_CARD | MMC_WIDTH), ata);
else
writew(readw(ata) & ~(MMC_CARD | MMC_WIDTH), ata);
}
writeb(val, host->ioaddr + reg);
}
#else
#define mv_sdhci_writeb NULL
#endif /* CONFIG_SHEEVA_88SV331xV5 */
#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
static char *MVSDH_NAME = "mv_sdh";
int mv_sdh_init(unsigned long regbase, u32 max_clk, u32 min_clk, u32 quirks)
{
struct sdhci_host *host = NULL;
host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
if (!host) {
printf("sdh_host malloc fail!\n");
return 1;
}
host->name = MVSDH_NAME;
host->ioaddr = (void *)regbase;
host->quirks = quirks;
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
memset(&mv_ops, 0, sizeof(struct sdhci_ops));
mv_ops.write_b = mv_sdhci_writeb;
host->ops = &mv_ops;
#endif
if (CONFIG_IS_ENABLED(ARCH_MVEBU)) {
/* Configure SDHCI MBUS mbus bridge windows */
sdhci_mvebu_mbus_config((void __iomem *)regbase);
}
if (quirks & SDHCI_QUIRK_REG32_RW)
host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
else
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
return add_sdhci(host, max_clk, min_clk);
}

View File

@@ -0,0 +1,438 @@
/*
* Marvell MMC/SD/SDIO driver
*
* (C) Copyright 2012-2014
* Marvell Semiconductor <www.marvell.com>
* Written-by: Maen Suleiman, Gerald Kerma
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <part.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#include <mvebu_mmc.h>
DECLARE_GLOBAL_DATA_PTR;
#define DRIVER_NAME "MVEBU_MMC"
#define MVEBU_TARGET_DRAM 0
#define TIMEOUT_DELAY 5*CONFIG_SYS_HZ /* wait 5 seconds */
static void mvebu_mmc_write(u32 offs, u32 val)
{
writel(val, CONFIG_SYS_MMC_BASE + (offs));
}
static u32 mvebu_mmc_read(u32 offs)
{
return readl(CONFIG_SYS_MMC_BASE + (offs));
}
static int mvebu_mmc_setup_data(struct mmc_data *data)
{
u32 ctrl_reg;
debug("%s, data %s : blocks=%d blksz=%d\n", DRIVER_NAME,
(data->flags & MMC_DATA_READ) ? "read" : "write",
data->blocks, data->blocksize);
/* default to maximum timeout */
ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL);
ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg);
if (data->flags & MMC_DATA_READ) {
mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->dest & 0xffff);
mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->dest >> 16);
} else {
mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->src & 0xffff);
mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->src >> 16);
}
mvebu_mmc_write(SDIO_BLK_COUNT, data->blocks);
mvebu_mmc_write(SDIO_BLK_SIZE, data->blocksize);
return 0;
}
static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
ulong start;
ushort waittype = 0;
ushort resptype = 0;
ushort xfertype = 0;
ushort resp_indx = 0;
debug("%s: cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n",
DRIVER_NAME, cmd->cmdidx, cmd->resp_type, cmd->cmdarg);
debug("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME,
cmd->cmdidx, mvebu_mmc_read(SDIO_HW_STATE));
/*
* Hardware weirdness. The FIFO_EMPTY bit of the HW_STATE
* register is sometimes not set before a while when some
* "unusual" data block sizes are used (such as with the SWITCH
* command), even despite the fact that the XFER_DONE interrupt
* was raised. And if another data transfer starts before
* this bit comes to good sense (which eventually happens by
* itself) then the new transfer simply fails with a timeout.
*/
if (!(mvebu_mmc_read(SDIO_HW_STATE) & CMD_FIFO_EMPTY)) {
ushort hw_state, count = 0;
start = get_timer(0);
do {
hw_state = mvebu_mmc_read(SDIO_HW_STATE);
if ((get_timer(0) - start) > TIMEOUT_DELAY) {
printf("%s : FIFO_EMPTY bit missing\n",
DRIVER_NAME);
break;
}
count++;
} while (!(hw_state & CMD_FIFO_EMPTY));
debug("%s *** wait for FIFO_EMPTY bit (hw=0x%04x, count=%d, jiffies=%ld)\n",
DRIVER_NAME, hw_state, count, (get_timer(0) - (start)));
}
/* Clear status */
mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
resptype = SDIO_CMD_INDEX(cmd->cmdidx);
/* Analyzing resptype/xfertype/waittype for the command */
if (cmd->resp_type & MMC_RSP_BUSY)
resptype |= SDIO_CMD_RSP_48BUSY;
else if (cmd->resp_type & MMC_RSP_136)
resptype |= SDIO_CMD_RSP_136;
else if (cmd->resp_type & MMC_RSP_PRESENT)
resptype |= SDIO_CMD_RSP_48;
else
resptype |= SDIO_CMD_RSP_NONE;
if (cmd->resp_type & MMC_RSP_CRC)
resptype |= SDIO_CMD_CHECK_CMDCRC;
if (cmd->resp_type & MMC_RSP_OPCODE)
resptype |= SDIO_CMD_INDX_CHECK;
if (cmd->resp_type & MMC_RSP_PRESENT) {
resptype |= SDIO_UNEXPECTED_RESP;
waittype |= SDIO_NOR_UNEXP_RSP;
}
if (data) {
int err = mvebu_mmc_setup_data(data);
if (err) {
debug("%s: command DATA error :%x\n",
DRIVER_NAME, err);
return err;
}
resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16;
xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN;
if (data->flags & MMC_DATA_READ) {
xfertype |= SDIO_XFER_MODE_TO_HOST;
waittype = SDIO_NOR_DMA_INI;
} else {
waittype |= SDIO_NOR_XFER_DONE;
}
} else {
waittype |= SDIO_NOR_CMD_DONE;
}
/* Setting cmd arguments */
mvebu_mmc_write(SDIO_ARG_LOW, cmd->cmdarg & 0xffff);
mvebu_mmc_write(SDIO_ARG_HI, cmd->cmdarg >> 16);
/* Setting Xfer mode */
mvebu_mmc_write(SDIO_XFER_MODE, xfertype);
/* Sending command */
mvebu_mmc_write(SDIO_CMD, resptype);
start = get_timer(0);
while (!((mvebu_mmc_read(SDIO_NOR_INTR_STATUS)) & waittype)) {
if (mvebu_mmc_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) {
debug("%s: error! cmdidx : %d, err reg: %04x\n",
DRIVER_NAME, cmd->cmdidx,
mvebu_mmc_read(SDIO_ERR_INTR_STATUS));
if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) &
(SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) {
debug("%s: command READ timed out\n",
DRIVER_NAME);
return TIMEOUT;
}
debug("%s: command READ error\n", DRIVER_NAME);
return COMM_ERR;
}
if ((get_timer(0) - start) > TIMEOUT_DELAY) {
debug("%s: command timed out\n", DRIVER_NAME);
return TIMEOUT;
}
}
/* Handling response */
if (cmd->resp_type & MMC_RSP_136) {
uint response[8];
for (resp_indx = 0; resp_indx < 8; resp_indx++)
response[resp_indx]
= mvebu_mmc_read(SDIO_RSP(resp_indx));
cmd->response[0] = ((response[0] & 0x03ff) << 22) |
((response[1] & 0xffff) << 6) |
((response[2] & 0xfc00) >> 10);
cmd->response[1] = ((response[2] & 0x03ff) << 22) |
((response[3] & 0xffff) << 6) |
((response[4] & 0xfc00) >> 10);
cmd->response[2] = ((response[4] & 0x03ff) << 22) |
((response[5] & 0xffff) << 6) |
((response[6] & 0xfc00) >> 10);
cmd->response[3] = ((response[6] & 0x03ff) << 22) |
((response[7] & 0x3fff) << 8);
} else if (cmd->resp_type & MMC_RSP_PRESENT) {
uint response[3];
for (resp_indx = 0; resp_indx < 3; resp_indx++)
response[resp_indx]
= mvebu_mmc_read(SDIO_RSP(resp_indx));
cmd->response[0] = ((response[2] & 0x003f) << (8 - 8)) |
((response[1] & 0xffff) << (14 - 8)) |
((response[0] & 0x03ff) << (30 - 8));
cmd->response[1] = ((response[0] & 0xfc00) >> 10);
cmd->response[2] = 0;
cmd->response[3] = 0;
} else {
cmd->response[0] = 0;
cmd->response[1] = 0;
cmd->response[2] = 0;
cmd->response[3] = 0;
}
debug("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type);
debug("[0x%x] ", cmd->response[0]);
debug("[0x%x] ", cmd->response[1]);
debug("[0x%x] ", cmd->response[2]);
debug("[0x%x] ", cmd->response[3]);
debug("\n");
if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) &
(SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT))
return TIMEOUT;
return 0;
}
static void mvebu_mmc_power_up(void)
{
debug("%s: power up\n", DRIVER_NAME);
/* disable interrupts */
mvebu_mmc_write(SDIO_NOR_INTR_EN, 0);
mvebu_mmc_write(SDIO_ERR_INTR_EN, 0);
/* SW reset */
mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
mvebu_mmc_write(SDIO_XFER_MODE, 0);
/* enable status */
mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
/* enable interrupts status */
mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
}
static void mvebu_mmc_set_clk(unsigned int clock)
{
unsigned int m;
if (clock == 0) {
debug("%s: clock off\n", DRIVER_NAME);
mvebu_mmc_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK);
mvebu_mmc_write(SDIO_CLK_DIV, MVEBU_MMC_BASE_DIV_MAX);
} else {
m = MVEBU_MMC_BASE_FAST_CLOCK/(2*clock) - 1;
if (m > MVEBU_MMC_BASE_DIV_MAX)
m = MVEBU_MMC_BASE_DIV_MAX;
mvebu_mmc_write(SDIO_CLK_DIV, m & MVEBU_MMC_BASE_DIV_MAX);
debug("%s: clock (%d) div : %d\n", DRIVER_NAME, clock, m);
}
}
static void mvebu_mmc_set_bus(unsigned int bus)
{
u32 ctrl_reg = 0;
ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL);
ctrl_reg &= ~SDIO_HOST_CTRL_DATA_WIDTH_4_BITS;
switch (bus) {
case 4:
ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_4_BITS;
break;
case 1:
default:
ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_1_BIT;
}
/* default transfer mode */
ctrl_reg |= SDIO_HOST_CTRL_BIG_ENDIAN;
ctrl_reg &= ~SDIO_HOST_CTRL_LSB_FIRST;
/* default to maximum timeout */
ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
ctrl_reg |= SDIO_HOST_CTRL_TMOUT_EN;
ctrl_reg |= SDIO_HOST_CTRL_PUSH_PULL_EN;
ctrl_reg |= SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY;
debug("%s: ctrl 0x%04x: %s %s %s\n", DRIVER_NAME, ctrl_reg,
(ctrl_reg & SDIO_HOST_CTRL_PUSH_PULL_EN) ?
"push-pull" : "open-drain",
(ctrl_reg & SDIO_HOST_CTRL_DATA_WIDTH_4_BITS) ?
"4bit-width" : "1bit-width",
(ctrl_reg & SDIO_HOST_CTRL_HI_SPEED_EN) ?
"high-speed" : "");
mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg);
}
static void mvebu_mmc_set_ios(struct mmc *mmc)
{
debug("%s: bus[%d] clock[%d]\n", DRIVER_NAME,
mmc->bus_width, mmc->clock);
mvebu_mmc_set_bus(mmc->bus_width);
mvebu_mmc_set_clk(mmc->clock);
}
/*
* Set window register.
*/
static void mvebu_window_setup(void)
{
int i;
for (i = 0; i < 4; i++) {
mvebu_mmc_write(WINDOW_CTRL(i), 0);
mvebu_mmc_write(WINDOW_BASE(i), 0);
}
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
u32 size, base, attrib;
/* Enable DRAM bank */
switch (i) {
case 0:
attrib = KWCPU_ATTR_DRAM_CS0;
break;
case 1:
attrib = KWCPU_ATTR_DRAM_CS1;
break;
case 2:
attrib = KWCPU_ATTR_DRAM_CS2;
break;
case 3:
attrib = KWCPU_ATTR_DRAM_CS3;
break;
default:
/* invalide bank, disable access */
attrib = 0;
break;
}
size = gd->bd->bi_dram[i].size;
base = gd->bd->bi_dram[i].start;
if (size && attrib) {
mvebu_mmc_write(WINDOW_CTRL(i),
MVCPU_WIN_CTRL_DATA(size,
MVEBU_TARGET_DRAM,
attrib,
MVCPU_WIN_ENABLE));
} else {
mvebu_mmc_write(WINDOW_CTRL(i), MVCPU_WIN_DISABLE);
}
mvebu_mmc_write(WINDOW_BASE(i), base);
}
}
static int mvebu_mmc_initialize(struct mmc *mmc)
{
debug("%s: mvebu_mmc_initialize\n", DRIVER_NAME);
/*
* Setting host parameters
* Initial Host Ctrl : Timeout : max , Normal Speed mode,
* 4-bit data mode, Big Endian, SD memory Card, Push_pull CMD Line
*/
mvebu_mmc_write(SDIO_HOST_CTRL,
SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX) |
SDIO_HOST_CTRL_DATA_WIDTH_4_BITS |
SDIO_HOST_CTRL_BIG_ENDIAN |
SDIO_HOST_CTRL_PUSH_PULL_EN |
SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY);
mvebu_mmc_write(SDIO_CLK_CTRL, 0);
/* enable status */
mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
/* disable interrupts */
mvebu_mmc_write(SDIO_NOR_INTR_EN, 0);
mvebu_mmc_write(SDIO_ERR_INTR_EN, 0);
mvebu_window_setup();
/* SW reset */
mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
return 0;
}
static const struct mmc_ops mvebu_mmc_ops = {
.send_cmd = mvebu_mmc_send_cmd,
.set_ios = mvebu_mmc_set_ios,
.init = mvebu_mmc_initialize,
};
static struct mmc_config mvebu_mmc_cfg = {
.name = DRIVER_NAME,
.ops = &mvebu_mmc_ops,
.f_min = MVEBU_MMC_BASE_FAST_CLOCK / MVEBU_MMC_BASE_DIV_MAX,
.f_max = MVEBU_MMC_CLOCKRATE_MAX,
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
.host_caps = MMC_MODE_4BIT | MMC_MODE_HS |
MMC_MODE_HS_52MHz,
.part_type = PART_TYPE_DOS,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
int mvebu_mmc_init(bd_t *bis)
{
struct mmc *mmc;
mvebu_mmc_power_up();
mmc = mmc_create(&mvebu_mmc_cfg, bis);
if (mmc == NULL)
return -1;
return 0;
}

521
u-boot/drivers/mmc/mxcmmc.c Normal file
View File

@@ -0,0 +1,521 @@
/*
* This is a driver for the SDHC controller found in Freescale MX2/MX3
* SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
* Unlike the hardware found on MX1, this hardware just works and does
* not need all the quirks found in imxmmc.c, hence the seperate driver.
*
* Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
* Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
*
* derived from pxamci.c by Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <config.h>
#include <common.h>
#include <command.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
#include <mmc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#define DRIVER_NAME "mxc-mmc"
struct mxcmci_regs {
u32 str_stp_clk;
u32 status;
u32 clk_rate;
u32 cmd_dat_cont;
u32 res_to;
u32 read_to;
u32 blk_len;
u32 nob;
u32 rev_no;
u32 int_cntr;
u32 cmd;
u32 arg;
u32 pad;
u32 res_fifo;
u32 buffer_access;
};
#define STR_STP_CLK_RESET (1 << 3)
#define STR_STP_CLK_START_CLK (1 << 1)
#define STR_STP_CLK_STOP_CLK (1 << 0)
#define STATUS_CARD_INSERTION (1 << 31)
#define STATUS_CARD_REMOVAL (1 << 30)
#define STATUS_YBUF_EMPTY (1 << 29)
#define STATUS_XBUF_EMPTY (1 << 28)
#define STATUS_YBUF_FULL (1 << 27)
#define STATUS_XBUF_FULL (1 << 26)
#define STATUS_BUF_UND_RUN (1 << 25)
#define STATUS_BUF_OVFL (1 << 24)
#define STATUS_SDIO_INT_ACTIVE (1 << 14)
#define STATUS_END_CMD_RESP (1 << 13)
#define STATUS_WRITE_OP_DONE (1 << 12)
#define STATUS_DATA_TRANS_DONE (1 << 11)
#define STATUS_READ_OP_DONE (1 << 11)
#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10)
#define STATUS_CARD_BUS_CLK_RUN (1 << 8)
#define STATUS_BUF_READ_RDY (1 << 7)
#define STATUS_BUF_WRITE_RDY (1 << 6)
#define STATUS_RESP_CRC_ERR (1 << 5)
#define STATUS_CRC_READ_ERR (1 << 3)
#define STATUS_CRC_WRITE_ERR (1 << 2)
#define STATUS_TIME_OUT_RESP (1 << 1)
#define STATUS_TIME_OUT_READ (1 << 0)
#define STATUS_ERR_MASK 0x2f
#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12)
#define CMD_DAT_CONT_STOP_READWAIT (1 << 11)
#define CMD_DAT_CONT_START_READWAIT (1 << 10)
#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8)
#define CMD_DAT_CONT_INIT (1 << 7)
#define CMD_DAT_CONT_WRITE (1 << 4)
#define CMD_DAT_CONT_DATA_ENABLE (1 << 3)
#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0)
#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0)
#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0)
#define INT_SDIO_INT_WKP_EN (1 << 18)
#define INT_CARD_INSERTION_WKP_EN (1 << 17)
#define INT_CARD_REMOVAL_WKP_EN (1 << 16)
#define INT_CARD_INSERTION_EN (1 << 15)
#define INT_CARD_REMOVAL_EN (1 << 14)
#define INT_SDIO_IRQ_EN (1 << 13)
#define INT_DAT0_EN (1 << 12)
#define INT_BUF_READ_EN (1 << 4)
#define INT_BUF_WRITE_EN (1 << 3)
#define INT_END_CMD_RES_EN (1 << 2)
#define INT_WRITE_OP_DONE_EN (1 << 1)
#define INT_READ_OP_EN (1 << 0)
struct mxcmci_host {
struct mmc *mmc;
struct mxcmci_regs *base;
int irq;
int detect_irq;
int dma;
int do_dma;
unsigned int power_mode;
struct mmc_cmd *cmd;
struct mmc_data *data;
unsigned int dma_nents;
unsigned int datasize;
unsigned int dma_dir;
u16 rev_no;
unsigned int cmdat;
int clock;
};
static struct mxcmci_host mxcmci_host;
/* maintainer note: do we really want to have a global host pointer? */
static struct mxcmci_host *host = &mxcmci_host;
static inline int mxcmci_use_dma(struct mxcmci_host *host)
{
return host->do_dma;
}
static void mxcmci_softreset(struct mxcmci_host *host)
{
int i;
/* reset sequence */
writel(STR_STP_CLK_RESET, &host->base->str_stp_clk);
writel(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
&host->base->str_stp_clk);
for (i = 0; i < 8; i++)
writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk);
writel(0xff, &host->base->res_to);
}
static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
{
unsigned int nob = data->blocks;
unsigned int blksz = data->blocksize;
unsigned int datasize = nob * blksz;
host->data = data;
writel(nob, &host->base->nob);
writel(blksz, &host->base->blk_len);
host->datasize = datasize;
}
static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_cmd *cmd,
unsigned int cmdat)
{
if (host->cmd != NULL)
printf("mxcmci: error!\n");
host->cmd = cmd;
switch (cmd->resp_type) {
case MMC_RSP_R1: /* short CRC, OPCODE */
case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC;
break;
case MMC_RSP_R2: /* long 136 bit + CRC */
cmdat |= CMD_DAT_CONT_RESPONSE_136BIT;
break;
case MMC_RSP_R3: /* short */
cmdat |= CMD_DAT_CONT_RESPONSE_48BIT;
break;
case MMC_RSP_NONE:
break;
default:
printf("mxcmci: unhandled response type 0x%x\n",
cmd->resp_type);
return -EINVAL;
}
writel(cmd->cmdidx, &host->base->cmd);
writel(cmd->cmdarg, &host->base->arg);
writel(cmdat, &host->base->cmd_dat_cont);
return 0;
}
static void mxcmci_finish_request(struct mxcmci_host *host,
struct mmc_cmd *cmd, struct mmc_data *data)
{
host->cmd = NULL;
host->data = NULL;
}
static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
{
int data_error = 0;
if (stat & STATUS_ERR_MASK) {
printf("request failed. status: 0x%08x\n",
stat);
if (stat & STATUS_CRC_READ_ERR) {
data_error = -EILSEQ;
} else if (stat & STATUS_CRC_WRITE_ERR) {
u32 err_code = (stat >> 9) & 0x3;
if (err_code == 2) /* No CRC response */
data_error = TIMEOUT;
else
data_error = -EILSEQ;
} else if (stat & STATUS_TIME_OUT_READ) {
data_error = TIMEOUT;
} else {
data_error = -EIO;
}
}
host->data = NULL;
return data_error;
}
static int mxcmci_read_response(struct mxcmci_host *host, unsigned int stat)
{
struct mmc_cmd *cmd = host->cmd;
int i;
u32 a, b, c;
u32 *resp = (u32 *)cmd->response;
if (!cmd)
return 0;
if (stat & STATUS_TIME_OUT_RESP) {
printf("CMD TIMEOUT\n");
return TIMEOUT;
} else if (stat & STATUS_RESP_CRC_ERR && cmd->resp_type & MMC_RSP_CRC) {
printf("cmd crc error\n");
return -EILSEQ;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
for (i = 0; i < 4; i++) {
a = readl(&host->base->res_fifo) & 0xFFFF;
b = readl(&host->base->res_fifo) & 0xFFFF;
resp[i] = a << 16 | b;
}
} else {
a = readl(&host->base->res_fifo) & 0xFFFF;
b = readl(&host->base->res_fifo) & 0xFFFF;
c = readl(&host->base->res_fifo) & 0xFFFF;
resp[0] = a << 24 | b << 8 | c >> 8;
}
}
return 0;
}
static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
{
u32 stat;
unsigned long timeout = get_ticks() + CONFIG_SYS_HZ;
do {
stat = readl(&host->base->status);
if (stat & STATUS_ERR_MASK)
return stat;
if (timeout < get_ticks())
return STATUS_TIME_OUT_READ;
if (stat & mask)
return 0;
} while (1);
}
static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
{
unsigned int stat;
u32 *buf = _buf;
while (bytes > 3) {
stat = mxcmci_poll_status(host,
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
if (stat)
return stat;
*buf++ = readl(&host->base->buffer_access);
bytes -= 4;
}
if (bytes) {
u8 *b = (u8 *)buf;
u32 tmp;
stat = mxcmci_poll_status(host,
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
if (stat)
return stat;
tmp = readl(&host->base->buffer_access);
memcpy(b, &tmp, bytes);
}
return 0;
}
static int mxcmci_push(struct mxcmci_host *host, const void *_buf, int bytes)
{
unsigned int stat;
const u32 *buf = _buf;
while (bytes > 3) {
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
if (stat)
return stat;
writel(*buf++, &host->base->buffer_access);
bytes -= 4;
}
if (bytes) {
const u8 *b = (u8 *)buf;
u32 tmp;
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
if (stat)
return stat;
memcpy(&tmp, b, bytes);
writel(tmp, &host->base->buffer_access);
}
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
if (stat)
return stat;
return 0;
}
static int mxcmci_transfer_data(struct mxcmci_host *host)
{
struct mmc_data *data = host->data;
int stat;
unsigned long length;
length = data->blocks * data->blocksize;
host->datasize = 0;
if (data->flags & MMC_DATA_READ) {
stat = mxcmci_pull(host, data->dest, length);
if (stat)
return stat;
host->datasize += length;
} else {
stat = mxcmci_push(host, (const void *)(data->src), length);
if (stat)
return stat;
host->datasize += length;
stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
if (stat)
return stat;
}
return 0;
}
static int mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
{
int datastat;
int ret;
ret = mxcmci_read_response(host, stat);
if (ret) {
mxcmci_finish_request(host, host->cmd, host->data);
return ret;
}
if (!host->data) {
mxcmci_finish_request(host, host->cmd, host->data);
return 0;
}
datastat = mxcmci_transfer_data(host);
ret = mxcmci_finish_data(host, datastat);
mxcmci_finish_request(host, host->cmd, host->data);
return ret;
}
static int mxcmci_request(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct mxcmci_host *host = mmc->priv;
unsigned int cmdat = host->cmdat;
u32 stat;
int ret;
host->cmdat &= ~CMD_DAT_CONT_INIT;
if (data) {
mxcmci_setup_data(host, data);
cmdat |= CMD_DAT_CONT_DATA_ENABLE;
if (data->flags & MMC_DATA_WRITE)
cmdat |= CMD_DAT_CONT_WRITE;
}
if ((ret = mxcmci_start_cmd(host, cmd, cmdat))) {
mxcmci_finish_request(host, cmd, data);
return ret;
}
do {
stat = readl(&host->base->status);
writel(stat, &host->base->status);
} while (!(stat & STATUS_END_CMD_RESP));
return mxcmci_cmd_done(host, stat);
}
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
{
unsigned int divider;
int prescaler = 0;
unsigned long clk_in = mxc_get_clock(MXC_ESDHC_CLK);
while (prescaler <= 0x800) {
for (divider = 1; divider <= 0xF; divider++) {
int x;
x = (clk_in / (divider + 1));
if (prescaler)
x /= (prescaler * 2);
if (x <= clk_ios)
break;
}
if (divider < 0x10)
break;
if (prescaler == 0)
prescaler = 1;
else
prescaler <<= 1;
}
writel((prescaler << 4) | divider, &host->base->clk_rate);
}
static void mxcmci_set_ios(struct mmc *mmc)
{
struct mxcmci_host *host = mmc->priv;
if (mmc->bus_width == 4)
host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
else
host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
if (mmc->clock) {
mxcmci_set_clk_rate(host, mmc->clock);
writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk);
} else {
writel(STR_STP_CLK_STOP_CLK, &host->base->str_stp_clk);
}
host->clock = mmc->clock;
}
static int mxcmci_init(struct mmc *mmc)
{
struct mxcmci_host *host = mmc->priv;
mxcmci_softreset(host);
host->rev_no = readl(&host->base->rev_no);
if (host->rev_no != 0x400) {
printf("wrong rev.no. 0x%08x. aborting.\n",
host->rev_no);
return -ENODEV;
}
/* recommended in data sheet */
writel(0x2db4, &host->base->read_to);
writel(0, &host->base->int_cntr);
return 0;
}
static const struct mmc_ops mxcmci_ops = {
.send_cmd = mxcmci_request,
.set_ios = mxcmci_set_ios,
.init = mxcmci_init,
};
static struct mmc_config mxcmci_cfg = {
.name = "MXC MCI",
.ops = &mxcmci_ops,
.host_caps = MMC_MODE_4BIT,
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
static int mxcmci_initialize(bd_t *bis)
{
host->base = (struct mxcmci_regs *)CONFIG_MXC_MCI_REGS_BASE;
mxcmci_cfg.f_min = mxc_get_clock(MXC_ESDHC_CLK) >> 7;
mxcmci_cfg.f_max = mxc_get_clock(MXC_ESDHC_CLK) >> 1;
host->mmc = mmc_create(&mxcmci_cfg, host);
if (host->mmc == NULL)
return -1;
return 0;
}
int mxc_mmc_init(bd_t *bis)
{
return mxcmci_initialize(bis);
}

427
u-boot/drivers/mmc/mxsmmc.c Normal file
View File

@@ -0,0 +1,427 @@
/*
* Freescale i.MX28 SSP MMC driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* (C) Copyright 2008-2010 Freescale Semiconductor, Inc.
* Terry Lv
*
* Copyright 2007, Freescale Semiconductor, Inc
* Andy Fleming
*
* Based vaguely on the pxa mmc code:
* (C) Copyright 2003
* Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <mmc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
#include <bouncebuf.h>
struct mxsmmc_priv {
int id;
struct mxs_ssp_regs *regs;
uint32_t buswidth;
int (*mmc_is_wp)(int);
int (*mmc_cd)(int);
struct mxs_dma_desc *desc;
struct mmc_config cfg; /* mmc configuration */
};
#define MXSMMC_MAX_TIMEOUT 10000
#define MXSMMC_SMALL_TRANSFER 512
static int mxsmmc_cd(struct mxsmmc_priv *priv)
{
struct mxs_ssp_regs *ssp_regs = priv->regs;
if (priv->mmc_cd)
return priv->mmc_cd(priv->id);
return !(readl(&ssp_regs->hw_ssp_status) & SSP_STATUS_CARD_DETECT);
}
static int mxsmmc_send_cmd_pio(struct mxsmmc_priv *priv, struct mmc_data *data)
{
struct mxs_ssp_regs *ssp_regs = priv->regs;
uint32_t *data_ptr;
int timeout = MXSMMC_MAX_TIMEOUT;
uint32_t reg;
uint32_t data_count = data->blocksize * data->blocks;
if (data->flags & MMC_DATA_READ) {
data_ptr = (uint32_t *)data->dest;
while (data_count && --timeout) {
reg = readl(&ssp_regs->hw_ssp_status);
if (!(reg & SSP_STATUS_FIFO_EMPTY)) {
*data_ptr++ = readl(&ssp_regs->hw_ssp_data);
data_count -= 4;
timeout = MXSMMC_MAX_TIMEOUT;
} else
udelay(1000);
}
} else {
data_ptr = (uint32_t *)data->src;
timeout *= 100;
while (data_count && --timeout) {
reg = readl(&ssp_regs->hw_ssp_status);
if (!(reg & SSP_STATUS_FIFO_FULL)) {
writel(*data_ptr++, &ssp_regs->hw_ssp_data);
data_count -= 4;
timeout = MXSMMC_MAX_TIMEOUT;
} else
udelay(1000);
}
}
return timeout ? 0 : COMM_ERR;
}
static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data)
{
uint32_t data_count = data->blocksize * data->blocks;
int dmach;
struct mxs_dma_desc *desc = priv->desc;
void *addr;
unsigned int flags;
struct bounce_buffer bbstate;
memset(desc, 0, sizeof(struct mxs_dma_desc));
desc->address = (dma_addr_t)desc;
if (data->flags & MMC_DATA_READ) {
priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
addr = data->dest;
flags = GEN_BB_WRITE;
} else {
priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
addr = (void *)data->src;
flags = GEN_BB_READ;
}
bounce_buffer_start(&bbstate, addr, data_count, flags);
priv->desc->cmd.address = (dma_addr_t)bbstate.bounce_buffer;
priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |
(data_count << MXS_DMA_DESC_BYTES_OFFSET);
dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id;
mxs_dma_desc_append(dmach, priv->desc);
if (mxs_dma_go(dmach)) {
bounce_buffer_stop(&bbstate);
return COMM_ERR;
}
bounce_buffer_stop(&bbstate);
return 0;
}
/*
* Sends a command out on the bus. Takes the mmc pointer,
* a command pointer, and an optional data pointer.
*/
static int
mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
struct mxsmmc_priv *priv = mmc->priv;
struct mxs_ssp_regs *ssp_regs = priv->regs;
uint32_t reg;
int timeout;
uint32_t ctrl0;
int ret;
debug("MMC%d: CMD%d\n", mmc->block_dev.devnum, cmd->cmdidx);
/* Check bus busy */
timeout = MXSMMC_MAX_TIMEOUT;
while (--timeout) {
udelay(1000);
reg = readl(&ssp_regs->hw_ssp_status);
if (!(reg &
(SSP_STATUS_BUSY | SSP_STATUS_DATA_BUSY |
SSP_STATUS_CMD_BUSY))) {
break;
}
}
if (!timeout) {
printf("MMC%d: Bus busy timeout!\n", mmc->block_dev.devnum);
return TIMEOUT;
}
/* See if card is present */
if (!mxsmmc_cd(priv)) {
printf("MMC%d: No card detected!\n", mmc->block_dev.devnum);
return NO_CARD_ERR;
}
/* Start building CTRL0 contents */
ctrl0 = priv->buswidth;
/* Set up command */
if (!(cmd->resp_type & MMC_RSP_CRC))
ctrl0 |= SSP_CTRL0_IGNORE_CRC;
if (cmd->resp_type & MMC_RSP_PRESENT) /* Need to get response */
ctrl0 |= SSP_CTRL0_GET_RESP;
if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */
ctrl0 |= SSP_CTRL0_LONG_RESP;
if (data && (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER))
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
else
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
/* Command index */
reg = readl(&ssp_regs->hw_ssp_cmd0);
reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC);
reg |= cmd->cmdidx << SSP_CMD0_CMD_OFFSET;
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
reg |= SSP_CMD0_APPEND_8CYC;
writel(reg, &ssp_regs->hw_ssp_cmd0);
/* Command argument */
writel(cmd->cmdarg, &ssp_regs->hw_ssp_cmd1);
/* Set up data */
if (data) {
/* READ or WRITE */
if (data->flags & MMC_DATA_READ) {
ctrl0 |= SSP_CTRL0_READ;
} else if (priv->mmc_is_wp &&
priv->mmc_is_wp(mmc->block_dev.devnum)) {
printf("MMC%d: Can not write a locked card!\n",
mmc->block_dev.devnum);
return UNUSABLE_ERR;
}
ctrl0 |= SSP_CTRL0_DATA_XFER;
reg = data->blocksize * data->blocks;
#if defined(CONFIG_MX23)
ctrl0 |= reg & SSP_CTRL0_XFER_COUNT_MASK;
clrsetbits_le32(&ssp_regs->hw_ssp_cmd0,
SSP_CMD0_BLOCK_SIZE_MASK | SSP_CMD0_BLOCK_COUNT_MASK,
((data->blocks - 1) << SSP_CMD0_BLOCK_COUNT_OFFSET) |
((ffs(data->blocksize) - 1) <<
SSP_CMD0_BLOCK_SIZE_OFFSET));
#elif defined(CONFIG_MX28)
writel(reg, &ssp_regs->hw_ssp_xfer_size);
reg = ((data->blocks - 1) <<
SSP_BLOCK_SIZE_BLOCK_COUNT_OFFSET) |
((ffs(data->blocksize) - 1) <<
SSP_BLOCK_SIZE_BLOCK_SIZE_OFFSET);
writel(reg, &ssp_regs->hw_ssp_block_size);
#endif
}
/* Kick off the command */
ctrl0 |= SSP_CTRL0_WAIT_FOR_IRQ | SSP_CTRL0_ENABLE | SSP_CTRL0_RUN;
writel(ctrl0, &ssp_regs->hw_ssp_ctrl0);
/* Wait for the command to complete */
timeout = MXSMMC_MAX_TIMEOUT;
while (--timeout) {
udelay(1000);
reg = readl(&ssp_regs->hw_ssp_status);
if (!(reg & SSP_STATUS_CMD_BUSY))
break;
}
if (!timeout) {
printf("MMC%d: Command %d busy\n",
mmc->block_dev.devnum, cmd->cmdidx);
return TIMEOUT;
}
/* Check command timeout */
if (reg & SSP_STATUS_RESP_TIMEOUT) {
printf("MMC%d: Command %d timeout (status 0x%08x)\n",
mmc->block_dev.devnum, cmd->cmdidx, reg);
return TIMEOUT;
}
/* Check command errors */
if (reg & (SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR)) {
printf("MMC%d: Command %d error (status 0x%08x)!\n",
mmc->block_dev.devnum, cmd->cmdidx, reg);
return COMM_ERR;
}
/* Copy response to response buffer */
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[3] = readl(&ssp_regs->hw_ssp_sdresp0);
cmd->response[2] = readl(&ssp_regs->hw_ssp_sdresp1);
cmd->response[1] = readl(&ssp_regs->hw_ssp_sdresp2);
cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp3);
} else
cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp0);
/* Return if no data to process */
if (!data)
return 0;
if (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER) {
ret = mxsmmc_send_cmd_pio(priv, data);
if (ret) {
printf("MMC%d: Data timeout with command %d "
"(status 0x%08x)!\n",
mmc->block_dev.devnum, cmd->cmdidx, reg);
return ret;
}
} else {
ret = mxsmmc_send_cmd_dma(priv, data);
if (ret) {
printf("MMC%d: DMA transfer failed\n",
mmc->block_dev.devnum);
return ret;
}
}
/* Check data errors */
reg = readl(&ssp_regs->hw_ssp_status);
if (reg &
(SSP_STATUS_TIMEOUT | SSP_STATUS_DATA_CRC_ERR |
SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW)) {
printf("MMC%d: Data error with command %d (status 0x%08x)!\n",
mmc->block_dev.devnum, cmd->cmdidx, reg);
return COMM_ERR;
}
return 0;
}
static void mxsmmc_set_ios(struct mmc *mmc)
{
struct mxsmmc_priv *priv = mmc->priv;
struct mxs_ssp_regs *ssp_regs = priv->regs;
/* Set the clock speed */
if (mmc->clock)
mxs_set_ssp_busclock(priv->id, mmc->clock / 1000);
switch (mmc->bus_width) {
case 1:
priv->buswidth = SSP_CTRL0_BUS_WIDTH_ONE_BIT;
break;
case 4:
priv->buswidth = SSP_CTRL0_BUS_WIDTH_FOUR_BIT;
break;
case 8:
priv->buswidth = SSP_CTRL0_BUS_WIDTH_EIGHT_BIT;
break;
}
/* Set the bus width */
clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0,
SSP_CTRL0_BUS_WIDTH_MASK, priv->buswidth);
debug("MMC%d: Set %d bits bus width\n",
mmc->block_dev.devnum, mmc->bus_width);
}
static int mxsmmc_init(struct mmc *mmc)
{
struct mxsmmc_priv *priv = mmc->priv;
struct mxs_ssp_regs *ssp_regs = priv->regs;
/* Reset SSP */
mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
/* Reconfigure the SSP block for MMC operation */
writel(SSP_CTRL1_SSP_MODE_SD_MMC |
SSP_CTRL1_WORD_LENGTH_EIGHT_BITS |
SSP_CTRL1_DMA_ENABLE |
SSP_CTRL1_POLARITY |
SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
SSP_CTRL1_DATA_CRC_IRQ_EN |
SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
SSP_CTRL1_RESP_ERR_IRQ_EN,
&ssp_regs->hw_ssp_ctrl1_set);
/* Set initial bit clock 400 KHz */
mxs_set_ssp_busclock(priv->id, 400);
/* Send initial 74 clock cycles (185 us @ 400 KHz)*/
writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_set);
udelay(200);
writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_clr);
return 0;
}
static const struct mmc_ops mxsmmc_ops = {
.send_cmd = mxsmmc_send_cmd,
.set_ios = mxsmmc_set_ios,
.init = mxsmmc_init,
};
int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int))
{
struct mmc *mmc = NULL;
struct mxsmmc_priv *priv = NULL;
int ret;
const unsigned int mxsmmc_clk_id = mxs_ssp_clock_by_bus(id);
if (!mxs_ssp_bus_id_valid(id))
return -ENODEV;
priv = malloc(sizeof(struct mxsmmc_priv));
if (!priv)
return -ENOMEM;
priv->desc = mxs_dma_desc_alloc();
if (!priv->desc) {
free(priv);
return -ENOMEM;
}
ret = mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + id);
if (ret)
return ret;
priv->mmc_is_wp = wp;
priv->mmc_cd = cd;
priv->id = id;
priv->regs = mxs_ssp_regs_by_bus(id);
priv->cfg.name = "MXS MMC";
priv->cfg.ops = &mxsmmc_ops;
priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT |
MMC_MODE_HS_52MHz | MMC_MODE_HS;
/*
* SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz
* SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
* CLOCK_DIVIDE has to be an even value from 2 to 254, and
* CLOCK_RATE could be any integer from 0 to 255.
*/
priv->cfg.f_min = 400000;
priv->cfg.f_max = mxc_get_clock(MXC_SSP0_CLK + mxsmmc_clk_id) * 1000 / 2;
priv->cfg.b_max = 0x20;
mmc = mmc_create(&priv->cfg, priv);
if (mmc == NULL) {
mxs_dma_desc_free(priv->desc);
free(priv);
return -ENOMEM;
}
return 0;
}

View File

@@ -0,0 +1,850 @@
/*
* (C) Copyright 2008
* Texas Instruments, <www.ti.com>
* Sukumar Ghorai <s-ghorai@ti.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation's version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <common.h>
#include <malloc.h>
#include <mmc.h>
#include <part.h>
#include <i2c.h>
#include <twl4030.h>
#include <twl6030.h>
#include <palmas.h>
#include <asm/io.h>
#include <asm/arch/mmc_host_def.h>
#if !defined(CONFIG_SOC_KEYSTONE)
#include <asm/gpio.h>
#include <asm/arch/sys_proto.h>
#endif
#include <dm.h>
DECLARE_GLOBAL_DATA_PTR;
/* simplify defines to OMAP_HSMMC_USE_GPIO */
#if (defined(CONFIG_OMAP_GPIO) && !defined(CONFIG_SPL_BUILD)) || \
(defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT))
#define OMAP_HSMMC_USE_GPIO
#else
#undef OMAP_HSMMC_USE_GPIO
#endif
/* common definitions for all OMAPs */
#define SYSCTL_SRC (1 << 25)
#define SYSCTL_SRD (1 << 26)
struct omap_hsmmc_data {
struct hsmmc *base_addr;
struct mmc_config cfg;
#ifdef OMAP_HSMMC_USE_GPIO
#ifdef CONFIG_DM_MMC
struct gpio_desc cd_gpio; /* Change Detect GPIO */
struct gpio_desc wp_gpio; /* Write Protect GPIO */
bool cd_inverted;
#else
int cd_gpio;
int wp_gpio;
#endif
#endif
};
/* If we fail after 1 second wait, something is really bad */
#define MAX_RETRY_MS 1000
static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
unsigned int siz);
#if defined(OMAP_HSMMC_USE_GPIO) && !defined(CONFIG_DM_MMC)
static int omap_mmc_setup_gpio_in(int gpio, const char *label)
{
int ret;
#ifndef CONFIG_DM_GPIO
if (!gpio_is_valid(gpio))
return -1;
#endif
ret = gpio_request(gpio, label);
if (ret)
return ret;
ret = gpio_direction_input(gpio);
if (ret)
return ret;
return gpio;
}
#endif
#if defined(CONFIG_OMAP44XX)
static void omap4_vmmc_pbias_config(struct mmc *mmc)
{
u32 value = 0;
value = readl((*ctrl)->control_pbiaslite);
value &= ~(MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ);
writel(value, (*ctrl)->control_pbiaslite);
value = readl((*ctrl)->control_pbiaslite);
value |= MMC1_PBIASLITE_VMODE | MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ;
writel(value, (*ctrl)->control_pbiaslite);
}
#endif
#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER)
static void omap5_pbias_config(struct mmc *mmc)
{
u32 value = 0;
value = readl((*ctrl)->control_pbias);
value &= ~SDCARD_PWRDNZ;
writel(value, (*ctrl)->control_pbias);
udelay(10); /* wait 10 us */
value &= ~SDCARD_BIAS_PWRDNZ;
writel(value, (*ctrl)->control_pbias);
palmas_mmc1_poweron_ldo();
value = readl((*ctrl)->control_pbias);
value |= SDCARD_BIAS_PWRDNZ;
writel(value, (*ctrl)->control_pbias);
udelay(150); /* wait 150 us */
value |= SDCARD_PWRDNZ;
writel(value, (*ctrl)->control_pbias);
udelay(150); /* wait 150 us */
}
#endif
static unsigned char mmc_board_init(struct mmc *mmc)
{
#if defined(CONFIG_OMAP34XX)
t2_t *t2_base = (t2_t *)T2_BASE;
struct prcm *prcm_base = (struct prcm *)PRCM_BASE;
u32 pbias_lite;
pbias_lite = readl(&t2_base->pbias_lite);
pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0);
#ifdef CONFIG_TARGET_OMAP3_CAIRO
/* for cairo board, we need to set up 1.8 Volt bias level on MMC1 */
pbias_lite &= ~PBIASLITEVMODE0;
#endif
writel(pbias_lite, &t2_base->pbias_lite);
writel(pbias_lite | PBIASLITEPWRDNZ1 |
PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0,
&t2_base->pbias_lite);
writel(readl(&t2_base->devconf0) | MMCSDIO1ADPCLKISEL,
&t2_base->devconf0);
writel(readl(&t2_base->devconf1) | MMCSDIO2ADPCLKISEL,
&t2_base->devconf1);
/* Change from default of 52MHz to 26MHz if necessary */
if (!(mmc->cfg->host_caps & MMC_MODE_HS_52MHz))
writel(readl(&t2_base->ctl_prog_io1) & ~CTLPROGIO1SPEEDCTRL,
&t2_base->ctl_prog_io1);
writel(readl(&prcm_base->fclken1_core) |
EN_MMC1 | EN_MMC2 | EN_MMC3,
&prcm_base->fclken1_core);
writel(readl(&prcm_base->iclken1_core) |
EN_MMC1 | EN_MMC2 | EN_MMC3,
&prcm_base->iclken1_core);
#endif
#if defined(CONFIG_OMAP44XX)
/* PBIAS config needed for MMC1 only */
if (mmc->block_dev.devnum == 0)
omap4_vmmc_pbias_config(mmc);
#endif
#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER)
if (mmc->block_dev.devnum == 0)
omap5_pbias_config(mmc);
#endif
return 0;
}
void mmc_init_stream(struct hsmmc *mmc_base)
{
ulong start;
writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con);
writel(MMC_CMD0, &mmc_base->cmd);
start = get_timer(0);
while (!(readl(&mmc_base->stat) & CC_MASK)) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for cc!\n", __func__);
return;
}
}
writel(CC_MASK, &mmc_base->stat)
;
writel(MMC_CMD0, &mmc_base->cmd)
;
start = get_timer(0);
while (!(readl(&mmc_base->stat) & CC_MASK)) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for cc2!\n", __func__);
return;
}
}
writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);
}
static int omap_hsmmc_init_setup(struct mmc *mmc)
{
struct hsmmc *mmc_base;
unsigned int reg_val;
unsigned int dsor;
ulong start;
mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr;
mmc_board_init(mmc);
writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET,
&mmc_base->sysconfig);
start = get_timer(0);
while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for cc2!\n", __func__);
return TIMEOUT;
}
}
writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl);
start = get_timer(0);
while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for softresetall!\n",
__func__);
return TIMEOUT;
}
}
writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
&mmc_base->capa);
reg_val = readl(&mmc_base->con) & RESERVED_MASK;
writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH |
MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK |
HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con);
dsor = 240;
mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
(ICE_STOP | DTO_15THDTO | CEN_DISABLE));
mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
(dsor << CLKD_OFFSET) | ICE_OSCILLATE);
start = get_timer(0);
while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for ics!\n", __func__);
return TIMEOUT;
}
}
writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
&mmc_base->ie);
mmc_init_stream(mmc_base);
return 0;
}
/*
* MMC controller internal finite state machine reset
*
* Used to reset command or data internal state machines, using respectively
* SRC or SRD bit of SYSCTL register
*/
static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
{
ulong start;
mmc_reg_out(&mmc_base->sysctl, bit, bit);
/*
* CMD(DAT) lines reset procedures are slightly different
* for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx).
* According to OMAP3 TRM:
* Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it
* returns to 0x0.
* According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset
* procedure steps must be as follows:
* 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in
* MMCHS_SYSCTL register (SD_SYSCTL for AM335x).
* 2. Poll the SRC(SRD) bit until it is set to 0x1.
* 3. Wait until the SRC (SRD) bit returns to 0x0
* (reset procedure is completed).
*/
#if defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \
defined(CONFIG_AM33XX) || defined(CONFIG_AM43XX)
if (!(readl(&mmc_base->sysctl) & bit)) {
start = get_timer(0);
while (!(readl(&mmc_base->sysctl) & bit)) {
if (get_timer(0) - start > MAX_RETRY_MS)
return;
}
}
#endif
start = get_timer(0);
while ((readl(&mmc_base->sysctl) & bit) != 0) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for sysctl %x to clear\n",
__func__, bit);
return;
}
}
}
static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct hsmmc *mmc_base;
unsigned int flags, mmc_stat;
ulong start;
mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr;
start = get_timer(0);
while ((readl(&mmc_base->pstate) & (DATI_MASK | CMDI_MASK)) != 0) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting on cmd inhibit to clear\n",
__func__);
return TIMEOUT;
}
}
writel(0xFFFFFFFF, &mmc_base->stat);
start = get_timer(0);
while (readl(&mmc_base->stat)) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for STAT (%x) to clear\n",
__func__, readl(&mmc_base->stat));
return TIMEOUT;
}
}
/*
* CMDREG
* CMDIDX[13:8] : Command index
* DATAPRNT[5] : Data Present Select
* ENCMDIDX[4] : Command Index Check Enable
* ENCMDCRC[3] : Command CRC Check Enable
* RSPTYP[1:0]
* 00 = No Response
* 01 = Length 136
* 10 = Length 48
* 11 = Length 48 Check busy after response
*/
/* Delay added before checking the status of frq change
* retry not supported by mmc.c(core file)
*/
if (cmd->cmdidx == SD_CMD_APP_SEND_SCR)
udelay(50000); /* wait 50 ms */
if (!(cmd->resp_type & MMC_RSP_PRESENT))
flags = 0;
else if (cmd->resp_type & MMC_RSP_136)
flags = RSP_TYPE_LGHT136 | CICE_NOCHECK;
else if (cmd->resp_type & MMC_RSP_BUSY)
flags = RSP_TYPE_LGHT48B;
else
flags = RSP_TYPE_LGHT48;
/* enable default flags */
flags = flags | (CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK |
MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE);
if (cmd->resp_type & MMC_RSP_CRC)
flags |= CCCE_CHECK;
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= CICE_CHECK;
if (data) {
if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) ||
(cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) {
flags |= (MSBS_MULTIBLK | BCE_ENABLE);
data->blocksize = 512;
writel(data->blocksize | (data->blocks << 16),
&mmc_base->blk);
} else
writel(data->blocksize | NBLK_STPCNT, &mmc_base->blk);
if (data->flags & MMC_DATA_READ)
flags |= (DP_DATA | DDIR_READ);
else
flags |= (DP_DATA | DDIR_WRITE);
}
writel(cmd->cmdarg, &mmc_base->arg);
udelay(20); /* To fix "No status update" error on eMMC */
writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);
start = get_timer(0);
do {
mmc_stat = readl(&mmc_base->stat);
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s : timeout: No status update\n", __func__);
return TIMEOUT;
}
} while (!mmc_stat);
if ((mmc_stat & IE_CTO) != 0) {
mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
return TIMEOUT;
} else if ((mmc_stat & ERRI_MASK) != 0)
return -1;
if (mmc_stat & CC_MASK) {
writel(CC_MASK, &mmc_base->stat);
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
/* response type 2 */
cmd->response[3] = readl(&mmc_base->rsp10);
cmd->response[2] = readl(&mmc_base->rsp32);
cmd->response[1] = readl(&mmc_base->rsp54);
cmd->response[0] = readl(&mmc_base->rsp76);
} else
/* response types 1, 1b, 3, 4, 5, 6 */
cmd->response[0] = readl(&mmc_base->rsp10);
}
}
if (data && (data->flags & MMC_DATA_READ)) {
mmc_read_data(mmc_base, data->dest,
data->blocksize * data->blocks);
} else if (data && (data->flags & MMC_DATA_WRITE)) {
mmc_write_data(mmc_base, data->src,
data->blocksize * data->blocks);
}
return 0;
}
static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size)
{
unsigned int *output_buf = (unsigned int *)buf;
unsigned int mmc_stat;
unsigned int count;
/*
* Start Polled Read
*/
count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;
count /= 4;
while (size) {
ulong start = get_timer(0);
do {
mmc_stat = readl(&mmc_base->stat);
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for status!\n",
__func__);
return TIMEOUT;
}
} while (mmc_stat == 0);
if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0)
mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
if ((mmc_stat & ERRI_MASK) != 0)
return 1;
if (mmc_stat & BRR_MASK) {
unsigned int k;
writel(readl(&mmc_base->stat) | BRR_MASK,
&mmc_base->stat);
for (k = 0; k < count; k++) {
*output_buf = readl(&mmc_base->data);
output_buf++;
}
size -= (count*4);
}
if (mmc_stat & BWR_MASK)
writel(readl(&mmc_base->stat) | BWR_MASK,
&mmc_base->stat);
if (mmc_stat & TC_MASK) {
writel(readl(&mmc_base->stat) | TC_MASK,
&mmc_base->stat);
break;
}
}
return 0;
}
static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
unsigned int size)
{
unsigned int *input_buf = (unsigned int *)buf;
unsigned int mmc_stat;
unsigned int count;
/*
* Start Polled Write
*/
count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;
count /= 4;
while (size) {
ulong start = get_timer(0);
do {
mmc_stat = readl(&mmc_base->stat);
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for status!\n",
__func__);
return TIMEOUT;
}
} while (mmc_stat == 0);
if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0)
mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
if ((mmc_stat & ERRI_MASK) != 0)
return 1;
if (mmc_stat & BWR_MASK) {
unsigned int k;
writel(readl(&mmc_base->stat) | BWR_MASK,
&mmc_base->stat);
for (k = 0; k < count; k++) {
writel(*input_buf, &mmc_base->data);
input_buf++;
}
size -= (count*4);
}
if (mmc_stat & BRR_MASK)
writel(readl(&mmc_base->stat) | BRR_MASK,
&mmc_base->stat);
if (mmc_stat & TC_MASK) {
writel(readl(&mmc_base->stat) | TC_MASK,
&mmc_base->stat);
break;
}
}
return 0;
}
static void omap_hsmmc_set_ios(struct mmc *mmc)
{
struct hsmmc *mmc_base;
unsigned int dsor = 0;
ulong start;
mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr;
/* configue bus width */
switch (mmc->bus_width) {
case 8:
writel(readl(&mmc_base->con) | DTW_8_BITMODE,
&mmc_base->con);
break;
case 4:
writel(readl(&mmc_base->con) & ~DTW_8_BITMODE,
&mmc_base->con);
writel(readl(&mmc_base->hctl) | DTW_4_BITMODE,
&mmc_base->hctl);
break;
case 1:
default:
writel(readl(&mmc_base->con) & ~DTW_8_BITMODE,
&mmc_base->con);
writel(readl(&mmc_base->hctl) & ~DTW_4_BITMODE,
&mmc_base->hctl);
break;
}
/* configure clock with 96Mhz system clock.
*/
if (mmc->clock != 0) {
dsor = (MMC_CLOCK_REFERENCE * 1000000 / mmc->clock);
if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > mmc->clock)
dsor++;
}
mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
(ICE_STOP | DTO_15THDTO | CEN_DISABLE));
mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
(dsor << CLKD_OFFSET) | ICE_OSCILLATE);
start = get_timer(0);
while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for ics!\n", __func__);
return;
}
}
writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
}
#ifdef OMAP_HSMMC_USE_GPIO
#ifdef CONFIG_DM_MMC
static int omap_hsmmc_getcd(struct mmc *mmc)
{
struct omap_hsmmc_data *priv = mmc->priv;
int value;
value = dm_gpio_get_value(&priv->cd_gpio);
/* if no CD return as 1 */
if (value < 0)
return 1;
if (priv->cd_inverted)
return !value;
return value;
}
static int omap_hsmmc_getwp(struct mmc *mmc)
{
struct omap_hsmmc_data *priv = mmc->priv;
int value;
value = dm_gpio_get_value(&priv->wp_gpio);
/* if no WP return as 0 */
if (value < 0)
return 0;
return value;
}
#else
static int omap_hsmmc_getcd(struct mmc *mmc)
{
struct omap_hsmmc_data *priv_data = mmc->priv;
int cd_gpio;
/* if no CD return as 1 */
cd_gpio = priv_data->cd_gpio;
if (cd_gpio < 0)
return 1;
/* NOTE: assumes card detect signal is active-low */
return !gpio_get_value(cd_gpio);
}
static int omap_hsmmc_getwp(struct mmc *mmc)
{
struct omap_hsmmc_data *priv_data = mmc->priv;
int wp_gpio;
/* if no WP return as 0 */
wp_gpio = priv_data->wp_gpio;
if (wp_gpio < 0)
return 0;
/* NOTE: assumes write protect signal is active-high */
return gpio_get_value(wp_gpio);
}
#endif
#endif
static const struct mmc_ops omap_hsmmc_ops = {
.send_cmd = omap_hsmmc_send_cmd,
.set_ios = omap_hsmmc_set_ios,
.init = omap_hsmmc_init_setup,
#ifdef OMAP_HSMMC_USE_GPIO
.getcd = omap_hsmmc_getcd,
.getwp = omap_hsmmc_getwp,
#endif
};
#ifndef CONFIG_DM_MMC
int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,
int wp_gpio)
{
struct mmc *mmc;
struct omap_hsmmc_data *priv_data;
struct mmc_config *cfg;
uint host_caps_val;
priv_data = malloc(sizeof(*priv_data));
if (priv_data == NULL)
return -1;
host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS;
switch (dev_index) {
case 0:
priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;
break;
#ifdef OMAP_HSMMC2_BASE
case 1:
priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE;
#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \
defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) || \
defined(CONFIG_AM33XX) || \
defined(CONFIG_AM43XX) || defined(CONFIG_SOC_KEYSTONE)) && \
defined(CONFIG_HSMMC2_8BIT)
/* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */
host_caps_val |= MMC_MODE_8BIT;
#endif
break;
#endif
#ifdef OMAP_HSMMC3_BASE
case 2:
priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE;
#if (defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX)) && defined(CONFIG_HSMMC3_8BIT)
/* Enable 8-bit interface for eMMC on DRA7XX */
host_caps_val |= MMC_MODE_8BIT;
#endif
break;
#endif
default:
priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;
return 1;
}
#ifdef OMAP_HSMMC_USE_GPIO
/* on error gpio values are set to -1, which is what we want */
priv_data->cd_gpio = omap_mmc_setup_gpio_in(cd_gpio, "mmc_cd");
priv_data->wp_gpio = omap_mmc_setup_gpio_in(wp_gpio, "mmc_wp");
#endif
cfg = &priv_data->cfg;
cfg->name = "OMAP SD/MMC";
cfg->ops = &omap_hsmmc_ops;
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
cfg->host_caps = host_caps_val & ~host_caps_mask;
cfg->f_min = 400000;
if (f_max != 0)
cfg->f_max = f_max;
else {
if (cfg->host_caps & MMC_MODE_HS) {
if (cfg->host_caps & MMC_MODE_HS_52MHz)
cfg->f_max = 52000000;
else
cfg->f_max = 26000000;
} else
cfg->f_max = 20000000;
}
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
#if defined(CONFIG_OMAP34XX)
/*
* Silicon revs 2.1 and older do not support multiblock transfers.
*/
if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21))
cfg->b_max = 1;
#endif
mmc = mmc_create(cfg, priv_data);
if (mmc == NULL)
return -1;
return 0;
}
#else
static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev)
{
struct omap_hsmmc_data *priv = dev_get_priv(dev);
const void *fdt = gd->fdt_blob;
int node = dev->of_offset;
struct mmc_config *cfg;
int val;
priv->base_addr = map_physmem(dev_get_addr(dev), sizeof(struct hsmmc *),
MAP_NOCACHE);
cfg = &priv->cfg;
cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
val = fdtdec_get_int(fdt, node, "bus-width", -1);
if (val < 0) {
printf("error: bus-width property missing\n");
return -ENOENT;
}
switch (val) {
case 0x8:
cfg->host_caps |= MMC_MODE_8BIT;
case 0x4:
cfg->host_caps |= MMC_MODE_4BIT;
break;
default:
printf("error: invalid bus-width property\n");
return -ENOENT;
}
cfg->f_min = 400000;
cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000);
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
priv->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted");
return 0;
}
static int omap_hsmmc_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct omap_hsmmc_data *priv = dev_get_priv(dev);
struct mmc_config *cfg;
struct mmc *mmc;
cfg = &priv->cfg;
cfg->name = "OMAP SD/MMC";
cfg->ops = &omap_hsmmc_ops;
mmc = mmc_create(cfg, priv);
if (mmc == NULL)
return -1;
#ifdef OMAP_HSMMC_USE_GPIO
gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);
#endif
mmc->dev = dev;
upriv->mmc = mmc;
return 0;
}
static const struct udevice_id omap_hsmmc_ids[] = {
{ .compatible = "ti,omap3-hsmmc" },
{ .compatible = "ti,omap4-hsmmc" },
{ .compatible = "ti,am33xx-hsmmc" },
{ }
};
U_BOOT_DRIVER(omap_hsmmc) = {
.name = "omap_hsmmc",
.id = UCLASS_MMC,
.of_match = omap_hsmmc_ids,
.ofdata_to_platdata = omap_hsmmc_ofdata_to_platdata,
.probe = omap_hsmmc_probe,
.priv_auto_alloc_size = sizeof(struct omap_hsmmc_data),
};
#endif

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2015, Google, Inc
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <errno.h>
#include <malloc.h>
#include <sdhci.h>
#include <asm/pci.h>
int pci_mmc_init(const char *name, struct pci_device_id *mmc_supported)
{
struct sdhci_host *mmc_host;
u32 iobase;
int ret;
int i;
for (i = 0; ; i++) {
struct udevice *dev;
ret = pci_find_device_id(mmc_supported, i, &dev);
if (ret)
return ret;
mmc_host = malloc(sizeof(struct sdhci_host));
if (!mmc_host)
return -ENOMEM;
mmc_host->name = name;
dm_pci_read_config32(dev, PCI_BASE_ADDRESS_0, &iobase);
mmc_host->ioaddr = (void *)iobase;
mmc_host->quirks = 0;
ret = add_sdhci(mmc_host, 0, 0);
if (ret)
return ret;
}
return 0;
}

View File

@@ -0,0 +1,63 @@
/*
* Support of SDHCI for Microchip PIC32 SoC.
*
* Copyright (C) 2015 Microchip Technology Inc.
* Andrei Pistirica <andrei.pistirica@microchip.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <dm.h>
#include <common.h>
#include <sdhci.h>
#include <asm/errno.h>
#include <mach/pic32.h>
DECLARE_GLOBAL_DATA_PTR;
static int pic32_sdhci_probe(struct udevice *dev)
{
struct sdhci_host *host = dev_get_priv(dev);
const void *fdt = gd->fdt_blob;
u32 f_min_max[2];
fdt_addr_t addr;
fdt_size_t size;
int ret;
addr = fdtdec_get_addr_size(fdt, dev->of_offset, "reg", &size);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
host->ioaddr = ioremap(addr, size);
host->name = dev->name;
host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_CD;
host->bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"bus-width", 4);
ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
"clock-freq-min-max", f_min_max, 2);
if (ret) {
printf("sdhci: clock-freq-min-max not found\n");
return ret;
}
ret = add_sdhci(host, f_min_max[1], f_min_max[0]);
if (ret)
return ret;
host->mmc->dev = dev;
return 0;
}
static const struct udevice_id pic32_sdhci_ids[] = {
{ .compatible = "microchip,pic32mzda-sdhci" },
{ }
};
U_BOOT_DRIVER(pic32_sdhci_drv) = {
.name = "pic32_sdhci",
.id = UCLASS_MMC,
.of_match = pic32_sdhci_ids,
.probe = pic32_sdhci_probe,
.priv_auto_alloc_size = sizeof(struct sdhci_host),
};

View File

@@ -0,0 +1,430 @@
/*
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
*
* Loosely based on the old code and Linux's PXA MMC driver
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/hardware.h>
#include <asm/arch/regs-mmc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <malloc.h>
#include <mmc.h>
/* PXAMMC Generic default config for various CPUs */
#if defined(CONFIG_CPU_PXA25X)
#define PXAMMC_FIFO_SIZE 1
#define PXAMMC_MIN_SPEED 312500
#define PXAMMC_MAX_SPEED 20000000
#define PXAMMC_HOST_CAPS (0)
#elif defined(CONFIG_CPU_PXA27X)
#define PXAMMC_CRC_SKIP
#define PXAMMC_FIFO_SIZE 32
#define PXAMMC_MIN_SPEED 304000
#define PXAMMC_MAX_SPEED 19500000
#define PXAMMC_HOST_CAPS (MMC_MODE_4BIT)
#elif defined(CONFIG_CPU_MONAHANS)
#define PXAMMC_FIFO_SIZE 32
#define PXAMMC_MIN_SPEED 304000
#define PXAMMC_MAX_SPEED 26000000
#define PXAMMC_HOST_CAPS (MMC_MODE_4BIT | MMC_MODE_HS)
#else
#error "This CPU isn't supported by PXA MMC!"
#endif
#define MMC_STAT_ERRORS \
(MMC_STAT_RES_CRC_ERROR | MMC_STAT_SPI_READ_ERROR_TOKEN | \
MMC_STAT_CRC_READ_ERROR | MMC_STAT_TIME_OUT_RESPONSE | \
MMC_STAT_READ_TIME_OUT | MMC_STAT_CRC_WRITE_ERROR)
/* 1 millisecond (in wait cycles below it's 100 x 10uS waits) */
#define PXA_MMC_TIMEOUT 100
struct pxa_mmc_priv {
struct pxa_mmc_regs *regs;
};
/* Wait for bit to be set */
static int pxa_mmc_wait(struct mmc *mmc, uint32_t mask)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
unsigned int timeout = PXA_MMC_TIMEOUT;
/* Wait for bit to be set */
while (--timeout) {
if (readl(&regs->stat) & mask)
break;
udelay(10);
}
if (!timeout)
return -ETIMEDOUT;
return 0;
}
static int pxa_mmc_stop_clock(struct mmc *mmc)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
unsigned int timeout = PXA_MMC_TIMEOUT;
/* If the clock aren't running, exit */
if (!(readl(&regs->stat) & MMC_STAT_CLK_EN))
return 0;
/* Tell the controller to turn off the clock */
writel(MMC_STRPCL_STOP_CLK, &regs->strpcl);
/* Wait until the clock are off */
while (--timeout) {
if (!(readl(&regs->stat) & MMC_STAT_CLK_EN))
break;
udelay(10);
}
/* The clock refused to stop, scream and die a painful death */
if (!timeout)
return -ETIMEDOUT;
/* The clock stopped correctly */
return 0;
}
static int pxa_mmc_start_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
uint32_t cmdat)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
int ret;
/* The card can send a "busy" response */
if (cmd->resp_type & MMC_RSP_BUSY)
cmdat |= MMC_CMDAT_BUSY;
/* Inform the controller about response type */
switch (cmd->resp_type) {
case MMC_RSP_R1:
case MMC_RSP_R1b:
cmdat |= MMC_CMDAT_R1;
break;
case MMC_RSP_R2:
cmdat |= MMC_CMDAT_R2;
break;
case MMC_RSP_R3:
cmdat |= MMC_CMDAT_R3;
break;
default:
break;
}
/* Load command and it's arguments into the controller */
writel(cmd->cmdidx, &regs->cmd);
writel(cmd->cmdarg >> 16, &regs->argh);
writel(cmd->cmdarg & 0xffff, &regs->argl);
writel(cmdat, &regs->cmdat);
/* Start the controller clock and wait until they are started */
writel(MMC_STRPCL_START_CLK, &regs->strpcl);
ret = pxa_mmc_wait(mmc, MMC_STAT_CLK_EN);
if (ret)
return ret;
/* Correct and happy end */
return 0;
}
static int pxa_mmc_cmd_done(struct mmc *mmc, struct mmc_cmd *cmd)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
uint32_t a, b, c;
int i;
int stat;
/* Read the controller status */
stat = readl(&regs->stat);
/*
* Linux says:
* Did I mention this is Sick. We always need to
* discard the upper 8 bits of the first 16-bit word.
*/
a = readl(&regs->res) & 0xffff;
for (i = 0; i < 4; i++) {
b = readl(&regs->res) & 0xffff;
c = readl(&regs->res) & 0xffff;
cmd->response[i] = (a << 24) | (b << 8) | (c >> 8);
a = c;
}
/* The command response didn't arrive */
if (stat & MMC_STAT_TIME_OUT_RESPONSE)
return -ETIMEDOUT;
else if (stat & MMC_STAT_RES_CRC_ERROR
&& cmd->resp_type & MMC_RSP_CRC) {
#ifdef PXAMMC_CRC_SKIP
if (cmd->resp_type & MMC_RSP_136
&& cmd->response[0] & (1 << 31))
printf("Ignoring CRC, this may be dangerous!\n");
else
#endif
return -EILSEQ;
}
/* The command response was successfully read */
return 0;
}
static int pxa_mmc_do_read_xfer(struct mmc *mmc, struct mmc_data *data)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
uint32_t len;
uint32_t *buf = (uint32_t *)data->dest;
int size;
int ret;
len = data->blocks * data->blocksize;
while (len) {
/* The controller has data ready */
if (readl(&regs->i_reg) & MMC_I_REG_RXFIFO_RD_REQ) {
size = min(len, (uint32_t)PXAMMC_FIFO_SIZE);
len -= size;
size /= 4;
/* Read data into the buffer */
while (size--)
*buf++ = readl(&regs->rxfifo);
}
if (readl(&regs->stat) & MMC_STAT_ERRORS)
return -EIO;
}
/* Wait for the transmission-done interrupt */
ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE);
if (ret)
return ret;
return 0;
}
static int pxa_mmc_do_write_xfer(struct mmc *mmc, struct mmc_data *data)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
uint32_t len;
uint32_t *buf = (uint32_t *)data->src;
int size;
int ret;
len = data->blocks * data->blocksize;
while (len) {
/* The controller is ready to receive data */
if (readl(&regs->i_reg) & MMC_I_REG_TXFIFO_WR_REQ) {
size = min(len, (uint32_t)PXAMMC_FIFO_SIZE);
len -= size;
size /= 4;
while (size--)
writel(*buf++, &regs->txfifo);
if (min(len, (uint32_t)PXAMMC_FIFO_SIZE) < 32)
writel(MMC_PRTBUF_BUF_PART_FULL, &regs->prtbuf);
}
if (readl(&regs->stat) & MMC_STAT_ERRORS)
return -EIO;
}
/* Wait for the transmission-done interrupt */
ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE);
if (ret)
return ret;
/* Wait until the data are really written to the card */
ret = pxa_mmc_wait(mmc, MMC_STAT_PRG_DONE);
if (ret)
return ret;
return 0;
}
static int pxa_mmc_request(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
uint32_t cmdat = 0;
int ret;
/* Stop the controller */
ret = pxa_mmc_stop_clock(mmc);
if (ret)
return ret;
/* If we're doing data transfer, configure the controller accordingly */
if (data) {
writel(data->blocks, &regs->nob);
writel(data->blocksize, &regs->blklen);
/* This delay can be optimized, but stick with max value */
writel(0xffff, &regs->rdto);
cmdat |= MMC_CMDAT_DATA_EN;
if (data->flags & MMC_DATA_WRITE)
cmdat |= MMC_CMDAT_WRITE;
}
/* Run in 4bit mode if the card can do it */
if (mmc->bus_width == 4)
cmdat |= MMC_CMDAT_SD_4DAT;
/* Execute the command */
ret = pxa_mmc_start_cmd(mmc, cmd, cmdat);
if (ret)
return ret;
/* Wait until the command completes */
ret = pxa_mmc_wait(mmc, MMC_STAT_END_CMD_RES);
if (ret)
return ret;
/* Read back the result */
ret = pxa_mmc_cmd_done(mmc, cmd);
if (ret)
return ret;
/* In case there was a data transfer scheduled, do it */
if (data) {
if (data->flags & MMC_DATA_WRITE)
pxa_mmc_do_write_xfer(mmc, data);
else
pxa_mmc_do_read_xfer(mmc, data);
}
return 0;
}
static void pxa_mmc_set_ios(struct mmc *mmc)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
uint32_t tmp;
uint32_t pxa_mmc_clock;
if (!mmc->clock) {
pxa_mmc_stop_clock(mmc);
return;
}
/* PXA3xx can do 26MHz with special settings. */
if (mmc->clock == 26000000) {
writel(0x7, &regs->clkrt);
return;
}
/* Set clock to the card the usual way. */
pxa_mmc_clock = 0;
tmp = mmc->cfg->f_max / mmc->clock;
tmp += tmp % 2;
while (tmp > 1) {
pxa_mmc_clock++;
tmp >>= 1;
}
writel(pxa_mmc_clock, &regs->clkrt);
}
static int pxa_mmc_init(struct mmc *mmc)
{
struct pxa_mmc_priv *priv = mmc->priv;
struct pxa_mmc_regs *regs = priv->regs;
/* Make sure the clock are stopped */
pxa_mmc_stop_clock(mmc);
/* Turn off SPI mode */
writel(0, &regs->spi);
/* Set up maximum timeout to wait for command response */
writel(MMC_RES_TO_MAX_MASK, &regs->resto);
/* Mask all interrupts */
writel(~(MMC_I_MASK_TXFIFO_WR_REQ | MMC_I_MASK_RXFIFO_RD_REQ),
&regs->i_mask);
return 0;
}
static const struct mmc_ops pxa_mmc_ops = {
.send_cmd = pxa_mmc_request,
.set_ios = pxa_mmc_set_ios,
.init = pxa_mmc_init,
};
static struct mmc_config pxa_mmc_cfg = {
.name = "PXA MMC",
.ops = &pxa_mmc_ops,
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
.f_max = PXAMMC_MAX_SPEED,
.f_min = PXAMMC_MIN_SPEED,
.host_caps = PXAMMC_HOST_CAPS,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
int pxa_mmc_register(int card_index)
{
struct mmc *mmc;
struct pxa_mmc_priv *priv;
uint32_t reg;
int ret = -ENOMEM;
priv = malloc(sizeof(struct pxa_mmc_priv));
if (!priv)
goto err0;
memset(priv, 0, sizeof(*priv));
switch (card_index) {
case 0:
priv->regs = (struct pxa_mmc_regs *)MMC0_BASE;
break;
case 1:
priv->regs = (struct pxa_mmc_regs *)MMC1_BASE;
break;
default:
ret = -EINVAL;
printf("PXA MMC: Invalid MMC controller ID (card_index = %d)\n",
card_index);
goto err1;
}
#ifndef CONFIG_CPU_MONAHANS /* PXA2xx */
reg = readl(CKEN);
reg |= CKEN12_MMC;
writel(reg, CKEN);
#else /* PXA3xx */
reg = readl(CKENA);
reg |= CKENA_12_MMC0 | CKENA_13_MMC1;
writel(reg, CKENA);
#endif
mmc = mmc_create(&pxa_mmc_cfg, priv);
if (mmc == NULL)
goto err1;
return 0;
err1:
free(priv);
err0:
return ret;
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2013 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <dwmmc.h>
#include <errno.h>
#include <pwrseq.h>
#include <syscon.h>
#include <asm/gpio.h>
#include <asm/arch/clock.h>
#include <asm/arch/periph.h>
#include <linux/err.h>
DECLARE_GLOBAL_DATA_PTR;
struct rockchip_mmc_plat {
struct mmc_config cfg;
struct mmc mmc;
};
struct rockchip_dwmmc_priv {
struct clk clk;
struct dwmci_host host;
};
static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
{
struct udevice *dev = host->priv;
struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
int ret;
ret = clk_set_rate(&priv->clk, freq);
if (ret < 0) {
debug("%s: err=%d\n", __func__, ret);
return ret;
}
return freq;
}
static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev)
{
struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
host->name = dev->name;
host->ioaddr = (void *)dev_get_addr(dev);
host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"bus-width", 4);
host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
host->priv = dev;
/* use non-removeable as sdcard and emmc as judgement */
if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "non-removable"))
host->dev_index = 0;
else
host->dev_index = 1;
return 0;
}
static int rockchip_dwmmc_probe(struct udevice *dev)
{
#ifdef CONFIG_BLK
struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
#endif
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
struct udevice *pwr_dev __maybe_unused;
u32 minmax[2];
int ret;
int fifo_depth;
ret = clk_get_by_index(dev, 0, &priv->clk);
if (ret < 0)
return ret;
if (fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
"clock-freq-min-max", minmax, 2))
return -EINVAL;
fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"fifo-depth", 0);
if (fifo_depth < 0)
return -EINVAL;
host->fifoth_val = MSIZE(0x2) |
RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2);
if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "fifo-mode"))
host->fifo_mode = true;
#ifdef CONFIG_PWRSEQ
/* Enable power if needed */
ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq",
&pwr_dev);
if (!ret) {
ret = pwrseq_set_power(pwr_dev, true);
if (ret)
return ret;
}
#endif
#ifdef CONFIG_BLK
dwmci_setup_cfg(&plat->cfg, dev->name, host->buswidth, host->caps,
minmax[1], minmax[0]);
host->mmc = &plat->mmc;
#else
ret = add_dwmci(host, minmax[1], minmax[0]);
if (ret)
return ret;
#endif
host->mmc->priv = &priv->host;
host->mmc->dev = dev;
upriv->mmc = host->mmc;
return 0;
}
static int rockchip_dwmmc_bind(struct udevice *dev)
{
#ifdef CONFIG_BLK
struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
int ret;
ret = dwmci_bind(dev, &plat->mmc, &plat->cfg);
if (ret)
return ret;
#endif
return 0;
}
static const struct udevice_id rockchip_dwmmc_ids[] = {
{ .compatible = "rockchip,rk3288-dw-mshc" },
{ }
};
U_BOOT_DRIVER(rockchip_dwmmc_drv) = {
.name = "rockchip_dwmmc",
.id = UCLASS_MMC,
.of_match = rockchip_dwmmc_ids,
.ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata,
.bind = rockchip_dwmmc_bind,
.probe = rockchip_dwmmc_probe,
.priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv),
.platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat),
};
#ifdef CONFIG_PWRSEQ
static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable)
{
struct gpio_desc reset;
int ret;
ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT);
if (ret)
return ret;
dm_gpio_set_value(&reset, 1);
udelay(1);
dm_gpio_set_value(&reset, 0);
udelay(200);
return 0;
}
static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = {
.set_power = rockchip_dwmmc_pwrseq_set_power,
};
static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = {
{ .compatible = "mmc-pwrseq-emmc" },
{ }
};
U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = {
.name = "mmc_pwrseq_emmc",
.id = UCLASS_PWRSEQ,
.of_match = rockchip_dwmmc_pwrseq_ids,
.ops = &rockchip_dwmmc_pwrseq_ops,
};
#endif

324
u-boot/drivers/mmc/rpmb.c Normal file
View File

@@ -0,0 +1,324 @@
/*
* Copyright 2014, Staubli Faverges
* Pierre Aubert
*
* eMMC- Replay Protected Memory Block
* According to JEDEC Standard No. 84-A441
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <memalign.h>
#include <mmc.h>
#include <u-boot/sha256.h>
#include "mmc_private.h"
/* Request codes */
#define RPMB_REQ_KEY 1
#define RPMB_REQ_WCOUNTER 2
#define RPMB_REQ_WRITE_DATA 3
#define RPMB_REQ_READ_DATA 4
#define RPMB_REQ_STATUS 5
/* Response code */
#define RPMB_RESP_KEY 0x0100
#define RPMB_RESP_WCOUNTER 0x0200
#define RPMB_RESP_WRITE_DATA 0x0300
#define RPMB_RESP_READ_DATA 0x0400
/* Error codes */
#define RPMB_OK 0
#define RPMB_ERR_GENERAL 1
#define RPMB_ERR_AUTH 2
#define RPMB_ERR_COUNTER 3
#define RPMB_ERR_ADDRESS 4
#define RPMB_ERR_WRITE 5
#define RPMB_ERR_READ 6
#define RPMB_ERR_KEY 7
#define RPMB_ERR_CNT_EXPIRED 0x80
#define RPMB_ERR_MSK 0x7
/* Sizes of RPMB data frame */
#define RPMB_SZ_STUFF 196
#define RPMB_SZ_MAC 32
#define RPMB_SZ_DATA 256
#define RPMB_SZ_NONCE 16
#define SHA256_BLOCK_SIZE 64
/* Error messages */
static const char * const rpmb_err_msg[] = {
"",
"General failure",
"Authentication failure",
"Counter failure",
"Address failure",
"Write failure",
"Read failure",
"Authentication key not yet programmed",
};
/* Structure of RPMB data frame. */
struct s_rpmb {
unsigned char stuff[RPMB_SZ_STUFF];
unsigned char mac[RPMB_SZ_MAC];
unsigned char data[RPMB_SZ_DATA];
unsigned char nonce[RPMB_SZ_NONCE];
unsigned long write_counter;
unsigned short address;
unsigned short block_count;
unsigned short result;
unsigned short request;
};
static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
bool is_rel_write)
{
struct mmc_cmd cmd = {0};
cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
cmd.cmdarg = blockcount & 0x0000FFFF;
if (is_rel_write)
cmd.cmdarg |= 1 << 31;
cmd.resp_type = MMC_RSP_R1;
return mmc_send_cmd(mmc, &cmd, NULL);
}
static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
unsigned int count, bool is_rel_write)
{
struct mmc_cmd cmd = {0};
struct mmc_data data;
int ret;
ret = mmc_set_blockcount(mmc, count, is_rel_write);
if (ret) {
#ifdef CONFIG_MMC_RPMB_TRACE
printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
#endif
return 1;
}
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
data.src = (const char *)s;
data.blocks = 1;
data.blocksize = MMC_MAX_BLOCK_LEN;
data.flags = MMC_DATA_WRITE;
ret = mmc_send_cmd(mmc, &cmd, &data);
if (ret) {
#ifdef CONFIG_MMC_RPMB_TRACE
printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
#endif
return 1;
}
return 0;
}
static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
unsigned short expected)
{
struct mmc_cmd cmd = {0};
struct mmc_data data;
int ret;
ret = mmc_set_blockcount(mmc, 1, false);
if (ret) {
#ifdef CONFIG_MMC_RPMB_TRACE
printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
#endif
return -1;
}
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1;
data.dest = (char *)s;
data.blocks = 1;
data.blocksize = MMC_MAX_BLOCK_LEN;
data.flags = MMC_DATA_READ;
ret = mmc_send_cmd(mmc, &cmd, &data);
if (ret) {
#ifdef CONFIG_MMC_RPMB_TRACE
printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
#endif
return -1;
}
/* Check the response and the status */
if (be16_to_cpu(s->request) != expected) {
#ifdef CONFIG_MMC_RPMB_TRACE
printf("%s:response= %x\n", __func__,
be16_to_cpu(s->request));
#endif
return -1;
}
ret = be16_to_cpu(s->result);
if (ret) {
printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
(ret & RPMB_ERR_CNT_EXPIRED) ?
"Write counter has expired" : "");
}
/* Return the status of the command */
return ret;
}
static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
{
ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
return -1;
/* Read the result */
return mmc_rpmb_response(mmc, rpmb_frame, expected);
}
static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
unsigned char *output)
{
sha256_context ctx;
int i;
unsigned char k_ipad[SHA256_BLOCK_SIZE];
unsigned char k_opad[SHA256_BLOCK_SIZE];
sha256_starts(&ctx);
/* According to RFC 4634, the HMAC transform looks like:
SHA(K XOR opad, SHA(K XOR ipad, text))
where K is an n byte key.
ipad is the byte 0x36 repeated blocksize times
opad is the byte 0x5c repeated blocksize times
and text is the data being protected.
*/
for (i = 0; i < RPMB_SZ_MAC; i++) {
k_ipad[i] = key[i] ^ 0x36;
k_opad[i] = key[i] ^ 0x5c;
}
/* remaining pad bytes are '\0' XOR'd with ipad and opad values */
for ( ; i < SHA256_BLOCK_SIZE; i++) {
k_ipad[i] = 0x36;
k_opad[i] = 0x5c;
}
sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
sha256_update(&ctx, buff, len);
sha256_finish(&ctx, output);
/* Init context for second pass */
sha256_starts(&ctx);
/* start with outer pad */
sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
/* then results of 1st hash */
sha256_update(&ctx, output, RPMB_SZ_MAC);
/* finish up 2nd pass */
sha256_finish(&ctx, output);
}
int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
{
int ret;
ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
/* Fill the request */
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
return -1;
/* Read the result */
ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
if (ret)
return ret;
*pcounter = be32_to_cpu(rpmb_frame->write_counter);
return 0;
}
int mmc_rpmb_set_key(struct mmc *mmc, void *key)
{
ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
/* Fill the request */
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
return -1;
/* read the operation status */
return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
}
int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
unsigned short cnt, unsigned char *key)
{
ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
int i;
for (i = 0; i < cnt; i++) {
/* Fill the request */
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
rpmb_frame->address = cpu_to_be16(blk + i);
rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
break;
/* Read the result */
if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
break;
/* Check the HMAC if key is provided */
if (key) {
unsigned char ret_hmac[RPMB_SZ_MAC];
rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
printf("MAC error on block #%d\n", i);
break;
}
}
/* Copy data */
memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
}
return i;
}
int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
unsigned short cnt, unsigned char *key)
{
ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
unsigned long wcount;
int i;
for (i = 0; i < cnt; i++) {
if (mmc_rpmb_get_counter(mmc, &wcount)) {
printf("Cannot read RPMB write counter\n");
break;
}
/* Fill the request */
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
rpmb_frame->address = cpu_to_be16(blk + i);
rpmb_frame->block_count = cpu_to_be16(1);
rpmb_frame->write_counter = cpu_to_be32(wcount);
rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
/* Computes HMAC */
rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
break;
/* Get status */
if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
break;
}
return i;
}

View File

@@ -0,0 +1,321 @@
/*
* S3C24xx SD/MMC driver
*
* Based on OpenMoko S3C24xx driver by Harald Welte <laforge@openmoko.org>
*
* Copyright (C) 2014 Marek Vasut <marex@denx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <mmc.h>
#include <errno.h>
#include <asm/arch/s3c24x0_cpu.h>
#include <asm/io.h>
#include <asm/unaligned.h>
#define S3C2440_SDICON_SDRESET (1 << 8)
#define S3C2410_SDICON_FIFORESET (1 << 1)
#define S3C2410_SDICON_CLOCKTYPE (1 << 0)
#define S3C2410_SDICMDCON_LONGRSP (1 << 10)
#define S3C2410_SDICMDCON_WAITRSP (1 << 9)
#define S3C2410_SDICMDCON_CMDSTART (1 << 8)
#define S3C2410_SDICMDCON_SENDERHOST (1 << 6)
#define S3C2410_SDICMDCON_INDEX 0x3f
#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12)
#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11)
#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10)
#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9)
#define S3C2440_SDIDCON_DS_WORD (2 << 22)
#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20)
#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19)
#define S3C2410_SDIDCON_BLOCKMODE (1 << 17)
#define S3C2410_SDIDCON_WIDEBUS (1 << 16)
#define S3C2440_SDIDCON_DATSTART (1 << 14)
#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12)
#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12)
#define S3C2410_SDIDCON_BLKNUM 0x7ff
#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8)
#define S3C2410_SDIDSTA_CRCFAIL (1 << 7)
#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6)
#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5)
#define S3C2410_SDIDSTA_XFERFINISH (1 << 4)
#define S3C2410_SDIFSTA_TFHALF (1 << 11)
#define S3C2410_SDIFSTA_COUNTMASK 0x7f
/*
* WARNING: We only support one SD IP block.
* NOTE: It's not likely there will ever exist an S3C24xx with two,
* at least not in this universe all right.
*/
static int wide_bus;
static int
s3cmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
uint32_t sdiccon, sdicsta, sdidcon, sdidsta, sdidat, sdifsta;
uint32_t sdicsta_wait_bit = S3C2410_SDICMDSTAT_CMDSENT;
unsigned int timeout = 100000;
int ret = 0, xfer_len, data_offset = 0;
const uint32_t sdidsta_err_mask = S3C2410_SDIDSTA_FIFOFAIL |
S3C2410_SDIDSTA_CRCFAIL | S3C2410_SDIDSTA_RXCRCFAIL |
S3C2410_SDIDSTA_DATATIMEOUT;
writel(0xffffffff, &sdi_regs->sdicsta);
writel(0xffffffff, &sdi_regs->sdidsta);
writel(0xffffffff, &sdi_regs->sdifsta);
/* Set up data transfer (if applicable). */
if (data) {
writel(data->blocksize, &sdi_regs->sdibsize);
sdidcon = data->blocks & S3C2410_SDIDCON_BLKNUM;
sdidcon |= S3C2410_SDIDCON_BLOCKMODE;
#if defined(CONFIG_S3C2440)
sdidcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART;
#endif
if (wide_bus)
sdidcon |= S3C2410_SDIDCON_WIDEBUS;
if (data->flags & MMC_DATA_READ) {
sdidcon |= S3C2410_SDIDCON_RXAFTERCMD;
sdidcon |= S3C2410_SDIDCON_XFER_RXSTART;
} else {
sdidcon |= S3C2410_SDIDCON_TXAFTERRESP;
sdidcon |= S3C2410_SDIDCON_XFER_TXSTART;
}
writel(sdidcon, &sdi_regs->sdidcon);
}
/* Write CMD arg. */
writel(cmd->cmdarg, &sdi_regs->sdicarg);
/* Write CMD index. */
sdiccon = cmd->cmdidx & S3C2410_SDICMDCON_INDEX;
sdiccon |= S3C2410_SDICMDCON_SENDERHOST;
sdiccon |= S3C2410_SDICMDCON_CMDSTART;
/* Command with short response. */
if (cmd->resp_type & MMC_RSP_PRESENT) {
sdiccon |= S3C2410_SDICMDCON_WAITRSP;
sdicsta_wait_bit = S3C2410_SDICMDSTAT_RSPFIN;
}
/* Command with long response. */
if (cmd->resp_type & MMC_RSP_136)
sdiccon |= S3C2410_SDICMDCON_LONGRSP;
/* Start the command. */
writel(sdiccon, &sdi_regs->sdiccon);
/* Wait for the command to complete or for response. */
for (timeout = 100000; timeout; timeout--) {
sdicsta = readl(&sdi_regs->sdicsta);
if (sdicsta & sdicsta_wait_bit)
break;
if (sdicsta & S3C2410_SDICMDSTAT_CMDTIMEOUT)
timeout = 1;
}
/* Clean the status bits. */
setbits_le32(&sdi_regs->sdicsta, 0xf << 9);
if (!timeout) {
puts("S3C SDI: Command timed out!\n");
ret = TIMEOUT;
goto error;
}
/* Read out the response. */
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = readl(&sdi_regs->sdirsp0);
cmd->response[1] = readl(&sdi_regs->sdirsp1);
cmd->response[2] = readl(&sdi_regs->sdirsp2);
cmd->response[3] = readl(&sdi_regs->sdirsp3);
} else {
cmd->response[0] = readl(&sdi_regs->sdirsp0);
}
/* If there are no data, we're done. */
if (!data)
return 0;
xfer_len = data->blocksize * data->blocks;
while (xfer_len > 0) {
sdidsta = readl(&sdi_regs->sdidsta);
sdifsta = readl(&sdi_regs->sdifsta);
if (sdidsta & sdidsta_err_mask) {
printf("S3C SDI: Data error (sdta=0x%08x)\n", sdidsta);
ret = -EIO;
goto error;
}
if (data->flags & MMC_DATA_READ) {
if ((sdifsta & S3C2410_SDIFSTA_COUNTMASK) < 4)
continue;
sdidat = readl(&sdi_regs->sdidat);
put_unaligned_le32(sdidat, data->dest + data_offset);
} else { /* Write */
/* TX FIFO half full. */
if (!(sdifsta & S3C2410_SDIFSTA_TFHALF))
continue;
/* TX FIFO is below 32b full, write. */
sdidat = get_unaligned_le32(data->src + data_offset);
writel(sdidat, &sdi_regs->sdidat);
}
data_offset += 4;
xfer_len -= 4;
}
/* Wait for the command to complete or for response. */
for (timeout = 100000; timeout; timeout--) {
sdidsta = readl(&sdi_regs->sdidsta);
if (sdidsta & S3C2410_SDIDSTA_XFERFINISH)
break;
if (sdidsta & S3C2410_SDIDSTA_DATATIMEOUT)
timeout = 1;
}
/* Clear status bits. */
writel(0x6f8, &sdi_regs->sdidsta);
if (!timeout) {
puts("S3C SDI: Command timed out!\n");
ret = TIMEOUT;
goto error;
}
writel(0, &sdi_regs->sdidcon);
return 0;
error:
return ret;
}
static void s3cmmc_set_ios(struct mmc *mmc)
{
struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
uint32_t divider = 0;
wide_bus = (mmc->bus_width == 4);
if (!mmc->clock)
return;
divider = DIV_ROUND_UP(get_PCLK(), mmc->clock);
if (divider)
divider--;
writel(divider, &sdi_regs->sdipre);
mdelay(125);
}
static int s3cmmc_init(struct mmc *mmc)
{
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
/* Start the clock. */
setbits_le32(&clk_power->clkcon, 1 << 9);
#if defined(CONFIG_S3C2440)
writel(S3C2440_SDICON_SDRESET, &sdi_regs->sdicon);
mdelay(10);
writel(0x7fffff, &sdi_regs->sdidtimer);
#else
writel(0xffff, &sdi_regs->sdidtimer);
#endif
writel(MMC_MAX_BLOCK_LEN, &sdi_regs->sdibsize);
writel(0x0, &sdi_regs->sdiimsk);
writel(S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE,
&sdi_regs->sdicon);
mdelay(125);
return 0;
}
struct s3cmmc_priv {
struct mmc_config cfg;
int (*getcd)(struct mmc *);
int (*getwp)(struct mmc *);
};
static int s3cmmc_getcd(struct mmc *mmc)
{
struct s3cmmc_priv *priv = mmc->priv;
if (priv->getcd)
return priv->getcd(mmc);
else
return 0;
}
static int s3cmmc_getwp(struct mmc *mmc)
{
struct s3cmmc_priv *priv = mmc->priv;
if (priv->getwp)
return priv->getwp(mmc);
else
return 0;
}
static const struct mmc_ops s3cmmc_ops = {
.send_cmd = s3cmmc_send_cmd,
.set_ios = s3cmmc_set_ios,
.init = s3cmmc_init,
.getcd = s3cmmc_getcd,
.getwp = s3cmmc_getwp,
};
int s3cmmc_initialize(bd_t *bis, int (*getcd)(struct mmc *),
int (*getwp)(struct mmc *))
{
struct s3cmmc_priv *priv;
struct mmc *mmc;
struct mmc_config *cfg;
priv = calloc(1, sizeof(*priv));
if (!priv)
return -ENOMEM;
cfg = &priv->cfg;
cfg->name = "S3C MMC";
cfg->ops = &s3cmmc_ops;
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HS;
cfg->f_min = 400000;
cfg->f_max = get_PCLK() / 2;
cfg->b_max = 0x80;
#if defined(CONFIG_S3C2410)
/*
* S3C2410 has some bug that prevents reliable
* operation at higher speed
*/
cfg->f_max /= 2;
#endif
mmc = mmc_create(cfg, priv);
if (!mmc) {
free(priv);
return -ENOMEM;
}
return 0;
}

View File

@@ -0,0 +1,217 @@
/*
* (C) Copyright 2012 SAMSUNG Electronics
* Jaehoon Chung <jh80.chung@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <asm/gpio.h>
#include <asm/arch/mmc.h>
#include <asm/arch/clk.h>
#include <errno.h>
#include <asm/arch/pinmux.h>
static char *S5P_NAME = "SAMSUNG SDHCI";
static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
{
unsigned long val, ctrl;
/*
* SELCLKPADDS[17:16]
* 00 = 2mA
* 01 = 4mA
* 10 = 7mA
* 11 = 9mA
*/
sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4);
val = sdhci_readl(host, SDHCI_CONTROL2);
val &= SDHCI_CTRL2_SELBASECLK_MASK(3);
val |= SDHCI_CTRL2_ENSTAASYNCCLR |
SDHCI_CTRL2_ENCMDCNFMSK |
SDHCI_CTRL2_ENFBCLKRX |
SDHCI_CTRL2_ENCLKOUTHOLD;
sdhci_writel(host, val, SDHCI_CONTROL2);
/*
* FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7]
* FCSel[1:0] : Rx Feedback Clock Delay Control
* Inverter delay means10ns delay if SDCLK 50MHz setting
* 01 = Delay1 (basic delay)
* 11 = Delay2 (basic delay + 2ns)
* 00 = Delay3 (inverter delay)
* 10 = Delay4 (inverter delay + 2ns)
*/
val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1;
sdhci_writel(host, val, SDHCI_CONTROL3);
/*
* SELBASECLK[5:4]
* 00/01 = HCLK
* 10 = EPLL
* 11 = XTI or XEXTCLK
*/
ctrl = sdhci_readl(host, SDHCI_CONTROL2);
ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3);
ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2);
sdhci_writel(host, ctrl, SDHCI_CONTROL2);
}
static int s5p_sdhci_core_init(struct sdhci_host *host)
{
host->name = S5P_NAME;
host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8;
host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->set_control_reg = &s5p_sdhci_set_control_reg;
host->set_clock = set_mmc_clk;
if (host->bus_width == 8)
host->host_caps |= MMC_MODE_8BIT;
return add_sdhci(host, 52000000, 400000);
}
int s5p_sdhci_init(u32 regbase, int index, int bus_width)
{
struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host));
if (!host) {
printf("sdhci__host allocation fail!\n");
return 1;
}
host->ioaddr = (void *)regbase;
host->index = index;
host->bus_width = bus_width;
return s5p_sdhci_core_init(host);
}
#if CONFIG_IS_ENABLED(OF_CONTROL)
struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS];
static int do_sdhci_init(struct sdhci_host *host)
{
int dev_id, flag, ret;
flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
dev_id = host->index + PERIPH_ID_SDMMC0;
ret = exynos_pinmux_config(dev_id, flag);
if (ret) {
printf("external SD not configured\n");
return ret;
}
if (dm_gpio_is_valid(&host->pwr_gpio)) {
dm_gpio_set_value(&host->pwr_gpio, 1);
ret = exynos_pinmux_config(dev_id, flag);
if (ret) {
debug("MMC not configured\n");
return ret;
}
}
if (dm_gpio_is_valid(&host->cd_gpio)) {
ret = dm_gpio_get_value(&host->cd_gpio);
if (ret) {
debug("no SD card detected (%d)\n", ret);
return -ENODEV;
}
}
return s5p_sdhci_core_init(host);
}
static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
{
int bus_width, dev_id;
unsigned int base;
/* Get device id */
dev_id = pinmux_decode_periph_id(blob, node);
if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) {
debug("MMC: Can't get device id\n");
return -1;
}
host->index = dev_id - PERIPH_ID_SDMMC0;
/* Get bus width */
bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
if (bus_width <= 0) {
debug("MMC: Can't get bus-width\n");
return -1;
}
host->bus_width = bus_width;
/* Get the base address from the device node */
base = fdtdec_get_addr(blob, node, "reg");
if (!base) {
debug("MMC: Can't get base address\n");
return -1;
}
host->ioaddr = (void *)base;
gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio,
GPIOD_IS_OUT);
gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio,
GPIOD_IS_IN);
return 0;
}
static int process_nodes(const void *blob, int node_list[], int count)
{
struct sdhci_host *host;
int i, node, ret;
int failed = 0;
debug("%s: count = %d\n", __func__, count);
/* build sdhci_host[] for each controller */
for (i = 0; i < count; i++) {
node = node_list[i];
if (node <= 0)
continue;
host = &sdhci_host[i];
ret = sdhci_get_config(blob, node, host);
if (ret) {
printf("%s: failed to decode dev %d (%d)\n", __func__, i, ret);
failed++;
continue;
}
ret = do_sdhci_init(host);
if (ret && ret != -ENODEV) {
printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret);
failed++;
}
}
/* we only consider it an error when all nodes fail */
return (failed == count ? -1 : 0);
}
int exynos_mmc_init(const void *blob)
{
int count;
int node_list[SDHCI_MAX_HOSTS];
count = fdtdec_find_aliases_for_id(blob, "mmc",
COMPAT_SAMSUNG_EXYNOS_MMC, node_list,
SDHCI_MAX_HOSTS);
return process_nodes(blob, node_list, count);
}
#endif

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <mmc.h>
#include <asm/test.h>
DECLARE_GLOBAL_DATA_PTR;
struct sandbox_mmc_plat {
struct mmc_config cfg;
struct mmc mmc;
};
/**
* sandbox_mmc_send_cmd() - Emulate SD commands
*
* This emulate an SD card version 2. Single-block reads result in zero data.
* Multiple-block reads return a test string.
*/
static int sandbox_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
switch (cmd->cmdidx) {
case MMC_CMD_ALL_SEND_CID:
break;
case SD_CMD_SEND_RELATIVE_ADDR:
cmd->response[0] = 0 << 16; /* mmc->rca */
case MMC_CMD_GO_IDLE_STATE:
break;
case SD_CMD_SEND_IF_COND:
cmd->response[0] = 0xaa;
break;
case MMC_CMD_SEND_STATUS:
cmd->response[0] = MMC_STATUS_RDY_FOR_DATA;
break;
case MMC_CMD_SELECT_CARD:
break;
case MMC_CMD_SEND_CSD:
cmd->response[0] = 0;
cmd->response[1] = 10 << 16; /* 1 << block_len */
break;
case SD_CMD_SWITCH_FUNC: {
u32 *resp = (u32 *)data->dest;
resp[7] = cpu_to_be32(SD_HIGHSPEED_BUSY);
break;
}
case MMC_CMD_READ_SINGLE_BLOCK:
memset(data->dest, '\0', data->blocksize);
break;
case MMC_CMD_READ_MULTIPLE_BLOCK:
strcpy(data->dest, "this is a test");
break;
case MMC_CMD_STOP_TRANSMISSION:
break;
case SD_CMD_APP_SEND_OP_COND:
cmd->response[0] = OCR_BUSY | OCR_HCS;
cmd->response[1] = 0;
cmd->response[2] = 0;
break;
case MMC_CMD_APP_CMD:
break;
case MMC_CMD_SET_BLOCKLEN:
debug("block len %d\n", cmd->cmdarg);
break;
case SD_CMD_APP_SEND_SCR: {
u32 *scr = (u32 *)data->dest;
scr[0] = cpu_to_be32(2 << 24 | 1 << 15); /* SD version 3 */
break;
}
default:
debug("%s: Unknown command %d\n", __func__, cmd->cmdidx);
break;
}
return 0;
}
static void sandbox_mmc_set_ios(struct mmc *mmc)
{
}
static int sandbox_mmc_init(struct mmc *mmc)
{
return 0;
}
static int sandbox_mmc_getcd(struct mmc *mmc)
{
return 1;
}
static const struct mmc_ops sandbox_mmc_ops = {
.send_cmd = sandbox_mmc_send_cmd,
.set_ios = sandbox_mmc_set_ios,
.init = sandbox_mmc_init,
.getcd = sandbox_mmc_getcd,
};
int sandbox_mmc_probe(struct udevice *dev)
{
struct sandbox_mmc_plat *plat = dev_get_platdata(dev);
return mmc_init(&plat->mmc);
}
int sandbox_mmc_bind(struct udevice *dev)
{
struct sandbox_mmc_plat *plat = dev_get_platdata(dev);
struct mmc_config *cfg = &plat->cfg;
int ret;
cfg->name = dev->name;
cfg->ops = &sandbox_mmc_ops;
cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_8BIT;
cfg->voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
cfg->f_min = 1000000;
cfg->f_max = 52000000;
cfg->b_max = U32_MAX;
ret = mmc_bind(dev, &plat->mmc, cfg);
if (ret)
return ret;
return 0;
}
int sandbox_mmc_unbind(struct udevice *dev)
{
mmc_unbind(dev);
return 0;
}
static const struct udevice_id sandbox_mmc_ids[] = {
{ .compatible = "sandbox,mmc" },
{ }
};
U_BOOT_DRIVER(mmc_sandbox) = {
.name = "mmc_sandbox",
.id = UCLASS_MMC,
.of_match = sandbox_mmc_ids,
.bind = sandbox_mmc_bind,
.unbind = sandbox_mmc_unbind,
.probe = sandbox_mmc_probe,
.platdata_auto_alloc_size = sizeof(struct sandbox_mmc_plat),
};

559
u-boot/drivers/mmc/sdhci.c Normal file
View File

@@ -0,0 +1,559 @@
/*
* Copyright 2011, Marvell Semiconductor Inc.
* Lei Wen <leiwen@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Back ported to the 8xx platform (from the 8260 platform) by
* Murray.Jensen@cmst.csiro.au, 27-Jan-01.
*/
#include <common.h>
#include <malloc.h>
#include <mmc.h>
#include <sdhci.h>
#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER)
void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER;
#else
void *aligned_buffer;
#endif
static void sdhci_reset(struct sdhci_host *host, u8 mask)
{
unsigned long timeout;
/* Wait max 100 ms */
timeout = 100;
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
printf("%s: Reset 0x%x never completed.\n",
__func__, (int)mask);
return;
}
timeout--;
udelay(1000);
}
}
static void sdhci_cmd_done(struct sdhci_host *host, struct mmc_cmd *cmd)
{
int i;
if (cmd->resp_type & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0; i < 4; i++) {
cmd->response[i] = sdhci_readl(host,
SDHCI_RESPONSE + (3-i)*4) << 8;
if (i != 3)
cmd->response[i] |= sdhci_readb(host,
SDHCI_RESPONSE + (3-i)*4-1);
}
} else {
cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE);
}
}
static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data)
{
int i;
char *offs;
for (i = 0; i < data->blocksize; i += 4) {
offs = data->dest + i;
if (data->flags == MMC_DATA_READ)
*(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER);
else
sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER);
}
}
static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data,
unsigned int start_addr)
{
unsigned int stat, rdy, mask, timeout, block = 0;
#ifdef CONFIG_MMC_SDMA
unsigned char ctrl;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~SDHCI_CTRL_DMA_MASK;
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
#endif
timeout = 1000000;
rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL;
mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE;
do {
stat = sdhci_readl(host, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR) {
printf("%s: Error detected in status(0x%X)!\n",
__func__, stat);
return -1;
}
if (stat & rdy) {
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
continue;
sdhci_writel(host, rdy, SDHCI_INT_STATUS);
sdhci_transfer_pio(host, data);
data->dest += data->blocksize;
if (++block >= data->blocks)
break;
}
#ifdef CONFIG_MMC_SDMA
if (stat & SDHCI_INT_DMA_END) {
sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS);
start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1);
start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE;
sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS);
}
#endif
if (timeout-- > 0)
udelay(10);
else {
printf("%s: Transfer data timeout\n", __func__);
return -1;
}
} while (!(stat & SDHCI_INT_DATA_END));
return 0;
}
/*
* No command will be sent by driver if card is busy, so driver must wait
* for card ready state.
* Every time when card is busy after timeout then (last) timeout value will be
* increased twice but only if it doesn't exceed global defined maximum.
* Each function call will use last timeout value. Max timeout can be redefined
* in board config file.
*/
#ifndef CONFIG_SDHCI_CMD_MAX_TIMEOUT
#define CONFIG_SDHCI_CMD_MAX_TIMEOUT 3200
#endif
#define CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT 100
#define SDHCI_READ_STATUS_TIMEOUT 1000
static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct sdhci_host *host = mmc->priv;
unsigned int stat = 0;
int ret = 0;
int trans_bytes = 0, is_aligned = 1;
u32 mask, flags, mode;
unsigned int time = 0, start_addr = 0;
int mmc_dev = mmc_get_blk_desc(mmc)->devnum;
unsigned start = get_timer(0);
/* Timeout unit - ms */
static unsigned int cmd_timeout = CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT;
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
/* We shouldn't wait for data inihibit for stop commands, even
though they might use busy signaling */
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
mask &= ~SDHCI_DATA_INHIBIT;
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (time >= cmd_timeout) {
printf("%s: MMC: %d busy ", __func__, mmc_dev);
if (2 * cmd_timeout <= CONFIG_SDHCI_CMD_MAX_TIMEOUT) {
cmd_timeout += cmd_timeout;
printf("timeout increasing to: %u ms.\n",
cmd_timeout);
} else {
puts("timeout.\n");
return COMM_ERR;
}
}
time++;
udelay(1000);
}
mask = SDHCI_INT_RESPONSE;
if (!(cmd->resp_type & MMC_RSP_PRESENT))
flags = SDHCI_CMD_RESP_NONE;
else if (cmd->resp_type & MMC_RSP_136)
flags = SDHCI_CMD_RESP_LONG;
else if (cmd->resp_type & MMC_RSP_BUSY) {
flags = SDHCI_CMD_RESP_SHORT_BUSY;
mask |= SDHCI_INT_DATA_END;
} else
flags = SDHCI_CMD_RESP_SHORT;
if (cmd->resp_type & MMC_RSP_CRC)
flags |= SDHCI_CMD_CRC;
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= SDHCI_CMD_INDEX;
if (data)
flags |= SDHCI_CMD_DATA;
/* Set Transfer mode regarding to data flag */
if (data != 0) {
sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
mode = SDHCI_TRNS_BLK_CNT_EN;
trans_bytes = data->blocks * data->blocksize;
if (data->blocks > 1)
mode |= SDHCI_TRNS_MULTI;
if (data->flags == MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
#ifdef CONFIG_MMC_SDMA
if (data->flags == MMC_DATA_READ)
start_addr = (unsigned long)data->dest;
else
start_addr = (unsigned long)data->src;
if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
(start_addr & 0x7) != 0x0) {
is_aligned = 0;
start_addr = (unsigned long)aligned_buffer;
if (data->flags != MMC_DATA_READ)
memcpy(aligned_buffer, data->src, trans_bytes);
}
#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER)
/*
* Always use this bounce-buffer when
* CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined
*/
is_aligned = 0;
start_addr = (unsigned long)aligned_buffer;
if (data->flags != MMC_DATA_READ)
memcpy(aligned_buffer, data->src, trans_bytes);
#endif
sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS);
mode |= SDHCI_TRNS_DMA;
#endif
sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
data->blocksize),
SDHCI_BLOCK_SIZE);
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
} else if (cmd->resp_type & MMC_RSP_BUSY) {
sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
}
sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT);
#ifdef CONFIG_MMC_SDMA
flush_cache(start_addr, trans_bytes);
#endif
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND);
start = get_timer(0);
do {
stat = sdhci_readl(host, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR)
break;
} while (((stat & mask) != mask) &&
(get_timer(start) < SDHCI_READ_STATUS_TIMEOUT));
if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) {
if (host->quirks & SDHCI_QUIRK_BROKEN_R1B)
return 0;
else {
printf("%s: Timeout for status update!\n", __func__);
return TIMEOUT;
}
}
if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
sdhci_cmd_done(host, cmd);
sdhci_writel(host, mask, SDHCI_INT_STATUS);
} else
ret = -1;
if (!ret && data)
ret = sdhci_transfer_data(host, data, start_addr);
if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD)
udelay(1000);
stat = sdhci_readl(host, SDHCI_INT_STATUS);
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
if (!ret) {
if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
!is_aligned && (data->flags == MMC_DATA_READ))
memcpy(data->dest, aligned_buffer, trans_bytes);
return 0;
}
sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA);
if (stat & SDHCI_INT_TIMEOUT)
return TIMEOUT;
else
return COMM_ERR;
}
static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
{
struct sdhci_host *host = mmc->priv;
unsigned int div, clk, timeout, reg;
/* Wait max 20 ms */
timeout = 200;
while (sdhci_readl(host, SDHCI_PRESENT_STATE) &
(SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
if (timeout == 0) {
printf("%s: Timeout to wait cmd & data inhibit\n",
__func__);
return -1;
}
timeout--;
udelay(100);
}
reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
reg &= ~SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
if (clock == 0)
return 0;
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
/* Version 3.00 divisors must be a multiple of 2. */
if (mmc->cfg->f_max <= clock)
div = 1;
else {
for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
if ((mmc->cfg->f_max / div) <= clock)
break;
}
}
} else {
/* Version 2.00 divisors must be a power of 2. */
for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
if ((mmc->cfg->f_max / div) <= clock)
break;
}
}
div >>= 1;
if (host->set_clock)
host->set_clock(host->index, div);
clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
<< SDHCI_DIVIDER_HI_SHIFT;
clk |= SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
/* Wait max 20 ms */
timeout = 20;
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
printf("%s: Internal clock never stabilised.\n",
__func__);
return -1;
}
timeout--;
udelay(1000);
}
clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
return 0;
}
static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
{
u8 pwr = 0;
if (power != (unsigned short)-1) {
switch (1 << power) {
case MMC_VDD_165_195:
pwr = SDHCI_POWER_180;
break;
case MMC_VDD_29_30:
case MMC_VDD_30_31:
pwr = SDHCI_POWER_300;
break;
case MMC_VDD_32_33:
case MMC_VDD_33_34:
pwr = SDHCI_POWER_330;
break;
}
}
if (pwr == 0) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
return;
}
if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
}
static void sdhci_set_ios(struct mmc *mmc)
{
u32 ctrl;
struct sdhci_host *host = mmc->priv;
if (host->set_control_reg)
host->set_control_reg(host);
if (mmc->clock != host->clock)
sdhci_set_clock(mmc, mmc->clock);
/* Set bus width */
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (mmc->bus_width == 8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ||
(host->quirks & SDHCI_QUIRK_USE_WIDE8))
ctrl |= SDHCI_CTRL_8BITBUS;
} else {
if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ||
(host->quirks & SDHCI_QUIRK_USE_WIDE8))
ctrl &= ~SDHCI_CTRL_8BITBUS;
if (mmc->bus_width == 4)
ctrl |= SDHCI_CTRL_4BITBUS;
else
ctrl &= ~SDHCI_CTRL_4BITBUS;
}
if (mmc->clock > 26000000)
ctrl |= SDHCI_CTRL_HISPD;
else
ctrl &= ~SDHCI_CTRL_HISPD;
if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)
ctrl &= ~SDHCI_CTRL_HISPD;
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
static int sdhci_init(struct mmc *mmc)
{
struct sdhci_host *host = mmc->priv;
if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !aligned_buffer) {
aligned_buffer = memalign(8, 512*1024);
if (!aligned_buffer) {
printf("%s: Aligned buffer alloc failed!!!\n",
__func__);
return -1;
}
}
sdhci_set_power(host, fls(mmc->cfg->voltages) - 1);
if (host->quirks & SDHCI_QUIRK_NO_CD) {
#if defined(CONFIG_PIC32_SDHCI)
/* PIC32 SDHCI CD errata:
* - set CD_TEST and clear CD_TEST_INS bit
*/
sdhci_writeb(host, SDHCI_CTRL_CD_TEST, SDHCI_HOST_CONTROL);
#else
unsigned int status;
sdhci_writeb(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST,
SDHCI_HOST_CONTROL);
status = sdhci_readl(host, SDHCI_PRESENT_STATE);
while ((!(status & SDHCI_CARD_PRESENT)) ||
(!(status & SDHCI_CARD_STATE_STABLE)) ||
(!(status & SDHCI_CARD_DETECT_PIN_LEVEL)))
status = sdhci_readl(host, SDHCI_PRESENT_STATE);
#endif
}
/* Enable only interrupts served by the SD controller */
sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
SDHCI_INT_ENABLE);
/* Mask all sdhci interrupt sources */
sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
return 0;
}
static const struct mmc_ops sdhci_ops = {
.send_cmd = sdhci_send_command,
.set_ios = sdhci_set_ios,
.init = sdhci_init,
};
int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
{
unsigned int caps;
host->cfg.name = host->name;
host->cfg.ops = &sdhci_ops;
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
#ifdef CONFIG_MMC_SDMA
if (!(caps & SDHCI_CAN_DO_SDMA)) {
printf("%s: Your controller doesn't support SDMA!!\n",
__func__);
return -1;
}
#endif
if (max_clk)
host->cfg.f_max = max_clk;
else {
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)
host->cfg.f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
else
host->cfg.f_max = (caps & SDHCI_CLOCK_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
host->cfg.f_max *= 1000000;
}
if (host->cfg.f_max == 0) {
printf("%s: Hardware doesn't specify base clock frequency\n",
__func__);
return -1;
}
if (min_clk)
host->cfg.f_min = min_clk;
else {
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)
host->cfg.f_min = host->cfg.f_max /
SDHCI_MAX_DIV_SPEC_300;
else
host->cfg.f_min = host->cfg.f_max /
SDHCI_MAX_DIV_SPEC_200;
}
host->cfg.voltages = 0;
if (caps & SDHCI_CAN_VDD_330)
host->cfg.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
if (caps & SDHCI_CAN_VDD_300)
host->cfg.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
if (caps & SDHCI_CAN_VDD_180)
host->cfg.voltages |= MMC_VDD_165_195;
if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE)
host->cfg.voltages |= host->voltages;
host->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
if (caps & SDHCI_CAN_DO_8BIT)
host->cfg.host_caps |= MMC_MODE_8BIT;
}
if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)
host->cfg.host_caps &= ~(MMC_MODE_HS | MMC_MODE_HS_52MHz);
if (host->host_caps)
host->cfg.host_caps |= host->host_caps;
host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
sdhci_reset(host, SDHCI_RESET_ALL);
host->mmc = mmc_create(&host->cfg, host);
if (host->mmc == NULL) {
printf("%s: mmc create fail!\n", __func__);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,606 @@
/*
* MMCIF driver.
*
* Copyright (C) 2011 Renesas Solutions Corp.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <config.h>
#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <mmc.h>
#include <malloc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include "sh_mmcif.h"
#define DRIVER_NAME "sh_mmcif"
static int sh_mmcif_intr(void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
u32 state = 0;
state = sh_mmcif_read(&host->regs->ce_int);
state &= sh_mmcif_read(&host->regs->ce_int_mask);
if (state & INT_RBSYE) {
sh_mmcif_write(~(INT_RBSYE | INT_CRSPE), &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MRBSYE, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_CRSPE) {
sh_mmcif_write(~INT_CRSPE, &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MCRSPE, &host->regs->ce_int_mask);
/* one more interrupt (INT_RBSYE) */
if (sh_mmcif_read(&host->regs->ce_cmd_set) & CMD_SET_RBSY)
return -EAGAIN;
goto end;
} else if (state & INT_BUFREN) {
sh_mmcif_write(~INT_BUFREN, &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MBUFREN, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_BUFWEN) {
sh_mmcif_write(~INT_BUFWEN, &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MBUFWEN, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_CMD12DRE) {
sh_mmcif_write(~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE |
INT_BUFRE), &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MCMD12DRE, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_BUFRE) {
sh_mmcif_write(~INT_BUFRE, &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MBUFRE, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_DTRANE) {
sh_mmcif_write(~INT_DTRANE, &host->regs->ce_int);
sh_mmcif_bitclr(MASK_MDTRANE, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_CMD12RBE) {
sh_mmcif_write(~(INT_CMD12RBE | INT_CMD12CRE),
&host->regs->ce_int);
sh_mmcif_bitclr(MASK_MCMD12RBE, &host->regs->ce_int_mask);
goto end;
} else if (state & INT_ERR_STS) {
/* err interrupts */
sh_mmcif_write(~state, &host->regs->ce_int);
sh_mmcif_bitclr(state, &host->regs->ce_int_mask);
goto err;
} else
return -EAGAIN;
err:
host->sd_error = 1;
debug("%s: int err state = %08x\n", DRIVER_NAME, state);
end:
host->wait_int = 1;
return 0;
}
static int mmcif_wait_interrupt_flag(struct sh_mmcif_host *host)
{
int timeout = 10000000;
while (1) {
timeout--;
if (timeout < 0) {
printf("timeout\n");
return 0;
}
if (!sh_mmcif_intr(host))
break;
udelay(1); /* 1 usec */
}
return 1; /* Return value: NOT 0 = complete waiting */
}
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
{
sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl);
sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl);
if (!clk)
return;
if (clk == CLKDEV_EMMC_DATA)
sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl);
else
sh_mmcif_bitset((fls(DIV_ROUND_UP(host->clk,
clk) - 1) - 1) << 16,
&host->regs->ce_clk_ctrl);
sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl);
}
static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
{
u32 tmp;
tmp = sh_mmcif_read(&host->regs->ce_clk_ctrl) & (CLK_ENABLE |
CLK_CLEAR);
sh_mmcif_write(SOFT_RST_ON, &host->regs->ce_version);
sh_mmcif_write(SOFT_RST_OFF, &host->regs->ce_version);
sh_mmcif_bitset(tmp | SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29,
&host->regs->ce_clk_ctrl);
/* byte swap on */
sh_mmcif_bitset(BUF_ACC_ATYP, &host->regs->ce_buf_acc);
}
static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
{
u32 state1, state2;
int ret, timeout = 10000000;
host->sd_error = 0;
host->wait_int = 0;
state1 = sh_mmcif_read(&host->regs->ce_host_sts1);
state2 = sh_mmcif_read(&host->regs->ce_host_sts2);
debug("%s: ERR HOST_STS1 = %08x\n", \
DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts1));
debug("%s: ERR HOST_STS2 = %08x\n", \
DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts2));
if (state1 & STS1_CMDSEQ) {
debug("%s: Forced end of command sequence\n", DRIVER_NAME);
sh_mmcif_bitset(CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl);
sh_mmcif_bitset(~CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl);
while (1) {
timeout--;
if (timeout < 0) {
printf(DRIVER_NAME": Forceed end of " \
"command sequence timeout err\n");
return -EILSEQ;
}
if (!(sh_mmcif_read(&host->regs->ce_host_sts1)
& STS1_CMDSEQ))
break;
}
sh_mmcif_sync_reset(host);
return -EILSEQ;
}
if (state2 & STS2_CRC_ERR)
ret = -EILSEQ;
else if (state2 & STS2_TIMEOUT_ERR)
ret = TIMEOUT;
else
ret = -EILSEQ;
return ret;
}
static int sh_mmcif_single_read(struct sh_mmcif_host *host,
struct mmc_data *data)
{
long time;
u32 blocksize, i;
unsigned long *p = (unsigned long *)data->dest;
if ((unsigned long)p & 0x00000001) {
printf("%s: The data pointer is unaligned.", __func__);
return -EIO;
}
host->wait_int = 0;
/* buf read enable */
sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
blocksize = (BLOCK_SIZE_MASK &
sh_mmcif_read(&host->regs->ce_block_set)) + 3;
for (i = 0; i < blocksize / 4; i++)
*p++ = sh_mmcif_read(&host->regs->ce_data);
/* buffer read end */
sh_mmcif_bitset(MASK_MBUFRE, &host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
return 0;
}
static int sh_mmcif_multi_read(struct sh_mmcif_host *host,
struct mmc_data *data)
{
long time;
u32 blocksize, i, j;
unsigned long *p = (unsigned long *)data->dest;
if ((unsigned long)p & 0x00000001) {
printf("%s: The data pointer is unaligned.", __func__);
return -EIO;
}
host->wait_int = 0;
blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set);
for (j = 0; j < data->blocks; j++) {
sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
for (i = 0; i < blocksize / 4; i++)
*p++ = sh_mmcif_read(&host->regs->ce_data);
WATCHDOG_RESET();
}
return 0;
}
static int sh_mmcif_single_write(struct sh_mmcif_host *host,
struct mmc_data *data)
{
long time;
u32 blocksize, i;
const unsigned long *p = (unsigned long *)data->dest;
if ((unsigned long)p & 0x00000001) {
printf("%s: The data pointer is unaligned.", __func__);
return -EIO;
}
host->wait_int = 0;
sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
blocksize = (BLOCK_SIZE_MASK &
sh_mmcif_read(&host->regs->ce_block_set)) + 3;
for (i = 0; i < blocksize / 4; i++)
sh_mmcif_write(*p++, &host->regs->ce_data);
/* buffer write end */
sh_mmcif_bitset(MASK_MDTRANE, &host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
return 0;
}
static int sh_mmcif_multi_write(struct sh_mmcif_host *host,
struct mmc_data *data)
{
long time;
u32 i, j, blocksize;
const unsigned long *p = (unsigned long *)data->dest;
if ((unsigned long)p & 0x00000001) {
printf("%s: The data pointer is unaligned.", __func__);
return -EIO;
}
host->wait_int = 0;
blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set);
for (j = 0; j < data->blocks; j++) {
sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
for (i = 0; i < blocksize / 4; i++)
sh_mmcif_write(*p++, &host->regs->ce_data);
WATCHDOG_RESET();
}
return 0;
}
static void sh_mmcif_get_response(struct sh_mmcif_host *host,
struct mmc_cmd *cmd)
{
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp3);
cmd->response[1] = sh_mmcif_read(&host->regs->ce_resp2);
cmd->response[2] = sh_mmcif_read(&host->regs->ce_resp1);
cmd->response[3] = sh_mmcif_read(&host->regs->ce_resp0);
debug(" RESP %08x, %08x, %08x, %08x\n", cmd->response[0],
cmd->response[1], cmd->response[2], cmd->response[3]);
} else {
cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp0);
}
}
static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
struct mmc_cmd *cmd)
{
cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp_cmd12);
}
static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
struct mmc_data *data, struct mmc_cmd *cmd)
{
u32 tmp = 0;
u32 opc = cmd->cmdidx;
/* Response Type check */
switch (cmd->resp_type) {
case MMC_RSP_NONE:
tmp |= CMD_SET_RTYP_NO;
break;
case MMC_RSP_R1:
case MMC_RSP_R1b:
case MMC_RSP_R3:
tmp |= CMD_SET_RTYP_6B;
break;
case MMC_RSP_R2:
tmp |= CMD_SET_RTYP_17B;
break;
default:
printf(DRIVER_NAME": Not support type response.\n");
break;
}
/* RBSY */
if (opc == MMC_CMD_SWITCH)
tmp |= CMD_SET_RBSY;
/* WDAT / DATW */
if (host->data) {
tmp |= CMD_SET_WDAT;
switch (host->bus_width) {
case MMC_BUS_WIDTH_1:
tmp |= CMD_SET_DATW_1;
break;
case MMC_BUS_WIDTH_4:
tmp |= CMD_SET_DATW_4;
break;
case MMC_BUS_WIDTH_8:
tmp |= CMD_SET_DATW_8;
break;
default:
printf(DRIVER_NAME": Not support bus width.\n");
break;
}
}
/* DWEN */
if (opc == MMC_CMD_WRITE_SINGLE_BLOCK ||
opc == MMC_CMD_WRITE_MULTIPLE_BLOCK)
tmp |= CMD_SET_DWEN;
/* CMLTE/CMD12EN */
if (opc == MMC_CMD_READ_MULTIPLE_BLOCK ||
opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN;
sh_mmcif_bitset(data->blocks << 16, &host->regs->ce_block_set);
}
/* RIDXC[1:0] check bits */
if (opc == MMC_CMD_SEND_OP_COND || opc == MMC_CMD_ALL_SEND_CID ||
opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID)
tmp |= CMD_SET_RIDXC_BITS;
/* RCRC7C[1:0] check bits */
if (opc == MMC_CMD_SEND_OP_COND)
tmp |= CMD_SET_CRC7C_BITS;
/* RCRC7C[1:0] internal CRC7 */
if (opc == MMC_CMD_ALL_SEND_CID ||
opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID)
tmp |= CMD_SET_CRC7C_INTERNAL;
return opc = ((opc << 24) | tmp);
}
static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host,
struct mmc_data *data, u16 opc)
{
u32 ret;
switch (opc) {
case MMC_CMD_READ_MULTIPLE_BLOCK:
ret = sh_mmcif_multi_read(host, data);
break;
case MMC_CMD_WRITE_MULTIPLE_BLOCK:
ret = sh_mmcif_multi_write(host, data);
break;
case MMC_CMD_WRITE_SINGLE_BLOCK:
ret = sh_mmcif_single_write(host, data);
break;
case MMC_CMD_READ_SINGLE_BLOCK:
case MMC_CMD_SEND_EXT_CSD:
ret = sh_mmcif_single_read(host, data);
break;
default:
printf(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc);
ret = -EINVAL;
break;
}
return ret;
}
static int sh_mmcif_start_cmd(struct sh_mmcif_host *host,
struct mmc_data *data, struct mmc_cmd *cmd)
{
long time;
int ret = 0, mask = 0;
u32 opc = cmd->cmdidx;
if (opc == MMC_CMD_STOP_TRANSMISSION) {
/* MMCIF sends the STOP command automatically */
if (host->last_cmd == MMC_CMD_READ_MULTIPLE_BLOCK)
sh_mmcif_bitset(MASK_MCMD12DRE,
&host->regs->ce_int_mask);
else
sh_mmcif_bitset(MASK_MCMD12RBE,
&host->regs->ce_int_mask);
time = mmcif_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_mmcif_error_manage(host);
sh_mmcif_get_cmd12response(host, cmd);
return 0;
}
if (opc == MMC_CMD_SWITCH)
mask = MASK_MRBSYE;
else
mask = MASK_MCRSPE;
mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR |
MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR |
MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO |
MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO;
if (host->data) {
sh_mmcif_write(0, &host->regs->ce_block_set);
sh_mmcif_write(data->blocksize, &host->regs->ce_block_set);
}
opc = sh_mmcif_set_cmd(host, data, cmd);
sh_mmcif_write(INT_START_MAGIC, &host->regs->ce_int);
sh_mmcif_write(mask, &host->regs->ce_int_mask);
debug("CMD%d ARG:%08x\n", cmd->cmdidx, cmd->cmdarg);
/* set arg */
sh_mmcif_write(cmd->cmdarg, &host->regs->ce_arg);
host->wait_int = 0;
/* set cmd */
sh_mmcif_write(opc, &host->regs->ce_cmd_set);
time = mmcif_wait_interrupt_flag(host);
if (time == 0)
return sh_mmcif_error_manage(host);
if (host->sd_error) {
switch (cmd->cmdidx) {
case MMC_CMD_ALL_SEND_CID:
case MMC_CMD_SELECT_CARD:
case MMC_CMD_APP_CMD:
ret = TIMEOUT;
break;
default:
printf(DRIVER_NAME": Cmd(d'%d) err\n", cmd->cmdidx);
ret = sh_mmcif_error_manage(host);
break;
}
host->sd_error = 0;
host->wait_int = 0;
return ret;
}
/* if no response */
if (!(opc & 0x00C00000))
return 0;
if (host->wait_int == 1) {
sh_mmcif_get_response(host, cmd);
host->wait_int = 0;
}
if (host->data)
ret = sh_mmcif_data_trans(host, data, cmd->cmdidx);
host->last_cmd = cmd->cmdidx;
return ret;
}
static int sh_mmcif_request(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct sh_mmcif_host *host = mmc->priv;
int ret;
WATCHDOG_RESET();
switch (cmd->cmdidx) {
case MMC_CMD_APP_CMD:
return TIMEOUT;
case MMC_CMD_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
if (data)
/* ext_csd */
break;
else
/* send_if_cond cmd (not support) */
return TIMEOUT;
default:
break;
}
host->sd_error = 0;
host->data = data;
ret = sh_mmcif_start_cmd(host, data, cmd);
host->data = NULL;
return ret;
}
static void sh_mmcif_set_ios(struct mmc *mmc)
{
struct sh_mmcif_host *host = mmc->priv;
if (mmc->clock)
sh_mmcif_clock_control(host, mmc->clock);
if (mmc->bus_width == 8)
host->bus_width = MMC_BUS_WIDTH_8;
else if (mmc->bus_width == 4)
host->bus_width = MMC_BUS_WIDTH_4;
else
host->bus_width = MMC_BUS_WIDTH_1;
debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
}
static int sh_mmcif_init(struct mmc *mmc)
{
struct sh_mmcif_host *host = mmc->priv;
sh_mmcif_sync_reset(host);
sh_mmcif_write(MASK_ALL, &host->regs->ce_int_mask);
return 0;
}
static const struct mmc_ops sh_mmcif_ops = {
.send_cmd = sh_mmcif_request,
.set_ios = sh_mmcif_set_ios,
.init = sh_mmcif_init,
};
static struct mmc_config sh_mmcif_cfg = {
.name = DRIVER_NAME,
.ops = &sh_mmcif_ops,
.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT |
MMC_MODE_8BIT,
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
int mmcif_mmc_init(void)
{
struct mmc *mmc;
struct sh_mmcif_host *host = NULL;
host = malloc(sizeof(struct sh_mmcif_host));
if (!host)
return -ENOMEM;
memset(host, 0, sizeof(*host));
host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR;
host->clk = CONFIG_SH_MMCIF_CLK;
sh_mmcif_cfg.f_min = MMC_CLK_DIV_MIN(host->clk);
sh_mmcif_cfg.f_max = MMC_CLK_DIV_MAX(host->clk);
mmc = mmc_create(&sh_mmcif_cfg, host);
if (mmc == NULL) {
free(host);
return -ENOMEM;
}
return 0;
}

View File

@@ -0,0 +1,241 @@
/*
* MMCIF driver.
*
* Copyright (C) 2011 Renesas Solutions Corp.
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef _SH_MMCIF_H_
#define _SH_MMCIF_H_
struct sh_mmcif_regs {
unsigned long ce_cmd_set;
unsigned long reserved;
unsigned long ce_arg;
unsigned long ce_arg_cmd12;
unsigned long ce_cmd_ctrl;
unsigned long ce_block_set;
unsigned long ce_clk_ctrl;
unsigned long ce_buf_acc;
unsigned long ce_resp3;
unsigned long ce_resp2;
unsigned long ce_resp1;
unsigned long ce_resp0;
unsigned long ce_resp_cmd12;
unsigned long ce_data;
unsigned long reserved2[2];
unsigned long ce_int;
unsigned long ce_int_mask;
unsigned long ce_host_sts1;
unsigned long ce_host_sts2;
unsigned long reserved3[11];
unsigned long ce_version;
};
/* CE_CMD_SET */
#define CMD_MASK 0x3f000000
#define CMD_SET_RTYP_NO ((0 << 23) | (0 << 22))
/* R1/R1b/R3/R4/R5 */
#define CMD_SET_RTYP_6B ((0 << 23) | (1 << 22))
/* R2 */
#define CMD_SET_RTYP_17B ((1 << 23) | (0 << 22))
/* R1b */
#define CMD_SET_RBSY (1 << 21)
#define CMD_SET_CCSEN (1 << 20)
/* 1: on data, 0: no data */
#define CMD_SET_WDAT (1 << 19)
/* 1: write to card, 0: read from card */
#define CMD_SET_DWEN (1 << 18)
/* 1: multi block trans, 0: single */
#define CMD_SET_CMLTE (1 << 17)
/* 1: CMD12 auto issue */
#define CMD_SET_CMD12EN (1 << 16)
/* index check */
#define CMD_SET_RIDXC_INDEX ((0 << 15) | (0 << 14))
/* check bits check */
#define CMD_SET_RIDXC_BITS ((0 << 15) | (1 << 14))
/* no check */
#define CMD_SET_RIDXC_NO ((1 << 15) | (0 << 14))
/* 1: CRC7 check*/
#define CMD_SET_CRC7C ((0 << 13) | (0 << 12))
/* 1: check bits check*/
#define CMD_SET_CRC7C_BITS ((0 << 13) | (1 << 12))
/* 1: internal CRC7 check*/
#define CMD_SET_CRC7C_INTERNAL ((1 << 13) | (0 << 12))
/* 1: CRC16 check*/
#define CMD_SET_CRC16C (1 << 10)
/* 1: not receive CRC status */
#define CMD_SET_CRCSTE (1 << 8)
/* 1: tran mission bit "Low" */
#define CMD_SET_TBIT (1 << 7)
/* 1: open/drain */
#define CMD_SET_OPDM (1 << 6)
#define CMD_SET_CCSH (1 << 5)
/* 1bit */
#define CMD_SET_DATW_1 ((0 << 1) | (0 << 0))
/* 4bit */
#define CMD_SET_DATW_4 ((0 << 1) | (1 << 0))
/* 8bit */
#define CMD_SET_DATW_8 ((1 << 1) | (0 << 0))
/* CE_CMD_CTRL */
#define CMD_CTRL_BREAK (1 << 0)
/* CE_BLOCK_SET */
#define BLOCK_SIZE_MASK 0x0000ffff
/* CE_CLK_CTRL */
#define CLK_ENABLE (1 << 24)
#define CLK_CLEAR ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16))
#define CLK_PCLK ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16))
/* respons timeout */
#define SRSPTO_256 ((1 << 13) | (0 << 12))
/* respons busy timeout */
#define SRBSYTO_29 ((1 << 11) | (1 << 10) | (1 << 9) | (1 << 8))
/* read/write timeout */
#define SRWDTO_29 ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4))
/* ccs timeout */
#define SCCSTO_29 ((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0))
/* CE_BUF_ACC */
#define BUF_ACC_DMAWEN (1 << 25)
#define BUF_ACC_DMAREN (1 << 24)
#define BUF_ACC_BUSW_32 (0 << 17)
#define BUF_ACC_BUSW_16 (1 << 17)
#define BUF_ACC_ATYP (1 << 16)
/* CE_INT */
#define INT_CCSDE (1 << 29)
#define INT_CMD12DRE (1 << 26)
#define INT_CMD12RBE (1 << 25)
#define INT_CMD12CRE (1 << 24)
#define INT_DTRANE (1 << 23)
#define INT_BUFRE (1 << 22)
#define INT_BUFWEN (1 << 21)
#define INT_BUFREN (1 << 20)
#define INT_CCSRCV (1 << 19)
#define INT_RBSYE (1 << 17)
#define INT_CRSPE (1 << 16)
#define INT_CMDVIO (1 << 15)
#define INT_BUFVIO (1 << 14)
#define INT_WDATERR (1 << 11)
#define INT_RDATERR (1 << 10)
#define INT_RIDXERR (1 << 9)
#define INT_RSPERR (1 << 8)
#define INT_CCSTO (1 << 5)
#define INT_CRCSTO (1 << 4)
#define INT_WDATTO (1 << 3)
#define INT_RDATTO (1 << 2)
#define INT_RBSYTO (1 << 1)
#define INT_RSPTO (1 << 0)
#define INT_ERR_STS (INT_CMDVIO | INT_BUFVIO | INT_WDATERR | \
INT_RDATERR | INT_RIDXERR | INT_RSPERR | \
INT_CCSTO | INT_CRCSTO | INT_WDATTO | \
INT_RDATTO | INT_RBSYTO | INT_RSPTO)
#define INT_START_MAGIC 0xD80430C0
/* CE_INT_MASK */
#define MASK_ALL 0x00000000
#define MASK_MCCSDE (1 << 29)
#define MASK_MCMD12DRE (1 << 26)
#define MASK_MCMD12RBE (1 << 25)
#define MASK_MCMD12CRE (1 << 24)
#define MASK_MDTRANE (1 << 23)
#define MASK_MBUFRE (1 << 22)
#define MASK_MBUFWEN (1 << 21)
#define MASK_MBUFREN (1 << 20)
#define MASK_MCCSRCV (1 << 19)
#define MASK_MRBSYE (1 << 17)
#define MASK_MCRSPE (1 << 16)
#define MASK_MCMDVIO (1 << 15)
#define MASK_MBUFVIO (1 << 14)
#define MASK_MWDATERR (1 << 11)
#define MASK_MRDATERR (1 << 10)
#define MASK_MRIDXERR (1 << 9)
#define MASK_MRSPERR (1 << 8)
#define MASK_MCCSTO (1 << 5)
#define MASK_MCRCSTO (1 << 4)
#define MASK_MWDATTO (1 << 3)
#define MASK_MRDATTO (1 << 2)
#define MASK_MRBSYTO (1 << 1)
#define MASK_MRSPTO (1 << 0)
/* CE_HOST_STS1 */
#define STS1_CMDSEQ (1 << 31)
/* CE_HOST_STS2 */
#define STS2_CRCSTE (1 << 31)
#define STS2_CRC16E (1 << 30)
#define STS2_AC12CRCE (1 << 29)
#define STS2_RSPCRC7E (1 << 28)
#define STS2_CRCSTEBE (1 << 27)
#define STS2_RDATEBE (1 << 26)
#define STS2_AC12REBE (1 << 25)
#define STS2_RSPEBE (1 << 24)
#define STS2_AC12IDXE (1 << 23)
#define STS2_RSPIDXE (1 << 22)
#define STS2_CCSTO (1 << 15)
#define STS2_RDATTO (1 << 14)
#define STS2_DATBSYTO (1 << 13)
#define STS2_CRCSTTO (1 << 12)
#define STS2_AC12BSYTO (1 << 11)
#define STS2_RSPBSYTO (1 << 10)
#define STS2_AC12RSPTO (1 << 9)
#define STS2_RSPTO (1 << 8)
#define STS2_CRC_ERR (STS2_CRCSTE | STS2_CRC16E | \
STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE)
#define STS2_TIMEOUT_ERR (STS2_CCSTO | STS2_RDATTO | \
STS2_DATBSYTO | STS2_CRCSTTO | \
STS2_AC12BSYTO | STS2_RSPBSYTO | \
STS2_AC12RSPTO | STS2_RSPTO)
/* CE_VERSION */
#define SOFT_RST_ON (1 << 31)
#define SOFT_RST_OFF (0 << 31)
#define CLKDEV_EMMC_DATA 52000000 /* 52MHz */
#ifdef CONFIG_RMOBILE
#define MMC_CLK_DIV_MIN(clk) (clk / (1 << 9))
#define MMC_CLK_DIV_MAX(clk) (clk / (1 << 1))
#else
#define MMC_CLK_DIV_MIN(clk) (clk / (1 << 8))
#define MMC_CLK_DIV_MAX(clk) CLKDEV_EMMC_DATA
#endif
#define MMC_BUS_WIDTH_1 0
#define MMC_BUS_WIDTH_4 2
#define MMC_BUS_WIDTH_8 3
struct sh_mmcif_host {
struct mmc_data *data;
struct sh_mmcif_regs *regs;
unsigned int clk;
int bus_width;
u16 wait_int;
u16 sd_error;
u8 last_cmd;
};
static inline u32 sh_mmcif_read(unsigned long *reg)
{
return readl(reg);
}
static inline void sh_mmcif_write(u32 val, unsigned long *reg)
{
writel(val, reg);
}
static inline void sh_mmcif_bitset(u32 val, unsigned long *reg)
{
sh_mmcif_write(val | sh_mmcif_read(reg), reg);
}
static inline void sh_mmcif_bitclr(u32 val, unsigned long *reg)
{
sh_mmcif_write(~val & sh_mmcif_read(reg), reg);
}
#endif /* _SH_MMCIF_H_ */

View File

@@ -0,0 +1,695 @@
/*
* drivers/mmc/sh_sdhi.c
*
* SD/MMC driver for Renesas rmobile ARM SoCs.
*
* Copyright (C) 2011,2013-2014 Renesas Electronics Corporation
* Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
* Copyright (C) 2008-2009 Renesas Solutions Corp.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <malloc.h>
#include <mmc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/rmobile.h>
#include <asm/arch/sh_sdhi.h>
#define DRIVER_NAME "sh-sdhi"
struct sh_sdhi_host {
unsigned long addr;
int ch;
int bus_shift;
unsigned long quirks;
unsigned char wait_int;
unsigned char sd_error;
unsigned char detect_waiting;
};
static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
{
writew(val, host->addr + (reg << host->bus_shift));
}
static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
{
return readw(host->addr + (reg << host->bus_shift));
}
static void *mmc_priv(struct mmc *mmc)
{
return (void *)mmc->priv;
}
static void sh_sdhi_detect(struct sh_sdhi_host *host)
{
sh_sdhi_writew(host, SDHI_OPTION,
OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
host->detect_waiting = 0;
}
static int sh_sdhi_intr(void *dev_id)
{
struct sh_sdhi_host *host = dev_id;
int state1 = 0, state2 = 0;
state1 = sh_sdhi_readw(host, SDHI_INFO1);
state2 = sh_sdhi_readw(host, SDHI_INFO2);
debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
/* CARD Insert */
if (state1 & INFO1_CARD_IN) {
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
if (!host->detect_waiting) {
host->detect_waiting = 1;
sh_sdhi_detect(host);
}
sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
INFO1M_ACCESS_END | INFO1M_CARD_IN |
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
return -EAGAIN;
}
/* CARD Removal */
if (state1 & INFO1_CARD_RE) {
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
if (!host->detect_waiting) {
host->detect_waiting = 1;
sh_sdhi_detect(host);
}
sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
INFO1M_ACCESS_END | INFO1M_CARD_RE |
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
return -EAGAIN;
}
if (state2 & INFO2_ALL_ERR) {
sh_sdhi_writew(host, SDHI_INFO2,
(unsigned short)~(INFO2_ALL_ERR));
sh_sdhi_writew(host, SDHI_INFO2_MASK,
INFO2M_ALL_ERR |
sh_sdhi_readw(host, SDHI_INFO2_MASK));
host->sd_error = 1;
host->wait_int = 1;
return 0;
}
/* Respons End */
if (state1 & INFO1_RESP_END) {
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
sh_sdhi_writew(host, SDHI_INFO1_MASK,
INFO1M_RESP_END |
sh_sdhi_readw(host, SDHI_INFO1_MASK));
host->wait_int = 1;
return 0;
}
/* SD_BUF Read Enable */
if (state2 & INFO2_BRE_ENABLE) {
sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
sh_sdhi_writew(host, SDHI_INFO2_MASK,
INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
sh_sdhi_readw(host, SDHI_INFO2_MASK));
host->wait_int = 1;
return 0;
}
/* SD_BUF Write Enable */
if (state2 & INFO2_BWE_ENABLE) {
sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
sh_sdhi_writew(host, SDHI_INFO2_MASK,
INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
sh_sdhi_readw(host, SDHI_INFO2_MASK));
host->wait_int = 1;
return 0;
}
/* Access End */
if (state1 & INFO1_ACCESS_END) {
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
sh_sdhi_writew(host, SDHI_INFO1_MASK,
INFO1_ACCESS_END |
sh_sdhi_readw(host, SDHI_INFO1_MASK));
host->wait_int = 1;
return 0;
}
return -EAGAIN;
}
static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
{
int timeout = 10000000;
while (1) {
timeout--;
if (timeout < 0) {
debug(DRIVER_NAME": %s timeout\n", __func__);
return 0;
}
if (!sh_sdhi_intr(host))
break;
udelay(1); /* 1 usec */
}
return 1; /* Return value: NOT 0 = complete waiting */
}
static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
{
u32 clkdiv, i, timeout;
if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
return -EBUSY;
}
sh_sdhi_writew(host, SDHI_CLK_CTRL,
~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
if (clk == 0)
return -EIO;
clkdiv = 0x80;
i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
i <<= 1;
sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
timeout = 100000;
/* Waiting for SD Bus busy to be cleared */
while (timeout--) {
if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
break;
}
if (timeout)
sh_sdhi_writew(host, SDHI_CLK_CTRL,
CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
else
return -EBUSY;
return 0;
}
static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
{
u32 timeout;
sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
sh_sdhi_writew(host, SDHI_CLK_CTRL,
CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
timeout = 100000;
while (timeout--) {
if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
break;
udelay(100);
}
if (!timeout)
return -EBUSY;
if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
return 0;
}
static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
{
unsigned short e_state1, e_state2;
int ret;
host->sd_error = 0;
host->wait_int = 0;
e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
if (e_state2 & ERR_STS2_SYS_ERROR) {
if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
ret = TIMEOUT;
else
ret = -EILSEQ;
debug("%s: ERR_STS2 = %04x\n",
DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
sh_sdhi_sync_reset(host);
sh_sdhi_writew(host, SDHI_INFO1_MASK,
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
return ret;
}
if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
ret = -EILSEQ;
else
ret = TIMEOUT;
debug("%s: ERR_STS1 = %04x\n",
DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
sh_sdhi_sync_reset(host);
sh_sdhi_writew(host, SDHI_INFO1_MASK,
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
return ret;
}
static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
{
long time;
unsigned short blocksize, i;
unsigned short *p = (unsigned short *)data->dest;
if ((unsigned long)p & 0x00000001) {
debug(DRIVER_NAME": %s: The data pointer is unaligned.",
__func__);
return -EIO;
}
host->wait_int = 0;
sh_sdhi_writew(host, SDHI_INFO2_MASK,
~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
sh_sdhi_readw(host, SDHI_INFO2_MASK));
sh_sdhi_writew(host, SDHI_INFO1_MASK,
~INFO1M_ACCESS_END &
sh_sdhi_readw(host, SDHI_INFO1_MASK));
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
host->wait_int = 0;
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
for (i = 0; i < blocksize / 2; i++)
*p++ = sh_sdhi_readw(host, SDHI_BUF0);
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
host->wait_int = 0;
return 0;
}
static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
{
long time;
unsigned short blocksize, i, sec;
unsigned short *p = (unsigned short *)data->dest;
if ((unsigned long)p & 0x00000001) {
debug(DRIVER_NAME": %s: The data pointer is unaligned.",
__func__);
return -EIO;
}
debug("%s: blocks = %d, blocksize = %d\n",
__func__, data->blocks, data->blocksize);
host->wait_int = 0;
for (sec = 0; sec < data->blocks; sec++) {
sh_sdhi_writew(host, SDHI_INFO2_MASK,
~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
sh_sdhi_readw(host, SDHI_INFO2_MASK));
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
host->wait_int = 0;
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
for (i = 0; i < blocksize / 2; i++)
*p++ = sh_sdhi_readw(host, SDHI_BUF0);
}
return 0;
}
static int sh_sdhi_single_write(struct sh_sdhi_host *host,
struct mmc_data *data)
{
long time;
unsigned short blocksize, i;
const unsigned short *p = (const unsigned short *)data->src;
if ((unsigned long)p & 0x00000001) {
debug(DRIVER_NAME": %s: The data pointer is unaligned.",
__func__);
return -EIO;
}
debug("%s: blocks = %d, blocksize = %d\n",
__func__, data->blocks, data->blocksize);
host->wait_int = 0;
sh_sdhi_writew(host, SDHI_INFO2_MASK,
~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
sh_sdhi_readw(host, SDHI_INFO2_MASK));
sh_sdhi_writew(host, SDHI_INFO1_MASK,
~INFO1M_ACCESS_END &
sh_sdhi_readw(host, SDHI_INFO1_MASK));
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
host->wait_int = 0;
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
for (i = 0; i < blocksize / 2; i++)
sh_sdhi_writew(host, SDHI_BUF0, *p++);
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
host->wait_int = 0;
return 0;
}
static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
{
long time;
unsigned short i, sec, blocksize;
const unsigned short *p = (const unsigned short *)data->src;
debug("%s: blocks = %d, blocksize = %d\n",
__func__, data->blocks, data->blocksize);
host->wait_int = 0;
for (sec = 0; sec < data->blocks; sec++) {
sh_sdhi_writew(host, SDHI_INFO2_MASK,
~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
sh_sdhi_readw(host, SDHI_INFO2_MASK));
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
host->wait_int = 0;
blocksize = sh_sdhi_readw(host, SDHI_SIZE);
for (i = 0; i < blocksize / 2; i++)
sh_sdhi_writew(host, SDHI_BUF0, *p++);
}
return 0;
}
static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
{
unsigned short i, j, cnt = 1;
unsigned short resp[8];
unsigned long *p1, *p2;
if (cmd->resp_type & MMC_RSP_136) {
cnt = 4;
resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
/* SDHI REGISTER SPECIFICATION */
for (i = 7, j = 6; i > 0; i--) {
resp[i] = (resp[i] << 8) & 0xff00;
resp[i] |= (resp[j--] >> 8) & 0x00ff;
}
resp[0] = (resp[0] << 8) & 0xff00;
/* SDHI REGISTER SPECIFICATION */
p1 = ((unsigned long *)resp) + 3;
} else {
resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
p1 = ((unsigned long *)resp);
}
p2 = (unsigned long *)cmd->response;
#if defined(__BIG_ENDIAN_BITFIELD)
for (i = 0; i < cnt; i++) {
*p2++ = ((*p1 >> 16) & 0x0000ffff) |
((*p1 << 16) & 0xffff0000);
p1--;
}
#else
for (i = 0; i < cnt; i++)
*p2++ = *p1--;
#endif /* __BIG_ENDIAN_BITFIELD */
}
static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
struct mmc_data *data, unsigned short opc)
{
switch (opc) {
case SD_CMD_APP_SEND_OP_COND:
case SD_CMD_APP_SEND_SCR:
opc |= SDHI_APP;
break;
case SD_CMD_APP_SET_BUS_WIDTH:
/* SD_APP_SET_BUS_WIDTH*/
if (!data)
opc |= SDHI_APP;
else /* SD_SWITCH */
opc = SDHI_SD_SWITCH;
break;
default:
break;
}
return opc;
}
static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
struct mmc_data *data, unsigned short opc)
{
unsigned short ret;
switch (opc) {
case MMC_CMD_READ_MULTIPLE_BLOCK:
ret = sh_sdhi_multi_read(host, data);
break;
case MMC_CMD_WRITE_MULTIPLE_BLOCK:
ret = sh_sdhi_multi_write(host, data);
break;
case MMC_CMD_WRITE_SINGLE_BLOCK:
ret = sh_sdhi_single_write(host, data);
break;
case MMC_CMD_READ_SINGLE_BLOCK:
case SDHI_SD_APP_SEND_SCR:
case SDHI_SD_SWITCH: /* SD_SWITCH */
ret = sh_sdhi_single_read(host, data);
break;
default:
printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
ret = -EINVAL;
break;
}
return ret;
}
static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
struct mmc_data *data, struct mmc_cmd *cmd)
{
long time;
unsigned short opc = cmd->cmdidx;
int ret = 0;
unsigned long timeout;
debug("opc = %d, arg = %x, resp_type = %x\n",
opc, cmd->cmdarg, cmd->resp_type);
if (opc == MMC_CMD_STOP_TRANSMISSION) {
/* SDHI sends the STOP command automatically by STOP reg */
sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
sh_sdhi_readw(host, SDHI_INFO1_MASK));
time = sh_sdhi_wait_interrupt_flag(host);
if (time == 0 || host->sd_error != 0)
return sh_sdhi_error_manage(host);
sh_sdhi_get_response(host, cmd);
return 0;
}
if (data) {
if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
}
sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
}
opc = sh_sdhi_set_cmd(host, data, opc);
/*
* U-Boot cannot use interrupt.
* So this flag may not be clear by timing
*/
sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
sh_sdhi_writew(host, SDHI_INFO1_MASK,
INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
sh_sdhi_writew(host, SDHI_ARG0,
(unsigned short)(cmd->cmdarg & ARG0_MASK));
sh_sdhi_writew(host, SDHI_ARG1,
(unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
timeout = 100000;
/* Waiting for SD Bus busy to be cleared */
while (timeout--) {
if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
break;
}
sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
host->wait_int = 0;
sh_sdhi_writew(host, SDHI_INFO1_MASK,
~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
sh_sdhi_writew(host, SDHI_INFO2_MASK,
~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
INFO2M_END_ERROR | INFO2M_TIMEOUT |
INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
sh_sdhi_readw(host, SDHI_INFO2_MASK));
time = sh_sdhi_wait_interrupt_flag(host);
if (!time)
return sh_sdhi_error_manage(host);
if (host->sd_error) {
switch (cmd->cmdidx) {
case MMC_CMD_ALL_SEND_CID:
case MMC_CMD_SELECT_CARD:
case SD_CMD_SEND_IF_COND:
case MMC_CMD_APP_CMD:
ret = TIMEOUT;
break;
default:
debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
ret = sh_sdhi_error_manage(host);
break;
}
host->sd_error = 0;
host->wait_int = 0;
return ret;
}
if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END)
return -EINVAL;
if (host->wait_int) {
sh_sdhi_get_response(host, cmd);
host->wait_int = 0;
}
if (data)
ret = sh_sdhi_data_trans(host, data, opc);
debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
ret, cmd->response[0], cmd->response[1],
cmd->response[2], cmd->response[3]);
return ret;
}
static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct sh_sdhi_host *host = mmc_priv(mmc);
int ret;
host->sd_error = 0;
ret = sh_sdhi_start_cmd(host, data, cmd);
return ret;
}
static void sh_sdhi_set_ios(struct mmc *mmc)
{
int ret;
struct sh_sdhi_host *host = mmc_priv(mmc);
ret = sh_sdhi_clock_control(host, mmc->clock);
if (ret)
return;
if (mmc->bus_width == 4)
sh_sdhi_writew(host, SDHI_OPTION, ~OPT_BUS_WIDTH_1 &
sh_sdhi_readw(host, SDHI_OPTION));
else
sh_sdhi_writew(host, SDHI_OPTION, OPT_BUS_WIDTH_1 |
sh_sdhi_readw(host, SDHI_OPTION));
debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
}
static int sh_sdhi_initialize(struct mmc *mmc)
{
struct sh_sdhi_host *host = mmc_priv(mmc);
int ret = sh_sdhi_sync_reset(host);
sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
#if defined(__BIG_ENDIAN_BITFIELD)
sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
#endif
sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
INFO1M_ACCESS_END | INFO1M_CARD_RE |
INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
return ret;
}
static const struct mmc_ops sh_sdhi_ops = {
.send_cmd = sh_sdhi_send_cmd,
.set_ios = sh_sdhi_set_ios,
.init = sh_sdhi_initialize,
};
static struct mmc_config sh_sdhi_cfg = {
.name = DRIVER_NAME,
.ops = &sh_sdhi_ops,
.f_min = CLKDEV_INIT,
.f_max = CLKDEV_HS_DATA,
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
.host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
.part_type = PART_TYPE_DOS,
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
};
int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
{
int ret = 0;
struct mmc *mmc;
struct sh_sdhi_host *host = NULL;
if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
return -ENODEV;
host = malloc(sizeof(struct sh_sdhi_host));
if (!host)
return -ENOMEM;
mmc = mmc_create(&sh_sdhi_cfg, host);
if (!mmc) {
ret = -1;
goto error;
}
host->ch = ch;
host->addr = addr;
host->quirks = quirks;
if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
host->bus_shift = 1;
return ret;
error:
if (host)
free(host);
return ret;
}

View File

@@ -0,0 +1,128 @@
/*
* (C) Copyright 2013 Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/clock_manager.h>
#include <asm/arch/system_manager.h>
#include <dm.h>
#include <dwmmc.h>
#include <errno.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <linux/err.h>
#include <malloc.h>
DECLARE_GLOBAL_DATA_PTR;
static const struct socfpga_clock_manager *clock_manager_base =
(void *)SOCFPGA_CLKMGR_ADDRESS;
static const struct socfpga_system_manager *system_manager_base =
(void *)SOCFPGA_SYSMGR_ADDRESS;
/* socfpga implmentation specific driver private data */
struct dwmci_socfpga_priv_data {
struct dwmci_host host;
unsigned int drvsel;
unsigned int smplsel;
};
static void socfpga_dwmci_clksel(struct dwmci_host *host)
{
struct dwmci_socfpga_priv_data *priv = host->priv;
u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) |
((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT);
/* Disable SDMMC clock. */
clrbits_le32(&clock_manager_base->per_pll.en,
CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK);
debug("%s: drvsel %d smplsel %d\n", __func__,
priv->drvsel, priv->smplsel);
writel(sdmmc_mask, &system_manager_base->sdmmcgrp_ctrl);
debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__,
readl(&system_manager_base->sdmmcgrp_ctrl));
/* Enable SDMMC clock */
setbits_le32(&clock_manager_base->per_pll.en,
CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK);
}
static int socfpga_dwmmc_ofdata_to_platdata(struct udevice *dev)
{
/* FIXME: probe from DT eventually too/ */
const unsigned long clk = cm_get_mmc_controller_clk_hz();
struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
int fifo_depth;
if (clk == 0) {
printf("DWMMC: MMC clock is zero!");
return -EINVAL;
}
fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"fifo-depth", 0);
if (fifo_depth < 0) {
printf("DWMMC: Can't get FIFO depth\n");
return -EINVAL;
}
host->name = dev->name;
host->ioaddr = (void *)dev_get_addr(dev);
host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"bus-width", 4);
host->clksel = socfpga_dwmci_clksel;
/*
* TODO(sjg@chromium.org): Remove the need for this hack.
* We only have one dwmmc block on gen5 SoCFPGA.
*/
host->dev_index = 0;
/* Fixed clock divide by 4 which due to the SDMMC wrapper */
host->bus_hz = clk;
host->fifoth_val = MSIZE(0x2) |
RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2);
priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset,
"drvsel", 3);
priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset,
"smplsel", 0);
host->priv = priv;
return 0;
}
static int socfpga_dwmmc_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
int ret;
ret = add_dwmci(host, host->bus_hz, 400000);
if (ret)
return ret;
upriv->mmc = host->mmc;
host->mmc->dev = dev;
return 0;
}
static const struct udevice_id socfpga_dwmmc_ids[] = {
{ .compatible = "altr,socfpga-dw-mshc" },
{ }
};
U_BOOT_DRIVER(socfpga_dwmmc_drv) = {
.name = "socfpga_dwmmc",
.id = UCLASS_MMC,
.of_match = socfpga_dwmmc_ids,
.ofdata_to_platdata = socfpga_dwmmc_ofdata_to_platdata,
.probe = socfpga_dwmmc_probe,
.priv_auto_alloc_size = sizeof(struct dwmci_socfpga_priv_data),
};

View File

@@ -0,0 +1,32 @@
/*
* (C) Copyright 2012
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <sdhci.h>
int spear_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)
{
struct sdhci_host *host = NULL;
host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
if (!host) {
printf("sdhci host malloc fail!\n");
return 1;
}
host->name = "sdhci";
host->ioaddr = (void *)regbase;
host->quirks = quirks;
if (quirks & SDHCI_QUIRK_REG32_RW)
host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
else
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
add_sdhci(host, max_clk, min_clk);
return 0;
}

View File

@@ -0,0 +1,499 @@
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Aaron <leafy.myeh@allwinnertech.com>
*
* MMC driver for allwinner sunxi platform.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <errno.h>
#include <malloc.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mmc.h>
#include <asm-generic/gpio.h>
struct sunxi_mmc_host {
unsigned mmc_no;
uint32_t *mclkreg;
unsigned fatal_err;
struct sunxi_mmc *reg;
struct mmc_config cfg;
};
/* support 4 mmc hosts */
struct sunxi_mmc_host mmc_host[4];
static int sunxi_mmc_getcd_gpio(int sdc_no)
{
switch (sdc_no) {
case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN);
case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN);
case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN);
case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN);
}
return -EINVAL;
}
static int mmc_resource_init(int sdc_no)
{
struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
int cd_pin, ret = 0;
debug("init mmc %d resource\n", sdc_no);
switch (sdc_no) {
case 0:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
mmchost->mclkreg = &ccm->sd0_clk_cfg;
break;
case 1:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
mmchost->mclkreg = &ccm->sd1_clk_cfg;
break;
case 2:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
mmchost->mclkreg = &ccm->sd2_clk_cfg;
break;
case 3:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
mmchost->mclkreg = &ccm->sd3_clk_cfg;
break;
default:
printf("Wrong mmc number %d\n", sdc_no);
return -1;
}
mmchost->mmc_no = sdc_no;
cd_pin = sunxi_mmc_getcd_gpio(sdc_no);
if (cd_pin >= 0) {
ret = gpio_request(cd_pin, "mmc_cd");
if (!ret) {
sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP);
ret = gpio_direction_input(cd_pin);
}
}
return ret;
}
static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
{
unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
if (hz <= 24000000) {
pll = CCM_MMC_CTRL_OSCM24;
pll_hz = 24000000;
} else {
#ifdef CONFIG_MACH_SUN9I
pll = CCM_MMC_CTRL_PLL_PERIPH0;
pll_hz = clock_get_pll4_periph0();
#else
pll = CCM_MMC_CTRL_PLL6;
pll_hz = clock_get_pll6();
#endif
}
div = pll_hz / hz;
if (pll_hz % hz)
div++;
n = 0;
while (div > 16) {
n++;
div = (div + 1) / 2;
}
if (n > 3) {
printf("mmc %u error cannot set clock to %u\n",
mmchost->mmc_no, hz);
return -1;
}
/* determine delays */
if (hz <= 400000) {
oclk_dly = 0;
sclk_dly = 0;
} else if (hz <= 25000000) {
oclk_dly = 0;
sclk_dly = 5;
#ifdef CONFIG_MACH_SUN9I
} else if (hz <= 50000000) {
oclk_dly = 5;
sclk_dly = 4;
} else {
/* hz > 50000000 */
oclk_dly = 2;
sclk_dly = 4;
#else
} else if (hz <= 50000000) {
oclk_dly = 3;
sclk_dly = 4;
} else {
/* hz > 50000000 */
oclk_dly = 1;
sclk_dly = 4;
#endif
}
writel(CCM_MMC_CTRL_ENABLE | pll | CCM_MMC_CTRL_SCLK_DLY(sclk_dly) |
CCM_MMC_CTRL_N(n) | CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
CCM_MMC_CTRL_M(div), mmchost->mclkreg);
debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n",
mmchost->mmc_no, hz, pll_hz, 1u << n, div,
pll_hz / (1u << n) / div);
return 0;
}
static int mmc_clk_io_on(int sdc_no)
{
struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
debug("init mmc %d clock and io\n", sdc_no);
/* config ahb clock */
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
#ifdef CONFIG_SUNXI_GEN_SUN6I
/* unassert reset */
setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
#endif
#if defined(CONFIG_MACH_SUN9I)
/* sun9i has a mmc-common module, also set the gate and reset there */
writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
#endif
return mmc_set_mod_clk(mmchost, 24000000);
}
static int mmc_update_clk(struct mmc *mmc)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
unsigned int cmd;
unsigned timeout_msecs = 2000;
cmd = SUNXI_MMC_CMD_START |
SUNXI_MMC_CMD_UPCLK_ONLY |
SUNXI_MMC_CMD_WAIT_PRE_OVER;
writel(cmd, &mmchost->reg->cmd);
while (readl(&mmchost->reg->cmd) & SUNXI_MMC_CMD_START) {
if (!timeout_msecs--)
return -1;
udelay(1000);
}
/* clock update sets various irq status bits, clear these */
writel(readl(&mmchost->reg->rint), &mmchost->reg->rint);
return 0;
}
static int mmc_config_clock(struct mmc *mmc)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
unsigned rval = readl(&mmchost->reg->clkcr);
/* Disable Clock */
rval &= ~SUNXI_MMC_CLK_ENABLE;
writel(rval, &mmchost->reg->clkcr);
if (mmc_update_clk(mmc))
return -1;
/* Set mod_clk to new rate */
if (mmc_set_mod_clk(mmchost, mmc->clock))
return -1;
/* Clear internal divider */
rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
writel(rval, &mmchost->reg->clkcr);
/* Re-enable Clock */
rval |= SUNXI_MMC_CLK_ENABLE;
writel(rval, &mmchost->reg->clkcr);
if (mmc_update_clk(mmc))
return -1;
return 0;
}
static void sunxi_mmc_set_ios(struct mmc *mmc)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
debug("set ios: bus_width: %x, clock: %d\n",
mmc->bus_width, mmc->clock);
/* Change clock first */
if (mmc->clock && mmc_config_clock(mmc) != 0) {
mmchost->fatal_err = 1;
return;
}
/* Change bus width */
if (mmc->bus_width == 8)
writel(0x2, &mmchost->reg->width);
else if (mmc->bus_width == 4)
writel(0x1, &mmchost->reg->width);
else
writel(0x0, &mmchost->reg->width);
}
static int sunxi_mmc_core_init(struct mmc *mmc)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
/* Reset controller */
writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl);
udelay(1000);
return 0;
}
static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
const int reading = !!(data->flags & MMC_DATA_READ);
const uint32_t status_bit = reading ? SUNXI_MMC_STATUS_FIFO_EMPTY :
SUNXI_MMC_STATUS_FIFO_FULL;
unsigned i;
unsigned *buff = (unsigned int *)(reading ? data->dest : data->src);
unsigned byte_cnt = data->blocksize * data->blocks;
unsigned timeout_msecs = byte_cnt >> 8;
if (timeout_msecs < 2000)
timeout_msecs = 2000;
/* Always read / write data through the CPU */
setbits_le32(&mmchost->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB);
for (i = 0; i < (byte_cnt >> 2); i++) {
while (readl(&mmchost->reg->status) & status_bit) {
if (!timeout_msecs--)
return -1;
udelay(1000);
}
if (reading)
buff[i] = readl(&mmchost->reg->fifo);
else
writel(buff[i], &mmchost->reg->fifo);
}
return 0;
}
static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs,
unsigned int done_bit, const char *what)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
unsigned int status;
do {
status = readl(&mmchost->reg->rint);
if (!timeout_msecs-- ||
(status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT)) {
debug("%s timeout %x\n", what,
status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT);
return TIMEOUT;
}
udelay(1000);
} while (!(status & done_bit));
return 0;
}
static int sunxi_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
unsigned int cmdval = SUNXI_MMC_CMD_START;
unsigned int timeout_msecs;
int error = 0;
unsigned int status = 0;
unsigned int bytecnt = 0;
if (mmchost->fatal_err)
return -1;
if (cmd->resp_type & MMC_RSP_BUSY)
debug("mmc cmd %d check rsp busy\n", cmd->cmdidx);
if (cmd->cmdidx == 12)
return 0;
if (!cmd->cmdidx)
cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ;
if (cmd->resp_type & MMC_RSP_PRESENT)
cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE;
if (cmd->resp_type & MMC_RSP_136)
cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE;
if (cmd->resp_type & MMC_RSP_CRC)
cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC;
if (data) {
if ((u32)(long)data->dest & 0x3) {
error = -1;
goto out;
}
cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER;
if (data->flags & MMC_DATA_WRITE)
cmdval |= SUNXI_MMC_CMD_WRITE;
if (data->blocks > 1)
cmdval |= SUNXI_MMC_CMD_AUTO_STOP;
writel(data->blocksize, &mmchost->reg->blksz);
writel(data->blocks * data->blocksize, &mmchost->reg->bytecnt);
}
debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", mmchost->mmc_no,
cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg);
writel(cmd->cmdarg, &mmchost->reg->arg);
if (!data)
writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
/*
* transfer data and check status
* STATREG[2] : FIFO empty
* STATREG[3] : FIFO full
*/
if (data) {
int ret = 0;
bytecnt = data->blocksize * data->blocks;
debug("trans data %d bytes\n", bytecnt);
writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
ret = mmc_trans_data_by_cpu(mmc, data);
if (ret) {
error = readl(&mmchost->reg->rint) & \
SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT;
error = TIMEOUT;
goto out;
}
}
error = mmc_rint_wait(mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, "cmd");
if (error)
goto out;
if (data) {
timeout_msecs = 120;
debug("cacl timeout %x msec\n", timeout_msecs);
error = mmc_rint_wait(mmc, timeout_msecs,
data->blocks > 1 ?
SUNXI_MMC_RINT_AUTO_COMMAND_DONE :
SUNXI_MMC_RINT_DATA_OVER,
"data");
if (error)
goto out;
}
if (cmd->resp_type & MMC_RSP_BUSY) {
timeout_msecs = 2000;
do {
status = readl(&mmchost->reg->status);
if (!timeout_msecs--) {
debug("busy timeout\n");
error = TIMEOUT;
goto out;
}
udelay(1000);
} while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY);
}
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = readl(&mmchost->reg->resp3);
cmd->response[1] = readl(&mmchost->reg->resp2);
cmd->response[2] = readl(&mmchost->reg->resp1);
cmd->response[3] = readl(&mmchost->reg->resp0);
debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
cmd->response[3], cmd->response[2],
cmd->response[1], cmd->response[0]);
} else {
cmd->response[0] = readl(&mmchost->reg->resp0);
debug("mmc resp 0x%08x\n", cmd->response[0]);
}
out:
if (error < 0) {
writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl);
mmc_update_clk(mmc);
}
writel(0xffffffff, &mmchost->reg->rint);
writel(readl(&mmchost->reg->gctrl) | SUNXI_MMC_GCTRL_FIFO_RESET,
&mmchost->reg->gctrl);
return error;
}
static int sunxi_mmc_getcd(struct mmc *mmc)
{
struct sunxi_mmc_host *mmchost = mmc->priv;
int cd_pin;
cd_pin = sunxi_mmc_getcd_gpio(mmchost->mmc_no);
if (cd_pin < 0)
return 1;
return !gpio_get_value(cd_pin);
}
int sunxi_mmc_has_egon_boot_signature(struct mmc *mmc)
{
char *buf = malloc(512);
int valid_signature = 0;
if (buf == NULL)
panic("Failed to allocate memory\n");
if (mmc_getcd(mmc) && mmc_init(mmc) == 0 &&
mmc->block_dev.block_read(&mmc->block_dev, 16, 1, buf) == 1 &&
strncmp(&buf[4], "eGON.BT0", 8) == 0)
valid_signature = 1;
free(buf);
return valid_signature;
}
static const struct mmc_ops sunxi_mmc_ops = {
.send_cmd = sunxi_mmc_send_cmd,
.set_ios = sunxi_mmc_set_ios,
.init = sunxi_mmc_core_init,
.getcd = sunxi_mmc_getcd,
};
struct mmc *sunxi_mmc_init(int sdc_no)
{
struct mmc_config *cfg = &mmc_host[sdc_no].cfg;
memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
cfg->name = "SUNXI SD/MMC";
cfg->ops = &sunxi_mmc_ops;
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
cfg->host_caps = MMC_MODE_4BIT;
#ifdef CONFIG_MACH_SUN50I
if (sdc_no == 2)
cfg->host_caps = MMC_MODE_8BIT;
#endif
cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
cfg->f_min = 400000;
cfg->f_max = 52000000;
if (mmc_resource_init(sdc_no) != 0)
return NULL;
mmc_clk_io_on(sdc_no);
return mmc_create(cfg, &mmc_host[sdc_no]);
}

View File

@@ -0,0 +1,740 @@
/*
* (C) Copyright 2009 SAMSUNG Electronics
* Minkyu Kang <mk7.kang@samsung.com>
* Jaehoon Chung <jh80.chung@samsung.com>
* Portions Copyright 2011-2015 NVIDIA Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <bouncebuf.h>
#include <common.h>
#include <asm/gpio.h>
#include <asm/io.h>
#ifndef CONFIG_TEGRA186
#include <asm/arch/clock.h>
#include <asm/arch-tegra/clk_rst.h>
#endif
#include <asm/arch-tegra/mmc.h>
#include <asm/arch-tegra/tegra_mmc.h>
#include <mmc.h>
DECLARE_GLOBAL_DATA_PTR;
struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE];
#if !CONFIG_IS_ENABLED(OF_CONTROL)
#error "Please enable device tree support to use this driver"
#endif
static void mmc_set_power(struct mmc_host *host, unsigned short power)
{
u8 pwr = 0;
debug("%s: power = %x\n", __func__, power);
if (power != (unsigned short)-1) {
switch (1 << power) {
case MMC_VDD_165_195:
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8;
break;
case MMC_VDD_29_30:
case MMC_VDD_30_31:
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0;
break;
case MMC_VDD_32_33:
case MMC_VDD_33_34:
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3;
break;
}
}
debug("%s: pwr = %X\n", __func__, pwr);
/* Set the bus voltage first (if any) */
writeb(pwr, &host->reg->pwrcon);
if (pwr == 0)
return;
/* Now enable bus power */
pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER;
writeb(pwr, &host->reg->pwrcon);
}
static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data,
struct bounce_buffer *bbstate)
{
unsigned char ctrl;
debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n",
bbstate->bounce_buffer, bbstate->user_buffer, data->blocks,
data->blocksize);
writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad);
/*
* DMASEL[4:3]
* 00 = Selects SDMA
* 01 = Reserved
* 10 = Selects 32-bit Address ADMA2
* 11 = Selects 64-bit Address ADMA2
*/
ctrl = readb(&host->reg->hostctl);
ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK;
ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA;
writeb(ctrl, &host->reg->hostctl);
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize);
writew(data->blocks, &host->reg->blkcnt);
}
static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)
{
unsigned short mode;
debug(" mmc_set_transfer_mode called\n");
/*
* TRNMOD
* MUL1SIN0[5] : Multi/Single Block Select
* RD1WT0[4] : Data Transfer Direction Select
* 1 = read
* 0 = write
* ENACMD12[2] : Auto CMD12 Enable
* ENBLKCNT[1] : Block Count Enable
* ENDMA[0] : DMA Enable
*/
mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE |
TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE);
if (data->blocks > 1)
mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT;
if (data->flags & MMC_DATA_READ)
mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ;
writew(mode, &host->reg->trnmod);
}
static int mmc_wait_inhibit(struct mmc_host *host,
struct mmc_cmd *cmd,
struct mmc_data *data,
unsigned int timeout)
{
/*
* PRNSTS
* CMDINHDAT[1] : Command Inhibit (DAT)
* CMDINHCMD[0] : Command Inhibit (CMD)
*/
unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD;
/*
* We shouldn't wait for data inhibit for stop commands, even
* though they might use busy signaling
*/
if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY))
mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT;
while (readl(&host->reg->prnsts) & mask) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return -1;
}
timeout--;
udelay(1000);
}
return 0;
}
static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data, struct bounce_buffer *bbstate)
{
struct mmc_host *host = mmc->priv;
int flags, i;
int result;
unsigned int mask = 0;
unsigned int retry = 0x100000;
debug(" mmc_send_cmd called\n");
result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */);
if (result < 0)
return result;
if (data)
mmc_prepare_data(host, data, bbstate);
debug("cmd->arg: %08x\n", cmd->cmdarg);
writel(cmd->cmdarg, &host->reg->argument);
if (data)
mmc_set_transfer_mode(host, data);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
/*
* CMDREG
* CMDIDX[13:8] : Command index
* DATAPRNT[5] : Data Present Select
* ENCMDIDX[4] : Command Index Check Enable
* ENCMDCRC[3] : Command CRC Check Enable
* RSPTYP[1:0]
* 00 = No Response
* 01 = Length 136
* 10 = Length 48
* 11 = Length 48 Check busy after response
*/
if (!(cmd->resp_type & MMC_RSP_PRESENT))
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE;
else if (cmd->resp_type & MMC_RSP_136)
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136;
else if (cmd->resp_type & MMC_RSP_BUSY)
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY;
else
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48;
if (cmd->resp_type & MMC_RSP_CRC)
flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK;
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK;
if (data)
flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER;
debug("cmd: %d\n", cmd->cmdidx);
writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg);
for (i = 0; i < retry; i++) {
mask = readl(&host->reg->norintsts);
/* Command Complete */
if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) {
if (!data)
writel(mask, &host->reg->norintsts);
break;
}
}
if (i == retry) {
printf("%s: waiting for status update\n", __func__);
writel(mask, &host->reg->norintsts);
return TIMEOUT;
}
if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) {
/* Timeout Error */
debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
writel(mask, &host->reg->norintsts);
return TIMEOUT;
} else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) {
/* Error Interrupt */
debug("error: %08x cmd %d\n", mask, cmd->cmdidx);
writel(mask, &host->reg->norintsts);
return -1;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0; i < 4; i++) {
unsigned long offset =
(unsigned long)(&host->reg->rspreg3 - i);
cmd->response[i] = readl(offset) << 8;
if (i != 3) {
cmd->response[i] |=
readb(offset - 1);
}
debug("cmd->resp[%d]: %08x\n",
i, cmd->response[i]);
}
} else if (cmd->resp_type & MMC_RSP_BUSY) {
for (i = 0; i < retry; i++) {
/* PRNTDATA[23:20] : DAT[3:0] Line Signal */
if (readl(&host->reg->prnsts)
& (1 << 20)) /* DAT[0] */
break;
}
if (i == retry) {
printf("%s: card is still busy\n", __func__);
writel(mask, &host->reg->norintsts);
return TIMEOUT;
}
cmd->response[0] = readl(&host->reg->rspreg0);
debug("cmd->resp[0]: %08x\n", cmd->response[0]);
} else {
cmd->response[0] = readl(&host->reg->rspreg0);
debug("cmd->resp[0]: %08x\n", cmd->response[0]);
}
}
if (data) {
unsigned long start = get_timer(0);
while (1) {
mask = readl(&host->reg->norintsts);
if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) {
/* Error Interrupt */
writel(mask, &host->reg->norintsts);
printf("%s: error during transfer: 0x%08x\n",
__func__, mask);
return -1;
} else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) {
/*
* DMA Interrupt, restart the transfer where
* it was interrupted.
*/
unsigned int address = readl(&host->reg->sysad);
debug("DMA end\n");
writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT,
&host->reg->norintsts);
writel(address, &host->reg->sysad);
} else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) {
/* Transfer Complete */
debug("r/w is done\n");
break;
} else if (get_timer(start) > 8000UL) {
writel(mask, &host->reg->norintsts);
printf("%s: MMC Timeout\n"
" Interrupt status 0x%08x\n"
" Interrupt status enable 0x%08x\n"
" Interrupt signal enable 0x%08x\n"
" Present status 0x%08x\n",
__func__, mask,
readl(&host->reg->norintstsen),
readl(&host->reg->norintsigen),
readl(&host->reg->prnsts));
return -1;
}
}
writel(mask, &host->reg->norintsts);
}
udelay(1000);
return 0;
}
static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
void *buf;
unsigned int bbflags;
size_t len;
struct bounce_buffer bbstate;
int ret;
if (data) {
if (data->flags & MMC_DATA_READ) {
buf = data->dest;
bbflags = GEN_BB_WRITE;
} else {
buf = (void *)data->src;
bbflags = GEN_BB_READ;
}
len = data->blocks * data->blocksize;
bounce_buffer_start(&bbstate, buf, len, bbflags);
}
ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate);
if (data)
bounce_buffer_stop(&bbstate);
return ret;
}
static void mmc_change_clock(struct mmc_host *host, uint clock)
{
int div;
unsigned short clk;
unsigned long timeout;
debug(" mmc_change_clock called\n");
/*
* Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0
*/
if (clock == 0)
goto out;
#ifndef CONFIG_TEGRA186
clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock,
&div);
#else
div = (20000000 + clock - 1) / clock;
#endif
debug("div = %d\n", div);
writew(0, &host->reg->clkcon);
/*
* CLKCON
* SELFREQ[15:8] : base clock divided by value
* ENSDCLK[2] : SD Clock Enable
* STBLINTCLK[1] : Internal Clock Stable
* ENINTCLK[0] : Internal Clock Enable
*/
div >>= 1;
clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) |
TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE);
writew(clk, &host->reg->clkcon);
/* Wait max 10 ms */
timeout = 10;
while (!(readw(&host->reg->clkcon) &
TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return;
}
timeout--;
udelay(1000);
}
clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE;
writew(clk, &host->reg->clkcon);
debug("mmc_change_clock: clkcon = %08X\n", clk);
out:
host->clock = clock;
}
static void tegra_mmc_set_ios(struct mmc *mmc)
{
struct mmc_host *host = mmc->priv;
unsigned char ctrl;
debug(" mmc_set_ios called\n");
debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
/* Change clock first */
mmc_change_clock(host, mmc->clock);
ctrl = readb(&host->reg->hostctl);
/*
* WIDE8[5]
* 0 = Depend on WIDE4
* 1 = 8-bit mode
* WIDE4[1]
* 1 = 4-bit mode
* 0 = 1-bit mode
*/
if (mmc->bus_width == 8)
ctrl |= (1 << 5);
else if (mmc->bus_width == 4)
ctrl |= (1 << 1);
else
ctrl &= ~(1 << 1);
writeb(ctrl, &host->reg->hostctl);
debug("mmc_set_ios: hostctl = %08X\n", ctrl);
}
static void mmc_reset(struct mmc_host *host, struct mmc *mmc)
{
unsigned int timeout;
debug(" mmc_reset called\n");
/*
* RSTALL[0] : Software reset for all
* 1 = reset
* 0 = work
*/
writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst);
host->clock = 0;
/* Wait max 100 ms */
timeout = 100;
/* hw clears the bit when it's done */
while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return;
}
timeout--;
udelay(1000);
}
/* Set SD bus voltage & enable bus power */
mmc_set_power(host, fls(mmc->cfg->voltages) - 1);
debug("%s: power control = %02X, host control = %02X\n", __func__,
readb(&host->reg->pwrcon), readb(&host->reg->hostctl));
/* Make sure SDIO pads are set up */
pad_init_mmc(host);
}
static int tegra_mmc_core_init(struct mmc *mmc)
{
struct mmc_host *host = mmc->priv;
unsigned int mask;
debug(" mmc_core_init called\n");
mmc_reset(host, mmc);
host->version = readw(&host->reg->hcver);
debug("host version = %x\n", host->version);
/* mask all */
writel(0xffffffff, &host->reg->norintstsen);
writel(0xffffffff, &host->reg->norintsigen);
writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */
/*
* NORMAL Interrupt Status Enable Register init
* [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable
* [4] ENSTABUFWTRDY : Buffer write Ready Status Enable
* [3] ENSTADMAINT : DMA boundary interrupt
* [1] ENSTASTANSCMPLT : Transfre Complete Status Enable
* [0] ENSTACMDCMPLT : Command Complete Status Enable
*/
mask = readl(&host->reg->norintstsen);
mask &= ~(0xffff);
mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE |
TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE |
TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT |
TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY |
TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
writel(mask, &host->reg->norintstsen);
/*
* NORMAL Interrupt Signal Enable Register init
* [1] ENSTACMDCMPLT : Transfer Complete Signal Enable
*/
mask = readl(&host->reg->norintsigen);
mask &= ~(0xffff);
mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE;
writel(mask, &host->reg->norintsigen);
return 0;
}
static int tegra_mmc_getcd(struct mmc *mmc)
{
struct mmc_host *host = mmc->priv;
debug("tegra_mmc_getcd called\n");
if (dm_gpio_is_valid(&host->cd_gpio))
return dm_gpio_get_value(&host->cd_gpio);
return 1;
}
static const struct mmc_ops tegra_mmc_ops = {
.send_cmd = tegra_mmc_send_cmd,
.set_ios = tegra_mmc_set_ios,
.init = tegra_mmc_core_init,
.getcd = tegra_mmc_getcd,
};
static int do_mmc_init(int dev_index, bool removable)
{
struct mmc_host *host;
struct mmc *mmc;
/* DT should have been read & host config filled in */
host = &mmc_host[dev_index];
if (!host->enabled)
return -1;
debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n",
dev_index, host->width, gpio_get_number(&host->pwr_gpio),
gpio_get_number(&host->cd_gpio));
host->clock = 0;
#ifndef CONFIG_TEGRA186
clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000);
#endif
if (dm_gpio_is_valid(&host->pwr_gpio))
dm_gpio_set_value(&host->pwr_gpio, 1);
memset(&host->cfg, 0, sizeof(host->cfg));
host->cfg.name = "Tegra SD/MMC";
host->cfg.ops = &tegra_mmc_ops;
host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
host->cfg.host_caps = 0;
if (host->width == 8)
host->cfg.host_caps |= MMC_MODE_8BIT;
if (host->width >= 4)
host->cfg.host_caps |= MMC_MODE_4BIT;
host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
/*
* min freq is for card identification, and is the highest
* low-speed SDIO card frequency (actually 400KHz)
* max freq is highest HS eMMC clock as per the SD/MMC spec
* (actually 52MHz)
*/
host->cfg.f_min = 375000;
#ifndef CONFIG_TEGRA186
host->cfg.f_max = 48000000;
#else
host->cfg.f_max = 375000;
#endif
host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
mmc = mmc_create(&host->cfg, host);
mmc->block_dev.removable = removable;
if (mmc == NULL)
return -1;
return 0;
}
/**
* Get the host address and peripheral ID for a node.
*
* @param blob fdt blob
* @param node Device index (0-3)
* @param host Structure to fill in (reg, width, mmc_id)
*/
static int mmc_get_config(const void *blob, int node, struct mmc_host *host,
bool *removablep)
{
debug("%s: node = %d\n", __func__, node);
host->enabled = fdtdec_get_is_enabled(blob, node);
host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg");
if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) {
debug("%s: no sdmmc base reg info found\n", __func__);
return -FDT_ERR_NOTFOUND;
}
#ifndef CONFIG_TEGRA186
host->mmc_id = clock_decode_periph_id(blob, node);
if (host->mmc_id == PERIPH_ID_NONE) {
debug("%s: could not decode periph id\n", __func__);
return -FDT_ERR_NOTFOUND;
}
#endif
/*
* NOTE: mmc->bus_width is determined by mmc.c dynamically.
* TBD: Override it with this value?
*/
host->width = fdtdec_get_int(blob, node, "bus-width", 0);
if (!host->width)
debug("%s: no sdmmc width found\n", __func__);
/* These GPIOs are optional */
gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio,
GPIOD_IS_IN);
gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio,
GPIOD_IS_IN);
gpio_request_by_name_nodev(blob, node, "power-gpios", 0,
&host->pwr_gpio, GPIOD_IS_OUT);
*removablep = !fdtdec_get_bool(blob, node, "non-removable");
debug("%s: found controller at %p, width = %d, periph_id = %d\n",
__func__, host->reg, host->width,
#ifndef CONFIG_TEGRA186
host->mmc_id
#else
-1
#endif
);
return 0;
}
/*
* Process a list of nodes, adding them to our list of SDMMC ports.
*
* @param blob fdt blob
* @param node_list list of nodes to process (any <=0 are ignored)
* @param count number of nodes to process
* @return 0 if ok, -1 on error
*/
static int process_nodes(const void *blob, int node_list[], int count)
{
struct mmc_host *host;
bool removable;
int i, node;
debug("%s: count = %d\n", __func__, count);
/* build mmc_host[] for each controller */
for (i = 0; i < count; i++) {
node = node_list[i];
if (node <= 0)
continue;
host = &mmc_host[i];
host->id = i;
if (mmc_get_config(blob, node, host, &removable)) {
printf("%s: failed to decode dev %d\n", __func__, i);
return -1;
}
do_mmc_init(i, removable);
}
return 0;
}
void tegra_mmc_init(void)
{
int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count;
const void *blob = gd->fdt_blob;
debug("%s entry\n", __func__);
/* See if any Tegra186 MMC controllers are present */
count = fdtdec_find_aliases_for_id(blob, "sdhci",
COMPAT_NVIDIA_TEGRA186_SDMMC, node_list,
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of Tegra186 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
printf("%s: Error processing T186 mmc node(s)!\n", __func__);
return;
}
/* See if any Tegra210 MMC controllers are present */
count = fdtdec_find_aliases_for_id(blob, "sdhci",
COMPAT_NVIDIA_TEGRA210_SDMMC, node_list,
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
printf("%s: Error processing T210 mmc node(s)!\n", __func__);
return;
}
/* See if any Tegra124 MMC controllers are present */
count = fdtdec_find_aliases_for_id(blob, "sdhci",
COMPAT_NVIDIA_TEGRA124_SDMMC, node_list,
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
printf("%s: Error processing T124 mmc node(s)!\n", __func__);
return;
}
/* See if any Tegra30 MMC controllers are present */
count = fdtdec_find_aliases_for_id(blob, "sdhci",
COMPAT_NVIDIA_TEGRA30_SDMMC, node_list,
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of T30 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
printf("%s: Error processing T30 mmc node(s)!\n", __func__);
return;
}
/* Now look for any Tegra20 MMC controllers */
count = fdtdec_find_aliases_for_id(blob, "sdhci",
COMPAT_NVIDIA_TEGRA20_SDMMC, node_list,
CONFIG_SYS_MMC_MAX_DEVICE);
debug("%s: count of T20 sdhci nodes is %d\n", __func__, count);
if (process_nodes(blob, node_list, count)) {
printf("%s: Error processing T20 mmc node(s)!\n", __func__);
return;
}
}

View File

@@ -0,0 +1,756 @@
/*
* 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),
};

View File

@@ -0,0 +1,65 @@
/*
* (C) Copyright 2013 - 2015 Xilinx, Inc.
*
* Xilinx Zynq SD Host Controller Interface
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <malloc.h>
#include <sdhci.h>
#ifndef CONFIG_ZYNQ_SDHCI_MIN_FREQ
# define CONFIG_ZYNQ_SDHCI_MIN_FREQ 0
#endif
static int arasan_sdhci_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct sdhci_host *host = dev_get_priv(dev);
host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD |
SDHCI_QUIRK_BROKEN_R1B;
#ifdef CONFIG_ZYNQ_HISPD_BROKEN
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
#endif
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
add_sdhci(host, CONFIG_ZYNQ_SDHCI_MAX_FREQ,
CONFIG_ZYNQ_SDHCI_MIN_FREQ);
upriv->mmc = host->mmc;
host->mmc->dev = dev;
return 0;
}
static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev)
{
struct sdhci_host *host = dev_get_priv(dev);
host->name = dev->name;
host->ioaddr = (void *)dev_get_addr(dev);
return 0;
}
static const struct udevice_id arasan_sdhci_ids[] = {
{ .compatible = "arasan,sdhci-8.9a" },
{ }
};
U_BOOT_DRIVER(arasan_sdhci_drv) = {
.name = "arasan_sdhci",
.id = UCLASS_MMC,
.of_match = arasan_sdhci_ids,
.ofdata_to_platdata = arasan_sdhci_ofdata_to_platdata,
.probe = arasan_sdhci_probe,
.priv_auto_alloc_size = sizeof(struct sdhci_host),
};