Xiang Wang has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/35118 )
Change subject: drivers/spi: add drivers for sdcard mounted on the spi bus ......................................................................
drivers/spi: add drivers for sdcard mounted on the spi bus
Currently supports initialization, read, write, and erase operations. Tested on HiFive Uneashed
Change-Id: I464d2334b8227e448c1c7e324c0455023cffb72a Signed-off-by: Xiang Wang merle@hardenedlinux.org --- M src/drivers/spi/Kconfig M src/drivers/spi/Makefile.inc A src/drivers/spi/spi_sdcard.c A src/include/spi_sdcard.h 4 files changed, 768 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/18/35118/1
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig index b15a502..dab6d0e 100644 --- a/src/drivers/spi/Kconfig +++ b/src/drivers/spi/Kconfig @@ -30,6 +30,13 @@ Select this option if your chipset driver needs to store certain data in the SPI flash.
+config SPI_SDCARD + bool + default n + help + Select this option if your chipset driver needs to store certain + data in the SPI sdcard. + if SPI_FLASH
# Keep at 0 because lots of boards assume this default. diff --git a/src/drivers/spi/Makefile.inc b/src/drivers/spi/Makefile.inc index e55233e..6dbc43a 100644 --- a/src/drivers/spi/Makefile.inc +++ b/src/drivers/spi/Makefile.inc @@ -15,6 +15,7 @@ $(1)-y += bitbang.c $(1)-$(CONFIG_COMMON_CBFS_SPI_WRAPPER) += cbfs_spi.c $(1)-$(CONFIG_SPI_FLASH) += spi_flash.c +$(1)-$(CONFIG_SPI_SDCARD) += spi_sdcard.c $(1)-$(CONFIG_BOOT_DEVICE_SPI_FLASH_RW_NOMMAP$(2)) += boot_device_rw_nommap.c $(1)-$(CONFIG_CONSOLE_SPI_FLASH) += flashconsole.c $(1)-$(CONFIG_SPI_FLASH_ADESTO) += adesto.c diff --git a/src/drivers/spi/spi_sdcard.c b/src/drivers/spi/spi_sdcard.c new file mode 100644 index 0000000..7fc6afd --- /dev/null +++ b/src/drivers/spi/spi_sdcard.c @@ -0,0 +1,706 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 HardenedLinux + * + * 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 of the License. + * + * 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. + */ +#include <stdint.h> +#include <spi-generic.h> +#include <spi_sdcard.h> +#include <commonlib/helpers.h> +#include <console/console.h> + +//#define SPI_SDCARD_DEBUG + +#ifdef SPI_SDCARD_DEBUG +#define dprintk(fmt, args...) \ + printk(BIOS_DEBUG, fmt, ##args) +#else +#define dprintk(fmt, args...) \ + do {} while (0) +#endif + +#define SDCARD_TYPE_SDSC 1 +#define SDCARD_TYPE_SDHC 2 +#define SDCARD_TYPE_SDXC 3 + +/* CMD */ +#define GO_IDLE_STATE 0 +#define SEND_OP_COND 1 +#define SWITCH_FUNC 6 +#define SEND_IF_COND 8 +#define SEND_CSD 9 +#define SEND_CID 10 +#define STOP_TRANSMISSION 12 +#define SEND_STATUS 13 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define READ_MULTIPLEBLOCK 18 +#define WRITE_BLOCK 24 +#define WRITE_MULTIPLEBLOCK 25 +#define PROGRAM_CSD 27 +#define SET_WRITE_PROT 28 +#define CLR_WRITE_PROT 29 +#define SEND_WRITE_PROT 30 +#define ERASE_WR_BLK_START_ADDR 32 +#define ERASE_WR_BLK_END_ADDR 33 +#define ERASE 38 +#define LOCK_UNLOCK 42 +#define APP_CMD 55 +#define GEN_CMD 56 +#define READ_OCR 58 +#define CRC_ON_OFF 59 + +/* ACMD */ +#define SD_STATUS 13 +#define SEND_NUM_WR_BLOCKS 22 +#define SET_WR_BLK_ERASE_COUNT 23 +#define SD_SEND_OP_COND 41 +#define SET_CLR_CARD_DETECT 42 +#define SEND_SCR 51 + +/* control tokens */ +#define CT_BLOCK_START 0xFE +#define CT_MULTIPLE_BLOCK_START 0xFC +#define CT_MULTIPLE_BLOCK_STOP 0xFD +#define CT_RESPONSE_MASK 0x1F +#define CT_RESPONSE_ACCEPTED 0x05 +#define CT_RESPONSE_REJECTED_CRC 0x0B +#define CT_RESPONSE_REJECTED_WRITE_ERR 0x0D + +/* response type */ +#define RSP_R1 0 +#define RSP_R1b 1 +#define RSP_R2 2 +#define RSP_R3 3 +#define RSP_R4 4 +#define RSP_R5 5 +#define RSP_R7 7 + +#define RSP_ERR_CARD_IS_LOCKED (1 << 0) +#define RSP_ERR_WP_ERASE_SKIP (1 << 1) +#define RSP_ERR_GENERAL (1 << 2) +#define RSP_ERR_CC (1 << 3) +#define RSP_ERR_ECC (1 << 4) +#define RSP_ERR_WP_VIOLATION (1 << 5) +#define RSP_ERR_ERASE_PARAM (1 << 6) +#define RSP_ERR_OUT_OF_RANGE (1 << 7) +#define RSP_ERR_IN_IDLE (1 << 8) +#define RSP_ERR_ERASE_RESET (1 << 9) +#define RSP_ERR_ILLEGAL_COMMAND (1 << 10) +#define RSP_ERR_COM_CRC (1 << 11) +#define RSP_ERR_ERASE_SEQUENCE (1 << 12) +#define RSP_ERR_ADDRESS (1 << 13) +#define RSP_ERR_PARAMETER (1 << 14) + +static const uint8_t crc7_table[256] = { + 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, + 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, + 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, + 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, + 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, + 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, + 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, + 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, + 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, + 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, + 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, + 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, + 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, + 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, + 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, + 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, + 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, + 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, + 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, + 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, + 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, + 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, + 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, + 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, + 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, + 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, + 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, + 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, + 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, + 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, + 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +}; + +static uint8_t crc7_byte(uint8_t crc, uint8_t data) +{ + return crc7_table[(crc << 1) ^ data]; +} + +static const uint16_t crc16_tab[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +}; + +static uint16_t crc16_byte(uint16_t prev, uint8_t in) +{ + return crc16_tab[((prev >> 8) ^ in) & 0xff] ^ (prev << 8); +} + +static unsigned long long extract_bits(uint8_t *buff, + int width, int start, int end) +{ + unsigned long long r = 0; + for (int i = end; i >= start; i--) { + int bitpos = width - i - 1; + int b = bitpos / 8; + int shift = 7 - bitpos % 8; + r = (r << 1) | ((buff[b] >> shift) & 1); + } + return r; +} + +static void spi_sdcard_sendbyte(const struct spi_sdcard *card, uint8_t b) +{ + dprintk("sdcard -> %#x\n", b); + spi_xfer(&card->slave, &b, 1, NULL, 0); +} + +static uint8_t spi_sdcard_recvbyte(const struct spi_sdcard *card) +{ + uint8_t b, t = 0xff; + spi_xfer(&card->slave, &t, 1, &b, 1); + dprintk("sdcard <- %#x\n", b); + return b; +} + +static uint8_t spi_sdcard_calculate_command_crc(uint8_t cmd, uint32_t argument) +{ + uint8_t crc = 0; + crc = crc7_byte(crc, (cmd | 0x40) & 0x7f); + crc = crc7_byte(crc, (argument >> (3 * 8)) & 0xff); + crc = crc7_byte(crc, (argument >> (2 * 8)) & 0xff); + crc = crc7_byte(crc, (argument >> (1 * 8)) & 0xff); + crc = crc7_byte(crc, (argument >> (0 * 8)) & 0xff); + return (crc << 1) | 1; +} + +static int lookup_cmd_response_type(uint8_t cmd) +{ + switch (cmd) { + case 0: + case 1: + case 6: + case 9: + case 10: + case 16: + case 17: + case 18: + case 24: + case 25: + case 27: + case 30: + case 32: + case 33: + case 42: + case 55: + case 56: + case 59: + return RSP_R1; + case 12: + case 28: + case 29: + case 38: + return RSP_R1b; + case 13: + return RSP_R2; + case 58: + return RSP_R3; + case 8: + return RSP_R7; + } + return -1; +} + +static int lookup_acmd_response_type(uint8_t cmd) +{ + switch (cmd) { + case 22: + case 23: + case 41: + case 42: + case 51: + return RSP_R1; + case 13: + return RSP_R2; + } + return -1; +} + +static int lookup_response_length(int response_type) +{ + switch (response_type) { + case RSP_R1: + case RSP_R1b: + return 1; + case RSP_R2: + return 2; + case RSP_R3: + case RSP_R7: + return 5; + } + return -1; +} + +static int response_resolve(int response_type, uint8_t *response, + uint32_t *out_register) +{ + __unused static const char * const sd_err[] = { + "Card is locked", + "wp erase skip | lock/unlok cmd failed", + "error", + "CC error", + "card err failed", + "wp violation", + "erase param", + "out of range | csd overwrite", + "in idle state", + "erase reset", + "illegal command", + "com crc error", + "erase sequence error", + "address error", + "parameter error" + }; + uint8_t r1 = 0, r2 = 0; + + if ((response_type == RSP_R1) + || (response_type == RSP_R1b) + || (response_type == RSP_R2) + || (response_type == RSP_R3) + || (response_type == RSP_R7)) + r1 = response[0]; + + if (response_type == RSP_R2) + r2 = response[1]; + + if (((response_type == RSP_R3) || (response_type == RSP_R7)) + && (out_register != NULL)) { + *out_register = 0; + *out_register = (*out_register << 8) | response[1]; + *out_register = (*out_register << 8) | response[2]; + *out_register = (*out_register << 8) | response[3]; + *out_register = (*out_register << 8) | response[4]; + } + + if (r1 != 0 || r2 != 0) { + int i = 0; + uint16_t r = (r1 << 8) | r2; + while (r) { + if (r & 1) + dprintk("SDCARD ERROR: %s\n", sd_err[i]); + r = r >> 1; + i++; + } + return (r1 << 8) | r2; + } + + return 0; +} + +static int spi_sdcard_do_command_help(const struct spi_sdcard *card, + int is_acmd, + uint8_t cmd, + uint32_t argument, + uint32_t *out_register) +{ + int ret, type, length; + uint8_t crc, c, response[5]; + + /* calculate crc for command */ + crc = spi_sdcard_calculate_command_crc(cmd, argument); + + if (is_acmd) + dprintk("\nsdcard execute acmd%d, argument = %#x, crc = %#x\n", + cmd, argument, crc); + else + dprintk("\nsdcard execute cmd%d, argument = %#x, crc = %#x\n", + cmd, argument, crc); + + /* lookup response type of command */ + type = lookup_cmd_response_type(cmd); + if (is_acmd) + type = lookup_acmd_response_type(cmd); + + /* lookup response length of command */ + length = lookup_response_length(type); + + /* enable CS to communicate */ + spi_claim_bus(&card->slave); + + spi_sdcard_recvbyte(card); + + /* send command */ + spi_sdcard_sendbyte(card, (cmd | 0x40) & 0x7f); + /* send argument */ + spi_sdcard_sendbyte(card, (argument >> (8 * 3)) & 0xff); + spi_sdcard_sendbyte(card, (argument >> (8 * 2)) & 0xff); + spi_sdcard_sendbyte(card, (argument >> (8 * 1)) & 0xff); + spi_sdcard_sendbyte(card, (argument >> (8 * 0)) & 0xff); + /* send crc */ + spi_sdcard_sendbyte(card, crc); + + /* waitting for response */ + while ((c = spi_sdcard_recvbyte(card)) & 0x80) + ; + + /* obtain response */ + response[0] = c; + for (int i = 1; i < length; i++) + response[i] = spi_sdcard_recvbyte(card); + + if (type == RSP_R1b) + while (spi_sdcard_recvbyte(card) != 0xff) + ;/* waitting done */ + + + ret = response_resolve(type, response, out_register); + if (ret) + /* disable CS if the command fail to execute */ + spi_release_bus(&card->slave); + + return ret; +} + +static int spi_sdcard_do_command(const struct spi_sdcard *card, + uint8_t cmd, + uint32_t argument, + uint32_t *out_register) +{ + return spi_sdcard_do_command_help(card, 0, cmd, argument, out_register); +} + +static int spi_sdcard_do_app_command(const struct spi_sdcard *card, + uint8_t cmd, + uint32_t argument, + uint32_t *out_register) +{ + /* CMD55 */ + spi_sdcard_do_command(card, APP_CMD, 0, NULL); + return spi_sdcard_do_command_help(card, 1, cmd, argument, out_register); +} + + +size_t spi_sdcard_size(const struct spi_sdcard *card) +{ + uint8_t csd[16]; + uint16_t c = 0; + + /* CMD9, send csd (128bits register) */ + if (spi_sdcard_do_command(card, SEND_CSD, 0, NULL)) { + spi_release_bus(&card->slave); + return -1; + } + + while (spi_sdcard_recvbyte(card) != CT_BLOCK_START) + ; + + for (int i = 0; i < 16; i++) { + csd[i] = spi_sdcard_recvbyte(card); + c = crc16_byte(c, csd[i]); + } + + if (((c >> 8) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_release_bus(&card->slave); + return -1; + } + if (((c >> 0) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_release_bus(&card->slave); + return -1; + } + + if (extract_bits(csd, 128, 126, 127) == 0) { + /* csd version 1.0 */ + size_t c_size = extract_bits(csd, 128, 62, 73); + size_t mult = extract_bits(csd, 128, 47, 49); + size_t read_bl_len = extract_bits(csd, 128, 80, 83); + return (c_size + 1) * mult * (1 << read_bl_len); + } + + if (extract_bits(csd, 128, 126, 127) == 1) { + /* csd version 2.0 */ + size_t c_size = extract_bits(csd, 128, 48, 69); + return (c_size + 1) * 512 * 1024; + } + + return -1; +} + +int spi_sdcard_init(struct spi_sdcard *card, + unsigned int bus, unsigned int cs) +{ + int resolve; + uint32_t ocr; + + /* initialize spi controller */ + spi_setup_slave(bus, cs, &card->slave); + + spi_release_bus(&card->slave); + for (int i = 0; i < 10; i++) + spi_sdcard_sendbyte(card, 0xff); + + /* CMD0, reset sdcard */ + while (spi_sdcard_do_command(card, GO_IDLE_STATE, 0, NULL) + != RSP_ERR_IN_IDLE) + ; + + /* CMD8 */ + resolve = spi_sdcard_do_command(card, SEND_IF_COND, 0x1aa, NULL); + if (resolve & RSP_ERR_ILLEGAL_COMMAND) + /* ACMD41, initialize card */ + while (spi_sdcard_do_app_command(card, SD_SEND_OP_COND, + 0, NULL)) + ; + else + /* ACMD41, initialize card */ + while (spi_sdcard_do_app_command(card, SD_SEND_OP_COND, + 0x40000000, NULL)) + ; + + /* CMD58, read ocr register */ + if (spi_sdcard_do_command(card, READ_OCR, 0, &ocr)) + return -1; + + /* CMD16, set block length to 512 bytes */ + if (spi_sdcard_do_command(card, SET_BLOCKLEN, 512, NULL)) + return -1; + + /* CCS is bit30 of ocr register + * CCS = 0 -> SDSC + * CCS = 1 -> SDHC/SDXC + * */ + if ((ocr & 0x40000000) == 0) + card->type = SDCARD_TYPE_SDSC; + else { + /* size > 32G -> SDXC */ + if (spi_sdcard_size(card) > 32LL * 1024 * 1024 * 1024) + card->type = SDCARD_TYPE_SDXC; + else + card->type = SDCARD_TYPE_SDHC; + } + + return 0; +} + +int spi_sdcard_single_read(const struct spi_sdcard *card, + size_t block_address, + void *buff) +{ + uint16_t c = 0; + + if (card->type == SDCARD_TYPE_SDSC) + block_address = block_address * 512; + + /* CMD17, start single block read */ + if (spi_sdcard_do_command(card, READ_SINGLE_BLOCK, block_address, NULL)) + return -1; + + while (spi_sdcard_recvbyte(card) != CT_BLOCK_START) + ; + for (int i = 0; i < 512; i++) { + ((uint8_t *)buff)[i] = spi_sdcard_recvbyte(card); + c = crc16_byte(c, ((uint8_t *)buff)[i]); + } + + if (((c >> 8) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_release_bus(&card->slave); + return -1; + } + if (((c >> 0) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_release_bus(&card->slave); + return -1; + } + + return 0; +} + +int spi_sdcard_multiple_read(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address, + void *buff) +{ + int block_num = end_block_address - start_block_address + 1; + if (card->type == SDCARD_TYPE_SDSC) { + start_block_address = start_block_address * 512; + end_block_address = end_block_address * 512; + } + /* CMD18, start multiple block read */ + if (spi_sdcard_do_command(card, + READ_MULTIPLEBLOCK, start_block_address, NULL)) + return -1; + + for (int i = 0; i < block_num; i++) { + uint16_t c = 0; + while (spi_sdcard_recvbyte(card) != CT_BLOCK_START) + ; + for (int k = 0; k < 512; k++) { + uint8_t tmp = spi_sdcard_recvbyte(card); + ((uint8_t *)buff)[512 * i + k] = tmp; + c = crc16_byte(c, tmp); + } + if (((c >> 8) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_release_bus(&card->slave); + return -1; + } + if (((c >> 0) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_release_bus(&card->slave); + return -1; + } + } + + if (spi_sdcard_do_command(card, STOP_TRANSMISSION, 0, NULL)) + return -1; + return 0; +} + +int spi_sdcard_single_write(const struct spi_sdcard *card, + size_t block_address, + void *buff) +{ + uint16_t c = 0; + if (card->type == SDCARD_TYPE_SDSC) + block_address = block_address * 512; + + if (spi_sdcard_do_command(card, WRITE_BLOCK, block_address, NULL)) + return -1; + + spi_sdcard_sendbyte(card, CT_BLOCK_START); + for (int i = 0; i < 512; i++) { + spi_sdcard_sendbyte(card, ((uint8_t *)buff)[i]); + c = crc16_byte(c, ((uint8_t *)buff)[i]); + } + spi_sdcard_sendbyte(card, 0xff & (c >> 8)); + spi_sdcard_sendbyte(card, 0xff & (c >> 0)); + + while (((c = spi_sdcard_recvbyte(card)) & 0x11) != 0x01) + ; + if ((c & CT_RESPONSE_MASK) == CT_RESPONSE_ACCEPTED) { + while (spi_sdcard_recvbyte(card) != 0xff) + ;/* wait for complete */ + return 0; + } + + if (spi_sdcard_do_command(card, STOP_TRANSMISSION, 0, NULL)) + return -1; + + return -1; +} + +int spi_sdcard_multiple_write(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address, + void *buff) +{ + int ret = 0; + int block_num = end_block_address - start_block_address + 1; + if (card->type == SDCARD_TYPE_SDSC) { + start_block_address = start_block_address * 512; + end_block_address = end_block_address * 512; + } + + if (spi_sdcard_do_command(card, WRITE_MULTIPLEBLOCK, + start_block_address, NULL)) + return -1; + + for (int i = 0; i < block_num; i++) { + uint16_t c = 0; + spi_sdcard_sendbyte(card, CT_MULTIPLE_BLOCK_START); + for (int k = 0; k < 512; k++) { + spi_sdcard_sendbyte(card, + ((uint8_t *)buff)[512 * i + k]); + c = crc16_byte(c, ((uint8_t *)buff)[512 * i + k]); + } + spi_sdcard_sendbyte(card, 0xff & (c >> 8)); + spi_sdcard_sendbyte(card, 0xff & (c >> 0)); + + while (((c = spi_sdcard_recvbyte(card)) & 0x11) != 0x01) + ; + if ((c & CT_RESPONSE_MASK) != CT_RESPONSE_ACCEPTED) { + ret = -1; + break; + } + while (spi_sdcard_recvbyte(card) != 0xff) + ;/* wait for complete */ + } + spi_sdcard_sendbyte(card, CT_MULTIPLE_BLOCK_STOP); + + if (spi_sdcard_do_command(card, STOP_TRANSMISSION, 0, NULL)) + return -1; + + return ret; +} + +int spi_sdcard_erase(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address) +{ + if (card->type == SDCARD_TYPE_SDSC) { + start_block_address = start_block_address * 512; + end_block_address = end_block_address * 512; + } + + /* CMD32, set erase start address */ + if (spi_sdcard_do_command(card, ERASE_WR_BLK_START_ADDR, + start_block_address, NULL)) + return -1; + + /* CMD33, set erase end address */ + if (spi_sdcard_do_command(card, ERASE_WR_BLK_END_ADDR, + end_block_address, NULL)) + return -1; + + /* CMD38, erase */ + if (spi_sdcard_do_command(card, ERASE, 0, NULL)) + return -1; + + return 0; +} + +int spi_sdcard_erase_all(const struct spi_sdcard *card) +{ + return spi_sdcard_erase(card, 0, spi_sdcard_size(card) / 512); +} diff --git a/src/include/spi_sdcard.h b/src/include/spi_sdcard.h new file mode 100644 index 0000000..40d351d --- /dev/null +++ b/src/include/spi_sdcard.h @@ -0,0 +1,54 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 HardenedLinux + * + * 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 of the License. + * + * 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. + */ + +#ifndef _SPI_SDCARD_H_ +#define _SPI_SDCARD_H_ + +struct spi_sdcard { + int type; + struct spi_slave slave; +}; + +int spi_sdcard_init(struct spi_sdcard *card, + unsigned int bus, + unsigned int cs); + +int spi_sdcard_single_read(const struct spi_sdcard *card, + size_t block_address, + void *buff); + +int spi_sdcard_multiple_read(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address, + void *buff); + +int spi_sdcard_single_write(const struct spi_sdcard *card, + size_t block_address, + void *buff); + +int spi_sdcard_multiple_write(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address, + void *buff); + +int spi_sdcard_erase(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address); + +int spi_sdcard_erase_all(const struct spi_sdcard *card); + +size_t spi_sdcard_size(const struct spi_sdcard *card); + +#endif /* _SPI_SDCARD_H_ */