mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
1a4a6eb445
Libbase should keep its RAM usage to a minimum as it is meant to be executed before the SDRAM is up and running. (Having lots of code is OK though as we XIP from the flash)
273 lines
6.1 KiB
C
273 lines
6.1 KiB
C
/*
|
|
* Milkymist SoC (Software)
|
|
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
|
|
*
|
|
* 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, version 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <hw/flash.h>
|
|
#include <hw/memcard.h>
|
|
#include <string.h>
|
|
|
|
#include <blockdev.h>
|
|
|
|
//#define MEMCARD_DEBUG
|
|
|
|
static void memcard_start_cmd_tx(void)
|
|
{
|
|
CSR_MEMCARD_ENABLE = MEMCARD_ENABLE_CMD_TX;
|
|
}
|
|
|
|
static void memcard_start_cmd_rx(void)
|
|
{
|
|
CSR_MEMCARD_PENDING = MEMCARD_PENDING_CMD_RX;
|
|
CSR_MEMCARD_START = MEMCARD_START_CMD_RX;
|
|
CSR_MEMCARD_ENABLE = MEMCARD_ENABLE_CMD_RX;
|
|
}
|
|
|
|
static void memcard_start_cmd_dat_rx(void)
|
|
{
|
|
CSR_MEMCARD_PENDING = MEMCARD_PENDING_CMD_RX|MEMCARD_PENDING_DAT_RX;
|
|
CSR_MEMCARD_START = MEMCARD_START_CMD_RX|MEMCARD_START_DAT_RX;
|
|
CSR_MEMCARD_ENABLE = MEMCARD_ENABLE_CMD_RX|MEMCARD_ENABLE_DAT_RX;
|
|
}
|
|
|
|
static void memcard_send_command(unsigned char cmd, unsigned int arg)
|
|
{
|
|
unsigned char packet[6];
|
|
int a;
|
|
int i;
|
|
unsigned char data;
|
|
unsigned char crc;
|
|
|
|
packet[0] = cmd | 0x40;
|
|
packet[1] = ((arg >> 24) & 0xff);
|
|
packet[2] = ((arg >> 16) & 0xff);
|
|
packet[3] = ((arg >> 8) & 0xff);
|
|
packet[4] = (arg & 0xff);
|
|
|
|
crc = 0;
|
|
for(a=0;a<5;a++) {
|
|
data = packet[a];
|
|
for(i=0;i<8;i++) {
|
|
crc <<= 1;
|
|
if((data & 0x80) ^ (crc & 0x80))
|
|
crc ^= 0x09;
|
|
data <<= 1;
|
|
}
|
|
}
|
|
crc = (crc<<1) | 1;
|
|
|
|
packet[5] = crc;
|
|
|
|
#ifdef MEMCARD_DEBUG
|
|
printf(">> %02x %02x %02x %02x %02x %02x\n", packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]);
|
|
#endif
|
|
|
|
for(i=0;i<6;i++) {
|
|
CSR_MEMCARD_CMD = packet[i];
|
|
while(CSR_MEMCARD_PENDING & MEMCARD_PENDING_CMD_TX);
|
|
}
|
|
}
|
|
|
|
static void memcard_send_dummy(void)
|
|
{
|
|
CSR_MEMCARD_CMD = 0xff;
|
|
while(CSR_MEMCARD_PENDING & MEMCARD_PENDING_CMD_TX);
|
|
}
|
|
|
|
static int memcard_receive_command(unsigned char *buffer, int len)
|
|
{
|
|
int i;
|
|
int timeout;
|
|
|
|
for(i=0;i<len;i++) {
|
|
timeout = 2000000;
|
|
while(!(CSR_MEMCARD_PENDING & MEMCARD_PENDING_CMD_RX)) {
|
|
timeout--;
|
|
if(timeout == 0) {
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("Command receive timeout\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
buffer[i] = CSR_MEMCARD_CMD;
|
|
CSR_MEMCARD_PENDING = MEMCARD_PENDING_CMD_RX;
|
|
}
|
|
|
|
while(!(CSR_MEMCARD_PENDING & MEMCARD_PENDING_CMD_RX));
|
|
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("<< ");
|
|
for(i=0;i<len;i++)
|
|
printf("%02x ", buffer[i]);
|
|
printf("\n");
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int memcard_receive_command_data(unsigned char *command, unsigned int *data)
|
|
{
|
|
int i, j;
|
|
int timeout;
|
|
|
|
i = 0;
|
|
j = 0;
|
|
while(j < 128) {
|
|
timeout = 2000000;
|
|
while(!(CSR_MEMCARD_PENDING & (MEMCARD_PENDING_CMD_RX|MEMCARD_PENDING_DAT_RX))) {
|
|
timeout--;
|
|
if(timeout == 0) {
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("Command receive timeout\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
if(CSR_MEMCARD_PENDING & MEMCARD_PENDING_CMD_RX) {
|
|
command[i++] = CSR_MEMCARD_CMD;
|
|
CSR_MEMCARD_PENDING = MEMCARD_PENDING_CMD_RX;
|
|
if(i == 6)
|
|
CSR_MEMCARD_ENABLE = MEMCARD_ENABLE_DAT_RX; /* disable command RX */
|
|
}
|
|
if(CSR_MEMCARD_PENDING & MEMCARD_PENDING_DAT_RX) {
|
|
data[j++] = CSR_MEMCARD_DAT;
|
|
CSR_MEMCARD_PENDING = MEMCARD_PENDING_DAT_RX;
|
|
}
|
|
}
|
|
|
|
/* Get CRC (ignored) */
|
|
for(i=0;i<2;i++) {
|
|
while(!(CSR_MEMCARD_PENDING & MEMCARD_PENDING_DAT_RX));
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("CRC: %08x\n", CSR_MEMCARD_DAT);
|
|
#endif
|
|
CSR_MEMCARD_PENDING = MEMCARD_PENDING_DAT_RX;
|
|
}
|
|
|
|
while(!(CSR_MEMCARD_PENDING & MEMCARD_PENDING_DAT_RX));
|
|
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("<< %02x %02x %02x %02x %02x %02x\n", command[0], command[1], command[2], command[3], command[4], command[5]);
|
|
#endif
|
|
|
|
//for(i=0;i<128;i++)
|
|
// printf("%08x ", data[i]);
|
|
//printf("\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int memcard_init(void)
|
|
{
|
|
unsigned char b[17];
|
|
unsigned int rca;
|
|
|
|
CSR_MEMCARD_CLK2XDIV = 250;
|
|
|
|
/* CMD0 */
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(0, 0);
|
|
|
|
memcard_send_dummy();
|
|
|
|
/* CMD8 */
|
|
memcard_send_command(8, 0x1aa);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
|
|
/* ACMD41 - initialize */
|
|
while(1) {
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(55, 0);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(41, 0x00300000);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
if(b[1] & 0x80) break;
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("Card is busy, retrying\n");
|
|
#endif
|
|
}
|
|
|
|
/* CMD2 - get CID */
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(2, 0);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 17)) return 0;
|
|
|
|
/* CMD3 - get RCA */
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(3, 0);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
rca = (((unsigned int)b[1]) << 8)|((unsigned int)b[2]);
|
|
#ifdef MEMCARD_DEBUG
|
|
printf("RCA: %04x\n", rca);
|
|
#endif
|
|
|
|
/* CMD7 - select card */
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(7, rca << 16);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
|
|
/* ACMD6 - set bus width */
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(55, rca << 16);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(6, 2);
|
|
memcard_start_cmd_rx();
|
|
if(!memcard_receive_command(b, 6)) return 0;
|
|
|
|
CSR_MEMCARD_CLK2XDIV = 3;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int memcard_readblock(unsigned int block, void *buffer)
|
|
{
|
|
unsigned char b[6];
|
|
|
|
/* CMD17 - read block */
|
|
memcard_start_cmd_tx();
|
|
memcard_send_command(17, block*512);
|
|
memcard_start_cmd_dat_rx();
|
|
if(!memcard_receive_command_data(b, (unsigned int *)buffer)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
int bd_init(int devnr)
|
|
{
|
|
return memcard_init();
|
|
}
|
|
|
|
int bd_readblock(unsigned int block, void *buffer)
|
|
{
|
|
return memcard_readblock(block, buffer);
|
|
}
|
|
|
|
void bd_done(void)
|
|
{
|
|
}
|
|
|
|
int bd_has_part_table(int devnr)
|
|
{
|
|
return 1;
|
|
}
|