Davinci MMCSD Support
Added support for MMC/SD cards for Davinci. This feature is enabled by CONFIG_DAVINCI_MMC and is dependant on CONFIG_MMC and CONFIG_GENERIC_MMC options. This is tested on DM355 and DM365 EVMs with both the available mmc controllers. Signed-off-by: Alagu Sankar <alagusankar@embwise.com> Signed-off-by: Sandeep Paulraj <s-paulraj@ti.com>
This commit is contained in:
parent
36b4e2dddd
commit
57418d2139
175
arch/arm/include/asm/arch-davinci/sdmmc_defs.h
Normal file
175
arch/arm/include/asm/arch-davinci/sdmmc_defs.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Davinci MMC Controller Defines - Based on Linux davinci_mmc.c
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Incorporated
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMC_DEFS_H_
|
||||
#define _SDMMC_DEFS_H_
|
||||
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
/* MMC Control Reg fields */
|
||||
#define MMCCTL_DATRST (1 << 0)
|
||||
#define MMCCTL_CMDRST (1 << 1)
|
||||
#define MMCCTL_WIDTH_4_BIT (1 << 2)
|
||||
#define MMCCTL_DATEG_DISABLED (0 << 6)
|
||||
#define MMCCTL_DATEG_RISING (1 << 6)
|
||||
#define MMCCTL_DATEG_FALLING (2 << 6)
|
||||
#define MMCCTL_DATEG_BOTH (3 << 6)
|
||||
#define MMCCTL_PERMDR_LE (0 << 9)
|
||||
#define MMCCTL_PERMDR_BE (1 << 9)
|
||||
#define MMCCTL_PERMDX_LE (0 << 10)
|
||||
#define MMCCTL_PERMDX_BE (1 << 10)
|
||||
|
||||
/* MMC Clock Control Reg fields */
|
||||
#define MMCCLK_CLKEN (1 << 8)
|
||||
#define MMCCLK_CLKRT_MASK (0xFF << 0)
|
||||
|
||||
/* MMC Status Reg0 fields */
|
||||
#define MMCST0_DATDNE (1 << 0)
|
||||
#define MMCST0_BSYDNE (1 << 1)
|
||||
#define MMCST0_RSPDNE (1 << 2)
|
||||
#define MMCST0_TOUTRD (1 << 3)
|
||||
#define MMCST0_TOUTRS (1 << 4)
|
||||
#define MMCST0_CRCWR (1 << 5)
|
||||
#define MMCST0_CRCRD (1 << 6)
|
||||
#define MMCST0_CRCRS (1 << 7)
|
||||
#define MMCST0_DXRDY (1 << 9)
|
||||
#define MMCST0_DRRDY (1 << 10)
|
||||
#define MMCST0_DATED (1 << 11)
|
||||
#define MMCST0_TRNDNE (1 << 12)
|
||||
|
||||
#define MMCST0_ERR_MASK (0x00F8)
|
||||
|
||||
/* MMC Status Reg1 fields */
|
||||
#define MMCST1_BUSY (1 << 0)
|
||||
#define MMCST1_CLKSTP (1 << 1)
|
||||
#define MMCST1_DXEMP (1 << 2)
|
||||
#define MMCST1_DRFUL (1 << 3)
|
||||
#define MMCST1_DAT3ST (1 << 4)
|
||||
#define MMCST1_FIFOEMP (1 << 5)
|
||||
#define MMCST1_FIFOFUL (1 << 6)
|
||||
|
||||
/* MMC INT Mask Reg fields */
|
||||
#define MMCIM_EDATDNE (1 << 0)
|
||||
#define MMCIM_EBSYDNE (1 << 1)
|
||||
#define MMCIM_ERSPDNE (1 << 2)
|
||||
#define MMCIM_ETOUTRD (1 << 3)
|
||||
#define MMCIM_ETOUTRS (1 << 4)
|
||||
#define MMCIM_ECRCWR (1 << 5)
|
||||
#define MMCIM_ECRCRD (1 << 6)
|
||||
#define MMCIM_ECRCRS (1 << 7)
|
||||
#define MMCIM_EDXRDY (1 << 9)
|
||||
#define MMCIM_EDRRDY (1 << 10)
|
||||
#define MMCIM_EDATED (1 << 11)
|
||||
#define MMCIM_ETRNDNE (1 << 12)
|
||||
|
||||
#define MMCIM_MASKALL (0xFFFFFFFF)
|
||||
|
||||
/* MMC Resp Tout Reg fields */
|
||||
#define MMCTOR_TOR_MASK (0xFF) /* dont write to reg, | it */
|
||||
#define MMCTOR_TOD_20_16_SHIFT (8)
|
||||
|
||||
/* MMC Data Read Tout Reg fields */
|
||||
#define MMCTOD_TOD_0_15_MASK (0xFFFF)
|
||||
|
||||
/* MMC Block len Reg fields */
|
||||
#define MMCBLEN_BLEN_MASK (0xFFF)
|
||||
|
||||
/* MMC Num Blocks Reg fields */
|
||||
#define MMCNBLK_NBLK_MASK (0xFFFF)
|
||||
#define MMCNBLK_NBLK_MAX (0xFFFF)
|
||||
|
||||
/* MMC Num Blocks Counter Reg fields */
|
||||
#define MMCNBLC_NBLC_MASK (0xFFFF)
|
||||
|
||||
/* MMC Cmd Reg fields */
|
||||
#define MMCCMD_CMD_MASK (0x3F)
|
||||
#define MMCCMD_PPLEN (1 << 7)
|
||||
#define MMCCMD_BSYEXP (1 << 8)
|
||||
#define MMCCMD_RSPFMT_NONE (0 << 9)
|
||||
#define MMCCMD_RSPFMT_R1567 (1 << 9)
|
||||
#define MMCCMD_RSPFMT_R2 (2 << 9)
|
||||
#define MMCCMD_RSPFMT_R3 (3 << 9)
|
||||
#define MMCCMD_DTRW (1 << 11)
|
||||
#define MMCCMD_STRMTP (1 << 12)
|
||||
#define MMCCMD_WDATX (1 << 13)
|
||||
#define MMCCMD_INITCK (1 << 14)
|
||||
#define MMCCMD_DCLR (1 << 15)
|
||||
#define MMCCMD_DMATRIG (1 << 16)
|
||||
|
||||
/* FIFO control Reg fields */
|
||||
#define MMCFIFOCTL_FIFORST (1 << 0)
|
||||
#define MMCFIFOCTL_FIFODIR (1 << 1)
|
||||
#define MMCFIFOCTL_FIFOLEV (1 << 2)
|
||||
#define MMCFIFOCTL_ACCWD_4 (0 << 3) /* access width of 4 bytes */
|
||||
#define MMCFIFOCTL_ACCWD_3 (1 << 3) /* access width of 3 bytes */
|
||||
#define MMCFIFOCTL_ACCWD_2 (2 << 3) /* access width of 2 bytes */
|
||||
#define MMCFIFOCTL_ACCWD_1 (3 << 3) /* access width of 1 byte */
|
||||
|
||||
/* Davinci MMC Register definitions */
|
||||
struct davinci_mmc_regs {
|
||||
dv_reg mmcctl;
|
||||
dv_reg mmcclk;
|
||||
dv_reg mmcst0;
|
||||
dv_reg mmcst1;
|
||||
dv_reg mmcim;
|
||||
dv_reg mmctor;
|
||||
dv_reg mmctod;
|
||||
dv_reg mmcblen;
|
||||
dv_reg mmcnblk;
|
||||
dv_reg mmcnblc;
|
||||
dv_reg mmcdrr;
|
||||
dv_reg mmcdxr;
|
||||
dv_reg mmccmd;
|
||||
dv_reg mmcarghl;
|
||||
dv_reg mmcrsp01;
|
||||
dv_reg mmcrsp23;
|
||||
dv_reg mmcrsp45;
|
||||
dv_reg mmcrsp67;
|
||||
dv_reg mmcdrsp;
|
||||
dv_reg mmcetok;
|
||||
dv_reg mmccidx;
|
||||
dv_reg mmcckc;
|
||||
dv_reg mmctorc;
|
||||
dv_reg mmctodc;
|
||||
dv_reg mmcblnc;
|
||||
dv_reg sdioctl;
|
||||
dv_reg sdiost0;
|
||||
dv_reg sdioien;
|
||||
dv_reg sdioist;
|
||||
dv_reg mmcfifoctl;
|
||||
};
|
||||
|
||||
/* Davinci MMC board definitions */
|
||||
struct davinci_mmc {
|
||||
struct davinci_mmc_regs *reg_base; /* Register base address */
|
||||
uint input_clk; /* Input clock to MMC controller */
|
||||
uint host_caps; /* Host capabilities */
|
||||
uint voltages; /* Host supported voltages */
|
||||
uint version; /* MMC Controller version */
|
||||
};
|
||||
|
||||
enum {
|
||||
MMC_CTLR_VERSION_1 = 0, /* DM644x and DM355 */
|
||||
MMC_CTLR_VERSION_2, /* DA830 */
|
||||
};
|
||||
|
||||
int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host);
|
||||
|
||||
#endif /* _SDMMC_DEFS_H */
|
@ -27,6 +27,7 @@ LIB := $(obj)libmmc.o
|
||||
|
||||
COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
|
||||
COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
|
||||
COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
|
||||
COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
|
||||
COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
|
||||
COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
|
||||
|
404
drivers/mmc/davinci_mmc.c
Normal file
404
drivers/mmc/davinci_mmc.c
Normal file
@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Davinci MMC Controller Driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Incorporated
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#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->f_min)
|
||||
clock = mmc->f_min;
|
||||
if (clock > mmc->f_max)
|
||||
clock = mmc->f_max;
|
||||
|
||||
set_val(®s->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(®s->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 mmcstatus1, wdog = WATCHDOG_COUNT;
|
||||
mmcstatus1 = get_val(®s->mmcst1);
|
||||
while (--wdog && ((get_val(®s->mmcst1) & status) != status))
|
||||
udelay(10);
|
||||
|
||||
if (!(get_val(®s->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 mmcstatus1, wdog = WATCHDOG_COUNT;
|
||||
|
||||
mmcstatus1 = get_val(®s->mmcst1);
|
||||
while (--wdog && (get_val(®s->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(®s->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(®s->mmcst0);
|
||||
}
|
||||
|
||||
printf("Status %x Timeout ST0:%x ST1:%x\n", st_ready, mmcstatus,
|
||||
get_val(®s->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(®s->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(®s->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(®s->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(®s->mmcfifoctl, MMCFIFOCTL_FIFOLEV);
|
||||
} else if (data->flags == MMC_DATA_WRITE) {
|
||||
set_val(®s->mmcfifoctl,
|
||||
(MMCFIFOCTL_FIFOLEV |
|
||||
MMCFIFOCTL_FIFODIR));
|
||||
cmddata |= MMCCMD_DTRW;
|
||||
}
|
||||
|
||||
set_val(®s->mmctod, 0xFFFF);
|
||||
set_val(®s->mmcnblk, (data->blocks & MMCNBLK_NBLK_MASK));
|
||||
set_val(®s->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(®s->mmcdxr, val);
|
||||
data_buf += 4;
|
||||
bytes_left -= 4;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set_val(®s->mmcblen, 0);
|
||||
set_val(®s->mmcnblk, 0);
|
||||
}
|
||||
|
||||
set_val(®s->mmctor, 0x1FFF);
|
||||
|
||||
/* Send the command */
|
||||
set_val(®s->mmcarghl, cmd->cmdarg);
|
||||
set_val(®s->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(®s->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(®s->mmcrsp67);
|
||||
cmd->response[1] = get_val(®s->mmcrsp45);
|
||||
cmd->response[2] = get_val(®s->mmcrsp23);
|
||||
cmd->response[3] = get_val(®s->mmcrsp01);
|
||||
} else if (cmd->resp_type & MMC_RSP_PRESENT) {
|
||||
cmd->response[0] = get_val(®s->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);
|
||||
|
||||
for (i = 0; bytes_left && (i < fifo_words); i++) {
|
||||
cmddata = get_val(®s->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(®s->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(®s->mmcst0);
|
||||
get_val(®s->mmcst1);
|
||||
|
||||
/* Hold software reset */
|
||||
set_bit(®s->mmcctl, MMCCTL_DATRST);
|
||||
set_bit(®s->mmcctl, MMCCTL_CMDRST);
|
||||
udelay(10);
|
||||
|
||||
set_val(®s->mmcclk, 0x0);
|
||||
set_val(®s->mmctor, 0x1FFF);
|
||||
set_val(®s->mmctod, 0xFFFF);
|
||||
|
||||
/* Clear software reset */
|
||||
clear_bit(®s->mmcctl, MMCCTL_DATRST);
|
||||
clear_bit(®s->mmcctl, MMCCTL_CMDRST);
|
||||
|
||||
udelay(10);
|
||||
|
||||
/* Reset FIFO - Always use the maximum fifo threshold */
|
||||
set_val(®s->mmcfifoctl, (MMCFIFOCTL_FIFOLEV | MMCFIFOCTL_FIFORST));
|
||||
set_val(®s->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(®s->mmcctl, MMCCTL_WIDTH_4_BIT);
|
||||
else
|
||||
clear_bit(®s->mmcctl, MMCCTL_WIDTH_4_BIT);
|
||||
|
||||
/* Set clock speed */
|
||||
if (mmc->clock)
|
||||
dmmc_set_clock(mmc, mmc->clock);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct mmc *mmc;
|
||||
|
||||
mmc = malloc(sizeof(struct mmc));
|
||||
memset(mmc, 0, sizeof(struct mmc));
|
||||
|
||||
sprintf(mmc->name, "davinci");
|
||||
mmc->priv = host;
|
||||
mmc->send_cmd = dmmc_send_cmd;
|
||||
mmc->set_ios = dmmc_set_ios;
|
||||
mmc->init = dmmc_init;
|
||||
|
||||
mmc->f_min = 200000;
|
||||
mmc->f_max = 25000000;
|
||||
mmc->voltages = host->voltages;
|
||||
mmc->host_caps = host->host_caps;
|
||||
|
||||
#ifdef CONFIG_MMC_MBLOCK
|
||||
mmc->b_max = DAVINCI_MAX_BLOCKS;
|
||||
#endif
|
||||
mmc_register(mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -274,6 +274,9 @@ struct mmc {
|
||||
struct mmc_cmd *cmd, struct mmc_data *data);
|
||||
void (*set_ios)(struct mmc *mmc);
|
||||
int (*init)(struct mmc *mmc);
|
||||
#ifdef CONFIG_MMC_MBLOCK
|
||||
uint b_max;
|
||||
#endif
|
||||
};
|
||||
|
||||
int mmc_register(struct mmc *mmc);
|
||||
|
Loading…
Reference in New Issue
Block a user