uboot-mt623x/drivers/mmc/mtk_msdc_mmc.c

834 lines
19 KiB
C

/*
* (C) 2010 by Tieto <www.tieto.com>
* Krzysztof Antonowicz <krzysztof.antonowicz@tieto.com>
*
* All Rights Reserved
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <malloc.h>
#include <common.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/arch/mtk_msdc.h>
#include <asm/arch/system.h>
#include <asm/arch/gpio.h>
#define MIN_SCLK_VALUE 0
#define MAX_SCLK_VALUE 255
/* According to documentation MCU is 104 MHz or 52 MHz. */
#define MCU_CLK_FREQ_HZ 104000000
#define MAX_SLAVE_FREQ_HZ 26000000
#define MAX_BLOCK_SIZE 2048
#define COMMAND_STATUS_READ_DELAY_US 10
#define POLL_TRANSFER_COND_TIMEOUT_US 500
#define POLL_CMD_STATUS_TIMEOUT_US 100
#define DATA_READ_TIMEOUT_US 500
#define DEBUG_LINE() do { \
debug("mtk_msdc_mmc[%s]: called.\n", __func__); \
} while (0)
#define DEBUG_MSG(fmt, arg...) do { \
debug("mtk_msdc_mmc[%s]: " fmt "\n", __func__, ##arg); \
} while (0)
#define ERROR(fmt, arg...) do { \
printf("mtk_msdc_mmc[%s] ERROR: " fmt "\n", __func__, ##arg); \
} while (0)
struct mmc_host {
struct mmc *mmc;
u32 clock;
u32 msdc_sdc_cmd;
struct mmc_cmd *cmd;
struct mmc_data *data;
};
static struct mmc_host mmc_host;
#ifdef MTK_MMC_CARD_DETECTION_SUPPORTED
/* Card detection mechanism is not supported by Sci-phone G2 hardware:
* 1. missing RCDEN resistor on PCB,
* 2. grounded MCINS(GPIO75) pin.
*
* Checks if memory card is present according to the algorithm:
* 1. Pull up CD/DAT3 (INS) pin.
* 2. Enable card detection and input pin at the same time.
* 3. Turn on power for memory card.
* 4. Detect insertion of memory card.
*/
static int check_card_presence(struct mmc_host *mmc_host)
{
int retval = -ENODEV;
DEBUG_LINE();
writel(readl(MTK_MSDC_CFG) | MTK_MSDC_CFG_PRCFG0_PU_EN_PD_DS,
MTK_MSDC_CFG);
writel(readl(MTK_MSDC_PS) | MTK_MSDC_PS_CDEN | MTK_MSDC_PS_PIEN0,
MTK_MSDC_PS);
/* Turn on the power for memory card. */
writel(readl(MTK_MSDC_CFG) | MTK_MSDC_CFG_VDDP, MTK_MSDC_CFG);
/* Check if a card is inserted. */
if (readl(MTK_MSDC_PS) & MTK_MSDC_PS_PINCHG)
retval = 0;
return retval;
}
#endif /* MTK_MMC_CARD_DETECTION_SUPPORTED */
/*
* Power up MSDC controller.
*/
static void mtk_power_up(void)
{
DEBUG_LINE();
/* Disable power down control for MSDC module. */
writew(PDN_CON1_MSDC, MTK_CONFG_PDN_CLR1);
}
/*
* Performs software and synchronous reset of MSDC controller.
*/
static void mtk_software_reset(void)
{
DEBUG_LINE();
writel(MTK_MSDC_CFG_RST, MTK_MSDC_CFG);
}
/*
* Configures GPIOs for MSDC controller.
*/
static void mtk_configure_gpios(void)
{
DEBUG_LINE();
u16 mode_9_clear_mask =
MTK_GPIO_MODE9_GPIO67_ALT3 |
MTK_GPIO_MODE9_GPIO68_ALT3 |
MTK_GPIO_MODE9_GPIO69_ALT3 |
MTK_GPIO_MODE9_GPIO70_ALT3 |
MTK_GPIO_MODE9_GPIO71_ALT3;
u16 mode_9_set_mask =
MTK_GPIO_MODE9_GPIO67_ALT1 |
MTK_GPIO_MODE9_GPIO68_ALT1 |
MTK_GPIO_MODE9_GPIO69_ALT1 |
MTK_GPIO_MODE9_GPIO70_ALT1 |
MTK_GPIO_MODE9_GPIO71_ALT1;
u16 mode_a_clear_mask =
MTK_GPIO_MODEA_GPIO72_ALT3 |
/* GPIO73 skipped (used by USB as regular GPIO) */
MTK_GPIO_MODEA_GPIO74_ALT3 |
MTK_GPIO_MODEA_GPIO75_ALT3;
u16 mode_a_set_mask =
MTK_GPIO_MODEA_GPIO72_ALT1 |
/* GPIO73 skipped (used by USB as regular GPIO) */
MTK_GPIO_MODEA_GPIO74_ALT1 |
MTK_GPIO_MODEA_GPIO75_ALT1;
writew((readw(MTK_GPIO_MODE9) & (~mode_9_clear_mask)) |
mode_9_set_mask, MTK_GPIO_MODE9);
writew((readw(MTK_GPIO_MODEA) & (~mode_a_clear_mask)) |
mode_a_set_mask, MTK_GPIO_MODEA);
}
/*
* Initialization of MMC/SD controller.
*/
static int mtk_init(struct mmc *mmc)
{
DEBUG_LINE();
(void)mmc;
mtk_configure_gpios();
mtk_power_up();
mtk_software_reset();
/* Configure the controller as the host of SD/MMC cards. */
writel(readl(MTK_MSDC_CFG) | MTK_MSDC_CFG_MSDC, MTK_MSDC_CFG);
/* Use MCU clock as source clock of memory card. */
writel(readl(MTK_MSDC_CFG) & (~MTK_MSDC_CFG_CLKSRC), MTK_MSDC_CFG);
/* Clear FIFO. */
writew(readl(MTK_MSDC_STA) | MTK_MSDC_STA_FIFOCLR, MTK_MSDC_STA);
return 0;
}
/*
* Calculates slave frequency for the given sclk value.
*/
static u32 mtk_calculate_f_slave(const u8 sclk)
{
u32 f_slave = 0;
if (!sclk)
f_slave = MCU_CLK_FREQ_HZ / 2;
else
f_slave = MCU_CLK_FREQ_HZ / (4 * sclk);
return f_slave;
}
/*
* Calculates max. slave frequency.
*/
static u32 mtk_calculate_f_slave_max(void)
{
u32 f_slave_max = 0;
f_slave_max = mtk_calculate_f_slave(MIN_SCLK_VALUE);
return f_slave_max;
}
/*
* Calculates min. slave frequency.
*/
static u32 mtk_calculate_f_slave_min(void)
{
u32 f_slave_min = 0;
f_slave_min = mtk_calculate_f_slave(MAX_SCLK_VALUE);
return f_slave_min;
}
/*
* Configures clock for MSDC.
*/
static void mtk_set_clk_rate(struct mmc_host *mmc_host, const u32 f_slave)
{
u32 f_host;
u8 sclk;
u32 calc_f_slave;
u32 min_f_slave;
u32 max_f_slave;
DEBUG_LINE();
DEBUG_MSG("Requested new slave frequency: %dHz.", f_slave);
f_host = MCU_CLK_FREQ_HZ;
max_f_slave = mtk_calculate_f_slave_max();
min_f_slave = mtk_calculate_f_slave_min();
if (max_f_slave < f_slave) {
DEBUG_MSG("Requested slave frequency is too high: %dHz! "
"The max. available is: %dHz", f_slave, max_f_slave);
sclk = MIN_SCLK_VALUE;
} else if (min_f_slave > f_slave) {
DEBUG_MSG("Requested slave frequency is too low: %dHz! "
"The min. available is: %dHz", f_slave, min_f_slave);
sclk = MAX_SCLK_VALUE;
} else if (((f_host / 2) == f_slave) || (f_host < (f_slave * 4))) {
sclk = MIN_SCLK_VALUE;
} else
sclk = f_host / (4 * f_slave);
calc_f_slave = mtk_calculate_f_slave(sclk);
/* If the calculated frequency is still too high,
it should be decreased. */
while (calc_f_slave > f_slave) {
sclk++;
DEBUG_MSG("Requested frequency out of range, it will "
"be decreased!");
calc_f_slave = mtk_calculate_f_slave(sclk);
}
DEBUG_MSG("f_host: %dHz, calc_f_slave: %dHz(f_slave: %dHz), "
"SCLK: %d.", f_host, calc_f_slave, f_slave, sclk);
writel((readl(MTK_MSDC_CFG) & (~MTK_MSDC_CFG_SCLKF_MASK)) | sclk << 8,
MTK_MSDC_CFG);
}
/*
* Perform configuration related settings for MMC host.
*/
static void mtk_set_ios(struct mmc *mmc)
{
struct mmc_host *mmc_host = mmc->priv;
DEBUG_LINE();
/* 4 -bit data line enable/disable. */
if (4 == mmc->bus_width)
writel(readl(MTK_MSDC_SDC_CFG) | MTK_MSDC_SDC_CFG_MDLEN,
MTK_MSDC_SDC_CFG);
else
writel(readl(MTK_MSDC_SDC_CFG) & (~MTK_MSDC_SDC_CFG_MDLEN),
MTK_MSDC_SDC_CFG);
if (mmc->clock && (mmc_host->clock != mmc->clock)) {
/* Before changing the frequency of serial clock on the bus,
it is necessary to disable serial interface
of the controller. */
writel(readl(MTK_MSDC_SDC_CFG) & (~MTK_MSDC_SDC_CFG_SIEN),
MTK_MSDC_SDC_CFG);
mtk_set_clk_rate(mmc_host, mmc->clock);
writel(readl(MTK_MSDC_SDC_CFG) | MTK_MSDC_SDC_CFG_SIEN,
MTK_MSDC_SDC_CFG);
DEBUG_MSG("Serial clock rate changed. The clock is enabled.");
mmc_host->clock = mmc->clock;
}
}
/*
* Configures controller for the given data type.
*/
static int mtk_setup_data(struct mmc_host *mmc_host, u32 *msdc_sdc_cmd)
{
u32 no_of_blocks = mmc_host->data->blocks;
u32 block_size = mmc_host->data->blocksize;
DEBUG_LINE();
/* Read/write command selection. */
if (mmc_host->data->flags & MMC_DATA_WRITE)
*msdc_sdc_cmd |= MTK_MSDC_SDC_CMD_RW;
else
*msdc_sdc_cmd &= ~MTK_MSDC_SDC_CMD_RW;
if (MAX_BLOCK_SIZE < block_size)
DEBUG_MSG("Block size excceeds max. block length!");
else {
/* Write length of block. */
writel((readl(MTK_MSDC_SDC_CFG) &
(~MTK_MSDC_SDC_CFG_BLKLEN_MASK)) | block_size,
MTK_MSDC_SDC_CFG);
DEBUG_MSG("Block size is: %d.", block_size);
}
DEBUG_MSG("Number of blocks: %d", no_of_blocks);
if (1 == no_of_blocks) {
*msdc_sdc_cmd |= (MTK_MSDC_SDC_CMD_DTYPE_SINGLE_BLOCK <<
MTK_MSDC_SDC_CMD_DTYPE);
} else if (1 < no_of_blocks) {
*msdc_sdc_cmd |= (MTK_MSDC_SDC_CMD_DTYPE_MULTI_BLOCK <<
MTK_MSDC_SDC_CMD_DTYPE);
} else {
DEBUG_MSG("Data supplied but block count is 0!");
return -EINVAL;
}
/* Clear FIFO. */
writew(readl(MTK_MSDC_STA) | MTK_MSDC_STA_FIFOCLR, MTK_MSDC_STA);
return 0;
}
/*
* Configures controller for the given command type.
*/
static int mtk_setup_cmd(struct mmc_host *mmc_host,
struct mmc_cmd *cmd, u32 *msdc_sdc_cmd)
{
u32 response_type;
DEBUG_LINE();
DEBUG_MSG("CMD ID: %d, CMD ARG: 0x%.8x, CMD RESP TYPE: %d",
cmd->cmdidx, cmd->cmdarg, cmd->resp_type);
/* Choose response type. */
switch (cmd->resp_type) {
case MMC_RSP_R1:
/* R1 response token is 48-bit. */
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R1;
break;
case MMC_RSP_R1b:
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R1B;
break;
case MMC_RSP_R2:
/* R2 response token is 136-bit. */
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R2;
break;
case MMC_RSP_R3:
/* R3 response token is 48-bit, no CRC */
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R3;
break;
#if 0
case MMC_RSP_R4:
/* R4 response token is 48-bit. (Only for MMC) */
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R4;
break;
case MMC_RSP_R5:
/* R5 response token is 48-bit. (Only for MMC) */
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R5;
break;
case MMC_RSP_R6:
/* R6 response token is 48-bit.*/
response_type = MTK_MSDC_SDC_CMD_RSPTYP_R6;
break;
#endif
case MMC_RSP_NONE:
response_type = MTK_MSDC_SDC_CMD_RSPTYP_NONE;
break;
default:
ERROR("Unexpected response type!");
return -EINVAL;
}
*msdc_sdc_cmd |= response_type << MTK_MSDC_SDC_CMD_RSPTYP;
/* Store the command argument first! */
writel(cmd->cmdarg, MTK_MSDC_SDC_ARG);
/* ..then the command id. */
*msdc_sdc_cmd |= cmd->cmdidx;
if (MMC_CMD_STOP_TRANSMISSION == cmd->cmdidx) {
*msdc_sdc_cmd |= MTK_MSDC_SDC_CMD_STOP;
writel(readl(MTK_MSDC_SDC_CFG) | 16 << MTK_MSDC_SDC_CFG_BSYDLY,
MTK_MSDC_SDC_CFG);
} else
*msdc_sdc_cmd &= ~MTK_MSDC_SDC_CMD_STOP;
return 0;
}
/*
* Read SD/MMC Memory Card bus response.
*/
static void mtk_read_response(struct mmc_host *mmc_host)
{
u32 resp0;
u32 resp1;
u32 resp2;
u32 resp3;
DEBUG_LINE();
if (!mmc_host->cmd)
return;
/* Check if a response is expected and read it.
For response of type R2 only bit 127 to 0 of response token is stored
in the register field SDC_RESP0, SDC_RESP1, SDC_RESP2 and SDC_RESP3.
For response of other types, only bit 39 to 8 of response token
is stored in the register field SDC_RESP0. */
if (mmc_host->cmd->resp_type & MMC_RSP_PRESENT) {
resp0 = readl(MTK_MSDC_SDC_RESP0);
if (mmc_host->cmd->resp_type & MMC_RSP_136) {
resp1 = readl(MTK_MSDC_SDC_RESP1);
resp2 = readl(MTK_MSDC_SDC_RESP2);
resp3 = readl(MTK_MSDC_SDC_RESP3);
mmc_host->cmd->response[0] = resp3;
mmc_host->cmd->response[1] = resp2;
mmc_host->cmd->response[2] = resp1;
mmc_host->cmd->response[3] = resp0;
DEBUG_MSG("Response[0]: 0x%.8x",
mmc_host->cmd->response[0]);
DEBUG_MSG("Response[1]: 0x%.8x",
mmc_host->cmd->response[1]);
DEBUG_MSG("Response[2]: 0x%.8x",
mmc_host->cmd->response[2]);
DEBUG_MSG("Response[3]: 0x%.8x",
mmc_host->cmd->response[3]);
} else {
mmc_host->cmd->response[0] = resp0;
DEBUG_MSG("Response[0]: 0x%.8x",
mmc_host->cmd->response[0]);
}
}
}
/*
* Checks if reading FIFO is possible.
*/
static int mtk_poll_data_transfer_conditions(const u16 transfer_condition)
{
int retval = 0;
u16 msdc_sdc_datsta;
u32 timeout_counter = 0;
DEBUG_LINE();
do {
/* First of all check data status. */
msdc_sdc_datsta = readw(MTK_MSDC_SDC_DATSTA);
if (msdc_sdc_datsta & MTK_MSDC_SDC_DATSTA_BLKDONE)
DEBUG_MSG("A data block was successfully transferred.");
if (msdc_sdc_datsta & MTK_MSDC_SDC_DATSTA_DATTO) {
ERROR("MS/SD controller detected a timeout "
"condition while waiting for data token on "
"the DAT line.");
retval = -1;
}
if (msdc_sdc_datsta & MTK_MSDC_SDC_DATSTA_DATCRCERR) {
ERROR("MS/SD controller detected a CRC error!");
retval = -1;
}
/* Check FIFO. */
if ((!(readw(MTK_MSDC_STA) & transfer_condition)) || retval)
break;
else
DEBUG_MSG("FIFO is empty!");
udelay(1);
} while (++timeout_counter != POLL_TRANSFER_COND_TIMEOUT_US);
if (timeout_counter == POLL_TRANSFER_COND_TIMEOUT_US) {
ERROR("Timeout occurred during polling "
"data transfer conditions.");
retval = TIMEOUT;
}
return retval;
}
/*
* Reads data received from memory card.
*/
static int mtk_read_data(void *dest, u32 length)
{
int retval = 0;
u32 *destl = dest;
u32 timeout_counter = 0;
DEBUG_LINE();
/* Wait for data transfer request. */
while (!(readw(MTK_MSDC_STA) & MTK_MSDC_STA_DRQ)) {
udelay(1);
if (++timeout_counter == DATA_READ_TIMEOUT_US) {
ERROR("Data read timeout occured!");
break;
}
}
DEBUG_MSG("Block size is: %d", readl(MTK_MSDC_SDC_CFG) &
MTK_MSDC_SDC_CFG_BLKLEN_MASK);
while (length) {
DEBUG_MSG("Bytes left to read: %d.", length);
/* Read data if there is any in the FIFO. */
retval = mtk_poll_data_transfer_conditions(MTK_MSDC_STA_BE);
if (retval)
goto finish_read;
if (3 < length) {
*destl++ = readl(MTK_MSDC_DAT);
length -= 4;
DEBUG_MSG("DATA[R]: 0x%.8x", *(destl - 1));
} else {
u8 *dest8 = (u8 *)destl;
u32 msdc_dat = readl(MTK_MSDC_DAT);
memcpy(dest8, &msdc_dat, length);
length = 0;
DEBUG_MSG("DATA[R]: 0x%.8x", msdc_dat);
}
}
if (!readw(MTK_MSDC_STA) & MTK_MSDC_STA_BE)
ERROR("There is still some data left in FIFO!");
finish_read:
return retval;
}
/*
* Writes data to be sent to memory card.
*/
static int mtk_write_data(const void *src, int length)
{
int retval = 0;
const u32 *srcl = src;
u32 timeout_counter = 0;
DEBUG_LINE();
/* Wait for data transfer request. */
while (!(readw(MTK_MSDC_STA) & MTK_MSDC_STA_DRQ)) {
udelay(1);
if (++timeout_counter == DATA_READ_TIMEOUT_US) {
ERROR("Data write timeout occured!");
break;
}
}
while (length) {
DEBUG_MSG("Length of the data to write: %d.", length);
/* Write data if there is enough space in the FIFO. */
retval = mtk_poll_data_transfer_conditions(MTK_MSDC_STA_BF);
if (retval)
goto finish_write;
if (3 < length) {
writel(*srcl++, MTK_MSDC_DAT);
length -= 4;
DEBUG_MSG("DATA[W]: 0x%.8x", *(srcl - 1));
} else {
u8 *src8 = (u8 *)srcl;
u32 temp_val;
memcpy(&temp_val, src8, length);
writel(temp_val, MTK_MSDC_DAT);
length = 0;
DEBUG_MSG("DATA[W]: 0x%.8x", temp_val);
}
}
finish_write:
return retval;
}
/*
* Performs data transfer between host and memory card.
*/
static int mtk_transfer_data(struct mmc_host *mmc_host)
{
int retval = 0;
struct mmc_data *data = mmc_host->data;
u32 length = data->blocks * data->blocksize;
DEBUG_LINE();
if (data->flags & MMC_DATA_READ) {
DEBUG_MSG("Reading data...");
retval = mtk_read_data(data->dest, length);
} else {
DEBUG_MSG("Writing data...");
retval = mtk_write_data(data->src, length);
}
return retval;
}
/*
* Sends a command out on the MMC/SD bus.
*/
static int mtk_send_cmd(struct mmc *mmc,
struct mmc_cmd *cmd, struct mmc_data *data)
{
int retval;
u16 cmd_status;
u16 controller_status;
struct mmc_host *mmc_host = mmc->priv;
u32 msdc_sdc_cmd = mmc_host->msdc_sdc_cmd;
u32 timeout_counter;
DEBUG_LINE();
mmc_host->cmd = cmd;
mmc_host->data = data;
#ifdef MTK_MMC_CARD_DETECTION_SUPPORTED
if (!check_card_presence(mmc_host)) {
DEBUG_MSG("Memory card inserted!");
} else {
DEBUG_MSG("Memory card not inserted");
retval = -1;
goto finish;
}
#endif /* MTK_MMC_CARD_DETECTION_SUPPORTED */
/* Check data supplied. */
if (mmc_host->data) {
retval = mtk_setup_data(mmc_host, &msdc_sdc_cmd);
if (retval)
goto finish;
} else
msdc_sdc_cmd |= (MTK_MSDC_SDC_CMD_DTYPE_NO_DATA <<
MTK_MSDC_SDC_CMD_DTYPE);
/* Prepared command to send. */
retval = mtk_setup_cmd(mmc_host, cmd, &msdc_sdc_cmd);
if (retval)
goto finish;
/* Clear command status register. */
(void)readl(MTK_MSDC_SDC_CMDSTA);
/* Send the command... */
writel(msdc_sdc_cmd, MTK_MSDC_SDC_CMD);
timeout_counter = 0;
/* Wait until the command is sent. */
do {
controller_status = readl(MTK_MSDC_SDC_STA);
DEBUG_MSG("Controller status: 0x%.4x", controller_status);
udelay(1);
} while ((controller_status & MTK_MSDC_SDC_STA_CMDBUSY) &&
(++timeout_counter != POLL_CMD_STATUS_TIMEOUT_US));
if (POLL_CMD_STATUS_TIMEOUT_US == timeout_counter) {
ERROR("Waiting for controller status timeout occured "
"(controller status: %d)!", controller_status);
retval = TIMEOUT;
goto finish;
}
/* Wait for command status. */
do {
cmd_status = readl(MTK_MSDC_SDC_CMDSTA);
DEBUG_MSG("Command status: 0x%.4x", cmd_status);
if (cmd_status & MTK_MSDC_SDC_CMDSTA_CMDRDY)
DEBUG_MSG("Command ready!");
if (cmd_status & MTK_MSDC_SDC_CMDSTA_CMDTO) {
ERROR("MS/SD controller detected a timeout "
"condition while waiting for a response on the "
"CMD line.");
retval = TIMEOUT;
goto finish;
}
if (cmd_status & MTK_MSDC_SDC_CMDSTA_RSPCRCERR) {
ERROR("MS/SD controller detected a CRC error after "
"reading a response from the CMD line.");
retval = -1;
goto finish;
}
if (cmd_status & MTK_MSDC_SDC_CMDSTA_MMCIRQ) {
ERROR("MMC supporting command class 9 issued an "
"interrupt request.");
retval = -1;
goto finish;
}
udelay(COMMAND_STATUS_READ_DELAY_US);
} while (!cmd_status);
/* Read command response. */
mtk_read_response(mmc_host);
/* Read/write data if supplied. */
if (mmc_host->data) {
retval = mtk_transfer_data(mmc_host);
if (retval)
goto finish;
}
/* Read memory card status register. */
DEBUG_MSG("=> Memory card status register: 0x%.8x.<=",
readl(MTK_MSDC_SDC_CSTA));
finish:
mmc_host->cmd = NULL;
mmc_host->data = NULL;
return retval;
}
/*
* Initialization of mmc.
*/
int mtk_mmc_init(bd_t *bis)
{
struct mmc *mmc = malloc(sizeof(struct mmc));
DEBUG_LINE();
if (!mmc)
return -1;
strcpy(mmc->name, "msdc_mmc");
mmc->send_cmd = mtk_send_cmd;
mmc->set_ios = mtk_set_ios;
mmc->init = mtk_init;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->host_caps = MMC_MODE_4BIT;
mmc->priv = &mmc_host;
mmc_host.mmc = mmc;
/* Min and max frequencies are determined by
max and min values of clock divider (SCLKF in MSDC_CFG). */
mmc->f_min = mtk_calculate_f_slave_min();
mmc->f_max = MAX_SLAVE_FREQ_HZ;
mmc_register(mmc);
return 0;
}