Initial version of Tercel SPI core

parent 6b12e787
// © 2020 Raptor Engineering, LLC
//
// Released under the terms of the AGPL v3
// See the LICENSE file for full details
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crc.h>
#include <irq.h>
#include <uart.h>
#include <console.h>
#include <generated/csr.h>
#include <generated/mem.h>
#include "fsi.h"
#define DEBUG_HOST_SPI_FLASH_READ 1
static char *readstr(void)
{
char c[2];
......@@ -101,6 +111,128 @@ static void console_service(void)
prompt();
}
static void memcpy32(uint32_t *destination, uint32_t *source, int words) {
int word;
for (word = 0; word < words; word++) {
*destination = *source;
destination++;
source++;
}
}
#define TERCEL_SPI_ENABLE_USER_MODE_MASK 0x1
#define TERCEL_SPI_ENABLE_USER_MODE_SHIFT 0x0
#define TERCEL_SPI_PHY_DUMMY_CYCLES_MASK 0xff
#define TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT 8
#define TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK 0xff
#define TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT 0
#define TERCEL_SPI_PHY_IO_TYPE_MASK 0x3
#define TERCEL_SPI_PHY_IO_TYPE_SHIFT 16
#define TERCEL_SPI_PHY_4BA_ENABLE_MASK 0x1
#define TERCEL_SPI_PHY_4BA_ENABLE_SHIFT 18
#define TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK 0x1
#define TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT 19
#define TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK 0xff
#define TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT 24
#define TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK 0x1
#define TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_SHIFT 1
#define TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK 0x1
#define TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT 0
#define TERCEL_SPI_PHY_IO_TYPE_SINGLE 0x0
#define TERCEL_SPI_PHY_IO_TYPE_QUAD 0x2
#define TERCEL_SPI_PHY_3BA_MODE 0x0
#define TERCEL_SPI_PHY_4BA_MODE 0x1
static uint32_t read_host_spi_flash_id(void) {
uint32_t flash_id = 0;
// Set user mode
hostspiflash_core_ctl1_write(hostspiflash_core_ctl1_read() | (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT));
// Send Flash ID command
*((volatile uint8_t*)HOSTSPIFLASH_BASE) = 0x9e;
// Read response
flash_id = (flash_id << 8) | (*((volatile uint8_t*)HOSTSPIFLASH_BASE) & 0xff);
flash_id = (flash_id << 8) | (*((volatile uint8_t*)HOSTSPIFLASH_BASE) & 0xff);
flash_id = (flash_id << 8) | (*((volatile uint8_t*)HOSTSPIFLASH_BASE) & 0xff);
flash_id = (flash_id << 8) | (*((volatile uint8_t*)HOSTSPIFLASH_BASE) & 0xff);
// Clear user mode
hostspiflash_core_ctl1_write(hostspiflash_core_ctl1_read() & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT));
return flash_id;
}
static int host_spi_flash_init(void) {
uint32_t dword;
if (!(hostspiflash_device_id_read() == 0x7c5250545350494d)) {
return -1;
}
printf("Raptor Tercel SPI master found\n");
// Set SPI core to automatic mode
hostspiflash_core_ctl1_write(hostspiflash_core_ctl1_read() & ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT));
#if 1
// Set extra CS delay cycle count to 0
dword = hostspiflash_phy_cfg1_read();
dword &= ~(TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK << TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT);
dword |= ((0 & TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_MASK) << TERCEL_SPI_PHY_CS_EXTRA_IDLE_CYC_SHIFT);
hostspiflash_phy_cfg1_write(dword);
// Set SPI fast read dummy cycles to 10
dword = hostspiflash_phy_cfg1_read();
dword &= ~(TERCEL_SPI_PHY_DUMMY_CYCLES_MASK << TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT);
dword |= ((10 & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK) << TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT);
hostspiflash_phy_cfg1_write(dword);
// Enable SPI fast read functionality
dword = hostspiflash_phy_cfg1_read();
dword &= ~(TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK << TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT);
dword |= ((1 & TERCEL_SPI_PHY_FAST_READ_ENABLE_MASK) << TERCEL_SPI_PHY_FAST_READ_ENABLE_SHIFT);
hostspiflash_phy_cfg1_write(dword);
// Set SPI controller to 4BA mode
dword = hostspiflash_phy_cfg1_read();
dword &= ~(TERCEL_SPI_PHY_4BA_ENABLE_MASK << TERCEL_SPI_PHY_4BA_ENABLE_SHIFT);
dword |= ((TERCEL_SPI_PHY_4BA_MODE & TERCEL_SPI_PHY_4BA_ENABLE_MASK) << TERCEL_SPI_PHY_4BA_ENABLE_SHIFT);
hostspiflash_phy_cfg1_write(dword);
// Set SPI controller to QSPI mode
dword = hostspiflash_phy_cfg1_read();
dword &= ~(TERCEL_SPI_PHY_IO_TYPE_MASK << TERCEL_SPI_PHY_IO_TYPE_SHIFT);
dword |= ((TERCEL_SPI_PHY_IO_TYPE_QUAD & TERCEL_SPI_PHY_IO_TYPE_MASK) << TERCEL_SPI_PHY_IO_TYPE_SHIFT);
hostspiflash_phy_cfg1_write(dword);
// Set SPI clock cycle divider to 4
dword = hostspiflash_phy_cfg1_read();
dword &= ~(TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK << TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT);
dword |= ((4 & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK) << TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT);
hostspiflash_phy_cfg1_write(dword);
#endif
// Calculate and dump configured SPI clock speed
uint8_t spi_divisor = (hostspiflash_phy_cfg1_read() >> TERCEL_SPI_PHY_CLOCK_DIVISOR_SHIFT) & TERCEL_SPI_PHY_CLOCK_DIVISOR_MASK;
spi_divisor = (spi_divisor + 1) * 2;
uint8_t spi_dummy_cycles = (hostspiflash_phy_cfg1_read() >> TERCEL_SPI_PHY_DUMMY_CYCLES_SHIFT) & TERCEL_SPI_PHY_DUMMY_CYCLES_MASK;
printf("Flash controller frequency configured to %d MHz (bus frequency %d MHz, dummy cycles %d)\n", (hostspiflash_sys_clk_freq_read() / spi_divisor) / 1000000, hostspiflash_sys_clk_freq_read() / 1000000, spi_dummy_cycles);
#if 1
// Enable read merging
hostspiflash_flash_cfg4_write(hostspiflash_flash_cfg4_read() | (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT));
// Enable write merging
hostspiflash_flash_cfg4_write(hostspiflash_flash_cfg4_read() | (TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_WRITE_SHIFT));
#endif
return 0;
}
int main(void)
{
#ifdef CONFIG_CPU_HAS_INTERRUPT
......@@ -109,6 +241,44 @@ int main(void)
#endif
uart_init();
host_spi_flash_init();
// Detect and print attached host SPI Flash ID
printf("Host SPI flash ID: 0x%08x\n", read_host_spi_flash_id());
// Allocate internal host SPI Flash ROM buffer
uint8_t* host_flash_buffer = (uint8_t*)(MAIN_RAM_BASE + 0x4000000);
printf("Copying host Flash ROM (%p) to internal buffer (%p)...\n", HOSTSPIFLASH_BASE, host_flash_buffer);
int chunk;
for (chunk = 0; chunk < 64; chunk++) {
memcpy32((uint32_t*)host_flash_buffer, (uint32_t*)HOSTSPIFLASH_BASE, (1 * 1024 * 1024LL) / 4);
printf("\r[%d/64]", chunk + 1);
// Reset ongoing multibyte access due to die switch requirements on the N25Q Flash devices
hostspiflash_flash_cfg4_write(hostspiflash_flash_cfg4_read() & ~(TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT));
hostspiflash_flash_cfg4_write(hostspiflash_flash_cfg4_read() | (TERCEL_SPI_FLASH_EN_MULTCYC_READ_MASK << TERCEL_SPI_FLASH_EN_MULTCYC_READ_SHIFT));
}
printf("\r%dMB copied\n", chunk);
#ifdef DEBUG_HOST_SPI_FLASH_READ
printf("host_flash_buffer: %p First 1KB:\n", host_flash_buffer);
int byte = 0;
int row = 0;
for (row = 0; row < 32; row++) {
for (byte = 0; byte < 32; byte++) {
printf("%02x ", host_flash_buffer[byte + (row * 32)]);
}
printf("\n");
}
printf("[1/5] CRC of first 64B: %08x\n", crc32(host_flash_buffer, 1 * 64LL));
printf("[2/5] CRC of first 1KB: %08x (next %08x, next %08x)\n", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), crc32(host_flash_buffer + (2 * 1024LL), 1024LL));
printf("[3/5] CRC of first 1KB: %08x (next %08x, next %08x)\n", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), crc32(host_flash_buffer + (2 * 1024LL), 1024LL));
printf("[4/5] CRC of first 1MB: %08x\n", crc32(host_flash_buffer, 1 * 1024 * 1024LL));
printf("[5/5] CRC of full 64MB: %08x\n", crc32(host_flash_buffer, 64 * 1024 * 1024LL));
#endif
puts("\nRaptor Open FSP\nBuilt "__DATE__" "__TIME__"\n");
help();
prompt();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment