/* * (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. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include 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; }