Xiang Wang has uploaded this change for review.

View Change

soc/sifive/fu540: add support boot from sdcard

Change-Id: Id97d293aee33082191cfaf47844769bdfa3d7a1b
Signed-off-by: Xiang Wang <wxjstz@126.com>
---
M src/mainboard/sifive/hifive-unleashed/Makefile.inc
D src/mainboard/sifive/hifive-unleashed/bootblock.c
A src/mainboard/sifive/hifive-unleashed/media.c
M src/soc/sifive/fu540/Makefile.inc
M src/soc/sifive/fu540/include/soc/memlayout.ld
A src/soc/sifive/fu540/include/soc/sd.h
D src/soc/sifive/fu540/media.c
A src/soc/sifive/fu540/sd.c
8 files changed, 490 insertions(+), 58 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/01/30901/1
diff --git a/src/mainboard/sifive/hifive-unleashed/Makefile.inc b/src/mainboard/sifive/hifive-unleashed/Makefile.inc
index 2dac283..54d97f7 100644
--- a/src/mainboard/sifive/hifive-unleashed/Makefile.inc
+++ b/src/mainboard/sifive/hifive-unleashed/Makefile.inc
@@ -11,12 +11,26 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

+bootblock-y += memlayout.ld
bootblock-y += flash.c
-bootblock-y += bootblock.c
+bootblock-y += media.c

+romstage-y += memlayout.ld
romstage-y += flash.c
+romstage-y += media.c
romstage-y += romstage.c

-bootblock-y += memlayout.ld
-romstage-y += memlayout.ld
ramstage-y += memlayout.ld
+ramstage-y += flash.c
+ramstage-y += media.c
+
+
+DTB=build/hifive-unleashed.dtb
+DTS=src/mainboard/sifive/hifive-unleashed/hifive-unleashed.dts
+$(DTB): $(DTS)
+ dtc -I dts -O dtb -o $(DTB) $(DTS)
+
+cbfs-files-y += fallback/DTB
+fallback/DTB-file := $(DTB)
+fallback/DTB-type := raw
+
diff --git a/src/mainboard/sifive/hifive-unleashed/bootblock.c b/src/mainboard/sifive/hifive-unleashed/bootblock.c
deleted file mode 100644
index 777b2aa..0000000
--- a/src/mainboard/sifive/hifive-unleashed/bootblock.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * This file is part of the coreboot project.
- *
- * Copyright (C) 2018 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 <soc/addressmap.h>
-#include <soc/spi.h>
-#include <soc/spi_flash.h>
-#include <soc/clock.h>
-
-extern void flash_init(void);
-extern void bootblock_mainboard_init(void);
-
-void bootblock_mainboard_init(void)
-{
- flash_init();
-}
diff --git a/src/mainboard/sifive/hifive-unleashed/media.c b/src/mainboard/sifive/hifive-unleashed/media.c
new file mode 100644
index 0000000..4a8f463
--- /dev/null
+++ b/src/mainboard/sifive/hifive-unleashed/media.c
@@ -0,0 +1,123 @@
+/*
+ * 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 <boot_device.h>
+#include <symbols.h>
+#include <cbfs.h>
+#include <arch/io.h>
+#include <soc/addressmap.h>
+#include <soc/spi.h>
+#include <soc/sd.h>
+#include <soc/clock.h>
+#include <console/console.h>
+#include <string.h>
+
+void flash_init(void);
+// void boot_device_init(void);
+// const struct region_device *boot_device_ro(void);
+
+/* At 0x20000000: A 256MiB long memory-mapped view of the flash at QSPI0 */
+static struct mem_region_device spi_mdev =
+ MEM_REGION_DEV_RO_INIT((void *)0x20000000, CONFIG_ROM_SIZE);
+
+static ssize_t unleashed_sd_readat(const struct region_device *rdev, void *dest,
+ size_t offset, size_t count)
+{
+ if (offset / 512 == (offset + count - 1) / 512) {
+ size_t lba = offset / 512;
+ size_t o = offset % 512;
+ size_t l = count;
+ uint8_t tmp[512];
+ if (sd_copy((spi_ctrl*)FU540_QSPI2, tmp, lba, 1) != 0) {
+ return -1;
+ }
+ memcpy(dest, tmp + o, l);
+ } else {
+ int has_begin = !!(offset % 512);
+ int has_end = !!((offset + count) % 512);
+ size_t first_lba = offset / 512;
+ size_t last_lba = (offset + count - 1) / 512;
+ if (has_begin) {
+ size_t lba = first_lba;
+ size_t o = offset % 512;
+ size_t l = 512 - o;
+ uint8_t tmp[512];
+ if (sd_copy((spi_ctrl*)FU540_QSPI2, tmp, lba, 1) != 0) {
+ return -1;
+ }
+ memcpy(dest, tmp + o, l);
+ }
+ if (first_lba + has_begin <= last_lba - has_end) {
+ size_t lba = first_lba + has_begin;
+ size_t size = (last_lba - has_end) - (first_lba + has_begin) + 1;
+ size_t o = 512 - offset % 512;
+ if (sd_copy((spi_ctrl*)FU540_QSPI2, dest + o, lba, size) != 0) {
+ return -1;
+ }
+ }
+ if (has_end) {
+ size_t lba = last_lba;
+ size_t o = 0;
+ size_t l = (offset + count) % 512;
+ uint8_t tmp[512];
+ if (sd_copy((spi_ctrl*)FU540_QSPI2, tmp, lba, 1) != 0) {
+ return -1;
+ }
+ memcpy(dest + count - l, tmp + o, l);
+ }
+ }
+ return count;
+}
+
+static const struct region_device_ops unleashed_sd_ops = {
+ .mmap = mmap_helper_rdev_mmap,
+ .munmap = mmap_helper_rdev_munmap,
+ .readat = unleashed_sd_readat,
+};
+
+
+static struct mmap_helper_region_device sd_mdev =
+ MMAP_HELPER_REGION_INIT(&unleashed_sd_ops, 0, CONFIG_ROM_SIZE);
+
+
+const struct region_device *boot_device_ro()
+{
+ switch(read32((uint32_t *)FU540_MSEL)) {
+ case 6:
+ case 10:
+ case 15:
+ return &spi_mdev.rdev;
+ case 11:
+ return &sd_mdev.rdev;
+ }
+ return NULL;
+}
+
+void boot_device_init()
+{
+ switch(read32((uint32_t *)FU540_MSEL)) {
+ case 6:
+ case 10:
+ case 15:
+ flash_init();
+ break;
+ case 11:
+ sd_init((spi_ctrl*)FU540_QSPI2, clock_get_tlclk_khz());
+ mmap_helper_device_init(&sd_mdev, _cbfs_cache, _cbfs_cache_size);
+ break;
+ default:
+ die("Wrong configuration of MSEL");
+ }
+}
diff --git a/src/soc/sifive/fu540/Makefile.inc b/src/soc/sifive/fu540/Makefile.inc
index 83c7b1c..866267e 100644
--- a/src/soc/sifive/fu540/Makefile.inc
+++ b/src/soc/sifive/fu540/Makefile.inc
@@ -15,28 +15,31 @@

bootblock-y += uart.c
bootblock-y += clint.c
-bootblock-y += media.c
+# bootblock-y += media.c
bootblock-y += bootblock.c
bootblock-y += clock.c
bootblock-y += spi.c
+bootblock-y += sd.c

romstage-y += uart.c
romstage-y += clint.c
-romstage-y += media.c
+# romstage-y += media.c
romstage-y += sdram.c
romstage-y += cbmem.c
romstage-y += otp.c
romstage-y += clock.c
romstage-y += spi.c
+romstage-y += sd.c

ramstage-y += uart.c
ramstage-y += clint.c
-ramstage-y += media.c
+# ramstage-y += media.c
ramstage-y += sdram.c
ramstage-y += cbmem.c
ramstage-y += otp.c
ramstage-y += clock.c
ramstage-y += spi.c
+ramstage-y += sd.c

CPPFLAGS_common += -Isrc/soc/sifive/fu540/include

diff --git a/src/soc/sifive/fu540/include/soc/memlayout.ld b/src/soc/sifive/fu540/include/soc/memlayout.ld
index b9b9c47..81a7f9b 100644
--- a/src/soc/sifive/fu540/include/soc/memlayout.ld
+++ b/src/soc/sifive/fu540/include/soc/memlayout.ld
@@ -28,9 +28,11 @@
CAR_STACK(FU540_L2LIM + 64K, 20K)
PRERAM_CBMEM_CONSOLE(FU540_L2LIM + 84K, 8K)
ROMSTAGE(FU540_L2LIM + 128K, 128K)
+ PRERAM_CBFS_CACHE(FU540_L2LIM + 256K, 128K)
L2LIM_END(FU540_L2LIM + 2M)

DRAM_START(FU540_DRAM)
RAMSTAGE(FU540_DRAM, 256K)
MEM_STACK(FU540_DRAM + 256K, 20K)
+ POSTRAM_CBFS_CACHE(FU540_DRAM + 512K, 32M - 512K)
}
diff --git a/src/soc/sifive/fu540/include/soc/sd.h b/src/soc/sifive/fu540/include/soc/sd.h
new file mode 100644
index 0000000..679d0f4
--- /dev/null
+++ b/src/soc/sifive/fu540/include/soc/sd.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 SiFive, Inc
+ * 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 _LIBRARIES_SD_H
+#define _LIBRARIES_SD_H
+
+#define SD_INIT_ERROR_CMD0 1
+#define SD_INIT_ERROR_CMD8 2
+#define SD_INIT_ERROR_ACMD41 3
+#define SD_INIT_ERROR_CMD58 4
+#define SD_INIT_ERROR_CMD16 5
+
+#define SD_COPY_ERROR_CMD18 1
+#define SD_COPY_ERROR_CMD18_CRC 2
+
+#ifndef __ASSEMBLER__
+
+#include <stdint.h>
+#include <stddef.h>
+
+int sd_init(spi_ctrl* spi, unsigned int input_clk_hz);
+int sd_copy(spi_ctrl* spi, void* dst, uint32_t src_lba, size_t size);
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* _LIBRARIES_SD_H */
diff --git a/src/soc/sifive/fu540/media.c b/src/soc/sifive/fu540/media.c
deleted file mode 100644
index 7b9ccb0..0000000
--- a/src/soc/sifive/fu540/media.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * This file is part of the coreboot project.
- *
- * Copyright (C) 2018 Jonathan Neuschäfer
- *
- * 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 <boot_device.h>
-
-/* At 0x20000000: A 256MiB long memory-mapped view of the flash at QSPI0 */
-static struct mem_region_device mdev =
- MEM_REGION_DEV_RO_INIT((void *)0x20000000, CONFIG_ROM_SIZE);
-
-const struct region_device *boot_device_ro(void)
-{
- return &mdev.rdev;
-}
diff --git a/src/soc/sifive/fu540/sd.c b/src/soc/sifive/fu540/sd.c
new file mode 100644
index 0000000..3af26ad
--- /dev/null
+++ b/src/soc/sifive/fu540/sd.c
@@ -0,0 +1,302 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 SiFive, Inc
+ * 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 <mcall.h>
+#include <arch/io.h>
+#include <soc/spi.h>
+#include <soc/sd.h>
+
+
+#define SPI_DIR_RX 0
+#define SPI_DIR_TX 1
+
+#define SPI_ENDIAN_MSB 0
+#define SPI_ENDIAN_LSB 1
+
+#define SPI_CSMODE_AUTO 0
+#define SPI_CSMODE_HOLD 2
+#define SPI_CSMODE_OFF 3
+
+#define SPI_PROTO_S 0
+#define SPI_PROTO_D 1
+#define SPI_PROTO_Q 2
+
+#define SD_CMD_GO_IDLE_STATE 0
+#define SD_CMD_SEND_IF_COND 8
+#define SD_CMD_STOP_TRANSMISSION 12
+#define SD_CMD_SET_BLOCKLEN 16
+#define SD_CMD_READ_BLOCK_MULTIPLE 18
+#define SD_CMD_APP_SEND_OP_COND 41
+#define SD_CMD_APP_CMD 55
+#define SD_CMD_READ_OCR 58
+#define SD_RESPONSE_IDLE 0x1
+// Data token for commands 17, 18, 24
+#define SD_DATA_TOKEN 0xfe
+
+
+// SD card initialization must happen at 100-400kHz
+#define SD_POWER_ON_FREQ_KHZ 400L
+// SD cards normally support reading/writing at 20MHz
+#define SD_POST_INIT_CLK_KHZ 20000L
+
+
+// Command frame starts by asserting low and then high for first two clock edges
+#define SD_CMD(cmd) (0x40 | (cmd))
+
+
+/**
+ * Send dummy byte (all ones).
+ *
+ * Used in many cases to read one byte from SD card, since SPI is a full-duplex
+ * protocol and it is necessary to send a byte in order to read a byte.
+ */
+static inline uint8_t sd_dummy(spi_ctrl* spi)
+{
+ return spi_txrx(spi, 0xFF);
+}
+
+
+static int sd_cmd(spi_ctrl* spi, uint8_t cmd, uint32_t arg, uint8_t crc)
+{
+ unsigned long n;
+ uint8_t r;
+
+ spi->csmode.mode = SPI_CSMODE_HOLD;
+ sd_dummy(spi);
+ spi_txrx(spi, cmd);
+ spi_txrx(spi, arg >> 24);
+ spi_txrx(spi, arg >> 16);
+ spi_txrx(spi, arg >> 8);
+ spi_txrx(spi, arg);
+ spi_txrx(spi, crc);
+
+ n = 1000;
+ do {
+ r = sd_dummy(spi);
+ if (!(r & 0x80)) {
+ break;
+ }
+ } while (--n > 0);
+ return r;
+}
+
+
+static inline void sd_cmd_end(spi_ctrl* spi)
+{
+ sd_dummy(spi);
+ spi->csmode.mode = SPI_CSMODE_AUTO;
+}
+
+
+static void sd_poweron(spi_ctrl* spi, unsigned int input_clk_khz)
+{
+ // Initialize SPI controller
+ spi->fmt.raw_bits = ((spi_reg_fmt) {
+ .proto = SPI_PROTO_S,
+ .dir = SPI_DIR_RX,
+ .endian = SPI_ENDIAN_MSB,
+ .len = 8,
+ }).raw_bits;
+ spi->csdef |= 0x1;
+ spi->csid = 0;
+
+ spi->sckdiv = spi_min_clk_divisor(input_clk_khz, SD_POWER_ON_FREQ_KHZ);
+ spi->csmode.mode = SPI_CSMODE_OFF;
+
+ for (int i = 10; i > 0; i--) {
+ // Set SD card pin DI high for 74+ cycles
+ // SD CMD pin is the same pin as SPI DI, so CMD = 0xff means assert DI high
+ // for 8 cycles.
+ sd_dummy(spi);
+ }
+
+ spi->csmode.mode = SPI_CSMODE_AUTO;
+}
+
+
+/**
+ * Reset card.
+ */
+static int sd_cmd0(spi_ctrl* spi)
+{
+ int rc;
+ rc = (sd_cmd(spi, SD_CMD(SD_CMD_GO_IDLE_STATE), 0, 0x95) != SD_RESPONSE_IDLE);
+ sd_cmd_end(spi);
+ return rc;
+}
+
+
+/**
+ * Check for SD version and supported voltages.
+ */
+static int sd_cmd8(spi_ctrl* spi)
+{
+ // Check for high capacity cards
+ // Fail if card does not support SDHC
+ int rc;
+ rc = (sd_cmd(spi, SD_CMD(SD_CMD_SEND_IF_COND), 0x000001AA, 0x87) != SD_RESPONSE_IDLE);
+ sd_dummy(spi); /* command version; reserved */
+ sd_dummy(spi); /* reserved */
+ rc |= ((sd_dummy(spi) & 0xF) != 0x1); /* voltage */
+ rc |= (sd_dummy(spi) != 0xAA); /* check pattern */
+ sd_cmd_end(spi);
+ return rc;
+}
+
+
+/**
+ * Send app command. Used as prefix to app commands (ACMD).
+ */
+static void sd_cmd55(spi_ctrl* spi)
+{
+ sd_cmd(spi, SD_CMD(SD_CMD_APP_CMD), 0, 0x65);
+ sd_cmd_end(spi);
+}
+
+
+/**
+ * Start SDC initialization process.
+ */
+static int sd_acmd41(spi_ctrl* spi)
+{
+ uint8_t r;
+ do {
+ sd_cmd55(spi);
+ r = sd_cmd(spi, SD_CMD(SD_CMD_APP_SEND_OP_COND), 0x40000000, 0x77); /* HCS = 1 */
+ sd_cmd_end(spi);
+ } while (r == SD_RESPONSE_IDLE);
+ return (r != 0x00);
+}
+
+
+/**
+ * Read operation conditions register (OCR) to check for availability of block
+ * addressing mode.
+ */
+static int sd_cmd58(spi_ctrl* spi)
+{
+ // HACK: Disabled due to bugs. It is not strictly necessary
+ // to issue this command if we only support SD cards that support SDHC mode.
+ return 0;
+ // int rc;
+ // rc = (sd_cmd(spi, SD_CMD(SD_CMD_READ_OCR), 0, 0xFD) != 0x00);
+ // rc |= ((sd_dummy(spi) & 0x80) != 0x80); /* Power up status */
+ // sd_dummy(spi); /* Supported voltages */
+ // sd_dummy(spi); /* Supported voltages */
+ // sd_dummy(spi); /* Supported voltages */
+ // sd_cmd_end(spi);
+ // return rc;
+}
+
+
+/**
+ * Set block addressing mode.
+ */
+static int sd_cmd16(spi_ctrl* spi)
+{
+ int rc;
+ rc = (sd_cmd(spi, SD_CMD(SD_CMD_SET_BLOCKLEN), 0x200, 0x15) != 0x00);
+ sd_cmd_end(spi);
+ return rc;
+}
+
+
+static uint8_t crc7(uint8_t prev, uint8_t in)
+{
+ // CRC polynomial 0x89
+ uint8_t remainder = prev & in;
+ remainder ^= (remainder >> 4) ^ (remainder >> 7);
+ remainder ^= remainder << 4;
+ return remainder & 0x7f;
+}
+
+
+static uint16_t crc16(uint16_t crc, uint8_t data)
+{
+ // CRC polynomial 0x11021
+ crc = (uint8_t)(crc >> 8) | (crc << 8);
+ crc ^= data;
+ crc ^= (uint8_t)(crc >> 4) & 0xf;
+ crc ^= crc << 12;
+ crc ^= (crc & 0xff) << 5;
+ return crc;
+}
+
+
+//int sd_init(spi_ctrl* spi, unsigned int input_clk_khz, int skip_sd_init_commands)
+int sd_init(spi_ctrl* spi, unsigned int input_clk_khz)
+{
+ sd_poweron(spi, input_clk_khz);
+ if (sd_cmd0(spi))
+ return SD_INIT_ERROR_CMD0;
+ if (sd_cmd8(spi))
+ return SD_INIT_ERROR_CMD8;
+ if (sd_acmd41(spi))
+ return SD_INIT_ERROR_ACMD41;
+ if (sd_cmd58(spi))
+ return SD_INIT_ERROR_CMD58;
+ if (sd_cmd16(spi))
+ return SD_INIT_ERROR_CMD16;
+
+ // Increase clock frequency after initialization for higher performance.
+ spi->sckdiv = spi_min_clk_divisor(input_clk_khz, SD_POST_INIT_CLK_KHZ);
+ return 0;
+}
+
+
+int sd_copy(spi_ctrl* spi, void* dst, uint32_t src_lba, size_t size)
+{
+ volatile uint8_t *p = dst;
+ long i = size;
+ int rc = 0;
+
+ uint8_t c = 0;
+ c = crc7(c, SD_CMD(SD_CMD_READ_BLOCK_MULTIPLE));
+ c = crc7(c, src_lba >> 24);
+ c = crc7(c, (src_lba >> 16) & 0xff);
+ c = crc7(c, (src_lba >> 8) & 0xff);
+ c = crc7(c, src_lba & 0xff);
+ c = (c << 1) | 1;
+ if (sd_cmd(spi, SD_CMD(SD_CMD_READ_BLOCK_MULTIPLE), src_lba, c) != 0x00) {
+ sd_cmd_end(spi);
+ return SD_COPY_ERROR_CMD18;
+ }
+ do {
+ uint16_t crc, crc_exp;
+ long n;
+
+ crc = 0;
+ n = 512;
+ while (sd_dummy(spi) != SD_DATA_TOKEN);
+ do {
+ uint8_t x = sd_dummy(spi);
+ *p++ = x;
+ crc = crc16(crc, x);
+ } while (--n > 0);
+
+ crc_exp = ((uint16_t)sd_dummy(spi) << 8);
+ crc_exp |= sd_dummy(spi);
+
+ if (crc != crc_exp) {
+ rc = SD_COPY_ERROR_CMD18_CRC;
+ break;
+ }
+ } while (--i > 0);
+
+ sd_cmd(spi, SD_CMD(SD_CMD_STOP_TRANSMISSION), 0, 0x01);
+ sd_cmd_end(spi);
+ return rc;
+}

To view, visit change 30901. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: Id97d293aee33082191cfaf47844769bdfa3d7a1b
Gerrit-Change-Number: 30901
Gerrit-PatchSet: 1
Gerrit-Owner: Xiang Wang <wxjstz@126.com>
Gerrit-MessageType: newchange