blob: 1c615ce0de7c06cb71b0c740d96dd40045122282 [file] [log] [blame]
/******************************************************************************
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;
}