| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2010 |
| * Stefano Babic, DENX Software Engineering, sbabic@denx.de. |
| * |
| * (C) Copyright 2002 |
| * Rich Ireland, Enterasys Networks, rireland@enterasys.com. |
| * |
| * ispVM functions adapted from Lattice's ispmVMEmbedded code: |
| * Copyright 2009 Lattice Semiconductor Corp. |
| */ |
| |
| #include <log.h> |
| #include <malloc.h> |
| #include <fpga.h> |
| #include <lattice.h> |
| #include <linux/delay.h> |
| |
| static lattice_board_specific_func *pfns; |
| static const char *fpga_image; |
| static unsigned long read_bytes; |
| static unsigned long bufsize; |
| static unsigned short expectedCRC; |
| |
| /* |
| * External variables and functions declared in ivm_core.c module. |
| */ |
| extern unsigned short g_usCalculatedCRC; |
| extern unsigned short g_usDataType; |
| extern unsigned char *g_pucIntelBuffer; |
| extern unsigned char *g_pucHeapMemory; |
| extern unsigned short g_iHeapCounter; |
| extern unsigned short g_iHEAPSize; |
| extern unsigned short g_usIntelDataIndex; |
| extern unsigned short g_usIntelBufferSize; |
| extern char *const g_szSupportedVersions[]; |
| |
| |
| /* |
| * ispVMDelay |
| * |
| * Users must implement a delay to observe a_usTimeDelay, where |
| * bit 15 of the a_usTimeDelay defines the unit. |
| * 1 = milliseconds |
| * 0 = microseconds |
| * Example: |
| * a_usTimeDelay = 0x0001 = 1 microsecond delay. |
| * a_usTimeDelay = 0x8001 = 1 millisecond delay. |
| * |
| * This subroutine is called upon to provide a delay from 1 millisecond to a few |
| * hundreds milliseconds each time. |
| * It is understood that due to a_usTimeDelay is defined as unsigned short, a 16 |
| * bits integer, this function is restricted to produce a delay to 64000 |
| * micro-seconds or 32000 milli-second maximum. The VME file will never pass on |
| * to this function a delay time > those maximum number. If it needs more than |
| * those maximum, the VME file will launch the delay function several times to |
| * realize a larger delay time cummulatively. |
| * It is perfectly alright to provide a longer delay than required. It is not |
| * acceptable if the delay is shorter. |
| */ |
| void ispVMDelay(unsigned short delay) |
| { |
| if (delay & 0x8000) |
| delay = (delay & ~0x8000) * 1000; |
| udelay(delay); |
| } |
| |
| void writePort(unsigned char a_ucPins, unsigned char a_ucValue) |
| { |
| a_ucValue = a_ucValue ? 1 : 0; |
| |
| switch (a_ucPins) { |
| case g_ucPinTDI: |
| pfns->jtag_set_tdi(a_ucValue); |
| break; |
| case g_ucPinTCK: |
| pfns->jtag_set_tck(a_ucValue); |
| break; |
| case g_ucPinTMS: |
| pfns->jtag_set_tms(a_ucValue); |
| break; |
| default: |
| printf("%s: requested unknown pin\n", __func__); |
| } |
| } |
| |
| unsigned char readPort(void) |
| { |
| return pfns->jtag_get_tdo(); |
| } |
| |
| void sclock(void) |
| { |
| writePort(g_ucPinTCK, 0x01); |
| writePort(g_ucPinTCK, 0x00); |
| } |
| |
| void calibration(void) |
| { |
| /* Apply 2 pulses to TCK. */ |
| writePort(g_ucPinTCK, 0x00); |
| writePort(g_ucPinTCK, 0x01); |
| writePort(g_ucPinTCK, 0x00); |
| writePort(g_ucPinTCK, 0x01); |
| writePort(g_ucPinTCK, 0x00); |
| |
| ispVMDelay(0x8001); |
| |
| /* Apply 2 pulses to TCK. */ |
| writePort(g_ucPinTCK, 0x01); |
| writePort(g_ucPinTCK, 0x00); |
| writePort(g_ucPinTCK, 0x01); |
| writePort(g_ucPinTCK, 0x00); |
| } |
| |
| /* |
| * GetByte |
| * |
| * Returns a byte to the caller. The returned byte depends on the |
| * g_usDataType register. If the HEAP_IN bit is set, then the byte |
| * is returned from the HEAP. If the LHEAP_IN bit is set, then |
| * the byte is returned from the intelligent buffer. Otherwise, |
| * the byte is returned directly from the VME file. |
| */ |
| unsigned char GetByte(void) |
| { |
| unsigned char ucData; |
| unsigned int block_size = 4 * 1024; |
| |
| if (g_usDataType & HEAP_IN) { |
| |
| /* |
| * Get data from repeat buffer. |
| */ |
| |
| if (g_iHeapCounter > g_iHEAPSize) { |
| |
| /* |
| * Data over-run. |
| */ |
| |
| return 0xFF; |
| } |
| |
| ucData = g_pucHeapMemory[g_iHeapCounter++]; |
| } else if (g_usDataType & LHEAP_IN) { |
| |
| /* |
| * Get data from intel buffer. |
| */ |
| |
| if (g_usIntelDataIndex >= g_usIntelBufferSize) { |
| return 0xFF; |
| } |
| |
| ucData = g_pucIntelBuffer[g_usIntelDataIndex++]; |
| } else { |
| if (read_bytes == bufsize) { |
| return 0xFF; |
| } |
| ucData = *fpga_image++; |
| read_bytes++; |
| |
| if (!(read_bytes % block_size)) { |
| printf("Downloading FPGA %ld/%ld completed\r", |
| read_bytes, |
| bufsize); |
| } |
| |
| if (expectedCRC != 0) { |
| ispVMCalculateCRC32(ucData); |
| } |
| } |
| |
| return ucData; |
| } |
| |
| signed char ispVM(void) |
| { |
| char szFileVersion[9] = { 0 }; |
| signed char cRetCode = 0; |
| signed char cIndex = 0; |
| signed char cVersionIndex = 0; |
| unsigned char ucReadByte = 0; |
| unsigned short crc; |
| |
| g_pucHeapMemory = NULL; |
| g_iHeapCounter = 0; |
| g_iHEAPSize = 0; |
| g_usIntelDataIndex = 0; |
| g_usIntelBufferSize = 0; |
| g_usCalculatedCRC = 0; |
| expectedCRC = 0; |
| ucReadByte = GetByte(); |
| switch (ucReadByte) { |
| case FILE_CRC: |
| crc = (unsigned char)GetByte(); |
| crc <<= 8; |
| crc |= GetByte(); |
| expectedCRC = crc; |
| |
| for (cIndex = 0; cIndex < 8; cIndex++) |
| szFileVersion[cIndex] = GetByte(); |
| |
| break; |
| default: |
| szFileVersion[0] = (signed char) ucReadByte; |
| for (cIndex = 1; cIndex < 8; cIndex++) |
| szFileVersion[cIndex] = GetByte(); |
| |
| break; |
| } |
| |
| /* |
| * |
| * Compare the VME file version against the supported version. |
| * |
| */ |
| |
| for (cVersionIndex = 0; g_szSupportedVersions[cVersionIndex] != 0; |
| cVersionIndex++) { |
| for (cIndex = 0; cIndex < 8; cIndex++) { |
| if (szFileVersion[cIndex] != |
| g_szSupportedVersions[cVersionIndex][cIndex]) { |
| cRetCode = VME_VERSION_FAILURE; |
| break; |
| } |
| cRetCode = 0; |
| } |
| |
| if (cRetCode == 0) { |
| break; |
| } |
| } |
| |
| if (cRetCode < 0) { |
| return VME_VERSION_FAILURE; |
| } |
| |
| printf("VME file checked: starting downloading to FPGA\n"); |
| |
| ispVMStart(); |
| |
| cRetCode = ispVMCode(); |
| |
| ispVMEnd(); |
| ispVMFreeMem(); |
| puts("\n"); |
| |
| if (cRetCode == 0 && expectedCRC != 0 && |
| (expectedCRC != g_usCalculatedCRC)) { |
| printf("Expected CRC: 0x%.4X\n", expectedCRC); |
| printf("Calculated CRC: 0x%.4X\n", g_usCalculatedCRC); |
| return VME_CRC_FAILURE; |
| } |
| return cRetCode; |
| } |
| |
| static int lattice_validate(Lattice_desc *desc, const char *fn) |
| { |
| int ret_val = false; |
| |
| if (desc) { |
| if ((desc->family > min_lattice_type) && |
| (desc->family < max_lattice_type)) { |
| if ((desc->iface > min_lattice_iface_type) && |
| (desc->iface < max_lattice_iface_type)) { |
| if (desc->size) { |
| ret_val = true; |
| } else { |
| printf("%s: NULL part size\n", fn); |
| } |
| } else { |
| printf("%s: Invalid Interface type, %d\n", |
| fn, desc->iface); |
| } |
| } else { |
| printf("%s: Invalid family type, %d\n", |
| fn, desc->family); |
| } |
| } else { |
| printf("%s: NULL descriptor!\n", fn); |
| } |
| |
| return ret_val; |
| } |
| |
| int lattice_load(Lattice_desc *desc, const void *buf, size_t bsize) |
| { |
| int ret_val = FPGA_FAIL; |
| |
| if (!lattice_validate(desc, (char *)__func__)) { |
| printf("%s: Invalid device descriptor\n", __func__); |
| } else { |
| pfns = desc->iface_fns; |
| |
| switch (desc->family) { |
| case Lattice_XP2: |
| fpga_image = buf; |
| read_bytes = 0; |
| bufsize = bsize; |
| debug("%s: Launching the Lattice ISPVME Loader:" |
| " addr %p size 0x%lx...\n", |
| __func__, fpga_image, bufsize); |
| ret_val = ispVM(); |
| if (ret_val) |
| printf("%s: error %d downloading FPGA image\n", |
| __func__, ret_val); |
| else |
| puts("FPGA downloaded successfully\n"); |
| break; |
| default: |
| printf("%s: Unsupported family type, %d\n", |
| __func__, desc->family); |
| } |
| } |
| |
| return ret_val; |
| } |
| |
| int lattice_dump(Lattice_desc *desc, const void *buf, size_t bsize) |
| { |
| puts("Dump not supported for Lattice FPGA\n"); |
| |
| return FPGA_FAIL; |
| |
| } |
| |
| int lattice_info(Lattice_desc *desc) |
| { |
| int ret_val = FPGA_FAIL; |
| |
| if (lattice_validate(desc, (char *)__func__)) { |
| printf("Family: \t"); |
| switch (desc->family) { |
| case Lattice_XP2: |
| puts("XP2\n"); |
| break; |
| /* Add new family types here */ |
| default: |
| printf("Unknown family type, %d\n", desc->family); |
| } |
| |
| puts("Interface type:\t"); |
| switch (desc->iface) { |
| case lattice_jtag_mode: |
| puts("JTAG Mode\n"); |
| break; |
| /* Add new interface types here */ |
| default: |
| printf("Unsupported interface type, %d\n", desc->iface); |
| } |
| |
| printf("Device Size: \t%d bytes\n", |
| desc->size); |
| |
| if (desc->iface_fns) { |
| printf("Device Function Table @ 0x%p\n", |
| desc->iface_fns); |
| switch (desc->family) { |
| case Lattice_XP2: |
| break; |
| /* Add new family types here */ |
| default: |
| break; |
| } |
| } else { |
| puts("No Device Function Table.\n"); |
| } |
| |
| if (desc->desc) |
| printf("Model: \t%s\n", desc->desc); |
| |
| ret_val = FPGA_SUCCESS; |
| } else { |
| printf("%s: Invalid device descriptor\n", __func__); |
| } |
| |
| return ret_val; |
| } |