blob: 057b6e900a0ad4a4b9f4cabfec972d03fc7498fe [file] [log] [blame]
/*
* (C) Copyright 2003
* Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
*
* 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; 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include "zx_mmc.h"
#include "type.h"
#include <asm/io.h>
#include <image.h>
#include <linux/byteorder/generic.h>
u32 mmc_rca;
static u32 ocr_avail = 0;
static u32 block_addr = 0;
#define MMC_BLOCK_SIZE 512
static u32 emmc_cmd(u32 cmd, u32 arg,
void *resp, u32 flags)
{
u32 *response = resp;
u32 i,response_words = 0;
u32 status,err_status = ZXMCI_STS_RE | ZXMCI_STS_RCRC | ZXMCI_STS_RTO;
u32 cmdreg;
/*Clear all raw interrupt status*/
REG32(SYS_EMMC_REGS_BASE+ZXMCI_RINTSTS) = 0xffffffff;
cmdreg = cmd & ZXMCI_CMD_INDEX_MASK;
cmdreg |= flags | ZXMCI_CMD_START;
cmdreg |= (0x1 << 29);
if(flags & ZXMCI_CMD_RSPLEN)
{
response_words = 4;
}
else if(flags & ZXMCI_CMD_RSPEXP)
{
response_words = 1;
}
REG32(SYS_EMMC_REGS_BASE+ZXMCI_CMDARG) = arg;
REG32(SYS_EMMC_REGS_BASE+ZXMCI_CMD) = cmdreg;
/*check command done*/
do {
status = REG32(SYS_EMMC_REGS_BASE+ZXMCI_RINTSTS);
} while (!(status & ZXMCI_STS_CD));
/*check error*/
if(status & err_status)
{
printf("send cmd error\n");
return 1;
}
for(i = 0;i < response_words;i++)
{
response[i] = REG32(SYS_EMMC_REGS_BASE+ZXMCI_RESP0 + i*4);
}
return 0;
}
s32 zx_mmc_read(u32 src, u8 * dst, u32 size)
{
u32 ret, i = 0,j = 0;
u32 resp;
u32 data;
u32 wordcount;
u32 *p = (u32 *)dst;
u32 status;
u32 start_addr;
u32 fifo_cnt;
if (size == 0)
return -1;
while((REG32(SYS_EMMC_REGS_BASE+ZXMCI_STATUS) & ZXMCI_STA_DATBUSY) != 0)
{
i++;
mmcdelay((80));
if(i>200)
break;
}
start_addr = src;
data = REG32(SYS_EMMC_REGS_BASE+ZXMCI_CTRL);
data |=(1<<1) ;
REG32(SYS_EMMC_REGS_BASE+ZXMCI_CTRL) = data;
i = 0;
do
{
i++;
mmcdelay((80));
if(i>100)
{
printf("fifo reset failure");
break;
}
}while(REG32(SYS_EMMC_REGS_BASE+ZXMCI_CTRL) & 2);
REG32(SYS_EMMC_REGS_BASE+ZXMCI_BYTCNT) = size;
REG32(SYS_EMMC_REGS_BASE+ZXMCI_BLKSIZ) = MMC_DEFAULT_BLKLEN;
if (size > MMC_DEFAULT_BLKLEN)
{
ret = emmc_cmd(MMC_CMD_READ_MULTIPLE_BLOCK,start_addr, &resp,(R1 | CF_DATA));
if (ret)
{
return -MMC_CMD_READ_MULTIPLE_BLOCK;
}
}
else
{
ret = emmc_cmd(MMC_CMD_READ_SINGLE_BLOCK,start_addr, &resp,(R1 | CF_DATA));
if (ret)
{
return -MMC_CMD_READ_SINGLE_BLOCK;
}
}
wordcount = 0;
do {
status = REG32(SYS_EMMC_REGS_BASE+ZXMCI_STATUS);
fifo_cnt =(status >> 17) & 0x1fff;
for(j = 0;j < fifo_cnt;j++)
{
data = REG32(SYS_EMMC_REGS_BASE+ZXMCI_FIFO);
*p++ = data;
wordcount++;
}
} while(wordcount < (size/4));
return 0;
}
static u32 mmc_bread(u32 dev_num, u32 blknr, u32 blkcnt, void *dst)
{
u32 src = 0;
s32 ret;
if (block_addr == 0)
{
src = blknr * MMC_BLOCK_SIZE;
}
else
{
src = blknr;
}
ret = zx_mmc_read(src, (u8 *) dst, blkcnt * MMC_BLOCK_SIZE);
if (ret < 0)
{
printf("mmc read error\n");
return 1;
}
return blkcnt;
}
void mmcdelay(unsigned long us)
{
volatile int i = 0; /* approximate */
for(i=0;i<5000;i++)
{
;
}
}
int mmc_read( u32 src, u32 length, void *dst)
{
u32 blkcnt = 0;
u32 start_blk = 0;
int ret = 0;
if(length%MMC_DEFAULT_BLKLEN)
blkcnt = (length/MMC_DEFAULT_BLKLEN)+1;
else
blkcnt = (length/MMC_DEFAULT_BLKLEN);
start_blk = src/MMC_DEFAULT_BLKLEN;
block_addr = 1;
printf("entry mmc_bread\n");
ret = mmc_bread(0, start_blk, blkcnt, (void *)dst);
if(ret == blkcnt )
return 0;
else
return -1;
}
uint32_t page_align(uint32_t offset)
{
uint32_t page_size = 2048;
if( offset & (page_size - 1) )
{
offset &= (~(page_size - 1));
offset += page_size;
}
return offset;
}
/*******************************************************************************
* Function: nand_init
* Description: mmc init
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
int32_t mmc_init (void)
{
return 0;
}