mini/sdhc.c
2009-05-15 05:33:08 -07:00

1218 lines
33 KiB
C

/*
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
sd host controller driver
Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
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 2.
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
*/
/* parts based on:
* * "SD Host Controller driver based on the SD Host Controller Standard" copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* * Simplified SD Host Controller Standard
*/
#include "hollywood.h"
#include "sdhc.h"
#include "utils.h"
#include "string.h"
#include "start.h"
#include "memory.h"
#define _READONLY 1
#define SDHC_PRINT_ERROR 1
//#define SDHC_DEBUG 1
//#define SDHC_DEBUG_V 1
#if defined(SDHC_DEBUG_V) && !defined(SDHC_DEBUG)
# define SDHC_DEBUG 1
#endif
#ifdef SDHC_DEBUG
# include "gecko.h"
# define sdhc_debug(reg, f, arg...) do { gecko_printf("sdhc%d: " f "\n", ((reg - SD_REG_BASE) / 0x100), ##arg); } while(0)
#else
# define sdhc_debug(reg, f, arg...) udelay(100);
#endif
#ifdef SDHC_PRINT_ERROR
# include "gecko.h"
# define sdhc_error(reg, f, arg...) do { gecko_printf("sdhc%d: " f "\n", ((reg - SD_REG_BASE) / 0x100), ##arg); } while(0)
#else
# define sdhc_error(reg, f, arg...)
#endif
#define SDHC_SOFTWARE_RESET_DAT (1 << 2)
#define SDHC_SOFTWARE_RESET_CMD (1 << 1)
#define SDHC_SOFTWARE_RESET_ALL (7)
#define SDHC_TIMEOUT_MAX (0x0e)
#define SDHC_BFREQ_KHZ(c) ((((c) >> 8) & 0x3f) * 1000)
#define SDHC_CAP_VOLTAGE_33 (1 << 24)
#define SDHC_CAP_VOLTAGE_30 (1 << 25)
#define SDHC_CAP_VOLTAGE_18 (1 << 26)
#define SDHC_CAP_SDMA (1 << 22)
#define SDHC_PCTRL_VOLTAGE_SHIFT 1
#define SDHC_PCTRL_VOLTAGE_33 (0x07 << SDHC_PCTRL_VOLTAGE_SHIFT)
#define SDHC_PCTRL_VOLTAGE_30 (0x06 << SDHC_PCTRL_VOLTAGE_SHIFT)
#define SDHC_PCTRL_VOLTAGE_18 (0x05 << SDHC_PCTRL_VOLTAGE_SHIFT)
#define SDHC_PCTRL_ENABLE 1
#define SDHC_CLOCK_INTERNAL_ENABLE (1 << 0)
#define SDHC_CLOCK_INTERNAL_STABLE (1 << 1)
#define SDHC_CLOCK_SD_ENABLE (1 << 2)
#define SDHC_CARD_INSERTED (1 << 16)
#define SDHC_WRITE_PROTECT (1 << 19)
#define SDHC_BLOCKS_MAX 65535
#define SDHC_CMDMODE_MULTIBLOCK (1 << 5)
#define SDHC_CMDMODE_READ (1 << 4)
#define SDHC_CMDMODE_WRITE (0 << 4)
#define SDHC_CMDMODE_ACMD12_ENABLE (1 << 2)
#define SDHC_CMDMODE_BLOCKCNT_ENABLE (1 << 1)
#define SDHC_CMDMODE_DMA_ENABLE (1 << 0)
#define SDHC_CMD_DATA (1 << 5)
#define SDHC_CMD_IDXCHECK (1 << 4)
#define SDHC_CMD_CRC (1 << 3)
#define SDHC_CMDTEST_READ 0x40
#define SDHC_CMD_MASK 0xff
#define SDHC_CMD_SHIFT 8
#define SDHC_CMD_NORESP 0
#define SDHC_CMD_RESP_136 1
#define SDHC_CMD_RESP_48 2
#define SDHC_CMD_RESP_BUSY 3
#define SDHC_PRESENT_CMD_INHIBIT_CMD 1
#define SDHC_PRESENT_CMD_INHIBIT_DAT 2
#define SDHC_PRESENT_CMD_INHIBIT_BOTH 3
#define SDHC_BFR_READ_ENABLE (1 << 11)
#define SDHC_BFR_WRITE_ENABLE (1 << 10)
#define SDHC_WAIT_TIMEOUT 10000
#define SDHC_WAIT_TIMEOUT_MULTIPLY 50
#define SDHC_WAIT_TIMEOUT_OUTER 50000
#define SDHC_WAIT_TIMEOUT_OUTER_MULTIPLY 5
#define SDHC_INTERRUPT_DMA (1 << 3)
#define SDHC_INTERRUPT_TRANSF_COMPLETE (1 << 1)
#define SD_RSP_BUSY 0x0100
#define SD_RSP_136 0x0200
#define SD_RSP_CRC 0x0400
#define SD_RSP_IDX 0x0800
#define SD_RSP_PRESENT 0x1000
#define SD_R0 0
#define SD_R1 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX)
#define SD_R1B (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX | SD_RSP_BUSY)
#define SD_R2 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_136)
#define SD_R3 SD_RSP_PRESENT
#define SD_R4 SD_RSP_PRESENT
#define SD_R5 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX)
#define SD_R5B (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX | SD_RSP_BUSY)
#define SD_R6 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX)
#define SD_R7 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX)
#define SD_READ 0x10000
#define SD_CMD_ACMD 0xC0
#define SD_CMD_RESET_CARD 0
#define SD_CMD_ALL_SEND_CID 2
#define SD_CMD_SEND_RELATIVE_ADDR 3
#define SD_CMD_SELECT_CARD 7
#define SD_CMD_SEND_IF_COND 8
#define SD_CMD_SEND_CSD 9
#define SD_CMD_SEND_STATUS 13
#define SD_CMD_SET_BLOCKLEN 16
#define SD_CMD_READ_MULTIPLE_BLOCK 18
#define SD_CMD_WRITE_MULTIPLE_BLOCK 25
#define SD_CMD_APP 55
#define SD_CMD_READ_OCR 58
#define SD_CMD_APP_SET_BUS_WIDTH (SD_CMD_ACMD + 6)
#define SD_CMD_APP_SEND_OP_COND (SD_CMD_ACMD + 41)
#define SDHC_HCR_BUSWIDTH_4 2
#define BLOCK_SIZE 512
#define BLOCK_SIZE_512K 7 /* SDMA block size */
#define SDMA_BLOCK_SIZE (512 * 1024)
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
#define MMC_VDD_165_195 0x00000080
#define OCR_POWERUP_STATUS (1 << 31)
#define OCR_HCS (1 << 30)
#define OCR_CCS OCR_HCS
#define INTERRUPT_ERROR (1 << 15)
#define INTERRUPT_CARD (1 << 8)
#define INTERRUPT_CARD_REMOVAL (1 << 7)
#define INTERRUPT_CARD_INSERTION (1 << 6)
#define INTERRUPT_BUFFER_READ_READY (1 << 5)
#define INTERRUPT_BUFFER_WRITE_READY (1 << 4)
#define INTERRUPT_DMA (1 << 3)
#define INTERRUPT_BLOCK_GAP_EVENT (1 << 2)
#define INTERRUPT_TRANSFER_COMPLETE (1 << 1)
#define INTERRUPT_COMMAND_COMPLETE (1 << 0)
#define INTERRUPT_ALL 0x81ff
#define EINTERRUPT_ADMA (1 << 9)
#define EINTERRUPT_ACMD12 (1 << 8)
#define EINTERRUPT_CURRENT_LIMIT (1 << 7)
#define EINTERRUPT_DATA_END_BIT (1 << 6)
#define EINTERRUPT_DATA_CRC (1 << 5)
#define EINTERRUPT_DATA_TIMEOUT (1 << 4)
#define EINTERRUPT_CMD_INDEX (1 << 3)
#define EINTERRUPT_CMD_END_BIT (1 << 2)
#define EINTERRUPT_CMD_CRC (1 << 1)
#define EINTERRUPT_CMD_TIMEOUT (1 << 0)
#define EINTERRUPT_ALL 0x3ff
static int ipc_code = 0;
static int ipc_tag = 0;
static sdhci_t sdhci;
// not currently used :(
void sd_irq(void)
{
int code, tag;
if (ipc_code != 0) {
code = ipc_code;
tag = ipc_tag;
ipc_code = ipc_tag = 0;
ipc_post(code, tag, 0);
}
}
unsigned int bswap32(unsigned int input) {
return ((input & 0x000000FF) << 24) | ((input & 0x0000FF00) << 8) |
((input & 0x00FF0000) >> 8) | ((input & 0xFF000000) >> 24);
}
u8 __sd_read8(u32 addr)
{
u32 mask;
u8 shift;
shift = (addr & 3) * 8;
mask = (0xFF << shift);
return (read32(addr & ~3) & mask) >> shift;
}
u16 __sd_read16(u32 addr)
{
if(addr & 3)
return (read32(addr & ~3) & 0xffff0000) >> 16;
else
return (read32(addr) & 0xffff);
}
inline u32 __sd_read32(u32 addr)
{
return read32(addr);
}
void __sd_write8(u32 addr, u8 data)
{
u32 mask;
u8 shift;
shift = (addr & 3) * 8;
mask = (0xFF << shift);
mask32(addr & ~3, mask, data << shift);
}
void __sd_write16(u32 addr, u16 data)
{
if(addr & 3)
mask32(addr & ~3, 0xffff0000, data << 16);
else
mask32(addr, 0xffff, ((u32)data));
}
inline void __sd_write32(u32 addr, u32 data)
{
write32(addr, data);
}
#if 0
static int __sd_wait8(u32 addr, u8 mask)
{
u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY;
do
{
if(__sd_read8(addr) & mask)
return 0;
udelay(SDHC_WAIT_TIMEOUT);
}
while(timeout--);
return SDHC_ETIMEDOUT;
}
#endif
static int __sd_wait16(u32 addr, u16 mask)
{
u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY;
do
{
if(__sd_read16(addr) & mask)
return 0;
udelay(SDHC_WAIT_TIMEOUT);
}
while(timeout--);
return SDHC_ETIMEDOUT;
}
static int __sd_wait32(u32 addr, u32 mask)
{
u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY;
do
{
if(__sd_read32(addr) & mask)
return 0;
udelay(SDHC_WAIT_TIMEOUT);
}
while(timeout--);
return SDHC_ETIMEDOUT;
}
static int __sd_wait8_r(u32 addr, u8 mask)
{
u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY;
do
{
if(!(__sd_read8(addr) & mask))
return 0;
udelay(SDHC_WAIT_TIMEOUT);
}
while(timeout--);
return SDHC_ETIMEDOUT;
}
#if 0
static int __sd_wait16_r(u32 addr, u16 mask)
{
u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY;
do
{
if(!(__sd_read16(addr) & mask))
return 0;
udelay(SDHC_WAIT_TIMEOUT);
}
while(timeout--);
return SDHC_ETIMEDOUT;
}
#endif
static int __sd_wait32_r(u32 addr, u32 mask)
{
u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY;
do
{
if(!(__sd_read32(addr) & mask))
return 0;
udelay(SDHC_WAIT_TIMEOUT);
}
while(timeout--);
return SDHC_ETIMEDOUT;
}
#ifdef SDHC_DEBUG_V
static void __sd_dumpregs(sdhci_t *sdhci)
{
sdhc_debug(sdhci->reg_base, " register dump:");
sdhc_debug(sdhci->reg_base, " sys addr: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_SDMA_ADDR));
sdhc_debug(sdhci->reg_base, " version: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_VERSION));
sdhc_debug(sdhci->reg_base, " bsize: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_BLOCK_SIZE));
sdhc_debug(sdhci->reg_base, " bcount: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_BLOCK_COUNT));
sdhc_debug(sdhci->reg_base, " argument: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_CMD_ARG));
sdhc_debug(sdhci->reg_base, " trans mode: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE));
sdhc_debug(sdhci->reg_base, " pres state: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE));
sdhc_debug(sdhci->reg_base, " hc: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_HOST_CONTROL));
sdhc_debug(sdhci->reg_base, " pwr ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_POWER_CONTROL));
sdhc_debug(sdhci->reg_base, " gap ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_BLOCK_GAP_CONTROL));
sdhc_debug(sdhci->reg_base, " wup ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_WAKEUP_CONTROL));
sdhc_debug(sdhci->reg_base, " clk ctrl: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CLOCK_CONTROL));
sdhc_debug(sdhci->reg_base, " to ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_TIMEOUT_CONTROL));
sdhc_debug(sdhci->reg_base, " int status: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS));
sdhc_debug(sdhci->reg_base, " int enable: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE));
sdhc_debug(sdhci->reg_base, " eint status: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS));
sdhc_debug(sdhci->reg_base, " eint enable: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE));
sdhc_debug(sdhci->reg_base, " caps: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES));
sdhc_debug(sdhci->reg_base, " max caps: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_MAX_CAPABILITIES));
sdhc_debug(sdhci->reg_base, " soft reset: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET));
sdhc_debug(sdhci->reg_base, " command: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CMD));
}
#else
#define __sd_dumpregs(s) while(0) { }
#endif
static int __sd_reset(sdhci_t *sdhci, int all)
{
int retval;
u32 mask;
sdhci->is_sdhc = 0;
sdhci->is_selected = 0;
sdhci->is_mounted = 0;
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, 0);
__sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE, 0);
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_SIGNAL_ENABLE, 0);
__sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_SIGNAL_ENABLE, 0);
if(all)
mask = SDHC_SOFTWARE_RESET_ALL;
else
mask = SDHC_SOFTWARE_RESET_CMD | SDHC_SOFTWARE_RESET_DAT;
sdhc_error(sdhci->reg_base, "resetting card (mask = %x)", mask);
__sd_write8(sdhci->reg_base + SDHC_SOFTWARE_RESET, mask);
retval = __sd_wait8_r(sdhci->reg_base + SDHC_SOFTWARE_RESET, mask);
if(retval < 0)
{
sdhc_debug(sdhci->reg_base, "reset failed, bits were never unset");
sdhc_debug(sdhci->reg_base, "software reset register: %02x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET));
return retval;
}
sdhc_debug(sdhci->reg_base, "reset done");
__sd_dumpregs(sdhci);
__sd_write8(sdhci->reg_base + SDHC_TIMEOUT_CONTROL, SDHC_TIMEOUT_MAX);
return 0;
}
static int __sd_clock_div(u32 base, u32 target)
{
int d;
for(d = 1; d <= 256; d *= 2)
{
if((base / d) <= target)
return (d/2);
}
return 256 / 2;
}
static int __sd_clock(sdhci_t *sdhci, u8 enable, u32 freq)
{
u32 caps;
int d, retval;
__sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, 0);
if(!enable)
return 0;
caps = read32(sdhci->reg_base + SDHC_CAPABILITIES);
if(SDHC_BFREQ_KHZ(caps) != 0)
d = __sd_clock_div(SDHC_BFREQ_KHZ(caps), freq);
else
d = 256 / 2;
__sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, (d << 8) | SDHC_CLOCK_INTERNAL_ENABLE);
__sd_dumpregs(sdhci);
sdhc_debug(sdhci->reg_base, "waiting for internal clock to become stable");
retval = __sd_wait16(sdhci->reg_base + SDHC_CLOCK_CONTROL, SDHC_CLOCK_INTERNAL_STABLE);
if(retval < 0)
{
sdhc_debug(sdhci->reg_base, "clock didn't become stable :/");
__sd_dumpregs(sdhci);
return retval;
}
sdhc_debug(sdhci->reg_base, "clock is stable; enabling sd clock");
__sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, (d << 8) | SDHC_CLOCK_SD_ENABLE | SDHC_CLOCK_INTERNAL_ENABLE);
return 0;
}
static int __sd_power(sdhci_t *sdhci, int vdd)
{
u32 caps;
u8 pwr;
caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES);
if(vdd == -1)
{
if(caps & SDHC_CAP_VOLTAGE_33)
vdd = SDHC_CAP_VOLTAGE_33;
else if(caps & SDHC_CAP_VOLTAGE_30)
vdd = SDHC_CAP_VOLTAGE_30;
else if(caps & SDHC_CAP_VOLTAGE_18)
vdd = SDHC_CAP_VOLTAGE_18;
else
{
sdhc_error(sdhci->reg_base, "no voltage supported by the host? this should never happen...");
return SDHC_ESTRANGE;
}
}
if(!(caps & vdd))
{
sdhc_error(sdhci->reg_base, "voltage %x not supported by the hc", vdd);
return SDHC_EINVAL;
}
pwr = 0;
switch(vdd)
{
case SDHC_CAP_VOLTAGE_33:
pwr |= SDHC_PCTRL_VOLTAGE_33;
break;
case SDHC_CAP_VOLTAGE_30:
pwr |= SDHC_PCTRL_VOLTAGE_30;
break;
case SDHC_CAP_VOLTAGE_18:
pwr |= SDHC_PCTRL_VOLTAGE_18;
break;
default:
sdhc_error(sdhci->reg_base, "invalid vdd: %x", vdd);
return SDHC_EINVAL;
}
__sd_write8(sdhci->reg_base + SDHC_POWER_CONTROL, pwr);
pwr |= SDHC_PCTRL_ENABLE;
__sd_write8(sdhci->reg_base + SDHC_POWER_CONTROL, pwr);
__sd_dumpregs(sdhci);
sdhc_debug(sdhci->reg_base, "card should get voltage now");
if(!(__sd_read8(sdhci->reg_base + SDHC_POWER_CONTROL) & SDHC_PCTRL_ENABLE))
{
sdhc_error(sdhci->reg_base, "pctrl = 0 again");
return SDHC_ESTRANGE;
}
return 0;
}
static s32 __sd_cmd(sdhci_t *sdhci, u32 cmd, u32 type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen)
{
int mode;
int command;
int mask;
int use_dma;
int retval;
int i, imax;
u32 caps;
if(cmd & SD_CMD_ACMD)
{
sdhc_debug(sdhci->reg_base, " cmd %X is ACMD%d, sending CMD55 first", cmd, cmd - SD_CMD_ACMD);
retval = __sd_cmd(sdhci, SD_CMD_APP, SD_R1, sdhci->rca << 16, 0, NULL, NULL, 0);
// TODO: also check the response here?
if(retval < 0)
return retval;
cmd -= SD_CMD_ACMD;
}
__sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS, 0);
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0);
sdhc_debug(sdhci->reg_base, "__sd_cmd: cmd = %X, type = %X, arg = %X, blk_cnt = %d, rlen = %d", cmd, type, arg, blk_cnt, rlen);
if(blk_cnt > SDHC_BLOCKS_MAX)
{
sdhc_error(sdhci->reg_base, "%d blocks are too much...", blk_cnt);
return SDHC_EOVERFLOW;
}
caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES);
if(caps & SDHC_CAP_SDMA && ((u32)buffer % 32 == 0) && blk_cnt > 0 && buffer != NULL)
use_dma = 1;
else
use_dma = 0;
sdhc_debug(sdhci->reg_base, "enable DMA: %d (buffer mod 32: %d)", use_dma, (u32)buffer % 32);
mode = mask = 0;
command = (cmd & SDHC_CMD_MASK) << SDHC_CMD_SHIFT;
if(blk_cnt > 0)
{
if(use_dma == 1)
mode |= SDHC_CMDMODE_DMA_ENABLE;
mode |= SDHC_CMDMODE_MULTIBLOCK;
mode |= SDHC_CMDMODE_ACMD12_ENABLE;
mode |= SDHC_CMDMODE_BLOCKCNT_ENABLE;
if(type & SD_READ)
{
mask = SDHC_BFR_READ_ENABLE;
mode |= SDHC_CMDMODE_READ;
}
else
{
mask = SDHC_BFR_WRITE_ENABLE;
mode |= SDHC_CMDMODE_WRITE;
}
command |= SDHC_CMD_DATA;
}
if(!(type & SD_RSP_PRESENT))
command |= SDHC_CMD_NORESP;
else if(type & SD_RSP_136)
command |= SDHC_CMD_RESP_136;
else if(type & SD_RSP_BUSY)
command |= SDHC_CMD_RESP_BUSY;
else
command |= SDHC_CMD_RESP_48;
if(type & SD_RSP_CRC)
command |= SDHC_CMD_CRC;
if(type & SD_RSP_IDX)
command |= SDHC_CMD_IDXCHECK;
sdhc_debug(sdhci->reg_base, "waiting for command inhibit bits to be cleared..");
retval = __sd_wait32_r(sdhci->reg_base + SDHC_PRESENT_STATE, SDHC_PRESENT_CMD_INHIBIT_BOTH);
if(retval < 0)
{
sdhc_error(sdhci->reg_base, "command inhibit bits were never cleared");
__sd_reset(sdhci, 0);
return retval;
}
sdhc_debug(sdhci->reg_base, "command inhibit bits cleared, sending command");
if(use_dma == 1 && blk_cnt > 0)
{
sdhc_debug(sdhci->reg_base, "preparing buffer for SDMA transfer");
if(mask == SDHC_BFR_WRITE_ENABLE) {
dc_flushrange(buffer, blk_cnt * BLOCK_SIZE);
ahb_flush_to(AHB_SDHC);
} else {
dc_invalidaterange(buffer, blk_cnt * BLOCK_SIZE);
}
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0);
__sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS, 0);
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, INTERRUPT_ALL);
__sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE, EINTERRUPT_ALL);
__sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, dma_addr(buffer));
}
__sd_dumpregs(sdhci);
if(blk_cnt > 0)
{
__sd_write16(sdhci->reg_base + SDHC_BLOCK_SIZE, (BLOCK_SIZE_512K << 12) | BLOCK_SIZE);
__sd_write16(sdhci->reg_base + SDHC_BLOCK_COUNT, blk_cnt);
}
sdhc_debug(sdhci->reg_base, "writing mode = %x, arg = %x, cmd = %x", mode, arg, command);
__sd_write32(sdhci->reg_base + SDHC_CMD_ARG, arg);
__sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE,__sd_read32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE) | INTERRUPT_COMMAND_COMPLETE);
__sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_COMMAND_COMPLETE);
__sd_write32(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE, ((u32)command << 16) | mode);
__sd_dumpregs(sdhci);
sdhc_debug(sdhci->reg_base, "waiting until command phase is done");
retval = __sd_wait32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS,
INTERRUPT_COMMAND_COMPLETE);
// retval = __sd_wait32_r(sdhci->reg_base + SDHC_PRESENT_STATE, SDHC_PRESENT_CMD_INHIBIT_CMD);
if(retval < 0)
{
__sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE,__sd_read32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE) & ~INTERRUPT_COMMAND_COMPLETE);
sdhc_error(sdhci->reg_base, "error: command phase not completed");
__sd_dumpregs(sdhci);
__sd_reset(sdhci, 0);
return retval;
}
sdhc_debug(sdhci->reg_base, "command phase is done");
__sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_COMMAND_COMPLETE);
__sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE,__sd_read32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE) & ~INTERRUPT_COMMAND_COMPLETE);
__sd_dumpregs(sdhci);
for(i = 0; i < 4; i++)
sdhc_debug(sdhci->reg_base, "response %d: %08X", i, __sd_read32(sdhci->reg_base + SDHC_RESPONSE + 4*i));
if(rlen < 4 && type & SD_RSP_PRESENT)
{
sdhc_debug(sdhci->reg_base, "response buffer not big enough for response...");
}
else if(type & SD_RSP_PRESENT)
{
if(type & SD_RSP_136)
{
u8 *p = (u8 *)response;
imax = 15 > rlen ? rlen : 15;
for(i = 0; i < imax; i++)
*p++ = __sd_read8(sdhci->reg_base + SDHC_RESPONSE + i);
for(i = 0; i < 4; i++)
sdhc_debug(sdhci->reg_base, "response %d: %X", i, __sd_read32(sdhci->reg_base + SDHC_RESPONSE + 4*i));
}
else
{
response[0] = __sd_read32(sdhci->reg_base + SDHC_RESPONSE);
sdhc_debug(sdhci->reg_base, "response = %08X", response[0]);
}
}
// FIXME: check response and abort on errors?
if(blk_cnt > 0)
{
sdhc_debug(sdhci->reg_base, "starting transfer of %d %d byte blocks", blk_cnt, BLOCK_SIZE);
if(use_dma == 0)
{
gecko_printf("sdhci: PIO mode is broken, align your buffer and use DMA.\n");
__sd_reset(sdhci, 0);
return SDHC_EIO;
}
else /* SDMA; transfer data in 512 KB blocks (i.e. 1024 512 byte blocks */
{
u8 *ptr = (u8 *)buffer;
sdhc_debug(sdhci->reg_base, "using SDMA to transfer data");
while(1)
{
sdhc_debug(sdhci->reg_base, "waiting for interrupts...");
retval = __sd_wait16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_DMA | INTERRUPT_TRANSFER_COMPLETE);
if(retval < 0)
{
sdhc_error(sdhci->reg_base, "failed while waiting for transfer complete or DMA interrupts...");
__sd_reset(sdhci, 0);
return retval;
}
retval = __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS);
if(retval & INTERRUPT_TRANSFER_COMPLETE)
{
__sd_dumpregs(sdhci);
sdhc_debug(sdhci->reg_base, "transfer completed. disabling interrupts again and returning.");
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_TRANSFER_COMPLETE | INTERRUPT_DMA);
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, 0);
if(mask == SDHC_BFR_READ_ENABLE)
ahb_flush_from(AHB_SDHC);
return 0;
}
else if(retval & INTERRUPT_DMA)
{
u32 blk_left = __sd_read16(sdhci->reg_base + SDHC_BLOCK_COUNT);
blk_left = blk_cnt - blk_left;
ptr = buffer + blk_cnt * SDHC_BLOCK_SIZE;
__sd_dumpregs(sdhci);
sdhc_debug(sdhci->reg_base, "DMA interrupt set, updating next SDMA address");
sdhc_debug(sdhci->reg_base, "sd blocks left: %d, addr: %p -> %p", blk_cnt, ptr, ptr + SDMA_BLOCK_SIZE);
if(blk_cnt == 0)
{
sdhc_error(sdhci->reg_base, "FATAL ERROR: hc wants to transfer more DMA data but no more blocks are left.");
__sd_reset(sdhci, 0);
return SDHC_EIO;
}
__sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_DMA);
__sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, dma_addr(ptr));
sdhc_debug(sdhci->reg_base, "next DMA transfer started.");
__sd_dumpregs(sdhci);
}
}
}
}
return 0;
}
void __sd_print_status(sdhci_t *sdhci)
{
#ifdef SDHC_DEBUG
u32 status;
u32 state;
int retval;
#if 0
// FIXME: doesn't work for some reason ?!?
// sdhc0: Card status: 00000900 [|ò¦|ó¦L]
char *state_name[] = {
"IDLE",
"READY",
"IDENT",
"STANDBY",
"TRANSFER",
"DATA (SEND)",
"RECEIVE",
"PROGRAM",
"DISCONNECT"
};
sdhc_debug(sdhci->reg_base, "Card status %08x [%s]", status, state_name[state]);
#endif
retval = __sd_cmd(sdhci, SD_CMD_SEND_STATUS, SD_R1, (u32)sdhci->rca << 16, 0, NULL, &status, sizeof(status));
state = (status >> 9) & 0x0f;
gecko_printf("sdhc%d: Card status: %08x [", ((sdhci->reg_base - SD_REG_BASE) / 0x100), status);
switch(state)
{
case 0:
gecko_printf("IDLE]\n");
break;
case 1:
gecko_printf("READY]\n");
break;
case 2:
gecko_printf("IDENT]\n");
break;
case 3:
gecko_printf("STANDBY]\n");
break;
case 4:
gecko_printf("TRANSFER]\n");
break;
case 5:
gecko_printf("DATA (SEND)]\n");
break;
case 6:
gecko_printf("DATA (RECEIVE)]\n");
break;
case 7:
gecko_printf("PROGRAM]\n");
break;
case 8:
gecko_printf("DISCONNECT]\n");
break;
default:
gecko_printf("Reserved]\n");
}
#endif
return;
}
static int __sd_getcid(sdhci_t *sdhci) {
u32 resp[4];
u32 swapped[4];
sdhc_debug(sdhci->reg_base, "sending ALL_SEND_CID command to get connected card");
int retval = __sd_cmd(sdhci, SD_CMD_ALL_SEND_CID, SD_R2, 0, 0, NULL, resp, 16);
if(retval < 0)
{
sdhc_error(sdhci->reg_base, "__sd_cmd returned %d, resetting controller.", retval);
__sd_reset(sdhci, 1);
return SDHC_EIO;
}
swapped[0] = bswap32(resp[3]);
swapped[1] = bswap32(resp[2]);
swapped[2] = bswap32(resp[1]);
swapped[3] = bswap32(resp[0]);
memcpy(&sdhci->cid, swapped, sizeof swapped);
sdhc_error(sdhci->reg_base, "MID=%02x OID='%c%c' PNM='%c%c%c%c%c' PRV=%d.%d PSN=%08x MDT=200%d/%02d\n",
sdhci->cid.mid, sdhci->cid.oid[0], sdhci->cid.oid[1],
sdhci->cid.pnm[0], sdhci->cid.pnm[1], sdhci->cid.pnm[2], sdhci->cid.pnm[3], sdhci->cid.pnm[4],
sdhci->cid.prv >> 4, sdhci->cid.prv & 0xf, sdhci->cid.psn, sdhci->cid.mdt >> 4, sdhci->cid.mdt & 0xf);
return 0;
}
static int __sd_getcsd(sdhci_t *sdhci) {
u8 resp[16];
sdhc_debug(sdhci->reg_base, "requesting CSD");
int retval = __sd_cmd(sdhci, SD_CMD_SEND_CSD, SD_R2, sdhci->rca << 16, 0, NULL, (u32 *)resp, 16);
if (retval < 0) {
sdhc_error(sdhci->reg_base, "failed to get CSD register (%d)", retval);
__sd_reset(sdhci, 1);
return SDHC_EIO;
}
sdhc_error(sdhci->reg_base, "CSD = %02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x",
resp[14], resp[13], resp[12], resp[11], resp[10], resp[9], resp[8],
resp[7], resp[6], resp[5], resp[4], resp[3], resp[2], resp[1], resp[0]);
if (resp[13] == 0xe) { // sdhc
unsigned int c_size = resp[7] << 16 | resp[6] << 8 | resp[5];
sdhc_error(sdhci->reg_base, "sdhc mode, c_size=%u, card size = %uk", c_size, (c_size + 1)* 512);
sdhci->timeout = 250 * 1000000; // spec says read timeout is 100ms and write/erase timeout is 250ms
sdhci->num_sectors = (c_size + 1) * 1024; // number of 512-byte sectors
}
else {
unsigned int taac, nsac, read_bl_len, c_size, c_size_mult;
taac = resp[13];
nsac = resp[12];
read_bl_len = resp[9] & 0xF;
c_size = (resp[8] & 3) << 10;
c_size |= (resp[7] << 2);
c_size |= (resp[6] >> 6);
c_size_mult = (resp[5] & 3) << 1;
c_size_mult |= resp[4] >> 7;
sdhc_error(sdhci->reg_base, "taac=%u nsac=%u read_bl_len=%u c_size=%u c_size_mult=%u card size=%u bytes",
taac, nsac, read_bl_len, c_size, c_size_mult, (c_size + 1) * (4 << c_size_mult) * (1 << read_bl_len));
static const unsigned int time_unit[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};
static const unsigned int time_value[] = {1, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80}; // must div by 10
sdhci->timeout = time_unit[taac & 7] * time_value[(taac >> 3) & 0xf] / 10;
sdhc_error(sdhci->reg_base, "calculated timeout = %uns", sdhci->timeout);
sdhci->num_sectors = (c_size + 1) * (4 << c_size_mult) * (1 << read_bl_len) / 512;
}
sdhc_error(sdhci->reg_base, "num sectors = %u", sdhci->num_sectors);
return 0;
}
int sd_mount(sdhci_t *sdhci)
{
u32 caps;
s32 retval;
u32 resp[4];
int tries;
__sd_dumpregs(sdhci);
retval = __sd_reset(sdhci, 1);
if(retval < 0)
return retval;
if(!sd_inserted(sdhci))
return SDHC_ENOCARD;
caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES);
retval = __sd_power(sdhci, -1);
if(retval < 0)
return retval;
retval = __sd_clock(sdhci, 1, 25000);
if(retval < 0)
return retval;
retval = __sd_cmd(sdhci, SD_CMD_RESET_CARD, SD_R0, 0x0, 0, NULL, NULL, 0);
if(retval < 0)
return retval;
// check for SDHC
retval = __sd_cmd(sdhci, SD_CMD_SEND_IF_COND, SD_R7, 0x1AA, 0, NULL, resp, 6);
if(retval < 0 || (resp[0] & 0xff) != 0xAA)
{
// SDv1 low-capacity card#
sdhc_error(sdhci->reg_base, "SDv1 low-capacity card deteced. resetting controller and card again.");
__sd_reset(sdhci, 1);
retval = __sd_power(sdhci, -1);
if(retval < 0)
return retval;
retval = __sd_clock(sdhci, 1, 25000);
if(retval < 0)
return retval;
retval = __sd_cmd(sdhci, SD_CMD_RESET_CARD, SD_R0, 0x0, 0, NULL, NULL, 0);
if(retval < 0)
return retval;
sdhci->is_sdhc = 0;
sdhci->ocr = 0;
}
else
{
// SDHC card deteced
sdhc_error(sdhci->reg_base, "SDv2 card detected.");
sdhci->is_sdhc = 1;
sdhci->ocr = OCR_HCS;
}
if(caps & SDHC_CAP_VOLTAGE_33)
sdhci->ocr |= MMC_VDD_32_33|MMC_VDD_33_34;
if(caps & SDHC_CAP_VOLTAGE_30)
sdhci->ocr |= MMC_VDD_29_30|MMC_VDD_30_31;
if(caps & SDHC_CAP_VOLTAGE_18)
sdhci->ocr |= MMC_VDD_165_195;
sdhc_debug(sdhci->reg_base, "waiting for card to finalize power up");
tries = 0;
resp[0] = 0;
while(tries++ <= SDHC_WAIT_TIMEOUT_OUTER_MULTIPLY)
{
retval = __sd_cmd(sdhci, SD_CMD_APP_SEND_OP_COND, SD_R3, sdhci->ocr, 0, NULL, resp, 6);
if(resp[0] & OCR_POWERUP_STATUS)
{
sdhc_error(sdhci->reg_base, "card power up is done.");
break;
}
udelay(SDHC_WAIT_TIMEOUT_OUTER);
}
if(!(resp[0] & OCR_POWERUP_STATUS))
{
sdhc_error(sdhci->reg_base, "powerup failed, resetting controller.");
__sd_reset(sdhci, 1);
return SDHC_EIO;
}
sdhci->ocr = resp[0];
if(sdhci->ocr & OCR_CCS)
{
sdhc_debug(sdhci->reg_base, "SDHC card detected, using block instead of byte offset address mode");
sdhci->is_sdhc = 1;
}
else
{
sdhc_debug(sdhci->reg_base, "low-capacity SD card detected. using byte offset address mode.");
sdhci->is_sdhc = 0;
}
retval = __sd_getcid(sdhci);
if (retval) return retval;
retval = __sd_cmd(sdhci, SD_CMD_SEND_RELATIVE_ADDR, SD_R6, 0, 0, NULL, resp, 6);
if(retval < 0)
{
sdhc_error(sdhci->reg_base, "failed at getting RCA (%d), resetting controller.", retval);
__sd_reset(sdhci, 1);
return SDHC_EIO;
}
sdhci->rca = (resp[0] >> 16) & 0xffff;
sdhc_debug(sdhci->reg_base, "RCA: %04X", sdhci->rca);
retval = __sd_getcsd(sdhci);
if (retval) return retval;
__sd_print_status(sdhci);
sd_select(sdhci);
__sd_print_status(sdhci);
retval = __sd_cmd(sdhci, SD_CMD_SET_BLOCKLEN, SD_R1, BLOCK_SIZE, 0, NULL, NULL, 0);
if(retval < 0)
{
sdhc_debug(sdhci->reg_base, "failed to set the block length to 512bytes (%d)", retval);
return retval;
}
__sd_print_status(sdhci);
sd_select(sdhci);
sdhc_debug(sdhci->reg_base, "setting bus width to 4");
__sd_write8(sdhci->reg_base + SDHC_HOST_CONTROL, __sd_read8(sdhci->reg_base + SDHC_HOST_CONTROL) | SDHC_HCR_BUSWIDTH_4);
retval = __sd_cmd(sdhci, SD_CMD_APP_SET_BUS_WIDTH, SD_R1, 2, 0, NULL, NULL, 0);
if(retval < 0)
{
sdhc_debug(sdhci->reg_base, "failed to set bus width to 4: %d", retval);
return retval;
}
sdhci->is_mounted = 1;
return 0;
}
int sd_inserted(sdhci_t *sdhci)
{
return (__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & SDHC_CARD_INSERTED) == SDHC_CARD_INSERTED;
}
#ifndef _READONLY
int sd_protected(sdhci_t *sdhci)
{
return (__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & SDHC_WRITE_PROTECT) == SDHC_WRITE_PROTECT;
}
#endif
int sd_init(sdhci_t *sdhci, int slot)
{
memset(sdhci, 0, sizeof *sdhci);
if(slot > 1)
return SDHC_EINVAL;
sdhci->reg_base = SD_REG_BASE + slot * 0x100;
return 0;
}
int sd_cmd(sdhci_t *sdhci, u32 cmd, u32 type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen)
{
return __sd_cmd(sdhci, cmd, type, arg, blk_cnt, buffer, response, rlen);
}
int sd_select(sdhci_t *sdhci)
{
int retval;
if(sdhci->is_selected == 1)
return 0;
retval = __sd_cmd(sdhci, SD_CMD_SELECT_CARD, SD_R1B, (u32)sdhci->rca << 16, 0, NULL, NULL, 0);
if(retval < 0)
{
sdhc_debug(sdhci->reg_base, "selecting card failed with %d", retval);
return retval;
}
sdhci->is_selected = 1;
sdhc_debug(sdhci->reg_base, "card selected");
return 0;
}
int sd_read(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, void *buffer)
{
int retval;
u32 response;
if(sdhci->is_mounted != 1)
return SDHC_EINVAL;
retval = sd_select(sdhci);
if(retval < 0)
return retval;
__sd_print_status(sdhci);
if(sdhci->is_sdhc == 0)
start_block *= 512;
dc_invalidaterange(buffer, blk_cnt * 512);
retval = __sd_cmd(sdhci, SD_CMD_READ_MULTIPLE_BLOCK, SD_R1 | SD_READ, start_block, blk_cnt, buffer, &response, sizeof(response));
if(retval < 0)
sdhc_debug(sdhci->reg_base, "reading blocks failed with %d.", retval);
__sd_print_status(sdhci);
return retval;
}
#if _READONLY == 1
int sd_write(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, const void *buffer)
{
int retval;
u32 response;
if(sdhci->is_mounted != 1)
return SDHC_EINVAL;
retval = sd_select(sdhci);
if(retval < 0)
return retval;
__sd_print_status(sdhci);
if(sdhci->is_sdhc == 0)
start_block *= 512;
dc_flushrange(buffer, blk_cnt * 512);
retval = __sd_cmd(sdhci, SD_CMD_WRITE_MULTIPLE_BLOCK, SD_R1, start_block, blk_cnt, (void *)buffer, &response, sizeof(response));
if(retval < 0)
sdhc_debug(sdhci->reg_base, "writing blocks failed with %d.", retval);
__sd_print_status(sdhci);
return retval;
}
#endif
void sd_initialize(void)
{
ipc_code = ipc_tag = 0;
sd_init(&sdhci, 0);
// irq_enable(IRQ_NAND); ??
}
void sd_ipc(volatile ipc_request *req)
{
int retval = 0;
if (ipc_code != 0 || ipc_tag != 0) {
gecko_printf("SDHC: previous IPC request is not done yet.");
ipc_post(req->code, req->tag, 1, -1);
return;
}
switch (req->req) {
case IPC_SD_MOUNT:
retval = sd_mount(&sdhci);
ipc_post(req->code, req->tag, 1, retval);
break;
case IPC_SD_SELECT:
retval = sd_select(&sdhci);
ipc_post(req->code, req->tag, 1, retval);
break;
case IPC_SD_GETSTATE:
retval = __sd_read32(sdhci.reg_base + SDHC_PRESENT_STATE);
ipc_post(req->code, req->tag, 1, retval);
break;
case IPC_SD_READ:
retval = sd_read(&sdhci, req->args[0], req->args[1],
(void *)req->args[2]);
ipc_post(req->code, req->tag, 1, retval);
break;
case IPC_SD_WRITE:
retval = sd_write(&sdhci, req->args[0], req->args[1],
(void *)req->args[2]);
ipc_post(req->code, req->tag, 1, retval);
break;
case IPC_SD_GETSIZE:
ipc_post(req->code, req->tag, 1, sdhci.num_sectors);
break;
default:
gecko_printf("IPC: unknown SLOW SDHC request %04x\n",
req->req);
}
}