/* ----------------------------------------------------------------------- * * * Copyright 2010-2021 H. Peter Anvin - All Rights Reserved * * 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, Inc., 51 Franklin St, Fifth Floor, * Boston MA 02110-1301, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ /* * sdcard.c * * SD card block driver * * Note: the handling of read operations is tricky, because they pick up * the results from the *previous* transaction. Therefore there are some * serious subtleties, especially with which byte position in the shift * register the result ends up in and what the size flag should be set to. */ #include #include #include #include "fw.h" #include "io.h" #include "sdcard.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) /* Command codes, including the leading 01 sequence */ enum sdcard_cmd { CMD_GO_IDLE_STATE = 0x40, /* a.k.a. reset */ CMD_SEND_OP_COND = 0x41, CMD_SWITCH_FUNC = 0x46, CMD_SEND_IF_COND = 0x48, CMD_SEND_CSD = 0x49, CMD_SEND_CID = 0x4a, CMD_STOP_TRANSMISSION = 0x4c, CMD_SEND_STATUS = 0x4d, CMD_SET_BLOCKLEN = 0x50, CMD_READ_SINGLE_BLOCK = 0x51, CMD_READ_MULTIPLE_BLOCK = 0x52, CMD_WRITE_BLOCK = 0x58, CMD_WRITE_MULTIPLE_BLOCK = 0x59, CMD_PROGRAM_CSD = 0x5b, CMD_SET_WRITE_PROT = 0x5c, CMD_CLR_WRITE_PROT = 0x5d, CMD_SEND_WRITE_PROT = 0x5e, CMD_ERASE_WR_BLK_START_ADDR = 0x60, CMD_ERASE_WR_BLK_END_ADDR = 0x61, CMD_ERASE = 0x66, CMD_LOCK_UNLOCK = 0x6a, CMD_APP_CMD = 0x77, /* == ACMD prefix */ CMD_GEN_CMD = 0x78, CMD_READ_OCR = 0x7a, CMD_CRC_ON_OFF = 0x7b }; enum sdcard_acmd { ACMD_SD_STATUS = 0x4d, ACMD_SEND_NUM_WR_BLOCKS = 0x56, ACMD_SET_WR_BLK_ERASE_COUNT = 0x57, ACMD_SD_SEND_OP_COND = 0x69, ACMD_SET_CLR_CARD_DETECT = 0x6a, ACMD_SEND_SCR = 0x73 }; struct sdcard_csd { uint32_t raw[4]; }; struct sdcard_cid { uint32_t raw[4]; }; struct sdcard_info { DSTATUS status; int8_t card_type; unsigned long lbasize; uint32_t if_cond; uint32_t ocr; struct sdcard_csd csd; struct sdcard_cid cid; }; struct sdcard_info sdc = { .status = STA_NOINIT }; /* < 0 if it should be left on, 0 for off, > 0 for off after timeout */ static volatile int8_t sdcard_led; /* * Called by the timer interrupt */ void sdcard_timer_tick(void) { #if 0 if (sdcard_led > 0) { if (--sdcard_led == 0) *IO_SYS_LED &= ~0x01; } #endif } /* * Enable LED */ static void sdcard_led_on(void) { #if 0 sdcard_led = -1; *IO_SYS_LED |= 0x01; #endif } /* * Disable LED after timeout */ static void sdcard_led_off(void) { sdcard_led = 4; /* 4 ticks @ 64 Hz = 62.5 ms */ } 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 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--) { status = sd_readb(SD_GO8); dbg_printf(" %02x", status); if ((int8_t)status >= 0) /* Bit 7 = 0 for a valid reply */ break; } dbg_putc('\n'); return status; } static int sdcard_send_acmd(uint8_t opcode, uint32_t argument) { int rv; /* CMD55 = application command follows */ rv = sdcard_send_cmd(CMD_APP_CMD, 0); if (rv & 0x04) /* Unknown command (very old card)? */ return rv; return sdcard_send_cmd(opcode, argument); } /* * Read a data block of length len, with a timeout of (timeout) * byte-times. Note that the minimum length is 4 by spec; * this function may fail if this is not the case. * * The input buffer is allowed to be unaligned. */ static int sdcard_read_block(void *buf, int len, int timeout, uint8_t xcmd, uint32_t xarg) { uint8_t tok; uint16_t zcrc; xptr_t 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 && !--badtimeout) { dbg_printf("\nsdcard: read_block: bad token: %02x\n", tok); return -1; /* Bad token */ } if (!--timeout) { 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 * the input shift register. After dealing with alignment, * use dummy reads to shift in the rest of the first longword. */ /* Shift in bytes if needed for alignment */ if (p.a & 1) { *p.b++ = sd_readb(SD_GO8); len--; } sd_readb(SD_GO8); /* Now total of 2 bytes latched */ if (p.a & 2) { *p.w++ = sd_readh(SD_GO16); len -= 2; } 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; } /* * 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. */ while (len--) *p.b++ = sd_readb(SD_GO8 | 1); /* * If we're sending a command in parallel, then we have to * read in the last 6 bytes in parallel with transmitting the * command out. Because pb may be misaligned at this point, * 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) { 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. */ zcrc = sd_crc16_rd(); 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; } /* * Read a number of sectors; returns the number of sectors read. * The input buffer may be unaligned. */ int sdcard_read_sectors(void *buf, uint32_t lba, int count) { int rv; int okcount = 0; static const uint16_t stop_transmission[3] = { (0x40|CMD_STOP_TRANSMISSION) << 8, 0x0000, 0x0061 }; uint8_t resp; uint8_t *p = buf; if (!count) return 0; sdcard_led_on(); 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 & ~1) { con_printf("sdcard: read_multiple error %02x\n", rv); goto out; } while (count--) { rv = sdcard_read_block(p, SECTOR_SIZE, 200000, count ? 0 : CMD_STOP_TRANSMISSION, 0); if (rv) goto out; okcount++; p += SECTOR_SIZE; } /* The first byte after the stop command is undefined */ sd_readb(SD_GO8); /* Wait for the response to the STOP TRANSMISSION command */ do { resp = sd_readb(SD_GO8); } while ((int8_t)resp < 0); if (resp) { con_printf("sdcard: read_sectors: terminate command error %02x\n", resp); } out: sdcard_led_off(); return okcount; } DRESULT disk_read(BYTE drive, BYTE *buffer, LBA_t sectornumber, UINT sectorcount) { int rv; (void)drive; rv = sdcard_read_sectors(buffer, sectornumber, sectorcount); return (rv == (int)sectorcount) ? RES_OK : RES_ERROR; } /* * Read CSD/CID */ 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 & ~1) goto err; rv = sdcard_read_block(buf, 16, 2000, 0, 0); if (rv) goto err; 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"); } /* * Write a number of sectors; returns the number of sectors written. * The buffer may be unaligned. */ int sdcard_write_sectors(const void *buf, uint32_t lba, int count) { int rv; int okcount = 0; uint16_t crc; uint8_t resp; xcptr_t p; if (!count) return 0; p.b = buf; sdcard_led_on(); con_printf("sdcard: writing %d sectors at %u from %p\n", count, lba, buf); if (sdc.card_type == 1) lba <<= SECTOR_SHIFT; /* Convert to a byte address */ rv = sdcard_send_cmd(CMD_WRITE_MULTIPLE_BLOCK, lba); if (rv) { con_printf("sdcard: write_multiple error %02x\n", rv); goto out; } while (count--) { unsigned int podd = p.a & 3; size_t endloop = (p.a + SECTOR_SIZE) & ~3; /* Start block token */ sd_writeb(0xfc, SD_GO8); /* Clear the CRC generator; dummy cycle */ sd_writeb(~0, SD_CLEARCRC); if (podd & 1) sd_writeb(*p.b++, SD_GO8); if (podd & 2) sd_writeh(*p.w++, SD_GO16); while (p.a < endloop) sd_writel(*p.l++, SD_GO32); podd = -podd; if (podd & 2) sd_writeh(*p.w++, SD_GO16); if (podd & 1) sd_writeb(*p.b++, SD_GO8); sd_writeh(sd_crc16_wr(), SD_GO16); /* Wait for data response token */ do { resp = sd_readb(SD_GO8); } while ((resp & 0x11) != 0x01); resp &= ~0xe0; if (resp != 0x05) { /* * Things are confusing here... the spec says * that on error we are supposed to issue a * STOP_TRANSMISSION command, which isn't the normal * thing to do for a write... figure this out later. */ break; /* Error */ } /* Wait until the card is ready for the next block */ do { resp = sd_readb(SD_GO8); } while (resp == 0x00); okcount++; } /* Send stop transmission token */ sd_writeb(0xfd, SD_GO8); /* Wait for the card to go busy, then unbusy */ do { resp = sd_readb(SD_GO8); } while (resp != 0x00); do { resp = sd_readb(SD_GO8); } while (resp == 0x00); out: sdcard_led_off(); return okcount; } DRESULT disk_write(BYTE drive, const BYTE *buffer, LBA_t sectornumber, UINT sectorcount) { int rv; if (drive != 0) return STA_NOINIT; rv = sdcard_write_sectors(buffer, sectornumber, sectorcount); return (rv == (int)sectorcount) ? RES_OK : RES_ERROR; } DRESULT disk_ioctl(BYTE drive, BYTE command, void *buffer) { if (drive != 0) return STA_NOINIT; switch (command) { case CTRL_SYNC: return RES_OK; case GET_SECTOR_SIZE: *(WORD *)buffer = 512; return RES_OK; case GET_SECTOR_COUNT: *(DWORD *)buffer = sdc.lbasize; return RES_OK; case GET_BLOCK_SIZE: *(DWORD *)buffer = 1; /* XXX */ return RES_OK; default: return RES_PARERR; } } DWORD get_fattime(void) { return SYSCLOCK_DATETIME; /* Already in FAT format */ } static unsigned long sdcard_compute_size(struct sdcard_info *sdi) { unsigned int c_size; unsigned int c_size_mult; unsigned int read_bl_len; unsigned long lbasize; sdi->card_type = (sdi->csd.raw[0] >> 30)+1; switch (sdi->card_type) { case 1: /* Classic SD/MMC card */ c_size = ((sdi->csd.raw[2] & 0x3ff) << 2) + (sdi->csd.raw[3] >> 30); c_size_mult = (sdi->csd.raw[2] >> 15) & 7; read_bl_len = (sdi->csd.raw[1] >> 16) & 0xf; lbasize = (c_size + 1) << (c_size_mult + read_bl_len + 2 - 9); break; case 2: /* SDHC/SDXC/eMMC card */ c_size = ((sdi->csd.raw[1] & 0x3f) << 16) + (sdi->csd.raw[2] >> 16); lbasize = c_size << 10; break; default: sdi->card_type = 0; return 0; } return sdi->lbasize = lbasize; } static const char *sdcard_type_name(uint8_t type) { static const char * const names[] = { "unknown", "SD/MMC", "SDHC/SDXC/eMMC" }; if (type >= sizeof names/sizeof names[0]) type = 0; return names[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 & ~1) { dbg_printf("sdcard: CMD6 returned %02x\n", rv); return; } if (1) { /* * Despite the spec, this doesn't seem to actually happen * in SPI mode? */ uint8_t swdata[64]; /* Response from CMD6 */ int i; for (i = 0; i < 64; i++) swdata[i] = sd_readb(SD_GO8); if ((swdata[47] & 0x0f) != 1) { dbg_printf("sdcard: CMD6 reported %X for high speed request\n", swdata[47] & 0x0f); return; /* Failed to switch to high speed 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 */ /* 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"); } DSTATUS disk_initialize(BYTE drive) { uint16_t status; uint32_t try_sdhc; bool is_sd; int i, j, rv; if (drive != 0) return STA_NOINIT; /* So we can wait for the ready condition */ SDCARD_CTL_IRQEN = SDCARD_IRQ_READY; memset(&sdc, 0, sizeof sdc); #if 0 status = /* Check card detect if present */ if (!(status & 0x04)) { con_printf("No memory card installed\n"); return sdc.status = STA_NOINIT|STA_NODISK; } #endif sdcard_led_on(); /* Allow 4 retries in case the card is in a funky state */ i = 4; while (1) { /* * Generate 256 clock cycles in slow mode, with CS# high. */ sd_set_mode(SD_SLOW, false); for (j = 0; j < 8; j++) sd_writel(~0, SD_GO32); /* Assert CS# and send reset command */ sd_set_mode(SD_SLOW, true); sd_writeb(~0, SD_GO8); /* Dummy byte after CS# assert */ rv = sdcard_send_cmd(CMD_GO_IDLE_STATE, 0); if (rv == 0x01) break; /* Success! */ if (!--i) { con_printf("sdcard: reset failed, assuming no card present\n", rv); sdcard_led_off(); return sdc.status = STA_NOINIT | STA_NODISK; } } /* Switch to 20 MHz */ sd_set_mode(SD_20MHZ, true); /* Enable command CRC checking (ignore result) */ sdcard_send_cmd(CMD_CRC_ON_OFF, 0x0001); /* Probe for extended features */ /* * Bit 7:0 = check pattern * Bit 11:8 = supply voltage (3.3 V) */ rv = sdcard_send_cmd(CMD_SEND_IF_COND, 0x01aa); if ((rv & 0x04) == 0) { /* CMD8 supported */ if (rv & ~0x03) { dbg_printf("sdcard; CMD8 error %02x\n", rv); sdcard_led_off(); return sdc.status = STA_NOINIT; } /* Shift in additional data bytes */ sd_readb(SD_GO8); sd_readh(SD_GO16); sdc.if_cond = sd_readl(SD_GO16|SD_BE); 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", sdc.if_cond); sdcard_led_off(); return sdc.status = STA_NOINIT; } try_sdhc = 1 << 30; } else { try_sdhc = 0; } /* Initialize card */ do { rv = sdcard_send_acmd(ACMD_SD_SEND_OP_COND, try_sdhc); if (rv & 0x04) break; if (rv & ~0x01) { con_printf("sdcard: ACMD41 error %02x\n", rv); sdcard_led_off(); return sdc.status = STA_NOINIT; } } while (rv); if (!rv) { /* ACMD41 successful; this is an SD card: can switch to 25 MHz */ is_sd = true; sd_set_mode(SD_25MHZ, true); rv = sdcard_send_cmd(CMD_READ_OCR, try_sdhc); if (rv) { con_printf("sdcard: CMD58 error %02x\n", rv); sdcard_led_off(); return sdc.status = STA_NOINIT; } /* Shift in additional data bytes */ sd_readb(SD_GO8); sd_readh(SD_GO16); sdc.ocr = sd_readl(SD_GO16|SD_BE); } else { /* ACMD41 unsupported, try CMD1 */ is_sd = false; do { rv = sdcard_send_cmd(CMD_SEND_OP_COND, 0); if (rv & ~0x01) { con_printf("sdcard: CMD1 error %02x\n", rv); sdcard_led_off(); return sdc.status = STA_NOINIT; } } while (rv); } /* * Set block length -- some cards power up to a larger block size * than 512 bytes even though that violates the spec. */ rv = sdcard_send_cmd(CMD_SET_BLOCKLEN, 512); if (rv) { con_printf("sdcard: CMD16 error %02x\n", rv); sdcard_led_off(); return sdc.status = STA_NOINIT; } /* * Read the CSD to figure out what command sets are available... */ sdcard_read_csd(); /* Read CSD */ /* * Try to switch to 50 MHz (optional) */ if (is_sd) sdcard_try_high_speed(); /* * 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); sdc.status = 0; sdcard_led_off(); return sdc.status; } DSTATUS disk_status(BYTE drive) { if (drive != 0) return STA_NOINIT; return sdc.status; } static FATFS sd_fs; int disk_init(void) { 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; }