| /****************************************************************************** |
| |
| Copyright (c) 2014-2015 Lantiq Deutschland GmbH |
| Copyright (c) 2015-2016 Lantiq Beteiligungs-GmbH & Co.KG |
| Copyright 2016, Intel Corporation. |
| |
| For licensing information, see the file 'LICENSE' in the root folder of |
| this software module. |
| |
| ******************************************************************************/ |
| |
| /** |
| \file dxs_dwld.c |
| Implementation of download functions. |
| */ |
| |
| /* ========================================================================== */ |
| /* Includes */ |
| /* ========================================================================== */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include "dxs_lib.h" |
| #include "dxs_errno.h" |
| #include "dxs_error.h" |
| #include "dxs_access.h" |
| #include "dxs_init.h" |
| #include "dxs_mbx.h" |
| #include "dxs_pollint.h" |
| |
| /* ========================================================================== */ |
| /* Macro definitions */ |
| /* ========================================================================== */ |
| #define DXS_PRAM_MAGIC_DXS1 0x44585331 |
| #define DXS_PRAM_MAGIC_DXS2 0x44585332 |
| |
| #define DXS_ROM_MASK_1_0 1 |
| #define DXS_ROM_MASK_2_0 2 |
| |
| #define DXS_CRC32_POLY 0xEDB88320 |
| #define DXS_SWAP32(a) ((((a) >> 24) & 0xFF) | (((a) >> 8) & 0xFF00) | \ |
| (((a) & 0xFF00) << 8) | (((a) & 0xFF) << 24)) |
| |
| /* FW download container header struct values */ |
| #define DXS_FW_TYPE_GLOBAL_V1 0xD0000001 /* type 0xD000, version 0x0001 */ |
| #define DXS_FW_GLOBAL_MAGIC 0x44585346 /* 'DXSF' character sequence */ |
| |
| /* ========================================================================== */ |
| /* Type definitions */ |
| /* ========================================================================== */ |
| struct pram_footer |
| { |
| /* version information */ |
| uint32_t fw_vers; |
| /* unix compilation timestamp */ |
| uint32_t timestamp; |
| /* 'DXS1' or 'DXS2' */ |
| uint32_t magic; |
| /* image size */ |
| uint32_t mem; |
| /* CRC32 */ |
| uint32_t crc; |
| }; |
| |
| /* FW download container header (big endian) */ |
| struct fw_container_header |
| { |
| /* Type and Version identifier */ |
| uint32_t nType; |
| /* Length of the payload following this header */ |
| uint32_t nLength; |
| /* MAGIC value for endianess checking */ |
| uint32_t nMagic; |
| /* Version, each of the 4 bytes represents a digit */ |
| uint32_t nVersion; |
| /* Epoch timestamp */ |
| uint32_t nTimestamp; |
| /* Offset of the DXS V11 FW within the payload section */ |
| uint32_t nDxsV11FwOffset; |
| /* Length of the DXS V11 FW within the payload section */ |
| uint32_t nDxsV11FwLength; |
| /* Offset of the DXS1 V12 FW within the payload section */ |
| uint32_t nDxs1V12FwOffset; |
| /* Length of the DXS1 V12 FW within the payload section */ |
| uint32_t nDxs1V12FwLength; |
| /* Offset of the DXS2 V12 FW within the payload section */ |
| uint32_t nDxs2V12FwOffset; |
| /* Length of the DXS2 V12 FW within the payload section */ |
| uint32_t nDxs2V12FwLength; |
| }; |
| |
| /* ========================================================================== */ |
| /* Global variables */ |
| /* ========================================================================== */ |
| |
| /* ========================================================================== */ |
| /* Function prototypes */ |
| /* ========================================================================== */ |
| |
| /* ========================================================================== */ |
| /* Function implementation */ |
| /* ========================================================================== */ |
| |
| /** |
| Update CRC32 checksum |
| |
| \param pData - data buffer |
| \param len - data buffer size in bytes |
| \param prev - previous CRC32 checksum (initial should be 0) |
| |
| \return |
| Newly calculated CRC32 value |
| */ |
| static uint32_t dxs_crc32_le(const void *pData, size_t len, uint32_t prev) |
| { |
| uint32_t crc = ~prev; |
| uint8_t *pByte = (uint8_t *)pData; |
| uint32_t j; |
| |
| while (len--) |
| { |
| crc ^= (uint32_t) *pByte++; |
| for (j = 0; j < 8; j++) |
| { |
| if (crc & 1) |
| crc = (crc >> 1) ^ DXS_CRC32_POLY; |
| else |
| crc = crc >> 1; |
| } |
| } |
| return ~crc; |
| } |
| |
| /** |
| Verify PRAM image CRC checksum |
| |
| \param pData - pointer to image data |
| \param size - size of the image in bytes |
| \param cksum - CRC checksum from image footer |
| |
| \return |
| 0 - verification failed |
| 1 - verification passed |
| */ |
| static int32_t pram_cksum_verify(uint8_t *pData, uint32_t size, uint32_t ftr_cksum) |
| { |
| uint32_t calc_cksum = 0, words32 = size/sizeof(uint32_t); |
| |
| while (words32--) |
| { |
| int32_t i = 1; |
| /* make 32-bit word of 4 bytes */ |
| uint32_t word = (*pData << 24)|(*(pData+1) << 16)|(*(pData+2) << 8)|(*(pData+3)); |
| |
| if (!(*((int8_t *)&i))) |
| { |
| /* to use LE CRC32 calculation on BE system - |
| swap bytes in a word */ |
| word = DXS_SWAP32(word); |
| } |
| |
| calc_cksum = dxs_crc32_le((void *)&word, sizeof(word), calc_cksum); |
| pData += sizeof(word); |
| } |
| |
| if (calc_cksum == ftr_cksum) |
| return 1; |
| |
| return 0; |
| } |
| |
| /** |
| Read the footer from PRAM patch file |
| |
| \param pData - pointer to PRAM image |
| \param size - size of the PRAM image |
| \param f - pointer to footer structure |
| |
| \return |
| - none |
| */ |
| static void pram_footer_read(uint8_t *pData, uint32_t size, |
| struct pram_footer *f) |
| { |
| uint32_t offset = size - sizeof(*f), word, i; |
| |
| for (i=0; i<(sizeof(*f)/sizeof(uint32_t)); i++) |
| { |
| word = 0; |
| |
| word |= (uint32_t) (pData[offset++]) << 24; |
| word |= (uint32_t) (pData[offset++]) << 16; |
| word |= (uint32_t) (pData[offset++]) << 8; |
| word |= (uint32_t) (pData[offset++]); |
| |
| *((uint32_t *)f + i) = word; |
| } |
| } |
| |
| /** |
| Function dxs_fw_download |
| |
| \param pDev - pointer to DXS device structure |
| \param pPatch - pointer to data buffer |
| \param nBytes - size of the buffer |
| |
| \return |
| - DXS_status_t |
| */ |
| static int32_t dxs_fw_download(DXS_DEVICE_t *pDev, |
| uint8_t *pPatch, uint32_t nBytes) |
| { |
| int32_t ret = DXS_statusOk; |
| uint16_t nDxsRegBootCfg = 0, |
| nDxsRegBootInfo = 0, |
| nDxsRegCfg = 0; |
| struct pram_footer ft; |
| |
| /* read PRAM patch footer */ |
| pram_footer_read(pPatch, nBytes, &ft); |
| |
| /* if the image has a footer, verify the checksum */ |
| if (ft.magic == DXS_PRAM_MAGIC_DXS1 || ft.magic == DXS_PRAM_MAGIC_DXS2) |
| { |
| /* do not pass the footer for checksum verification */ |
| nBytes -= sizeof(struct pram_footer); |
| |
| if (!pram_cksum_verify(pPatch, nBytes, ft.crc)) |
| { |
| DXS_RETURN(DXS_statusPramPatchCksumError); |
| } |
| |
| /* prepare image size for download - |
| minus one word of block delimiter */ |
| nBytes -= sizeof(uint32_t); |
| } |
| |
| /* stop polling timer */ |
| if (pDev->irqNumber == -1) |
| { |
| dxs_polling_timer_stop(); |
| } |
| |
| /* Set the controller startup configuration in the Boot Configuration |
| Register to boot from HOST_DATA inbox. */ |
| nDxsRegBootCfg = DXS_REG_BCFG_ASC_SPI; |
| ret = DXS_RegWrite(pDev, DXS_REG_BCFG, nDxsRegBootCfg); |
| if (ret != DXS_statusOk) |
| { |
| /* errmsg: Setting of boot configuration register failed. */ |
| ret = DXS_statusSetBootCfgErr; |
| } |
| |
| /* Reset controller to start the boot process from HOST_DATA inbox. */ |
| if (ret == DXS_statusOk) |
| { |
| nDxsRegCfg = DXS_REG_CFG_RST_RSTCORE|DXS_REG_CFG_8BIT_EN; |
| ret = DXS_RegWrite(pDev, DXS_REG_CFG, nDxsRegCfg); |
| if (ret != DXS_statusOk) |
| { |
| /* errmsg: Controller reset failed. */ |
| ret = DXS_statusCtrlResErr; |
| } |
| } |
| |
| /* setup waiting flag */ |
| pDev->bWaitingInPatchDwld = 1; |
| |
| /* Download firmware patch. */ |
| if (ret == DXS_statusOk) |
| { |
| ret = DXS_DwldPatch (pDev, pPatch, nBytes); |
| if (ret != DXS_statusOk) |
| { |
| /* errmsg: Download of the firmware binary failed. */ |
| ret = DXS_statusDwldBinErr; |
| } |
| } |
| |
| /* Check success of the download by reading the boot info register. */ |
| if (ret == DXS_statusOk) |
| { |
| uint8_t loop = 10; |
| |
| while (loop > 0) |
| { |
| struct timespec ts = {0, 2000000}; /* 2 ms */ |
| |
| nanosleep(&ts, NULL); |
| |
| /* read the boot state indication */ |
| ret = DXS_RegRead(pDev, DXS_REG_BINF, &nDxsRegBootInfo); |
| |
| if ((ret == DXS_statusOk) && |
| ((nDxsRegBootInfo & DXS_REG_BINF_BOOTSTATE_MASK) >= 0x10)) |
| { |
| break; |
| } |
| |
| loop--; |
| } |
| |
| if (loop == 0) |
| { |
| /* errmsg: Firmware download timeout. */ |
| ret = DXS_statusFwDwldTimeout; |
| } |
| } |
| |
| /* Set bootmode back to ROM in case a recovery is needed. */ |
| if (ret == DXS_statusOk) |
| { |
| nDxsRegBootCfg = DXS_REG_BCFG_ASC_ROM; |
| ret = DXS_RegWrite(pDev, DXS_REG_BCFG, nDxsRegBootCfg); |
| |
| if (ret != DXS_statusOk) |
| { |
| /* errmsg: Setting of boot configuration register failed. */ |
| ret = DXS_statusSetBootCfgErr; |
| } |
| } |
| |
| /* restart polling timer */ |
| if (pDev->irqNumber == -1) |
| { |
| dxs_polling_timer_start(); |
| } |
| |
| /* handle special test mode */ |
| if (pDev->irqNumber == 255) |
| { |
| sem_post(&pDev->obxSemaphore); |
| } |
| |
| /* wait for boot finished event */ |
| ret = sem_wait(&pDev->mtxPatchDwld); |
| if (ret) |
| DXS_RETURN(ret); |
| |
| /* enable interrupt self-clearing */ |
| if (ret == DXS_statusOk) |
| { |
| ret = DXS_RegRead(pDev, DXS_REG_CFG, &nDxsRegCfg); |
| if (ret == DXS_statusOk) |
| { |
| nDxsRegCfg |= DXS_REG_CFG_SC_MD; |
| ret = DXS_RegWrite(pDev, DXS_REG_CFG, nDxsRegCfg); |
| if (ret != DXS_statusOk) |
| { |
| ret = DXS_statusRegWriteError; |
| } |
| } |
| else |
| { |
| ret = DXS_statusRegReadError; |
| } |
| } |
| |
| /* read caps and version from the device, mark caps and |
| version structures updated */ |
| if (ret == DXS_statusOk) |
| { |
| ret = dxs_caps_vers_update(pDev); |
| } |
| |
| /* set default clock failure handling */ |
| if (ret == DXS_statusOk) |
| { |
| ret = DXS_CfEsdSwitch(pDev, 1); |
| } |
| |
| if (ret == DXS_statusOk) |
| { |
| pDev->flags |= DXS_DEV_PRAM_PATCH_DOWNLOADED; |
| } |
| |
| DXS_RETURN(ret); |
| } |
| |
| /** |
| Function DXS_FW_Select |
| |
| \param pDev - pointer to DXS device structure |
| \param pPatch - pointer to data buffer (container) |
| \param nBytes - size of the buffer |
| |
| \return |
| - DXS_status_t |
| */ |
| int32_t DXS_FW_Select(DXS_DEVICE_t *pDev, uint8_t *pPatch, uint32_t nBytes) |
| { |
| struct fw_container_header header; |
| struct fw_container_header *pHeaderNew = (struct fw_container_header*) pPatch; |
| uint32_t header_length = sizeof(header); |
| int32_t ret; |
| #if __BYTE_ORDER == __LITTLE_ENDIAN |
| int32_t i = 0; |
| #endif |
| |
| if ((pPatch == NULL) || (nBytes == 0) || (nBytes % 4) || |
| (nBytes < header_length)) |
| { |
| DXS_RETURN(DXS_statusInvalidParam); |
| } |
| |
| header = *pHeaderNew; |
| |
| /* on LE systems - swap bytes in every 32-bit word |
| of the header */ |
| #if __BYTE_ORDER == __LITTLE_ENDIAN |
| while (i < header_length/sizeof(uint32_t)) |
| { |
| uint32_t *pWord = (uint32_t *)&header + i; |
| |
| *pWord = DXS_SWAP32(*pWord); |
| i++; |
| } |
| #endif |
| |
| /* Process the container after verifying the header. */ |
| if (header.nType == DXS_FW_TYPE_GLOBAL_V1 && |
| header.nLength + header_length == nBytes && |
| header.nMagic == DXS_FW_GLOBAL_MAGIC && |
| header.nVersion != 0 && |
| header.nTimestamp != 0 && |
| header.nLength == header.nDxsV11FwLength + |
| header.nDxs1V12FwLength + |
| header.nDxs2V12FwLength) |
| { |
| uint8_t rom = 0, channels = 0; |
| |
| DXS_DevInfoGet(pDev, &rom, &channels); |
| |
| switch (rom) |
| { |
| |
| case 1: |
| /* ROM 1.x.x */ |
| if (header.nDxsV11FwLength > 0) |
| { |
| /* ROM 1.x.x devices are not allowed |
| for some types of DCDC */ |
| if (pDev->dcdcType == DXS_DCDC_IFB12CH8) |
| { |
| DXS_RETURN(DXS_statusPramPatchDwldFail); |
| } |
| |
| ret = dxs_fw_download(pDev, |
| pPatch + header_length + header.nDxsV11FwOffset, |
| header.nDxsV11FwLength); |
| if (ret != DXS_statusOk) |
| { |
| DXS_RETURN(ret); |
| } |
| } |
| break; |
| |
| case 2: |
| /* ROM 2.x.x */ |
| switch (channels) |
| { |
| case 1: |
| /* DXS 1-channel device */ |
| if (header.nDxs1V12FwLength > 0) |
| { |
| ret = dxs_fw_download(pDev, |
| pPatch + header_length + header.nDxs1V12FwOffset, |
| header.nDxs1V12FwLength); |
| if (ret != DXS_statusOk) |
| { |
| DXS_RETURN(ret); |
| } |
| } |
| break; |
| case 2: |
| /* DXS 2-channel device */ |
| if (header.nDxs2V12FwLength > 0) |
| { |
| ret = dxs_fw_download(pDev, |
| pPatch + header_length + header.nDxs2V12FwOffset, |
| header.nDxs2V12FwLength); |
| if (ret != DXS_statusOk) |
| { |
| DXS_RETURN(ret); |
| } |
| } |
| break; |
| default: |
| DXS_RETURN(DXS_statusPramPatchDwldFail); |
| break; |
| } |
| break; |
| |
| default: |
| { |
| DXS_RETURN(DXS_statusPramPatchDwldFail); |
| } |
| } |
| } |
| else |
| { |
| /* provided buffer has no header */ |
| DXS_RETURN(DXS_statusPramPatchInvalid); |
| } |
| |
| return DXS_statusOk; |
| } |