--- /dev/null
+// See LICENSE for license details.
+#include <stdint.h>
+
+#include <platform.h>
+
+#include "common.h"
+
+#define DEBUG
+#include "kprintf.h"
+
+#define MAX_CORES 8
+
+#define PAYLOAD_SIZE (16 << 11)
+
+#define F_CLK 50000000UL
+
+static volatile uint32_t * const spi = (void *)(SPI_CTRL_ADDR);
+
+static inline uint8_t spi_xfer(uint8_t d)
+{
+ int32_t r;
+
+ REG32(spi, SPI_REG_TXFIFO) = d;
+ do {
+ r = REG32(spi, SPI_REG_RXFIFO);
+ } while (r < 0);
+ return r;
+}
+
+static inline uint8_t sd_dummy(void)
+{
+ return spi_xfer(0xFF);
+}
+
+static uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc)
+{
+ unsigned long n;
+ uint8_t r;
+
+ REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+ sd_dummy();
+ spi_xfer(cmd);
+ spi_xfer(arg >> 24);
+ spi_xfer(arg >> 16);
+ spi_xfer(arg >> 8);
+ spi_xfer(arg);
+ spi_xfer(crc);
+
+ n = 1000;
+ do {
+ r = sd_dummy();
+ if (!(r & 0x80)) {
+// dprintf("sd:cmd: %hx\r\n", r);
+ goto done;
+ }
+ } while (--n > 0);
+ kputs("sd_cmd: timeout");
+done:
+ return r;
+}
+
+static inline void sd_cmd_end(void)
+{
+ sd_dummy();
+ REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+
+static void sd_poweron(void)
+{
+ long i;
+ REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 300000UL);
+ REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_OFF;
+ for (i = 10; i > 0; i--) {
+ sd_dummy();
+ }
+ REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+static int sd_cmd0(void)
+{
+ int rc;
+ dputs("CMD0");
+ rc = (sd_cmd(0x40, 0, 0x95) != 0x01);
+ sd_cmd_end();
+ return rc;
+}
+
+static int sd_cmd8(void)
+{
+ int rc;
+ dputs("CMD8");
+ rc = (sd_cmd(0x48, 0x000001AA, 0x87) != 0x01);
+ sd_dummy(); /* command version; reserved */
+ sd_dummy(); /* reserved */
+ rc |= ((sd_dummy() & 0xF) != 0x1); /* voltage */
+ rc |= (sd_dummy() != 0xAA); /* check pattern */
+ sd_cmd_end();
+ return rc;
+}
+
+static void sd_cmd55(void)
+{
+ sd_cmd(0x77, 0, 0x65);
+ sd_cmd_end();
+}
+
+static int sd_acmd41(void)
+{
+ uint8_t r;
+ dputs("ACMD41");
+ do {
+ sd_cmd55();
+ r = sd_cmd(0x69, 0x40000000, 0x77); /* HCS = 1 */
+ } while (r == 0x01);
+ return (r != 0x00);
+}
+
+static int sd_cmd58(void)
+{
+ int rc;
+ dputs("CMD58");
+ rc = (sd_cmd(0x7A, 0, 0xFD) != 0x00);
+ rc |= ((sd_dummy() & 0x80) != 0x80); /* Power up status */
+ sd_dummy();
+ sd_dummy();
+ sd_dummy();
+ sd_cmd_end();
+ return rc;
+}
+
+static int sd_cmd16(void)
+{
+ int rc;
+ dputs("CMD16");
+ rc = (sd_cmd(0x50, 0x200, 0x15) != 0x00);
+ sd_cmd_end();
+ return rc;
+}
+
+static uint16_t crc16_round(uint16_t crc, uint8_t data) {
+ crc = (uint8_t)(crc >> 8) | (crc << 8);
+ crc ^= data;
+ crc ^= (uint8_t)(crc >> 4) & 0xf;
+ crc ^= crc << 12;
+ crc ^= (crc & 0xff) << 5;
+ return crc;
+}
+
+#define SPIN_SHIFT 6
+#define SPIN_UPDATE(i) (!((i) & ((1 << SPIN_SHIFT)-1)))
+#define SPIN_INDEX(i) (((i) >> SPIN_SHIFT) & 0x3)
+
+static const char spinner[] = { '-', '/', '|', '\\' };
+
+static int copy(void)
+{
+ volatile uint8_t *p = (void *)(PAYLOAD_DEST);
+ long i = PAYLOAD_SIZE;
+ int rc = 0;
+
+ dputs("CMD18");
+ kprintf("LOADING ");
+
+ REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 20000000UL);
+ if (sd_cmd(0x52, 0, 0xE1) != 0x00) {
+ sd_cmd_end();
+ return 1;
+ }
+ do {
+ uint16_t crc, crc_exp;
+ long n;
+
+ crc = 0;
+ n = 512;
+ while (sd_dummy() != 0xFE);
+ do {
+ uint8_t x = sd_dummy();
+ *p++ = x;
+ crc = crc16_round(crc, x);
+ } while (--n > 0);
+
+ crc_exp = ((uint16_t)sd_dummy() << 8);
+ crc_exp |= sd_dummy();
+
+ if (crc != crc_exp) {
+ kputs("\b- CRC mismatch ");
+ rc = 1;
+ break;
+ }
+
+ if (SPIN_UPDATE(i)) {
+ kputc('\b');
+ kputc(spinner[SPIN_INDEX(i)]);
+ }
+ } while (--i > 0);
+ sd_cmd_end();
+
+ sd_cmd(0x4C, 0, 0x01);
+ sd_cmd_end();
+ kputs("\b ");
+ return rc;
+}
+
+int main(void)
+{
+ REG32(uart, UART_REG_TXCTRL) = UART_TXEN;
+
+ kputs("INIT");
+ sd_poweron();
+ if (sd_cmd0() ||
+ sd_cmd8() ||
+ sd_acmd41() ||
+ sd_cmd58() ||
+ sd_cmd16() ||
+ copy()) {
+ kputs("ERROR");
+ return 1;
+ }
+
+ kputs("BOOT");
+
+ __asm__ __volatile__ ("fence.i" : : : "memory");
+ return 0;
+}