savezelda/loader/sd.c

284 lines
5.2 KiB
C
Raw Normal View History

2009-05-26 10:12:55 +02:00
// Copyright 2008 Haxx Enterprises <bushing@gmail.com>
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
// This code is licensed to you under the terms of the GNU GPL, version 2;
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
#include "loader.h"
static int fd;
static u32 rca; // 16 bottom bits are stuff bits
static int sd_hc_write8(u8 reg, u8 data)
{
u32 param[6];
int err;
memset(param, 0, sizeof param);
param[0] = reg;
param[3] = 1; // reg size
param[4] = data;
err = ios_ioctl(fd, 1, param, sizeof param, 0, 0);
return err;
}
static int sd_hc_read8(u8 reg, u8 *x)
{
u32 param[6];
u32 data;
int err;
memset(param, 0, sizeof param);
param[0] = reg;
param[3] = 1; // reg size
param[4] = 0;
err = ios_ioctl(fd, 2, param, sizeof param, &data, sizeof data);
if (err)
return err;
*x = data;
return err;
}
static int sd_reset_card(void)
{
u32 reply;
int err;
err = ios_ioctl(fd, 4, 0, 0, &reply, sizeof reply);
if (err)
return err;
rca = reply & 0xffff0000;
// printf("sd_reset_card(): got reply = %08x\n", reply);
return 0;
}
static int sd_set_clock(void)
{
u32 clock;
int err;
clock = 1; // half of the sdclk divisor: a power of two or zero,
// should look at capabilities reg to compute this
err = ios_ioctl(fd, 6, &clock, sizeof clock, 0, 0);
return err;
}
static int sd_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
u32 block_count, u32 block_size, void *addr,
u32 *outreply, u32 reply_size)
{
u32 param[9];
u32 reply[4];
int err;
param[0] = cmd;
param[1] = cmd_type;
param[2] = resp_type;
param[3] = arg;
param[4] = block_count;
param[5] = block_size;
param[6] = (u32)addr;
param[7] = 0; // ???
param[8] = 0; // ???
err = ios_ioctl(fd, 7, param, sizeof param, reply, sizeof reply);
if (reply_size) // ???
memcpy(outreply, reply, reply_size);
return err;
}
#define TYPE_BC 1
#define TYPE_BCR 2
#define TYPE_AC 3
#define TYPE_ADTC 4
#define RESPONSE_NONE 0
#define RESPONSE_R1 1
#define RESPONSE_R1B 2
#define RESPONSE_R2 3
#define RESPONSE_R3 4
#define RESPONSE_R4 5
#define RESPONSE_R5 6
#define RESPONSE_R6 7
static int sd_app_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
u32 block_count, u32 block_size, void *addr,
u32 *outreply, u32 reply_size)
{
int err;
err = sd_command(55, TYPE_AC, RESPONSE_R1, rca, 0, 0, 0, 0, 0);
if (err)
return err;
err = sd_command(cmd, cmd_type, resp_type, arg,
block_count, block_size, addr,
outreply, reply_size);
return err;
}
static int sd_data_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
u32 block_count, u32 block_size, void *data,
u32 unk1, u32 unk2, u32 *outreply, u32 reply_size)
{
u32 param[9];
u32 reply[4];
struct ioctlv vec[3];
int err;
param[0] = cmd;
param[1] = cmd_type;
param[2] = resp_type;
param[3] = arg;
param[4] = block_count;
param[5] = block_size;
param[6] = (u32)data;
param[7] = unk1; // ???
param[8] = unk2; // ???
vec[0].data = param;
vec[0].len = sizeof param;
vec[1].data = data;
vec[1].len = block_count * block_size;
vec[2].data = reply;
vec[2].len = sizeof reply;
err = ios_ioctlv(fd, 7, 2, 1, vec);
if (reply_size) // ???
memcpy(outreply, reply, reply_size);
return err;
}
static int sd_select(void)
{
int err;
//printf("Selecting card:\n");
err = sd_command(7, TYPE_AC, RESPONSE_R1B, rca, 0, 0, 0, 0, 0);
return err;
}
static int sd_set_blocklength(u32 len)
{
int err;
//printf("sd_set_blocklength(%u)\n", len);
err = sd_command(16, TYPE_AC, RESPONSE_R1, len, 0, 0, 0, 0, 0);
return err;
}
static int sd_set_bus_width(int width)
{
u32 arg;
u8 reg;
int err;
// First notify the card.
arg = (width == 4) ? 2 : 0;
//printf("sd_set_bus_width()\n");
err = sd_app_command(6, TYPE_AC, RESPONSE_R1, arg, 0, 0, 0, 0, 0);
if (err)
return err;
// Now change the Host Control Register.
err = sd_hc_read8(0x28, &reg);
if (err)
return err;
reg = (reg & ~2) | arg;
err = sd_hc_write8(0x28, reg);
return err;
}
int sd_read_sector(u8 *data, u32 offset)
{
u32 reply[4];
int err;
if (offset >= 0x800000)
return -1;
err = sd_data_command(18, TYPE_AC, RESPONSE_R1, 0x200 * offset,
1, 0x200, data, 1, 0, reply, sizeof reply);
sync_before_read(data, 0x200);
//printf("READ block %d\r",offset);
if (err)
printf("SD READ %d: err=%08x, reply=%08x %08x %08x %08x\n",
offset, err, reply[0], reply[1], reply[2], reply[3]);
return err;
}
int sd_close(void)
{
return ios_close(fd);
}
int sd_init(void)
{
int err;
fd = ios_open("/dev/sdio/slot0", 0);
if (fd < 0)
return fd;
err = sd_reset_card();
if (err) {
printf("SD Card not present? (%d)\n", err);
goto out;
}
// now in standby state
err = sd_select();
if (err)
goto out;
// now in transfer state
// Some broken cards require this:
err = sd_set_blocklength(0x200);
if (err)
goto out;
err = sd_set_bus_width(4); // XXX: Should check in SCR first.
if (err)
goto out;
err = sd_set_clock(); // XXX: Should check.
if (err)
goto out;
return 0;
out:
sd_close();
return err;
}