#include "compiler.h" #include "common.h" #include "console.h" #include "io.h" #include "spiflash.h" #include "ff.h" #include #define SPIROM_DUAL_MODE 1 void __hot romcopy_download(void *dst, size_t offset, size_t len) { unsigned int cmd; unsigned int flags = ROMCOPY_SPI_CMDLEN(5) | ROMCOPY_WRITE_RAM; if (!len) return; if (SPIROM_DUAL_MODE) { cmd = ROM_FAST_READ_DUAL; flags |= ROMCOPY_SPI_DUAL; } else { cmd = ROM_FAST_READ; } ROMCOPY_RAMADDR = (size_t)dst; ROMCOPY_ROMCMD = __rom_offset + offset + (cmd << 24); ROMCOPY_DATALEN = len | flags; } void __hot romcopy_bzero(void *dst, size_t len) { if (!len) return; ROMCOPY_RAMADDR = (size_t)dst; ROMCOPY_ROMCMD = 0; ROMCOPY_DATALEN = len | ROMCOPY_ZERO_BUFFER | ROMCOPY_WRITE_RAM; } /* * Read unique serial number programmed into ROM * */ char __bss_hot rom_serial_str[16]; qword_t __bss_hot rom_serial; static __must_inline void rom_read_serial(void) { ROMCOPY_ROMCMD = ROM_READ_UNIQUE_ID << 24; ROMCOPY_DATALEN = ROMCOPY_SPI_CMDLEN(5) | ROMCOPY_SPI_MORE; waitfor(ROMCOPY_IRQ); ROMCOPY_DATALEN = ROMCOPY_SPI_CMDLEN(4) | ROMCOPY_SPI_MORE; waitfor(ROMCOPY_IRQ); rom_serial.l[1] = ROMCOPY_INPUT; ROMCOPY_DATALEN = ROMCOPY_SPI_CMDLEN(4); waitfor(ROMCOPY_IRQ); rom_serial.l[0] = ROMCOPY_INPUT; } /* * Convert the ROM serial number to a hex string and poke it into the * USB descriptor "ROM". Used to use base36, but this has to be done * very early, and it has turned out that making it consistent with * what one can easily get out of a debugger is really useful. * * Doing this as early as possible means a much better chance to see * the proper serial number during USB enumeration, so doing it * immediately after SPI ROM conditioning is a great time. */ static __must_inline void rom_mangle_serial(void) { volatile uint32_t *udp = &usbdesc_rom[2]; for (int i = 7; i >= 0; i--) { unsigned int v = rom_serial.b[i]; unsigned int c; c = (v >> 4)+'0'; if (c > '9') c += 'A'-'9'-1; udp[0] = c; c = (v & 15)+'0'; if (c > '9') c += 'A'-'9'-1; udp[2] = c; udp += 4; } } void rom_print_serial(void) { /* Print the ROM serial when we actually can */ con_printf("ROM serial: %08X%08X (%08x-%08x)\n", rom_serial.l[1], rom_serial.l[0], rom_serial.l[1], rom_serial.l[0]); } static __must_inline void romcopy_config_flash(void) { /* Enable writing volatile status register bits */ ROMCOPY_ROMCMD = ROM_VOLATILE_SR_WRITE_ENABLE << 24; ROMCOPY_DATALEN = ROMCOPY_SPI_CMDLEN(1); waitfor(ROMCOPY_IRQ); /* Write SR3 = 0; this sets the drive strength to maximum */ ROMCOPY_ROMCMD = ROM_WRITE_SR3 << 24; ROMCOPY_DATALEN = ROMCOPY_SPI_CMDLEN(2); waitfor(ROMCOPY_IRQ); } IRQHANDLER(romcopy,0) { static __sbss unsigned int romcopy_state; size_t len; switch (romcopy_state++) { case 0: /* Clear esplink as quickly as possible */ len = __esplink_end - __esplink_start; romcopy_bzero(__esplink_start, len); break; case 1: /* Condition flash ROM */ romcopy_config_flash(); /* Read serial number */ rom_read_serial(); /* Start copy DRAM data */ len = __dram_init_end - __dram_init_start; romcopy_download(__dram_init_start, 0, len); /* Convert serial number and export to USB */ rom_mangle_serial(); break; case 2: /* Zero .dram.bss */ len = __dram_bss_end - __dram_bss_start; romcopy_bzero(__dram_bss_start, len); break; default: mask_irq(ROMCOPY_IRQ); break; } } /* * SPI flash parameters and routines (for spiflash.c) */ static const struct spiflash_ops max80_spiflash_ops; static const struct spiflash_param max80_spiflash_param; /* * SPI flash operations */ static int max80_spi_write_buffer(const void *buf, unsigned int buflen, bool more) { const uint8_t *p = buf; uint32_t cmd = 0; unsigned int bytecmd = 0; unsigned int bitpos = 24; while (buflen) { cmd |= *p++ << bitpos; buflen--; bitpos -= 8; bytecmd += ROMCOPY_SPI_CMDLEN(1); if (!(buflen & 3)) { if (buflen || more) bytecmd |= ROMCOPY_SPI_MORE; waitfor(ROMCOPY_IRQ); ROMCOPY_ROMCMD = cmd; ROMCOPY_DATALEN = bytecmd; bytecmd = cmd = 0; bitpos = 24; } } return 0; } static int max80_spi_read_buffer(void *buf, unsigned int buflen, bool more) { uint8_t *p = buf; unsigned int bytecmd; uint32_t v; if (!buflen) return 0; waitfor(ROMCOPY_IRQ); ROMCOPY_ROMCMD = 0; while (buflen > 4) { ROMCOPY_DATALEN = ROMCOPY_SPI_CMDLEN(4) | ROMCOPY_SPI_MORE; waitfor(ROMCOPY_IRQ); v = ROMCOPY_INPUT; p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v; p += 4; buflen -= 4; } bytecmd = ROMCOPY_SPI_CMDLEN(buflen); if (more) bytecmd |= ROMCOPY_SPI_MORE; ROMCOPY_DATALEN = bytecmd; waitfor(ROMCOPY_IRQ); v = ROMCOPY_INPUT; while (buflen) { p[--buflen] = v; v >>= 8; } return 0; } static int max80_spi_write(void *cookie, const void *cmd, unsigned int cmd_len, const void *data, unsigned int data_len, int tshsl) { (void)cookie; (void)tshsl; /* Enforced in hardware */ waitfor(ROMCOPY_IRQ); udelay(1); if (cmd_len) max80_spi_write_buffer(cmd, cmd_len, !!data_len); if (data_len) max80_spi_write_buffer(data, data_len, false); return 0; } static int max80_spi_read(void *cookie, const void *cmd, unsigned int cmd_len, void *data, unsigned int data_len, int tshsl) { (void)cookie; (void)tshsl; /* Enforced in hardware */ waitfor(ROMCOPY_IRQ); udelay(1); if (cmd_len) max80_spi_write_buffer(cmd, cmd_len, !!data_len); if (data_len) max80_spi_read_buffer(data, data_len, false); return 0; } static const struct spiflash_ops max80_spiflash_ops = { .spi_write = max80_spi_write, .spi_read = max80_spi_read, .yield = NULL /* Nothing to yield to... */ }; /* Winbond W25Q128JV @ 3.3V */ #define NS(x) (((x)*(unsigned long long)CPU_HZ + 999999999ULL)/1000000000ULL) #define US(x) (((x)*(unsigned long long)CPU_HZ + 999999ULL)/1000000ULL) #define MS(x) (((x)*(unsigned long long)CPU_HZ + 999ULL)/1000ULL) static const struct spiflash_param max80_spiflash_param = { /* * For W25Q128JV _DYNAMIC is equivalent to _24BIT, but specifying * it as dynamic would most of the time support larger parts * transparently. */ .addr = SPIFLASH_ADDR_DYNAMIC, .tshsl = NS(3), .tshsl1 = NS(10), .tshsl2 = NS(50), .trst = NS(30000), .tw = MS(10), /* persistent; typ, max = 15 */ .tpp = NS(400), /* typ, max = 3000 */ .tse = MS(45), /* typ, max = 400 */ .tbe1 = MS(120), /* typ, max = 1600 */ .tbe2 = MS(150), /* typ, max = 2000 */ .tce = MS(40000), /* typ, max = 200000 */ }; static void rom_spiflash_init(struct spiflash *flash) { static char target[] = "MAX80 v?"; memset(flash, 0, sizeof *flash); target[sizeof target - 2] = SYS_BOARDFPGA + '0'; flash->target = target; flash->param = &max80_spiflash_param; } /* * Flash an image into SPI flash, and reload the FPGA if successful, * returns on failure only. */ void rom_flash_from_memory(void *buf, size_t buflen) { struct spiflash max80_flash; rom_spiflash_init(&max80_flash); if (spiflash_flash_file(&max80_flash, buf, buflen)) { con_puts("update: flash update data invalid\n"); return; } /* Now do it for real */ max80_flash.ops = &max80_spiflash_ops; if (spiflash_flash_file(&max80_flash, buf, buflen)) { con_puts("update: flash update failed\n"); return; } con_puts("update: flash complete, restarting in 500 ms...\n"); udelay(500000); reset(SYS_RESET_RECONFIG); } static int rom_sdcard_read_data(void *cookie, void *buf, unsigned int bufsize) { unsigned int bytesread; FRESULT fr = f_read(cookie, buf, bufsize, &bytesread); return fr == FR_OK ? bytesread : 0; } /* * Flash an image from an SD card, and reload the FPGA if successful */ void rom_flash_from_sdcard(void) { struct spiflash max80_flash; char fw_orig_file_name[32]; char fw_file_name[32]; int l; FRESULT fr; FILINFO fno; FIL f; rom_spiflash_init(&max80_flash); max80_flash.read_data = rom_sdcard_read_data; max80_flash.cookie = &f; l = snprintf(fw_orig_file_name, sizeof fw_orig_file_name, "/max80/v%u.fw", SYS_BOARDFPGA); fr = f_stat(fw_orig_file_name, &fno); if (fr != FR_OK || (fno.fattrib & AM_DIR)) return; /* No firmware file found */ con_printf("update: firmware update file %s found\n", fw_orig_file_name); /* Rename the firmware file to avoid repeated updates */ memcpy(fw_file_name, fw_orig_file_name, l); for (unsigned int i = 1; i < 100000; i++) { snprintf(fw_file_name+l, sizeof fw_file_name-l, ".%03u", i); fr = f_rename(fw_orig_file_name, fw_file_name); if (fr != FR_EXIST) break; } if (fr != FR_OK) { con_puts("update: unable to rename update file, skipping\n"); return; } con_printf("update: renamed %s -> %s\n", fw_orig_file_name, fw_file_name); fr = f_open(&f, fw_file_name, FA_READ); if (fr != FR_OK) { con_puts("update: cannot open file, terminating\n"); return; } if (spiflash_flash_file(&max80_flash, NULL, 0)) { con_puts("update: flash update data invalid\n"); f_close(&f); return; } /* Now do it for real */ f_rewind(&f); max80_flash.ops = &max80_spiflash_ops; if (spiflash_flash_file(&max80_flash, NULL, 0)) { con_puts("update: flash update failed\n"); f_close(&f); return; } /* Close file and then umount the filesystem to force sync */ f_close(&f); f_unmount(""); con_puts("update: flash complete, restarting in 500 ms...\n"); udelay(500000); reset(SYS_RESET_RECONFIG); }