rockbox/firmware/target/arm/as3525/ata_sd_as3525.c
Rafaël Carré 3952e6d008 AS3525: enable access to the embedded SD card
Don't reset the peripherals
Don't mind about the spare register
Don't mind about MEMSTICK peripheral
Use correct clock speeds for IDE

Tested on Clip only

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19036 a1c6a512-1295-4272-9138-f99709370657
2008-11-07 10:52:26 +00:00

326 lines
8.7 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright © 2008 Rafaël Carré
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/* Driver for the ARM PL180 SD/MMC controller inside AS3525 SoC */
#include "config.h" /* for HAVE_MULTIVOLUME */
#include "as3525.h"
#include "pl180.h"
#include "panic.h"
#include "stdbool.h"
#include "sd.h"
#define NAND_AS3525 0
#define SD_AS3525 1
static const int pl180_base[2] = { NAND_FLASH_BASE, SD_MCI_BASE };
/* ARM PL180 registers */
#define MMC_POWER(i) (*(volatile unsigned long *) (pl180_base[i]+0x00))
#define MMC_CLOCK(i) (*(volatile unsigned long *) (pl180_base[i]+0x04))
#define MMC_ARGUMENT(i) (*(volatile unsigned long *) (pl180_base[i]+0x08))
#define MMC_COMMAND(i) (*(volatile unsigned long *) (pl180_base[i]+0x0C))
#define MMC_RESPCMD(i) (*(volatile unsigned long *) (pl180_base[i]+0x10))
#define MMC_RESP0(i) (*(volatile unsigned long *) (pl180_base[i]+0x14))
#define MMC_RESP1(i) (*(volatile unsigned long *) (pl180_base[i]+0x18))
#define MMC_RESP2(i) (*(volatile unsigned long *) (pl180_base[i]+0x1C))
#define MMC_RESP3(i) (*(volatile unsigned long *) (pl180_base[i]+0x20))
#define MMC_DATACTRL(i) (*(volatile unsigned long *) (pl180_base[i]+0x2C))
#define MMC_STATUS(i) (*(volatile unsigned long *) (pl180_base[i]+0x34))
#define MMC_CLEAR(i) (*(volatile unsigned long *) (pl180_base[i]+0x38))
#define MMC_MASK0(i) (*(volatile unsigned long *) (pl180_base[i]+0x3C))
#define MMC_MASK1(i) (*(volatile unsigned long *) (pl180_base[i]+0x40))
#define MMC_SELECT(i) (*(volatile unsigned long *) (pl180_base[i]+0x44))
/* SD commands */
#define GO_IDLE_STATE 0
#define MMC_CMD_READ_CID 2
#define SEND_IF_COND 8
#define SEND_OP_COND 41
#define APP_CMD 55
/* command flags */
#define MMC_NO_FLAGS (0<<0)
#define MMC_RESP (1<<0)
#define MMC_LONG_RESP (1<<1)
#define MMC_ARG (1<<2)
#ifdef BOOTLOADER
#define DEBUG
void reset_screen(void);
void printf(const char *format, ...);
#endif
struct mmc_command
{
int cmd;
int arg;
int resp[4];
int flags;
};
static inline void mci_delay(void) { int i = 0xffff; while(i--) ; }
static void mci_set_clock_divider(const int drive, int divider)
{
int clock = MMC_CLOCK(drive);
if(divider > 1)
{
/* use divide logic */
clock &= ~MCI_CLOCK_BYPASS;
/* convert divider to MMC_CLOCK logic */
divider = (divider/2) - 1;
if(divider >= 256)
divider = 255;
}
else
{
/* bypass dividing logic */
clock |= MCI_CLOCK_BYPASS;
divider = 0;
}
MMC_CLOCK(drive) = clock | divider;
mci_delay();
}
static int send_cmd(const int drive, struct mmc_command *cmd)
{
int val, status;
while(MMC_STATUS(drive) & MCI_CMD_ACTIVE); /* useless */
if(MMC_COMMAND(drive) & MCI_COMMAND_ENABLE) /* clears existing command */
{
MMC_COMMAND(drive) = 0;
mci_delay();
}
val = cmd->cmd | MCI_COMMAND_ENABLE;
if(cmd->flags & MMC_RESP)
{
val |= MCI_COMMAND_RESPONSE;
if(cmd->flags & MMC_LONG_RESP)
val |= MCI_COMMAND_LONG_RESPONSE;
}
MMC_CLEAR(drive) = 0x7ff;
MMC_ARGUMENT(drive) = (cmd->flags & MMC_ARG) ? cmd->arg : 0;
MMC_COMMAND(drive) = val;
while(MMC_STATUS(drive) & MCI_CMD_ACTIVE);
MMC_COMMAND(drive) = 0;
MMC_ARGUMENT(drive) = ~0;
do
{
status = MMC_STATUS(drive);
if(cmd->flags & MMC_RESP)
{
if(status & MCI_CMD_TIMEOUT)
{
if(cmd->cmd == SEND_IF_COND)
break; /* SDHC test can fail */
panicf("Response timeout");
}
else if(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END))
{ /* resp received */
cmd->resp[0] = MMC_RESP0(drive);
if(cmd->flags & MMC_LONG_RESP)
{
cmd->resp[1] = MMC_RESP1(drive);
cmd->resp[2] = MMC_RESP2(drive);
cmd->resp[3] = MMC_RESP3(drive);
}
break;
}
}
else
if(status & MCI_CMD_SENT)
break;
} while(1);
MMC_CLEAR(drive) = 0x7ff;
return status;
}
static void sd_init_card(const int drive)
{
struct mmc_command cmd_app, cmd_op_cond, cmd_idle, cmd_if_cond;
int status;
bool sdhc;
#ifdef DEBUG
reset_screen();
printf("now - powered up");
#endif
cmd_idle.cmd = GO_IDLE_STATE;
cmd_idle.arg = 0;
cmd_idle.flags = MMC_NO_FLAGS;
if(send_cmd(drive, &cmd_idle) != MCI_CMD_SENT)
panicf("goto idle failed!");
#ifdef DEBUG
else
printf("now - idle");
#endif
mci_delay();
cmd_if_cond.cmd = SEND_IF_COND;
cmd_if_cond.arg = (1 /* 2.7-3.6V */ << 8) | 0xAA /* check pattern */;
cmd_if_cond.flags = MMC_RESP | MMC_ARG;
cmd_app.cmd = APP_CMD;
cmd_app.flags = MMC_RESP | MMC_ARG;
cmd_app.arg = 0; /* 31:16 RCA (0) , 15:0 stuff bits */
cmd_op_cond.cmd = SEND_OP_COND;
cmd_op_cond.flags = MMC_RESP | MMC_ARG;
#ifdef DEBUG
printf("now - card powering up");
#endif
sdhc = false;
status = send_cmd(drive, &cmd_if_cond);
if(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END))
{
if((cmd_if_cond.resp[0] & 0xFFF) == cmd_if_cond.arg)
sdhc = true;
#ifdef DEBUG
else
printf("Bad resp: %x",cmd_if_cond.arg);
#endif
}
#ifdef DEBUG
else
printf("cmd_if_cond stat: 0x%x",status);
printf("%s Capacity",sdhc?"High":"Normal");
mci_delay();
mci_delay();
mci_delay();
#endif
#ifdef DEBUG
int loop = 0;
#endif
do {
mci_delay();
mci_delay();
#ifdef DEBUG
reset_screen();
printf("Loop number #%d", ++loop);
#endif
/* app_cmd */
status = send_cmd(drive, &cmd_app);
if( !(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END)) ||
!(cmd_app.resp[0] & (1<<5)) )
{
panicf("app_cmd failed");
}
cmd_op_cond.arg = sdhc ? 0x40FF8000 : (8<<0x14); /* ocr */
status = send_cmd(drive, &cmd_op_cond);
if(!(status & (MCI_CMD_CRC_FAIL|MCI_CMD_RESP_END)))
panicf("cmd_op_cond failed");
#ifdef DEBUG
printf("OP COND: 0x%.8x", cmd_op_cond.resp[0]);
#endif
} while(!(cmd_op_cond.resp[0] & (1<<31))); /* until card is powered up */
#ifdef DEBUG
printf("now - card ready !");
#endif
}
static void init_pl180_controller(const int drive)
{
MMC_COMMAND(drive) = MMC_DATACTRL(drive) = 0;
MMC_CLEAR(drive) = 0x7ff;
MMC_MASK0(drive) = MMC_MASK1(drive) = 0; /* disable all interrupts */
MMC_POWER(drive) = MCI_POWER_UP|(10 /*voltage*/ << 2); /* use OF voltage */
mci_delay();
MMC_POWER(drive) |= MCI_POWER_ON;
mci_delay();
MMC_SELECT(drive) = 0;
MMC_CLOCK(drive) = MCI_CLOCK_ENABLE;
MMC_CLOCK(drive) &= ~MCI_CLOCK_POWERSAVE;
/* set MCLK divider */
mci_set_clock_divider(drive, 200);
}
int sd_init(void)
{
CGU_IDE = (1<<7) /* AHB interface enable */ |
(1<<6) /* interface enable */ |
(2<<2) /* clock didiver = 2+1 */ |
1 /* clock source = PLLA */;
CGU_PERI |= CGU_NAF_CLOCK_ENABLE;
#ifdef HAVE_MULTIVOLUME
CGU_PERI |= CGU_MCI_CLOCK_ENABLE;
#endif
CCU_IO &= ~8; /* bits 3:2 = 01, xpd is SD interface */
CCU_IO |= 4;
init_pl180_controller(NAND_AS3525);
sd_init_card(NAND_AS3525);
#ifdef HAVE_MULTIVOLUME
init_pl180_controller(SD_AS3525);
sd_init_card(SD_AS3525);
#endif
return 0;
}
int sd_read_sectors(IF_MV2(int drive,) unsigned long start, int count, void* buf)
{
(void)start;
(void)count;
(void)buf;
return 0; /* TODO */
}
int sd_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf)
{
(void)start;
(void)count;
(void)buf;
return 0; /* TODO */
}