You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
8.4 KiB
C
346 lines
8.4 KiB
C
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#include <generated/git.h>
|
|
|
|
#include "console.h"
|
|
#include "microwatt_soc.h"
|
|
#include "io.h"
|
|
#include "sdram.h"
|
|
#include "elf64.h"
|
|
|
|
#define FLASH_LOADER_USE_MAP
|
|
|
|
int _printf(const char *fmt, ...)
|
|
{
|
|
int count;
|
|
char buffer[128];
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
count = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
|
va_end(ap);
|
|
puts(buffer);
|
|
return count;
|
|
}
|
|
|
|
void flush_cpu_dcache(void)
|
|
{
|
|
}
|
|
|
|
void flush_cpu_icache(void)
|
|
{
|
|
__asm__ volatile ("icbi 0,0; isync" : : : "memory");
|
|
}
|
|
|
|
#define SPI_CMD_RDID 0x9f
|
|
#define SPI_CMD_READ 0x03
|
|
#define SPI_CMD_DUAL_FREAD 0x3b
|
|
#define SPI_CMD_QUAD_FREAD 0x6b
|
|
#define SPI_CMD_QUAD_FREAD_4BA 0x6c
|
|
#define SPI_CMD_RDCR 0x35
|
|
#define SPI_CMD_WREN 0x06
|
|
#define SPI_CMD_PP 0x02
|
|
#define SPI_CMD_RDSR 0x05
|
|
#define SPI_CMD_WWR 0x01
|
|
|
|
static void fl_cs_on(void)
|
|
{
|
|
writeb(SPI_REG_CTRL_MANUAL_CS, SPI_FCTRL_BASE + SPI_REG_CTRL);
|
|
}
|
|
|
|
static void fl_cs_off(void)
|
|
{
|
|
writeb(0, SPI_FCTRL_BASE + SPI_REG_CTRL);
|
|
__asm__ volatile("nop");
|
|
__asm__ volatile("nop");
|
|
__asm__ volatile("nop");
|
|
__asm__ volatile("nop");
|
|
__asm__ volatile("nop");
|
|
}
|
|
|
|
static void wait_wip(void)
|
|
{
|
|
for (;;) {
|
|
uint8_t sr;
|
|
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_RDSR, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
sr = readb(SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_off();
|
|
if ((sr & 1) == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void send_wren(void)
|
|
{
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_WREN, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_off();
|
|
}
|
|
|
|
static void check_spansion_quad_mode(void)
|
|
{
|
|
uint8_t cf1;
|
|
|
|
writeb(SPI_CMD_RDCR, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_RDCR, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
cf1 = readb(SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_off();
|
|
printf(" Cypress/Spansion (CF1=%02x)", cf1);
|
|
if (cf1 & 0x02)
|
|
return;
|
|
printf(" enabling QUAD");
|
|
send_wren();
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_WWR, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(0x00, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(cf1 | 0x02, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(0, SPI_FCTRL_BASE + SPI_REG_CTRL);
|
|
fl_cs_off();
|
|
wait_wip();
|
|
}
|
|
|
|
static uint32_t check_enable_issi_quad(void)
|
|
{
|
|
uint8_t sr;
|
|
|
|
/* Read status register to see if quad mode is already enabled */
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_RDSR, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
sr = readb(SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_off();
|
|
if ((sr & 0x40) == 0) {
|
|
printf(" [enabling quad]");
|
|
send_wren();
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_WWR, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(sr | 0x40, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_off();
|
|
wait_wip();
|
|
}
|
|
|
|
/* Enable quad mode and 4B addresses, 8 dummy cycles */
|
|
return SPI_CMD_QUAD_FREAD_4BA |
|
|
(0x07 << SPI_REG_AUTO_CFG_DUMMIES_SHIFT) |
|
|
SPI_REG_AUT_CFG_MODE_QUAD | SPI_REG_AUTO_CFG_ADDR4 |
|
|
(0x20 << SPI_REG_AUTO_CFG_CSTOUT_SHIFT);
|
|
}
|
|
|
|
static bool check_flash(void)
|
|
{
|
|
bool quad = false;
|
|
uint8_t id[3];
|
|
uint32_t autocfg;
|
|
|
|
/* default auto mode configuration for quad reads: */
|
|
/* Enable quad mode, 8 dummy clocks, 32 cycles CS timeout */
|
|
autocfg = SPI_CMD_QUAD_FREAD |
|
|
(0x07 << SPI_REG_AUTO_CFG_DUMMIES_SHIFT) |
|
|
SPI_REG_AUT_CFG_MODE_QUAD |
|
|
(0x20 << SPI_REG_AUTO_CFG_CSTOUT_SHIFT);
|
|
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_RDID, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
id[0] = readb(SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
id[1] = readb(SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
id[2] = readb(SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
fl_cs_off();
|
|
printf(" SPI FLASH ID: %02x%02x%02x", id[0], id[1], id[2]);
|
|
|
|
if ((id[0] | id[1] | id[2]) == 0 ||
|
|
(id[0] & id[1] & id[2]) == 0xff)
|
|
return false;
|
|
|
|
/* Supported flash types for quad mode */
|
|
if (id[0] == 0x01 &&
|
|
(id[1] == 0x02 || id[1] == 0x20 || id[1] == 0x60) &&
|
|
(id[2] == 0x18 || id[2] == 0x19)) {
|
|
check_spansion_quad_mode();
|
|
quad = true;
|
|
}
|
|
if (id[0] == 0x20 && id[1] == 0xba && id[2] == 0x18) {
|
|
printf(" Micron");
|
|
quad = true;
|
|
}
|
|
if (id[0] == 0x9d && (id[1] & ~0x10) == 0x60 &&
|
|
id[2] == 0x19) {
|
|
/* ISSI IS25LP256D or IS25WP256D */
|
|
printf(" ISSI");
|
|
autocfg = check_enable_issi_quad();
|
|
quad = true;
|
|
}
|
|
if (quad) {
|
|
uint32_t cfg;
|
|
printf(" [quad IO mode]");
|
|
|
|
/* Preserve the default clock div for the board */
|
|
cfg = readl(SPI_FCTRL_BASE + SPI_REG_AUTO_CFG);
|
|
cfg &= SPI_REG_AUTO_CFG_CKDIV_MASK;
|
|
cfg |= autocfg;
|
|
|
|
writel(cfg, SPI_FCTRL_BASE + SPI_REG_AUTO_CFG);
|
|
}
|
|
printf("\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool fl_read(void *dst, uint32_t offset, uint32_t size)
|
|
{
|
|
uint8_t *d = dst;
|
|
|
|
#ifdef FLASH_LOADER_USE_MAP
|
|
memcpy(d, (void *)(unsigned long)(SPI_FLASH_BASE + offset), size);
|
|
#else
|
|
if (size < 1)
|
|
return false;
|
|
fl_cs_on();
|
|
writeb(SPI_CMD_QUAD_FREAD, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(offset >> 16, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(offset >> 8, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(offset, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
writeb(0x00, SPI_FCTRL_BASE + SPI_REG_DATA);
|
|
while(size--)
|
|
*(d++) = readb(SPI_FCTRL_BASE + SPI_REG_DATA_QUAD);
|
|
fl_cs_off();
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
static unsigned long boot_flash(unsigned int offset)
|
|
{
|
|
Elf64_Ehdr ehdr;
|
|
Elf64_Phdr ph;
|
|
unsigned int i, poff, size, off;
|
|
void *addr;
|
|
|
|
printf("Trying flash...\n");
|
|
if (!fl_read(&ehdr, offset, sizeof(ehdr)))
|
|
return -1ul;
|
|
if (!IS_ELF(ehdr) || ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
|
|
printf("Doesn't look like an elf64\n");
|
|
goto dump;
|
|
}
|
|
if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB ||
|
|
ehdr.e_machine != EM_PPC64) {
|
|
printf("Not a ppc64le binary\n");
|
|
goto dump;
|
|
}
|
|
|
|
poff = offset + ehdr.e_phoff;
|
|
for (i = 0; i < ehdr.e_phnum; i++) {
|
|
if (!fl_read(&ph, poff, sizeof(ph)))
|
|
goto dump;
|
|
if (ph.p_type != PT_LOAD)
|
|
continue;
|
|
|
|
/* XXX Add bound checking ! */
|
|
size = ph.p_filesz;
|
|
addr = (void *)ph.p_vaddr;
|
|
off = offset + ph.p_offset;
|
|
printf("Copy segment %d (0x%x bytes) to %p\n", i, size, addr);
|
|
fl_read(addr, off, size);
|
|
poff += ehdr.e_phentsize;
|
|
}
|
|
|
|
printf("Booting from DRAM at %x\n", (unsigned int)ehdr.e_entry);
|
|
flush_cpu_icache();
|
|
return ehdr.e_entry;
|
|
dump:
|
|
printf("HDR: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
ehdr.e_ident[0], ehdr.e_ident[1], ehdr.e_ident[2], ehdr.e_ident[3],
|
|
ehdr.e_ident[4], ehdr.e_ident[5], ehdr.e_ident[6], ehdr.e_ident[7]);
|
|
return -1ul;
|
|
}
|
|
|
|
static void boot_sdram(void)
|
|
{
|
|
void *s = (void *)(DRAM_INIT_BASE + 0x6000);
|
|
void *d = (void *)DRAM_BASE;
|
|
int sz = (0x10000 - 0x6000);
|
|
printf("Copying payload to DRAM...\n");
|
|
memcpy(d, s, sz);
|
|
printf("Booting from DRAM...\n");
|
|
flush_cpu_icache();
|
|
}
|
|
|
|
uint64_t main(void)
|
|
{
|
|
unsigned long ftr, val;
|
|
unsigned int fl_off = 0;
|
|
bool try_flash = false;
|
|
|
|
/* Init the UART */
|
|
console_init();
|
|
|
|
printf("\n\nWelcome to Microwatt !\n\n");
|
|
|
|
/* TODO: Add core version information somewhere in syscon, possibly
|
|
* extracted from git
|
|
*/
|
|
printf(" Soc signature: %016llx\n",
|
|
(unsigned long long)readq(SYSCON_BASE + SYS_REG_SIGNATURE));
|
|
printf(" Soc features: ");
|
|
ftr = readq(SYSCON_BASE + SYS_REG_INFO);
|
|
if (ftr & SYS_REG_INFO_HAS_UART)
|
|
printf("UART ");
|
|
if (ftr & SYS_REG_INFO_HAS_DRAM)
|
|
printf("DRAM ");
|
|
if (ftr & SYS_REG_INFO_HAS_BRAM)
|
|
printf("BRAM ");
|
|
if (ftr & SYS_REG_INFO_HAS_SPI_FLASH)
|
|
printf("SPIFLASH ");
|
|
if (ftr & SYS_REG_INFO_HAS_LITEETH)
|
|
printf("ETHERNET ");
|
|
if (ftr & SYS_REG_INFO_HAS_LITESDCARD)
|
|
printf("SDCARD ");
|
|
printf("\n");
|
|
if (ftr & SYS_REG_INFO_HAS_BRAM) {
|
|
val = readq(SYSCON_BASE + SYS_REG_BRAMINFO) & SYS_REG_BRAMINFO_SIZE_MASK;
|
|
printf(" BRAM: %ld KB\n", val / 1024);
|
|
}
|
|
if (ftr & SYS_REG_INFO_HAS_DRAM) {
|
|
val = readq(SYSCON_BASE + SYS_REG_DRAMINFO) & SYS_REG_DRAMINFO_SIZE_MASK;
|
|
printf(" DRAM: %ld MB\n", val / (1024 * 1024));
|
|
val = readq(SYSCON_BASE + SYS_REG_DRAMINITINFO);
|
|
printf(" DRAM INIT: %ld KB\n", val / 1024);
|
|
}
|
|
val = readq(SYSCON_BASE + SYS_REG_CLKINFO) & SYS_REG_CLKINFO_FREQ_MASK;
|
|
printf(" CLK: %ld MHz\n", val / 1000000);
|
|
if (ftr & SYS_REG_INFO_HAS_SPI_FLASH) {
|
|
val = readq(SYSCON_BASE + SYS_REG_SPI_INFO);
|
|
try_flash = check_flash();
|
|
fl_off = val & SYS_REG_SPI_INFO_FLASH_OFF_MASK;
|
|
printf(" SPI FLASH OFF: 0x%x bytes\n", fl_off);
|
|
try_flash = true;
|
|
}
|
|
printf("\n");
|
|
if (ftr & SYS_REG_INFO_HAS_DRAM && !ddrctrl_init_done_read()) {
|
|
printf("LiteDRAM built from LiteX %s\n", LITEX_GIT_SHA1);
|
|
sdram_init();
|
|
}
|
|
|
|
val = readq(SYSCON_BASE + SYS_REG_CTRL);
|
|
writeq(val & ~SYS_REG_CTRL_ALT_RESET, SYSCON_BASE + SYS_REG_CTRL);
|
|
|
|
if (ftr & SYS_REG_INFO_HAS_BRAM) {
|
|
printf("Booting from BRAM...\n");
|
|
return 0;
|
|
}
|
|
if (try_flash) {
|
|
val = boot_flash(fl_off);
|
|
if (val != (unsigned long)-1)
|
|
return val;
|
|
}
|
|
boot_sdram();
|
|
return 0;
|
|
}
|