|
@@ -26,12 +26,26 @@
|
|
|
#include <string.h>
|
|
|
|
|
|
#include "fw.h"
|
|
|
-#include "console.h"
|
|
|
#include "io.h"
|
|
|
#include "systime.h"
|
|
|
+#include "console.h"
|
|
|
#include "ff.h"
|
|
|
#include "diskio.h"
|
|
|
|
|
|
+#ifndef DEBUG
|
|
|
+# define DEBUG 0
|
|
|
+#endif
|
|
|
+
|
|
|
+#if DEBUG
|
|
|
+# define dbg_printf con_printf
|
|
|
+# define dbg_puts con_puts
|
|
|
+# define dbg_putc con_putc
|
|
|
+#else
|
|
|
+# define dbg_printf(f,...) ((void)0)
|
|
|
+# define dbg_puts(x) ((void)0)
|
|
|
+# define dbg_putc(x) ((void)0)
|
|
|
+#endif
|
|
|
+
|
|
|
#define SECTOR_SHIFT 9
|
|
|
#define SECTOR_SIZE (1UL << SECTOR_SHIFT)
|
|
|
|
|
@@ -132,27 +146,29 @@ static void sdcard_led_off(void)
|
|
|
|
|
|
static int sdcard_send_cmd(uint8_t opcode, uint32_t argument)
|
|
|
{
|
|
|
+ int status = -1;
|
|
|
int i;
|
|
|
|
|
|
if (!opcode)
|
|
|
return 0; /* No command */
|
|
|
|
|
|
+ dbg_printf("sdcard: CMD%02u arg %08x:", opcode ^ 0x40, argument);
|
|
|
+
|
|
|
sd_writeb(opcode, SD_GO8|SD_CLEARCRC);
|
|
|
sd_writel(argument, SD_BE|SD_GO32);
|
|
|
- /*
|
|
|
- * GO16 here is to shift in the first byte after into the read
|
|
|
- * shift register.
|
|
|
- */
|
|
|
+
|
|
|
+ /* GO16 so we discard the shifted-in byte from during the CRC7 */
|
|
|
sd_writeb(sd_crc7_wr(), SD_GO16);
|
|
|
|
|
|
/* The spec says a reply within 8 cycles, cut it some slack */
|
|
|
for (i = 16; i; i--) {
|
|
|
- int8_t status = sd_readb(SD_GO8);
|
|
|
- if (status >= 0) /* Bit 7 = 0 for a valid reply */
|
|
|
- return status;
|
|
|
+ status = sd_readb(SD_GO8);
|
|
|
+ dbg_printf(" %02x", status);
|
|
|
+ if ((int8_t)status >= 0) /* Bit 7 = 0 for a valid reply */
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
- return -1; /* Error */
|
|
|
+ dbg_putc('\n');
|
|
|
+ return status;
|
|
|
}
|
|
|
|
|
|
static int sdcard_send_acmd(uint8_t opcode, uint32_t argument)
|
|
@@ -193,22 +209,40 @@ static int sdcard_read_block(void *buf, int len, int timeout,
|
|
|
uint16_t zcrc;
|
|
|
union xptr p;
|
|
|
int i;
|
|
|
+ int badtimeout = 8;
|
|
|
|
|
|
p.b = buf;
|
|
|
|
|
|
+ /*
|
|
|
+ * Are we supposed to send a command in parallel?
|
|
|
+ * This is unsafe for small blocks, but we only need to do this
|
|
|
+ * for full sectors (used to send STOP_TRANSMISSION).
|
|
|
+ */
|
|
|
+ if (xcmd)
|
|
|
+ len -= 6; /* Handle the last 6 bytes specially */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Wait for data token
|
|
|
+ */
|
|
|
+ dbg_puts("sdcard: read token:");
|
|
|
for (;;) {
|
|
|
tok = sd_readb(SD_GO8|SD_CLEARCRC);
|
|
|
+ dbg_printf(" %02x", tok);
|
|
|
+
|
|
|
if (tok == 0xfe)
|
|
|
break;
|
|
|
- if (tok < 0xfe) {
|
|
|
- con_printf("sdcard_read_block: bad token: %02x\n", tok);
|
|
|
+
|
|
|
+ if (tok < 0xfe && !--badtimeout) {
|
|
|
+ dbg_printf("\nsdcard: read_block: bad token: %02x\n", tok);
|
|
|
return -1; /* Bad token */
|
|
|
}
|
|
|
+
|
|
|
if (!--timeout) {
|
|
|
- con_printf("sdcard_read_block: reply timeout\n");
|
|
|
+ dbg_printf("\nsdcard: read_block: reply timeout\n");
|
|
|
return -1; /* Timeout */
|
|
|
}
|
|
|
}
|
|
|
+ dbg_putc('\n');
|
|
|
|
|
|
/*
|
|
|
* At this point the first byte after the token is latched into
|
|
@@ -227,25 +261,23 @@ static int sdcard_read_block(void *buf, int len, int timeout,
|
|
|
*p.w++ = sd_readh(SD_GO16);
|
|
|
len -= 2;
|
|
|
}
|
|
|
- sd_readh(SD_GO16); /* Now total of 4 bytes latched */
|
|
|
+
|
|
|
+ if (len >= 6) {
|
|
|
+ sd_readh(SD_GO16); /* Now total of 4 bytes latched */
|
|
|
+ while (len >= 6) {
|
|
|
+ *p.l++ = sd_readl(SD_GO32);
|
|
|
+ len -= 4;
|
|
|
+ }
|
|
|
+ *p.w++ = sd_readh(2); /* Consume the two least recent bytes */
|
|
|
+ len -= 2;
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
- * Are we supposed to send a command in parallel?
|
|
|
- * This is unsafe for small blocks, but we only need to do this
|
|
|
- * for full sectors (used to send STOP_TRANSMISSION).
|
|
|
+ * At this point, we are aligned to a 2-byte boundary with 2 bytes
|
|
|
+ * in the input shift register; we need to maintain those two bytes
|
|
|
+ * for the CRC.
|
|
|
*/
|
|
|
- if (xcmd)
|
|
|
- len -= 6; /* Handle the last 6 bytes specially */
|
|
|
-
|
|
|
- while (len >= 8) {
|
|
|
- *p.l++ = sd_readl(SD_GO32);
|
|
|
- len -= 4;
|
|
|
- }
|
|
|
- if (len & 4)
|
|
|
- *p.l++ = sd_readl(SD_GO16); /* Consume latched lword + shift in CRC */
|
|
|
- if (len & 2)
|
|
|
- *p.w++ = sd_readh(SD_GO16);
|
|
|
- if (len & 1)
|
|
|
+ while (len--)
|
|
|
*p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
|
|
|
/*
|
|
@@ -255,36 +287,44 @@ static int sdcard_read_block(void *buf, int len, int timeout,
|
|
|
* do the latch reads/writes to memory as byte I/O.
|
|
|
* We still have 2 bytes of data latched, and should end
|
|
|
* with the same (for the CRC).
|
|
|
+ *
|
|
|
+ * To keep the logic anything remotely consistent, passively stuff
|
|
|
+ * the new command into the transmit register before triggering
|
|
|
+ * active read operations.
|
|
|
+ *
|
|
|
+ * Note that the destination buffer may be unaligned here.
|
|
|
*/
|
|
|
if (xcmd) {
|
|
|
- uint8_t crc7;
|
|
|
-
|
|
|
- *p.b++ = sd_readb(1);
|
|
|
- *p.b++ = sd_readb(0);
|
|
|
- sd_writeb(xcmd, SD_GO8|SD_CLEARCRC);
|
|
|
- *p.b++ = sd_readb(0);
|
|
|
- sd_writel(xarg, SD_GO32|SD_BE);
|
|
|
- *p.b++ = sd_readb(3);
|
|
|
- *p.b++ = sd_readb(2);
|
|
|
- *p.b++ = sd_readb(1);
|
|
|
- crc7 = sd_crc7_wr();
|
|
|
- sd_writeb(crc7, SD_GO8);
|
|
|
+ sd_writeb(xcmd, SD_CLEARCRC);
|
|
|
+ *p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
+
|
|
|
+ sd_writel(xarg, SD_BE);
|
|
|
+ *p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
+ *p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
+ *p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
+ *p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
+
|
|
|
+ sd_writeb(sd_crc7_wr(), 0);
|
|
|
+ *p.b++ = sd_readb(SD_GO8 | 1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Now the CRC is latched in the shift register, and the CRC
|
|
|
- * in the CRC generator should be zero. Shift in the first
|
|
|
- * byte after the CRC for the next round.
|
|
|
+ * in the CRC generator should be zero.
|
|
|
*/
|
|
|
|
|
|
zcrc = sd_crc16_rd();
|
|
|
- sd_readb(SD_GO8);
|
|
|
-
|
|
|
if (zcrc != 0x0000) {
|
|
|
con_printf("sdcard_read_block: CRC error (zcrc = %04x)\n", zcrc);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Shift in the first
|
|
|
+ * byte after the CRC for the next round.
|
|
|
+ */
|
|
|
+ sd_readb(SD_GO8);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -306,13 +346,14 @@ int sdcard_read_sectors(void *buf, uint32_t lba, int count)
|
|
|
|
|
|
sdcard_led_on();
|
|
|
|
|
|
- con_printf("sdcard: reading %d sectors at %u to %p\n", count, lba, buf);
|
|
|
+ con_printf("sdcard: reading %d sector%s at %u to %p\n",
|
|
|
+ count, (count != 1) ? "s" : "", lba, buf);
|
|
|
|
|
|
if (sdc.card_type == 1)
|
|
|
lba <<= SECTOR_SHIFT; /* Convert to a byte address */
|
|
|
|
|
|
rv = sdcard_send_cmd(CMD_READ_MULTIPLE_BLOCK, lba);
|
|
|
- if (rv) {
|
|
|
+ if (rv & ~1) {
|
|
|
con_printf("sdcard: read_multiple error %02x\n", rv);
|
|
|
goto out;
|
|
|
}
|
|
@@ -359,22 +400,44 @@ DRESULT disk_read(BYTE drive, BYTE *buffer,
|
|
|
/*
|
|
|
* Read CSD/CID
|
|
|
*/
|
|
|
-static int sdcard_read_reg(uint8_t opcode, void *buf)
|
|
|
+static int sdcard_read_reg(uint8_t opcode, void *buf, const char *name)
|
|
|
{
|
|
|
int rv;
|
|
|
uint32_t *bp = buf;
|
|
|
unsigned int i;
|
|
|
|
|
|
+ memset(buf, 0, 16);
|
|
|
+
|
|
|
+ con_printf("sdcard: %s:", name);
|
|
|
+
|
|
|
rv = sdcard_send_cmd(opcode, 0);
|
|
|
- if (rv)
|
|
|
- return rv;
|
|
|
+ if (rv & ~1)
|
|
|
+ goto err;
|
|
|
|
|
|
rv = sdcard_read_block(buf, 16, 2000, 0, 0);
|
|
|
if (rv)
|
|
|
- return rv;
|
|
|
+ goto err;
|
|
|
|
|
|
- for (i = 0; i < 4; i++)
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
bp[i] = __builtin_bswap32(bp[i]);
|
|
|
+ con_printf(" %08x", bp[i]);
|
|
|
+ }
|
|
|
+ con_putc('\n');
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err:
|
|
|
+ con_printf(" failed, err %02x\n", rv);
|
|
|
+ return rv;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdcard_read_csd(void)
|
|
|
+{
|
|
|
+ return sdcard_read_reg(CMD_SEND_CSD, &sdc.csd, "CSD");
|
|
|
+}
|
|
|
+
|
|
|
+static int sdcard_read_cid(void)
|
|
|
+{
|
|
|
+ return sdcard_read_reg(CMD_SEND_CID, &sdc.cid, "CID");
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -561,41 +624,58 @@ static const char *sdcard_type_name(uint8_t type)
|
|
|
static void sdcard_try_high_speed(void)
|
|
|
{
|
|
|
int rv;
|
|
|
+ uint8_t tran_speed;
|
|
|
+
|
|
|
+ return;
|
|
|
|
|
|
if (!(sdc.csd.raw[1] & (1 << 30)))
|
|
|
return; /* Cmd group 10 = CMD6 not supported */
|
|
|
|
|
|
/* Try to switch to high speed mode */
|
|
|
rv = sdcard_send_cmd(CMD_SWITCH_FUNC, 0x80fffff1);
|
|
|
- if (rv) {
|
|
|
- con_printf("sdcard: CMD6 returned %02x\n", rv);
|
|
|
+ if (rv & ~1) {
|
|
|
+ dbg_printf("sdcard: CMD6 returned %02x\n", rv);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (0) {
|
|
|
+ if (1) {
|
|
|
/*
|
|
|
* Despite the spec, this doesn't seem to actually happen
|
|
|
* in SPI mode?
|
|
|
*/
|
|
|
uint8_t swdata[64]; /* Response from CMD6 */
|
|
|
+ int i;
|
|
|
|
|
|
- rv = sdcard_read_block(swdata, sizeof swdata, 2000, 0, 0);
|
|
|
- if (rv) {
|
|
|
- con_printf("sdcard: CMD6 failed to return data\n");
|
|
|
- return;
|
|
|
- }
|
|
|
+ for (i = 0; i < 64; i++)
|
|
|
+ swdata[i] = sd_readb(SD_GO8);
|
|
|
|
|
|
if ((swdata[47] & 0x0f) != 1) {
|
|
|
- con_printf("sdcard: CMD6 reported %X for high speed request\n",
|
|
|
+ dbg_printf("sdcard: CMD6 reported %X for high speed request\n",
|
|
|
swdata[47] & 0x0f);
|
|
|
return; /* Failed to switch to high speed mode */
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* Success, now switch mode! */
|
|
|
+ /*
|
|
|
+ * Success, we should have switched mode.
|
|
|
+ * This should be refleded in the TRAN_SPEED field in the CSD.
|
|
|
+ */
|
|
|
sd_readl(SD_GO32); /* Issue at least 8 clocks; go for 32 */
|
|
|
- sd_set_mode(SD_50MHZ, true);
|
|
|
|
|
|
+ /* Re-read the CSD */
|
|
|
+ sdcard_read_csd();
|
|
|
+
|
|
|
+ /* TRAN_SPEED should have changed now */
|
|
|
+ tran_speed = (uint8_t)sdc.csd.raw[0];
|
|
|
+
|
|
|
+ if ((tran_speed & 7) < 2 ||
|
|
|
+ ((tran_speed & 7) == 2 && (tran_speed >> 3) < 0xb)) {
|
|
|
+ dbg_printf("sdcard: speed switch failed, tran_speed %02x\n",
|
|
|
+ tran_speed);
|
|
|
+ return; /* High speed not available */
|
|
|
+ }
|
|
|
+
|
|
|
+ sd_set_mode(SD_50MHZ, true);
|
|
|
con_printf("sdcard: switched to high speed\n");
|
|
|
}
|
|
|
|
|
@@ -643,7 +723,7 @@ DSTATUS disk_initialize(BYTE drive)
|
|
|
break; /* Success! */
|
|
|
|
|
|
if (!--i) {
|
|
|
- con_printf("sdcard: CMD0 error %02x\n", rv);
|
|
|
+ con_printf("sdcard: reset failed, assuming no card present\n", rv);
|
|
|
sdcard_led_off();
|
|
|
return sdc.status = STA_NOINIT | STA_NODISK;
|
|
|
}
|
|
@@ -664,7 +744,7 @@ DSTATUS disk_initialize(BYTE drive)
|
|
|
if ((rv & 0x04) == 0) {
|
|
|
/* CMD8 supported */
|
|
|
if (rv & ~0x03) {
|
|
|
- con_printf("sdcard; CMD8 error %02x\n", rv);
|
|
|
+ dbg_printf("sdcard; CMD8 error %02x\n", rv);
|
|
|
sdcard_led_off();
|
|
|
return sdc.status = STA_NOINIT;
|
|
|
}
|
|
@@ -674,7 +754,7 @@ DSTATUS disk_initialize(BYTE drive)
|
|
|
sd_readh(SD_GO16);
|
|
|
sdc.if_cond = sd_readl(SD_GO16|SD_BE);
|
|
|
|
|
|
- con_printf("sdcard: CMD8 returned 0x%08x\n", sdc.if_cond);
|
|
|
+ dbg_printf("sdcard: CMD8 returned 0x%08x\n", sdc.if_cond);
|
|
|
|
|
|
if ((sdc.if_cond & 0x1ff) != 0x1aa) {
|
|
|
con_printf("sdcard: CMD8 reports unusable card (0x%x)\n",
|
|
@@ -741,32 +821,26 @@ DSTATUS disk_initialize(BYTE drive)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Read CSD and CID
|
|
|
+ * Read the CSD to figure out what command sets are available...
|
|
|
*/
|
|
|
- if (sdcard_read_reg(CMD_SEND_CSD, &sdc.csd))
|
|
|
- memset(&sdc.csd, 0, sizeof sdc.csd);
|
|
|
+ sdcard_read_csd(); /* Read CSD */
|
|
|
|
|
|
- con_printf("sdcard: CSD: %08x %08x %08x %08x\n",
|
|
|
- sdc.csd.raw[0], sdc.csd.raw[1], sdc.csd.raw[2], sdc.csd.raw[3]);
|
|
|
-
|
|
|
- if (sdcard_read_reg(CMD_SEND_CID, &sdc.cid))
|
|
|
- memset(&sdc.cid, 0, sizeof sdc.cid);
|
|
|
+ /*
|
|
|
+ * Try to switch to 50 MHz (optional)
|
|
|
+ */
|
|
|
+ if (is_sd)
|
|
|
+ sdcard_try_high_speed();
|
|
|
|
|
|
- con_printf("sdcard: CID: %08x %08x %08x %08x\n",
|
|
|
- sdc.cid.raw[0], sdc.cid.raw[1], sdc.cid.raw[2], sdc.cid.raw[3]);
|
|
|
+ /*
|
|
|
+ * Read the CID
|
|
|
+ */
|
|
|
+ sdcard_read_cid();
|
|
|
|
|
|
sdcard_compute_size(&sdc);
|
|
|
|
|
|
con_printf("sdcard: %s card found, capacity %u sectors\n",
|
|
|
sdcard_type_name(sdc.card_type), sdc.lbasize);
|
|
|
|
|
|
-
|
|
|
- /*
|
|
|
- * Try to switch to 50 MHz (optional)
|
|
|
- */
|
|
|
- if (is_sd)
|
|
|
- sdcard_try_high_speed();
|
|
|
-
|
|
|
sdc.status = 0;
|
|
|
|
|
|
sdcard_led_off();
|
|
@@ -785,5 +859,28 @@ static FATFS sd_fs;
|
|
|
|
|
|
int disk_init(void)
|
|
|
{
|
|
|
- return f_mount(&sd_fs, "", 1);
|
|
|
+ FRESULT rv;
|
|
|
+ char label[128];
|
|
|
+ uint32_t volid, freeclust;
|
|
|
+ FATFS *fs;
|
|
|
+
|
|
|
+ rv = f_mount(&sd_fs, "", 1);
|
|
|
+ if (rv != FR_OK) {
|
|
|
+ con_printf("sdcard: no volume found\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ label[0] = '\0';
|
|
|
+ volid = 0;
|
|
|
+
|
|
|
+ f_getlabel("", label, &volid);
|
|
|
+ con_printf("sdcard: volume found, label \"%s\", volid %08x\n", label, volid);
|
|
|
+
|
|
|
+ freeclust = 0;
|
|
|
+ f_getfree("", &freeclust, &fs);
|
|
|
+
|
|
|
+ con_printf("sdcard: %u/%u clusters free, clusters = %u bytes\n",
|
|
|
+ freeclust, fs->n_fatent - 2, fs->csize << 9);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|