Xiang Wang has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/30901
Change subject: soc/sifive/fu540: add support boot from sdcard ......................................................................
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; +}