226 lines
3.7 KiB
C
226 lines
3.7 KiB
C
|
// 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;
|
||
|
}
|