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>master
parent
36b4e2dddd
commit
57418d2139
@ -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 */ |
@ -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; |
||||
} |
||||
|
Loading…
Reference in new issue