summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/drivers/spi/Kconfig7
-rw-r--r--src/drivers/spi/Makefile.inc1
-rw-r--r--src/drivers/spi/spi_sdcard.c813
-rw-r--r--src/include/spi_sdcard.h60
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_ */