blob: 9638426d8a680475cd3339ae8205958ebd802a45 [file] [log] [blame]
/******************************************************************************
Copyright (c) 2006-2015 Lantiq Deutschland GmbH
Copyright (c) 2015 Lantiq Beteiligungs-GmbH & Co.KG
Copyright 2018, Intel Corporation.
For licensing information, see the file 'LICENSE' in the root folder of
this software module.
******************************************************************************/
/**
\file dxs_bbd.c
Implementation of BBD functions.
*/
/* ========================================================================== */
/* Includes */
/* ========================================================================== */
#include <stdio.h>
#include "dxs.h"
#include "dxs_config.h"
#include "dxs_lib.h"
#include "dxs_fw_cmd.h"
#include "dxs_errno.h"
#include "dxs_error.h"
#include "dxs_sdd.h"
#include "dxs_sig.h"
#include "dxs_init.h"
#include "dxs_access.h"
/* ========================================================================== */
/* Macro definitions */
/* ========================================================================== */
#define BBD_MAGIC 0x21626264
#define BBD_FAMILY_ID_DXS 0x44585321
#define BBD_FAMILY_ID_DX8 0x44583821
#define BBD_BLK_ID_MASTER 0x000a
#define BBD_BLK_ID_CRAM 0x1001
#define BBD_BLK_ID_CRAM2 0x1020
#define BBD_BLK_ID_RING 0x1003
#define BBD_BLK_ID_BASIC 0x1005
#define BBD_BLK_ID_DCDC 0x1009
#define BBD_BLK_ID_MWL 0x1008
#define BBD_BLK_ID_UTD 0x1007
#define BBD_BLK_ID_END 0x0000
/** Reads 8bit value from big endian byte buffer */
#define BBD_GET_VAL8(buf, pos, val8) \
val8 = (buf)[(pos)]; (pos) +=1;
/** Reads 16bit value from big endian byte buffer */
#define BBD_GET_VAL16(buf, pos, val16) \
val16 = (((buf)[(pos)] << 8) |(buf)[(pos)+1]);\
(pos) += 2;
/** Reads 32bit value from big endian byte buffer */
#define BBD_GET_VAL32(buf, pos, val32) \
val32 = (((buf)[(pos)] << 24) | ((buf)[(pos)+1] << 16) | \
((buf)[(pos)+2] << 8) | (buf)[(pos)+3]);\
(pos) += 4;
/** Reads a complete bbd head, assuming given position is correct.*/
#define BBD_GET_HEAD(buf, pos, head)\
do {\
BBD_GET_VAL16((buf), (pos), (head)->tag);\
BBD_GET_VAL16((buf), (pos), (head)->vers);\
BBD_GET_VAL32((buf), (pos), (head)->len);\
}while(0);
/** Reads a complete master block exclusive head,
assuming given position is correct. */
#define BBD_GET_MASTER(buf, pos, master)\
do {\
BBD_GET_VAL32((buf), (pos), (master)->magic);\
BBD_GET_VAL32((buf), (pos), (master)->family);\
BBD_GET_VAL8 ((buf), (pos), (master)->year);\
BBD_GET_VAL8 ((buf), (pos), (master)->month);\
BBD_GET_VAL8 ((buf), (pos), (master)->day);\
BBD_GET_VAL8 ((buf), (pos), (master)->padding);\
}while(0);
/** Skip the actual block, assuming given position is correct */
#define BBD_SKIP_BLOCK(pos, block_len)\
(pos) += (block_len);
/* ========================================================================== */
/* Type definitions */
/* ========================================================================== */
/** BBD block header */
struct bbd_block_header
{
/** block id */
uint16_t tag;
/** block version */
uint16_t vers;
/** length */
uint32_t len;
} __attribute__ ((packed));
/** BBD block master */
struct bbd_block_master
{
uint32_t magic;
uint32_t family;
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t padding;
} __attribute__ ((packed));
/* ========================================================================== */
/* Global variables */
/* ========================================================================== */
/* ========================================================================== */
/* Function prototypes */
/* ========================================================================== */
/* ========================================================================== */
/* Function implementation */
/* ========================================================================== */
/**
Function bbd_block_cram
\param pCh - pointer to DXS channel structure
\param pHdr - pointer to BBD block header structure
\param pCram - poiner to CRAM block data
\param family - value from master block
\return
- DXS_status_t
*/
static int32_t bbd_block_cram(DXS_CHANNEL_t *pCh, struct bbd_block_header *pHdr,
uint8_t *pCram, uint32_t family)
{
uint16_t cram_offset, cmdLenBytes, pos = 0, cramLenWords;
int16_t words16;
uint8_t *pData;
int32_t ret = DXS_statusOk;
uint16_t paddingBytes;
uint8_t dest;
uint16_t blockOffset;
/* skip unsupported family block */
if (family != BBD_FAMILY_ID_DXS && family != BBD_FAMILY_ID_DX8)
return DXS_statusOk;
if (pHdr->tag == BBD_BLK_ID_CRAM2)
dest = 1;
else
dest = 0;
/* read offset */
DXS_cpb2w (&cram_offset, pCram, sizeof(uint16_t));
pData = pCram + sizeof(cram_offset);
/* data len in words16, minus len of offset_16 and crc_16 */
words16 = (pHdr->len - 2 * sizeof(uint16_t)) >> 1;
while (words16 > 0)
{
if (words16 > ((DXS_FW_SDD_Coeff_MAXLENGTH >> 1) - 1))
cmdLenBytes = DXS_FW_SDD_Coeff_MAXLENGTH;
else
cmdLenBytes = (words16 + 1) << 1;
paddingBytes = cmdLenBytes % sizeof(uint32_t);
cramLenWords = (cmdLenBytes >> 1) - 1;
blockOffset = cram_offset + (pos >> 1);
ret = DXS_SDD_CoeffBBD(pCh, dest, blockOffset, cramLenWords,
&pData[pos], paddingBytes);
pos += (cmdLenBytes - 2);
words16 -= cramLenWords;
}
DXS_RETURN(ret);
}
/**
Function bbd_block_ring
\param pCh - pointer to DXS channel structure
\param pHdr - pointer to BBD block header structure
\param pRing - pointer to RING block data
\param family - value from master block
\return
- DXS_status_t
*/
static int32_t bbd_block_ring(DXS_CHANNEL_t *pCh,
struct bbd_block_header *pHdr,
uint8_t *pRing,
uint32_t family)
{
union DXS_FW_SDD_RingConfig_u ring = {0};
uint32_t len = pHdr->len;
int32_t ret;
/* skip unsupported family block */
if (family != BBD_FAMILY_ID_DXS && family != BBD_FAMILY_ID_DX8)
return DXS_statusOk;
DXS_cpb2dw(&ring.u32[0], pRing, len);
ret = DXS_SDD_RingConfigBBD(pCh, &ring.fwmsg);
DXS_RETURN(ret);
}
/**
Function bbd_block_basic
\param pCh - pointer to DXS channel structure
\param pHdr - pointer to BBD block header structure
\param pBasic - pointer to BASIC block data
\param family - value from master block
\return
- DXS_status_t
*/
static int32_t bbd_block_basic(DXS_CHANNEL_t *pCh,
struct bbd_block_header *pHdr, uint8_t *pBasic,
uint32_t family)
{
union DXS_FW_SDD_BasicConfig_u basic = {0};
uint32_t len = pHdr->len;
int32_t ret;
/* skip unsupported family block */
if (family != BBD_FAMILY_ID_DXS && family != BBD_FAMILY_ID_DX8)
return DXS_statusOk;
DXS_cpb2dw(&basic.bc_data.data_u32[0], pBasic, len);
ret = DXS_SDD_BasicConfigBBD (pCh, &basic.bc_data);
DXS_RETURN(ret);
}
/**
Function bbd_block_dcdc
\param pCh - pointer to DXS channel structure
\param pHdr - pointer to BBD block header structure
\param pDcdc - pointer to DC/DC block data
\param family - value from master block
\return
- DXS_status_t
*/
static int32_t bbd_block_dcdc(DXS_CHANNEL_t *pCh,
struct bbd_block_header *pHdr, uint8_t *pDcdc,
uint32_t family)
{
union DXS_FW_SDD_DcDcConfig_u dcdc_cfg = {0};
uint32_t len = pHdr->len;
int32_t ret = DXS_statusOk;
if (family == BBD_FAMILY_ID_DXS)
{
DXS_DCDC_Var_t dcdc_hw_bbd;
DXS_cpb2dw(&dcdc_cfg.dcdc_data.data_u32[0], pDcdc, len);
dcdc_hw_bbd = dcdc_cfg.fwmsg.DcDcHw;
/* sanity check */
if (dcdc_hw_bbd != DXS_DCDC_IBB12 &&
dcdc_hw_bbd != DXS_DCDC_CIBB12 &&
dcdc_hw_bbd != DXS_DCDC_IB12 &&
dcdc_hw_bbd != DXS_DCDC_BB48 &&
dcdc_hw_bbd != DXS_DCDC_CBB48 &&
dcdc_hw_bbd != DXS_DCDC_IFB12 &&
dcdc_hw_bbd != DXS_DCDC_CIFB12 &&
dcdc_hw_bbd != DXS_DCDC_IBGD12 &&
dcdc_hw_bbd != DXS_DCDC_IBVD3)
{
fprintf (stderr, "BBD: Unsupported DCDC HW option %d\n", dcdc_hw_bbd);
DXS_RETURN(DXS_statusBbdUnknownDcDcOpt);
}
/* check if DCDC HW matches the configured HW option */
if (dcdc_hw_bbd != pCh->pParent->dcdcType)
{
fprintf (stderr,
"BBD: DCDC HW configuration mismatch: init=%d, bbd=%d\n",
pCh->pParent->dcdcType, dcdc_hw_bbd);
DXS_RETURN(DXS_statusBbdDCDCHwCfgMismatch);
}
/* enable charge pump */
if (dcdc_hw_bbd == DXS_DCDC_IB12 || dcdc_hw_bbd == DXS_DCDC_IFB12 ||
dcdc_hw_bbd == DXS_DCDC_CIFB12)
{
ret = DXS_ChargePumpSwitchOn(pCh->pParent);
}
}
else if (family == BBD_FAMILY_ID_DX8)
{
DXS_cpb2dw(&dcdc_cfg.dcdc_data.data_u32[0], pDcdc, len);
if (pCh->pParent->dcdcType == DXS_DCDC_IFB12CH8)
{
/* sanity check - not perfect due to missing DcDcHw field
in the command */
if (dcdc_cfg.dx8fwmsg.Res00 || dcdc_cfg.dx8fwmsg.Res01 ||
dcdc_cfg.dx8fwmsg.Res02)
{
fprintf (stderr,
"BBD: DCDC block does not match the declared HW: init=%d\n",
pCh->pParent->dcdcType);
DXS_RETURN(DXS_statusBbdDCDCHwCfgMismatch);
}
/* enable charge pump */
ret = DXS_ChargePumpSwitchOn(pCh->pParent);
}
}
else
{
/* skip unsupported family block */
return DXS_statusOk;
}
if (ret == DXS_statusOk)
ret = DXS_SDD_DcDcConfigBBD(pCh, &dcdc_cfg.dcdc_data);
/* Set flag to start an automatic calibration after BBD download. */
if (ret == DXS_statusOk || ret == DXS_statusBbdDcDcReconfig)
(void) DXS_SDD_CalibrationNeededSet(pCh, 1);
/* It's not a real error for the entire BBD download */
if (ret == DXS_statusBbdDcDcReconfig)
ret = DXS_statusOk;
DXS_RETURN(ret);
}
/**
Function bbd_block_mwl
\param pCh - pointer to DXS channel structure
\param pHdr - pointer to BBD block header structure
\param pMwl - pointer to MWL block data
\param family - value from master block
\return
- DXS_status_t
*/
static int32_t bbd_block_mwl(DXS_CHANNEL_t *pCh, struct bbd_block_header *pHdr,
uint8_t *pMwl, uint32_t family)
{
union DXS_FW_SDD_MwlConfig_u mwl = {0};
uint32_t len = pHdr->len;
int32_t ret;
/* skip unsupported family block */
if (family != BBD_FAMILY_ID_DXS && family != BBD_FAMILY_ID_DX8)
return DXS_statusOk;
DXS_cpb2dw(&mwl.u32[0], pMwl, len);
ret = DXS_SDD_MwlConfigBBD (pCh, &mwl.fwmsg);
DXS_RETURN(ret);
}
/**
Function bbd_block_utd
\param pCh - pointer to DXS channel structure
\param pHdr - pointer to BBD block header structure
\param pUtd - pointer to UTD block data
\param family - value from master block
\return
- DXS_status_t
*/
static int32_t bbd_block_utd(DXS_CHANNEL_t *pCh, struct bbd_block_header *pHdr,
uint8_t *pUtd, uint32_t family)
{
#ifdef DXS_FEAT_UTD
union DXS_FW_SIG_UtdCoeff_u utd = {0};
uint32_t len = pHdr->len;
int32_t ret;
uint16_t unToneIdx;
/* skip unsupported family block */
if (family != BBD_FAMILY_ID_DXS && family != BBD_FAMILY_ID_DX8)
return DXS_statusOk;
/* Tone index */
DXS_cpb2w(&unToneIdx, pUtd, sizeof(uint16_t));
/* Tone coefficients */
DXS_cpb2dw(&utd.u32[0], pUtd + sizeof(uint16_t), len - sizeof(uint16_t));
ret = DXS_SIG_UtdCoeffUpdate(pCh, unToneIdx, &utd.fwmsg);
DXS_RETURN(ret);
#else
return DXS_statusOk;
#endif /* DXS_FEAT_UTD */
}
/**
Function dxs_bbd_download
\param pCh - pointer to DXS channel structure
\param pBbd - pointer to BBD data buffer
\param nBytes - size of BBD data buffer
\return
- DXS_status_t
*/
int32_t dxs_bbd_download(DXS_CHANNEL_t *pCh, uint8_t *pBbd, uint32_t nBytes)
{
struct bbd_block_header hdr = {0};
struct bbd_block_master master = {0};
int32_t ret = DXS_statusOk;
uint32_t byte_pos = 0;
uint8_t bCalibrationNeeded;
if (pCh==NULL || nBytes==0 || pBbd==NULL)
{
DXS_RETURN(DXS_statusInvalidParam);
}
/* for ROM 1.0 devices the patch download is mandatory */
if (pCh->pParent->rom_mask_ver < 2 &&
!(pCh->pParent->flags & DXS_DEV_PRAM_PATCH_DOWNLOADED))
{
DXS_RETURN(DXS_statusPramPatchNotDownloaded);
}
pCh->flags &= ~DXS_CH_BBD_DOWNLOADED;
/* TODO: overall bbd file integrity check:
- summary block length matches nBytes
- DCDC HW option matches configuration
- unsupported blocks present */
while (byte_pos < nBytes)
{
BBD_GET_HEAD (pBbd, byte_pos, &hdr);
switch (hdr.tag)
{
case BBD_BLK_ID_MASTER:
{
BBD_GET_MASTER(pBbd, byte_pos, &master);
if (master.magic != BBD_MAGIC)
{
/* invalid master block */
DXS_RETURN(DXS_statusBbdMasterBlkInvalid);
}
}
break;
case BBD_BLK_ID_CRAM:
case BBD_BLK_ID_CRAM2:
ret = bbd_block_cram(pCh, &hdr, pBbd + byte_pos, master.family);
break;
case BBD_BLK_ID_RING:
ret = bbd_block_ring(pCh, &hdr, pBbd + byte_pos, master.family);
break;
case BBD_BLK_ID_BASIC:
ret = bbd_block_basic(pCh, &hdr, pBbd + byte_pos, master.family);
break;
case BBD_BLK_ID_DCDC:
ret = bbd_block_dcdc(pCh, &hdr, pBbd + byte_pos, master.family);
break;
case BBD_BLK_ID_MWL:
ret = bbd_block_mwl(pCh, &hdr, pBbd + byte_pos, master.family);
break;
case BBD_BLK_ID_UTD:
ret = bbd_block_utd(pCh, &hdr, pBbd + byte_pos, master.family);
break;
case BBD_BLK_ID_END:
/* do nothing */
break;
default:
{
fprintf (stderr,
"Unknown unsupported block id 0x%04X in BBD at offset %lu.\n",
hdr.tag, (unsigned long int)(byte_pos - sizeof(struct bbd_block_header)));
DXS_RETURN(DXS_statusBbdUnknownBlk);
}
}
if (ret != DXS_statusOk)
{
DXS_RETURN(ret);
}
/* go to next block */
if (hdr.tag != BBD_BLK_ID_MASTER)
BBD_SKIP_BLOCK (byte_pos, hdr.len);
}
if (ret == DXS_statusOk)
{
pCh->flags |= DXS_CH_BBD_DOWNLOADED;
/* Do automatically a calibration on the channel on which the BBD was
downloaded to adapt to new coefficients. */
(void) DXS_SDD_CalibrationNeededGet(pCh, &bCalibrationNeeded);
if (bCalibrationNeeded == 1)
{
ret = DXS_SDD_Calibration(pCh);
}
if (ret != DXS_statusOk)
{
/* Automatic calibration failed. */
ret = DXS_statusAutomaticCalibrationFailed;
}
}
DXS_RETURN(ret);
}