diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/drivers/spi/Kconfig | 7 | ||||
-rw-r--r-- | src/drivers/spi/Makefile.inc | 1 | ||||
-rw-r--r-- | src/drivers/spi/spi_sdcard.c | 813 | ||||
-rw-r--r-- | src/include/spi_sdcard.h | 60 |
4 files changed, 881 insertions, 0 deletions
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig index 8b9c25ee98..8300026674 100644 --- a/src/drivers/spi/Kconfig +++ b/src/drivers/spi/Kconfig @@ -28,6 +28,13 @@ config SPI_FLASH 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 e55233e9d8..6dbc43a6d7 100644 --- a/src/drivers/spi/Makefile.inc +++ b/src/drivers/spi/Makefile.inc @@ -15,6 +15,7 @@ $(1)-y += spi-generic.c $(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 0000000000..c2f8da6ff4 --- /dev/null +++ b/src/drivers/spi/spi_sdcard.c @@ -0,0 +1,813 @@ +/* + * 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 <string.h> +#include <spi-generic.h> +#include <spi_sdcard.h> +#include <crc_byte.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) + +#define BLOCK_SIZE 512 + +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_enable_cs(const struct spi_sdcard *card) +{ + spi_claim_bus(&card->slave); +} + +static void spi_sdcard_disable_cs(const struct spi_sdcard *card) +{ + spi_release_bus(&card->slave); +} + +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; +} + +static int lookup_cmd_response_type(uint8_t cmd) +{ + switch (cmd) { + case GO_IDLE_STATE: + case SEND_OP_COND: + case SWITCH_FUNC: + case SEND_CSD: + case SEND_CID: + case SET_BLOCKLEN: + case READ_SINGLE_BLOCK: + case READ_MULTIPLEBLOCK: + case WRITE_BLOCK: + case WRITE_MULTIPLEBLOCK: + case PROGRAM_CSD: + case SEND_WRITE_PROT: + case ERASE_WR_BLK_START_ADDR: + case ERASE_WR_BLK_END_ADDR: + case LOCK_UNLOCK: + case APP_CMD: + case GEN_CMD: + case CRC_ON_OFF: + return RSP_R1; + case STOP_TRANSMISSION: + case SET_WRITE_PROT: + case CLR_WRITE_PROT: + case ERASE: + return RSP_R1b; + case SEND_STATUS: + return RSP_R2; + case READ_OCR: + return RSP_R3; + case SEND_IF_COND: + return RSP_R7; + } + return -1; +} + +static int lookup_acmd_response_type(uint8_t cmd) +{ + switch (cmd) { + case SEND_NUM_WR_BLOCKS: + case SET_WR_BLK_ERASE_COUNT: + case SD_SEND_OP_COND: + case SET_CLR_CARD_DETECT: + case SEND_SCR: + return RSP_R1; + case SD_STATUS: + 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, wait; + 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 */ + if (!is_acmd) + type = lookup_cmd_response_type(cmd); + else + type = lookup_acmd_response_type(cmd); + + /* lookup response length of command */ + length = lookup_response_length(type); + + /* enable cs */ + spi_sdcard_enable_cs(card); + + /* just delay 8 clocks */ + 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 */ + wait = 0xffff; + while (((c = spi_sdcard_recvbyte(card)) & 0x80) && --wait) + ; + if (!wait) { + spi_sdcard_disable_cs(card); + return -1; /* timeout */ + } + + /* obtain response */ + for (int i = 0; i < length; i++) { + response[i] = c; + c = spi_sdcard_recvbyte(card); + } + + if (type == RSP_R1b) { + /* waitting done */ + wait = 0xffffff; + while (c == 0 && --wait) + c = spi_sdcard_recvbyte(card); + if (!wait) { + spi_sdcard_disable_cs(card); + return -1; /* timeout */ + } + } + + spi_sdcard_disable_cs(card); + + ret = response_resolve(type, response, out_register); + + 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) +{ + int wait; + uint8_t csd[16]; + uint16_t c = 0; + + /* CMD9, send csd (128bits register) */ + if (spi_sdcard_do_command(card, SEND_CSD, 0, NULL)) + return -1; + + /* enable CS */ + spi_sdcard_enable_cs(card); + + /* waitting start block token */ + wait = 0xffff; + while ((spi_sdcard_recvbyte(card) != CT_BLOCK_START) && --wait) + ; + if (!wait) { + spi_sdcard_disable_cs(card); + return -1; + } + + /* receive data */ + for (int i = 0; i < 16; i++) { + csd[i] = spi_sdcard_recvbyte(card); + c = crc16_byte(c, csd[i]); + } + + /* receive crc and verify check sum */ + if (((c >> 8) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_sdcard_disable_cs(card); + return -1; + } + if (((c >> 0) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_sdcard_disable_cs(card); + return -1; + } + + /* disable cs */ + spi_sdcard_disable_cs(card); + + 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, + const unsigned int bus, const unsigned int cs) +{ + int resolve, wait; + uint32_t ocr; + + /* initialize spi controller */ + spi_setup_slave(bus, cs, &card->slave); + + /* must wait at least 74 clock ticks after reset + * disable cs pin to enter spi mode */ + spi_sdcard_disable_cs(card); + for (int i = 0; i < 10; i++) + spi_sdcard_sendbyte(card, 0xff); + + /* CMD0, reset sdcard */ + wait = 0xffff; + while ((spi_sdcard_do_command(card, GO_IDLE_STATE, 0, NULL) + != RSP_ERR_IN_IDLE) && --wait) + ; + if (!wait) + return -1; /* timeout */ + + /* CMD8 */ + resolve = spi_sdcard_do_command(card, SEND_IF_COND, 0x1aa, NULL); + if (resolve & RSP_ERR_ILLEGAL_COMMAND) { + /* ACMD41, initialize card */ + wait = 0xffff; + while ((resolve = spi_sdcard_do_app_command(card, + SD_SEND_OP_COND, 0, NULL)) && --wait) + ; + if ((resolve & RSP_ERR_ILLEGAL_COMMAND) || !wait) { + wait = 0xffff; + /* CMD1, initialize card for 2.1mm SD Memory Card */ + while (spi_sdcard_do_app_command(card, SEND_OP_COND, + 0, NULL) && --wait) + ; + if (!wait) + return -1; /* unknown card */ + } + } else { + /* ACMD41, initialize card */ + wait = 0xffff; + while (spi_sdcard_do_app_command(card, SD_SEND_OP_COND, + 0x40000000, NULL) && --wait) + ; + if (!wait) + return -1; + } + + /* 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) +{ + int wait; + 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; + + /* enable cs */ + spi_sdcard_enable_cs(card); + + /* waitting start block token */ + wait = 0xffff; + while ((spi_sdcard_recvbyte(card) != CT_BLOCK_START) && --wait) + ; + if (!wait) { /* timeout */ + spi_sdcard_disable_cs(card); + return -1; + } + + /* receive data */ + for (int i = 0; i < 512; i++) { + ((uint8_t *)buff)[i] = spi_sdcard_recvbyte(card); + c = crc16_byte(c, ((uint8_t *)buff)[i]); + } + + /* receive crc and verify check sum */ + if (((c >> 8) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_sdcard_disable_cs(card); + return -1; + } + if (((c >> 0) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_sdcard_disable_cs(card); + return -1; + } + + /* disable cs */ + spi_sdcard_disable_cs(card); + + 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 wait; + 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; + + /* enable cs */ + spi_sdcard_enable_cs(card); + + for (int i = 0; i < block_num; i++) { + uint16_t c = 0; + + /* waitting start block token */ + wait = 0xffff; + while ((spi_sdcard_recvbyte(card) != CT_BLOCK_START) && --wait) + ; + if (!wait) { /* timeout */ + spi_sdcard_disable_cs(card); + return -1; + } + + /* receive data */ + 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); + } + + /* receive crc and verify check sum */ + if (((c >> 8) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_sdcard_disable_cs(card); + return -1; + } + if (((c >> 0) & 0xff) != spi_sdcard_recvbyte(card)) { + spi_sdcard_disable_cs(card); + return -1; + } + } + + /* disable cs */ + spi_sdcard_disable_cs(card); + + if (spi_sdcard_do_command(card, STOP_TRANSMISSION, 0, NULL)) + if (spi_sdcard_do_command(card, SEND_STATUS, 0, NULL)) + return -1; + + return 0; +} + +int spi_sdcard_read(const struct spi_sdcard *card, + void *dest, + size_t offset, + size_t count) +{ + size_t start_block_address = offset / BLOCK_SIZE; + size_t end_block_address = (offset + count - 1) / BLOCK_SIZE; + size_t has_begin = !!(offset % BLOCK_SIZE); + size_t has_end = !!((offset + count) % BLOCK_SIZE); + + if (start_block_address == end_block_address) { + uint8_t tmp[BLOCK_SIZE]; + size_t o = offset % BLOCK_SIZE; + size_t l = count; + if (spi_sdcard_single_read(card, start_block_address, tmp)) + return -1; + memcpy(dest, tmp + o, l); + return 0; + } + + if (has_begin) { + uint8_t tmp[BLOCK_SIZE]; + size_t o = offset % BLOCK_SIZE; + size_t l = BLOCK_SIZE - o; + if (spi_sdcard_single_read(card, start_block_address, tmp)) + return -1; + memcpy(dest, tmp + o, l); + } + + if (start_block_address + has_begin <= end_block_address - has_end) { + size_t start_lba = start_block_address + has_begin; + size_t end_lba = end_block_address - has_end; + size_t o = has_begin ? BLOCK_SIZE - offset % BLOCK_SIZE : 0; + if (start_lba < end_lba) { + if (spi_sdcard_multiple_read(card, start_lba, end_lba, + dest + o)) + return -1; + } else { + if (spi_sdcard_single_read(card, start_lba, dest + o)) + return -1; + } + } + + if (has_end) { + uint8_t tmp[BLOCK_SIZE]; + size_t o = 0; + size_t l = (offset + count) % BLOCK_SIZE; + if (spi_sdcard_single_read(card, end_block_address, tmp)) + return -1; + memcpy(dest + count - l, tmp + o, l); + } + + return 0; +} + +int spi_sdcard_single_write(const struct spi_sdcard *card, + size_t block_address, + void *buff) +{ + int wait; + 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; + + /* eanbele cs */ + spi_sdcard_enable_cs(card); + + /* send start block token */ + spi_sdcard_sendbyte(card, CT_BLOCK_START); + + /* send data */ + for (int i = 0; i < 512; i++) { + spi_sdcard_sendbyte(card, ((uint8_t *)buff)[i]); + c = crc16_byte(c, ((uint8_t *)buff)[i]); + } + + /* send crc check sum */ + spi_sdcard_sendbyte(card, 0xff & (c >> 8)); + spi_sdcard_sendbyte(card, 0xff & (c >> 0)); + + /* recevie and verify data response token */ + c = spi_sdcard_recvbyte(card); + if ((c & CT_RESPONSE_MASK) != CT_RESPONSE_ACCEPTED) { + spi_sdcard_disable_cs(card); + return -1; + } + + wait = 0xffff; + while ((spi_sdcard_recvbyte(card) == 0) && --wait) + ;/* wait for complete */ + if (!wait) { + spi_sdcard_disable_cs(card); + return -1; + } + + /* disable cs */ + spi_sdcard_disable_cs(card); + + return 0; +} + +int spi_sdcard_multiple_write(const struct spi_sdcard *card, + size_t start_block_address, + size_t end_block_address, + void *buff) +{ + int wait, 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; + + /* enable cs */ + spi_sdcard_enable_cs(card); + + for (int i = 0; i < block_num; i++) { + uint16_t c = 0; + + ret = -1; + + /* send start block token */ + spi_sdcard_sendbyte(card, CT_MULTIPLE_BLOCK_START); + + /* send data */ + for (int k = 0; k < 512; k++) { + uint8_t tmp = ((uint8_t *)buff)[512 * i + k]; + spi_sdcard_sendbyte(card, tmp); + c = crc16_byte(c, tmp); + } + + /* send crc check sum */ + spi_sdcard_sendbyte(card, 0xff & (c >> 8)); + spi_sdcard_sendbyte(card, 0xff & (c >> 0)); + + /* recevie and verify data response token */ + c = spi_sdcard_recvbyte(card); + if ((c & CT_RESPONSE_MASK) != CT_RESPONSE_ACCEPTED) + break; + + wait = 0xffff; + while ((spi_sdcard_recvbyte(card) == 0) && --wait) + ;/* wait for complete */ + if (!wait) + break; + + ret = 0; + } + + /* send stop transmission token */ + spi_sdcard_sendbyte(card, CT_MULTIPLE_BLOCK_STOP); + + /* disable cs */ + spi_sdcard_disable_cs(card); + + if (spi_sdcard_do_command(card, STOP_TRANSMISSION, 0, NULL)) + if (spi_sdcard_do_command(card, SEND_STATUS, 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) / BLOCK_SIZE); +} diff --git a/src/include/spi_sdcard.h b/src/include/spi_sdcard.h new file mode 100644 index 0000000000..ca0dd1d10a --- /dev/null +++ b/src/include/spi_sdcard.h @@ -0,0 +1,60 @@ +/* + * 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, + const unsigned int bus, + const 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_read(const struct spi_sdcard *card, + void *dest, + size_t offset, + size_t count); + +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); + +/* get the sdcard size in bytes */ +size_t spi_sdcard_size(const struct spi_sdcard *card); + +#endif /* _SPI_SDCARD_H_ */ |