| /****************************************************************************** |
| |
| 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); |
| } |