vos/ambiq-hal-sys/ambiq-sparkfun-sdk/ambiq_ble/em9304/em9304_init.c
2022-10-23 23:45:43 -07:00

825 lines
30 KiB
C

//*****************************************************************************
//
//! @file hci_drv.c
//!
//! @brief HCI driver interface.
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "am_mcu_apollo.h"
#include "am_util.h"
#include "am_devices_em9304.h"
#include "em9304_init.h"
#include "em9304_patches.h"
#include "hci_apollo_config.h"
#define INVALIDATE_UNKNOWN_PATCHES
#define ENABLE_32K_CLK_FROM_APOLLO
#define SLEEP_CLK_PATCH_CONTAINER_ID (0x16)
// if a product is designed with step-up DCDC mode for EM9304
// and was programmed HCI v6 patch on OTP, it's required
// to define INVALIDATE_HCI_V6_PATCH_ON_OTP to do a special
// handling to invalidate the v6 patch on OTP and program
// v8 or later HCI patch.
#define INVALIDATE_HCI_V6_PATCH_ON_OTP
#ifdef INVALIDATE_HCI_V6_PATCH_ON_OTP
#define HCI_V6_PATCH_CONTAINER_ID 53
#endif
// This should be defined as the currently included HCI code patch
// e.g. currently v8 is latest, which has container ID 77.
#define HCI_CURRENT_CODE_PATCH_CONTAINER_ID 77
// Define the HCI Command Type locally.
#define HCI_CMD_TYPE 1
// EM_PatchQuery field offsets
#define PATCH_INDEX_OFFSET 3
// EM_PatchQuery response field offsets
#define CONTAINER_COUNT_INDEX 7
#define CONTAINER_ADDR_INDEX 15
#define CONTAINER_SIZE_INDEX 19
#define BUILD_NUMBER_INDEX 27
#define USER_BUILD_NUMBER_INDEX 29
#define CONTAINER_VERSION_INDEX 32
#define CONTAINER_TYPE_INDEX 33
#define CONTAINER_ID_INDEX 34
// EM_PatchQuery response values
#define CONTAINER_TYPE_CONFIG_DATA_WORD 1
#define CONTAINER_TYPE_RANDOM_DATA_WORD 2
#define CONTAINER_TYPE_RANDOM_DATA_BYTE 3
#define CONTAINER_TYPE_CONFIG_DATA_BYTE 11
// EM_PatchWrite and EM_PatchContine field offsets
#define PATCH_LENGTH_OFFSET 2
// EM_PatchWrite destination memory field offsets
#define PATCH_DEST_MEMORY_OFFSET 3
// EM_PatchWrite and EM_PatchContinue response field offsets
#define HCI_STATUS_OFFSET 6
#define EM_PATCH_STATUS_OFFSET 7
// EM_PatchWrite and EM_PatchContinue Patch Status values
#define EM_PATCH_APPLIED 1
#define EM_PATCH_CONTINUE 2
// Maximum number of attempts to wait for a response from EM9304.
#define EM9304_MAX_ATTEMPTS 100
#define EM9304_ATTEMPT_DELAY_MS 1
#define EM9304_IRAM1_START_ADDRESS 0x20000
// Initialization function error return status.
enum
{
EM9304_INIT_STATUS_SUCCESS,
EM9304_INIT_STATUS_ERROR
} e_em9304_init_status;
//*****************************************************************************
//
// HCI Commands for EM9304
//
//*****************************************************************************
uint8_t g_pui8EM_SleepDisable[] = {0x2D, 0xFC, 0x01, 0x00};
uint8_t g_pui8EM_SetOTPOn[] = {0x2B, 0xFC, 0x01, 0x01};
uint8_t g_pui8EM_SetOTPOff[] = {0x2B, 0xFC, 0x01, 0x00};
uint8_t g_pui8EM_SetIRAMOn[] = {0x2B, 0xFC, 0x01, 0x07};
uint8_t g_pui8EM_PatchQuery[] = {0x34, 0xFC, 0x02, 0x00, 0x00};
uint8_t g_pui8EM_SleepEnable[] = {0x2D, 0xFC, 0x01, 0x01};
uint8_t g_pui8EM_CpuReset[] = {0x32, 0xFC, 0x00};
uint32_t
applyEM9304Patches(uint32_t target_memory, uint32_t containerID);
//*****************************************************************************
//
// HCI RX packet buffer for EM9304 Driver.
//
//*****************************************************************************
static uint32_t g_pui32HCIRXBuffer[64];
//*****************************************************************************
//
// Static record of the EM9304 patch errors
//
//*****************************************************************************
uint32_t g_EMPatchErrors = 0;
//*****************************************************************************
//
//! @brief Patch Response helper functions for the EM9304 patches. This
//! routine blocks on a response from the EM9304 and filters the
//! vendor specific events.
//!
//! @return none.
//
//*****************************************************************************
uint32_t
waitEM9304Response(void)
{
uint32_t numBytesRx;
// HCI Respone should return in 1-2 messages at most, but driver returns
// 0 bytes when nothing is available, so wait up to 10msec.
for (uint32_t attempts = 0; attempts < EM9304_MAX_ATTEMPTS; attempts++)
{
numBytesRx = am_devices_em9304_block_read(&g_sEm9304, g_pui32HCIRXBuffer, 0);
// Look for "no message" return while filtering out the EM9304 vendor specific events.
if ((numBytesRx != 0) && (!((numBytesRx == 4) && (0x0000FF04 == (g_pui32HCIRXBuffer[0] & 0x0000FFFF)))))
{
return EM9304_INIT_STATUS_SUCCESS;
}
am_util_delay_ms(EM9304_ATTEMPT_DELAY_MS);
}
return EM9304_INIT_STATUS_ERROR;
}
//*****************************************************************************
//
//! @brief Function to check for valid patches in the em9304_patches.* files.
//! Invalid patches means that the scripts to generate the patch files
//! were run without valid *.emp files as input.
//!
//! @return bool (TRUE = patches are valid).
//
//*****************************************************************************
bool validEM9304Patches(void)
{
//
// Check to see if we have valid patches.
// NULL patch has a specific signature.
//
if ((1 == EM9304_PATCHES_NUM_PATCHES) &&
(0xFFFF == g_pEm9304Patches[0].buildNumber) &&
(0xFFFF == g_pEm9304Patches[0].userBuildNumber) &&
(0xFF == g_pEm9304Patches[0].containerVersion) &&
(0xFF == g_pEm9304Patches[0].containerType) &&
(0xFF == g_pEm9304Patches[0].containerID) &&
(0x00 == g_pEm9304Patches[0].applyPatch) &&
(0x00 == g_pEm9304Patches[0].startingPatch) &&
(0x00 == g_pEm9304Patches[0].endingPatch))
{
am_util_debug_printf("em9304_patches.c contains NULL patch only\n");
return false;
}
else
{
am_util_debug_printf("Valid em9304_patches.c file found\n");
return true;
}
}
//*****************************************************************************
//
//! @brief Function to invalidate a patch at a given address. The size field
//! is changed to corrupt the patch in OTP.
//!
//! @return status.
//
//*****************************************************************************
#ifdef INVALIDATE_UNKNOWN_PATCHES
static uint32_t invalidateEM9304Patch(uint32_t addr, uint32_t size)
{
uint8_t *bytePtr = (uint8_t *)&g_pui32HCIRXBuffer;
uint8_t payload[] =
{
0x22, 0xFC, //WriteAtAddr command
0x0C, //HCI param length
0, 0, 0, 0, // container address placeholder
0x33, 0x39, 0x6D, 0x65, //signature
0, 0, 0, 0 //size placeholder
};
payload[3] = (uint8_t)(addr & 0xFF);
payload[4] = (uint8_t)((addr & 0xFF00) >> 8);
payload[5] = (uint8_t)((addr & 0xFF0000) >> 16);
payload[6] = (uint8_t)((addr & 0xFF000000) >> 24);
size |= 0x36000000; // mask the size to change the patch (invalidate it).
payload[11] = (uint8_t)(size & 0xFF);
payload[12] = (uint8_t)((size & 0xFF00) >> 8);
payload[13] = (uint8_t)((size & 0xFF0000) >> 16);
payload[14] = (uint8_t)((size & 0xFF000000) >> 24);
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, payload, sizeof(payload));
if ((EM9304_INIT_STATUS_SUCCESS != waitEM9304Response()) || (bytePtr[HCI_STATUS_OFFSET] != 0))
{
am_util_debug_printf("Invalidating patch at %x status %d\n", addr, bytePtr[HCI_STATUS_OFFSET]);
return EM9304_INIT_STATUS_ERROR;
}
am_util_debug_printf("Invalidating patch at %x status OK\n", addr);
return EM9304_INIT_STATUS_SUCCESS;
}
#endif
//*****************************************************************************
//
//! @brief Query the EM9304 patches. This routine uses the EM_PatchQuery HCI
//! command to interogate the connected EM9304 about its current patch
//! state and then update the patch Container Info data structure.
//!
//! @return status.
//
//*****************************************************************************
uint32_t
queryEM9304Patches(void)
{
uint32_t containerCount;
uint32_t buildNumber, userBuildNumber, containerVersion, containerType, containerID;
#ifdef INVALIDATE_UNKNOWN_PATCHES
uint32_t containerAddr, containerSize;
bool invalidatePatch = false;
#endif
uint8_t *pBuf = (uint8_t *)g_pui32HCIRXBuffer;
// Initialize the container info patch status
for (uint32_t patch = 0; patch < EM9304_PATCHES_NUM_PATCHES; patch++)
{
// Check patch for enabling 32Khz clck from Apollo MCU
if ((g_pEm9304Patches[patch].userBuildNumber == 2) && (g_pEm9304Patches[patch].containerID == SLEEP_CLK_PATCH_CONTAINER_ID))
{
uint32_t ui32PN;
//
// Device identification
//
ui32PN = AM_REG(MCUCTRL, CHIP_INFO) &
AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_PN_M;
#ifdef ENABLE_32K_CLK_FROM_APOLLO
// Currently only enable this for Apollo2-Blue
if (ui32PN == AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_APOLLOBL)
{
g_pEm9304Patches[patch].applyPatch = true;
// GPIO 24 in Apollo2-blue connected to LFCLK in EM9304
am_hal_gpio_pin_config(24, AM_HAL_PIN_24_CLKOUT);
am_hal_clkgen_osc_start(AM_HAL_CLKGEN_OSC_XT);
am_util_delay_ms(500);
am_hal_clkgen_clkout_enable(AM_HAL_CLKGEN_CLKOUT_CKSEL_XT);
}
#endif
}
else
{
g_pEm9304Patches[patch].applyPatch = true;
}
}
// Send the EM_SetSleepOptions command to disable sleep and check the response.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SleepDisable, sizeof(g_pui8EM_SleepDisable));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Sleep Disable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the Sleep Disable.
if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2D != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 Sleep Disable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Send the EM_SetMemoryMode command to turn on OTP and check the response.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SetOTPOn, sizeof(g_pui8EM_SetOTPOn));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 OTP Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the OTP enable.
if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2B != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 OTP Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Query the EM9304 with the EM_PatchQuery and Patch Index = 0. This will return the Container Count.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_PatchQuery, sizeof(g_pui8EM_PatchQuery));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Patch Query\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the Patch Query.
if ((0x01200E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC34 != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 Patch Query\n");
return EM9304_INIT_STATUS_ERROR;
}
// Extract the container information from the query response.
containerCount = (uint32_t)pBuf[CONTAINER_COUNT_INDEX] +
((uint32_t)pBuf[CONTAINER_COUNT_INDEX + 1] << 8);
// Assume the first patch is the manufacturing trim patch.
// This is the only patch that never should be invalidated.
am_util_debug_printf("Number of patch containers on EM9304 excluding Patch#0: %d\n", containerCount - 1);
#ifdef INVALIDATE_UNKNOWN_PATCHES
#ifdef INVALIDATE_HCI_V6_PATCH_ON_OTP
bool old_patch_invalidated = false;
bool hci_v6_patch_present_on_otp = false;
// For each container in Container Count to see if HCI meta patch v6 patch
// is present
for (uint32_t container = 1; container < containerCount; container++)
{
// Send the EM_PatchQuery for the Container.
g_pui8EM_PatchQuery[PATCH_INDEX_OFFSET] = container;
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_PatchQuery, sizeof(g_pui8EM_PatchQuery));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Patch Query\n");
return EM9304_INIT_STATUS_ERROR;
}
containerID = pBuf[CONTAINER_ID_INDEX];
// v6 patch's container ID is 53 in decimal.
if (containerID == HCI_V6_PATCH_CONTAINER_ID)
{
hci_v6_patch_present_on_otp = true;
am_util_debug_printf("HCI v6 patch found in OTP\n");
break;
}
}
if (hci_v6_patch_present_on_otp)
{
// here we have to do code-reset to EM9304
am_hal_gpio_pin_config(HCI_APOLLO_RESET_PIN, AM_HAL_GPIO_OUTPUT);
am_hal_gpio_out_bit_clear(HCI_APOLLO_RESET_PIN);
am_util_delay_ms(10);
am_hal_gpio_out_bit_set(HCI_APOLLO_RESET_PIN);
am_util_delay_ms(20);
// Apply the latest code patch into IRAM in order to invalidate v6 patch in otp
// properly.
applyEM9304Patches(DEST_MEMORY_IRAM, HCI_CURRENT_CODE_PATCH_CONTAINER_ID);
// Send EM_CpuReset HCI command.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_CpuReset, sizeof(g_pui8EM_CpuReset));
// HCI Respone should return in 1-2 messages at most, but driver returns
// 0 bytes when nothing is available, so wait up to 10msec.
for (uint32_t attempts = 0; attempts < EM9304_MAX_ATTEMPTS; attempts++)
{
uint32_t numBytesRx;
numBytesRx = am_devices_em9304_block_read(&g_sEm9304, g_pui32HCIRXBuffer, 0);
if ((numBytesRx == 7) && (0x0000FC32 == (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("EM9304 CPU Reset Successfully\n");
break;
}
am_util_delay_ms(EM9304_ATTEMPT_DELAY_MS);
}
// Send the EM_SetSleepOptions command to disable sleep and check the response.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SleepDisable, sizeof(g_pui8EM_SleepDisable));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Sleep Disable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the Sleep Disable.
if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2D != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 Sleep Disable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Send the EM_SetMemoryMode command to turn on OTP and check the response.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SetOTPOn, sizeof(g_pui8EM_SetOTPOn));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 OTP Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the OTP enable.
if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2B != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 OTP Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Query the EM9304 with the EM_PatchQuery and Patch Index = 0. This will return the Container Count.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_PatchQuery, sizeof(g_pui8EM_PatchQuery));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Patch Query\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the Patch Query.
if ((0x01200E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC34 != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 Patch Query\n");
return EM9304_INIT_STATUS_ERROR;
}
// Extract the container information from the query response.
containerCount = (uint32_t)pBuf[CONTAINER_COUNT_INDEX] +
((uint32_t)pBuf[CONTAINER_COUNT_INDEX + 1] << 8);
// Assume the first patch is the manufacturing trim patch.
// This is the only patch that never should be invalidated.
am_util_debug_printf("Number of patch containers on EM9304 excluding Patch#0: %d\n", containerCount - 1);
}
#endif
#endif
// For each container in Container Count
for (uint32_t container = 1; container < containerCount; container++)
{
// Send the EM_PatchQuery for the Container.
g_pui8EM_PatchQuery[PATCH_INDEX_OFFSET] = container;
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_PatchQuery, sizeof(g_pui8EM_PatchQuery));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Patch Query\n");
return EM9304_INIT_STATUS_ERROR;
}
// Extract the container information from the query response.
containerCount = (uint32_t)pBuf[CONTAINER_COUNT_INDEX] +
((uint32_t)pBuf[CONTAINER_COUNT_INDEX + 1] << 8);
buildNumber = (uint32_t)pBuf[BUILD_NUMBER_INDEX] +
((uint32_t)(pBuf[BUILD_NUMBER_INDEX + 1] << 8));
userBuildNumber = (uint32_t)pBuf[USER_BUILD_NUMBER_INDEX] +
((uint32_t)(pBuf[USER_BUILD_NUMBER_INDEX + 1] << 8));
containerVersion = pBuf[CONTAINER_VERSION_INDEX];
containerType = pBuf[CONTAINER_TYPE_INDEX];
containerID = pBuf[CONTAINER_ID_INDEX];
#ifdef INVALIDATE_UNKNOWN_PATCHES
containerAddr = (uint32_t)((pBuf[CONTAINER_ADDR_INDEX + 3] << 24) +
(pBuf[CONTAINER_ADDR_INDEX + 2] << 16) +
(pBuf[CONTAINER_ADDR_INDEX + 1] << 8) +
pBuf[CONTAINER_ADDR_INDEX]);
containerSize = (uint32_t)((pBuf[CONTAINER_SIZE_INDEX + 3] << 24) +
(pBuf[CONTAINER_SIZE_INDEX + 2] << 16) +
(pBuf[CONTAINER_SIZE_INDEX + 1] << 8) +
pBuf[CONTAINER_SIZE_INDEX]);
am_util_debug_printf("Patch #%d: Container Address = %8.8X Container Size = %4.4d Container Type=%d Container ID=%d Container Version=%d Build Number=%d User Build Number=%d\n",
container, containerAddr, containerSize, containerType, containerID, containerVersion, buildNumber, userBuildNumber);
// if patch is on IRAM, we wont' do anything to it.
if (containerAddr & EM9304_IRAM1_START_ADDRESS)
{
continue;
}
// Check for patches that are likely not configuration managed by the customer.
// Avoid invalidating these patches.
if (((CONTAINER_TYPE_CONFIG_DATA_WORD == containerType) ||
(CONTAINER_TYPE_RANDOM_DATA_WORD == containerType) ||
(CONTAINER_TYPE_CONFIG_DATA_BYTE == containerType) ||
(CONTAINER_TYPE_RANDOM_DATA_BYTE == containerType)) &&
((0 == buildNumber) || (3089 == buildNumber)) &&
(0 == userBuildNumber))
{
invalidatePatch = false;
}
else
{
// Initialize the invalidate flag.
invalidatePatch = true;
}
#endif
// For each local patch, compare the Container Version, Container Type, and Container ID to the container info.
for (uint32_t patch = 0; patch < EM9304_PATCHES_NUM_PATCHES; patch++)
{
if ((g_pEm9304Patches[patch].buildNumber == buildNumber) &&
(g_pEm9304Patches[patch].userBuildNumber == userBuildNumber) &&
(g_pEm9304Patches[patch].containerVersion == containerVersion) &&
(g_pEm9304Patches[patch].containerType == containerType) &&
(g_pEm9304Patches[patch].containerID == containerID))
{
g_pEm9304Patches[patch].applyPatch = false; // Patch is already installed, so don't apply.
#ifdef INVALIDATE_UNKNOWN_PATCHES
// Note that we will "re-enable" patches here even if they met the criteria above (which can happen!)
invalidatePatch = false;
#endif
break;
}
}
#ifdef INVALIDATE_UNKNOWN_PATCHES
// Check to see if we need to invalidate the patch.
if (invalidatePatch)
{
invalidateEM9304Patch(containerAddr, containerSize);
// if any old patch on OTP got invalidated, we need to hard-reset em9304
// for the patch not to take effect so that subsequent patch applying can
// work reliably.
old_patch_invalidated = true;
}
#endif
}
#ifdef INVALIDATE_UNKNOWN_PATCHES
#ifdef INVALIDATE_HCI_V6_PATCH_ON_OTP
if (hci_v6_patch_present_on_otp || old_patch_invalidated)
{
// If we has invalidate v6 or other unknown patch on OTP
// we should do a cold reset to em9304 in order to clean
// the previously programmed patch in IRAM.
// here we have to do code-reset to EM9304
am_hal_gpio_pin_config(HCI_APOLLO_RESET_PIN, AM_HAL_GPIO_OUTPUT);
am_hal_gpio_out_bit_clear(HCI_APOLLO_RESET_PIN);
am_util_delay_ms(10);
am_hal_gpio_out_bit_set(HCI_APOLLO_RESET_PIN);
am_util_delay_ms(20);
}
#endif
#endif
// if (DEST_MEMORY_IRAM == EM9304_PATCHES_DEST_MEMORY)
// {
// // Send the EM_SetMemoryMode command to turn off OTP and check the response.
// am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SetOTPOff, sizeof(g_pui8EM_SetOTPOff) );
// if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
// {
// am_util_debug_printf("No Response to EM9304 OTP Disable\n");
// return EM9304_INIT_STATUS_ERROR;
// }
// // Check that the response is to the OTP Disable.
// if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2B != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
// {
// am_util_debug_printf("Invalid Response to EM9304 OTP Disable\n");
// return EM9304_INIT_STATUS_ERROR;
// }
// }
// Send the EM_SetSleepOptions command to disable sleep and check the response.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SleepEnable, sizeof(g_pui8EM_SleepEnable));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Sleep Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the Sleep Enable.
if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2D != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 Sleep Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
return EM9304_INIT_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Apply the EM9304 patches. This routine uses the EM_PatchQuery HCI
//! command to interogate the connected EM9304 about its current patch
//! state and then update the patch Container Info data structure.
//!
//!
//! @return Returns the status of the patch application (< 0 is an error).
//
//*****************************************************************************
uint32_t
applyEM9304Patches(uint32_t target_memory, uint32_t containerID)
{
uint8_t *bytePtr = (uint8_t *)&g_pui32HCIRXBuffer;
uint32_t ui32PN;
g_EMPatchErrors = 0;
//
// Device identification
//
ui32PN = AM_REG(MCUCTRL, CHIP_INFO) &
AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_PN_M;
// only enable clock for Apollo2-blue (maybe later Apollo3 as well)
if (ui32PN == AM_UTIL_MCUCTRL_CHIP_INFO_PARTNUM_APOLLOBL)
{
}
if (DEST_MEMORY_IRAM == target_memory)
{
// Send the EM_SetMemoryMode command to turn on IRAM and check the response.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_SetIRAMOn, sizeof(g_pui8EM_SetIRAMOn));
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 IRAM Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
// Check that the response is to the IRAM enable.
if ((0x01040E04 != g_pui32HCIRXBuffer[0]) || (0x0000FC2B != (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("Invalid Response to EM9304 IRAM Enable\n");
return EM9304_INIT_STATUS_ERROR;
}
}
// Loop through the patches and apply those that are not already there.
// For each local patch, compare the Container Version, Container Type, and Container ID to the container info.
for (uint32_t patch = 0; patch < EM9304_PATCHES_NUM_PATCHES; patch++)
{
if ((containerID != 0) && (containerID != (uint32_t)g_pEm9304Patches[patch].containerID))
{
continue;
}
if (g_pEm9304Patches[patch].applyPatch)
{
am_util_debug_printf("Applying Patch #%d: Container Type=%d Container ID=%d Container Version=%d Build Number=%d User Build Number=%d\n",
patch, g_pEm9304Patches[patch].containerType, g_pEm9304Patches[patch].containerID, g_pEm9304Patches[patch].containerVersion,
g_pEm9304Patches[patch].buildNumber, g_pEm9304Patches[patch].userBuildNumber);
for (uint32_t index = g_pEm9304Patches[patch].startingPatch; index < g_pEm9304Patches[patch].endingPatch; index++)
{
if ((index == g_pEm9304Patches[patch].startingPatch) &&
(target_memory != g_pEm9304PatchesHCICmd[index][PATCH_DEST_MEMORY_OFFSET]))
{
// max payload is 64 bytes for patch writing
uint8_t g_pEm9304PatchesHCICmd_temp[80];
memcpy(g_pEm9304PatchesHCICmd_temp, g_pEm9304PatchesHCICmd[index],
g_pEm9304PatchesHCICmd[index][PATCH_LENGTH_OFFSET] + 3);
// change destination memory type
g_pEm9304PatchesHCICmd_temp[PATCH_DEST_MEMORY_OFFSET] = target_memory;
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, (uint8_t *)g_pEm9304PatchesHCICmd_temp,
g_pEm9304PatchesHCICmd_temp[PATCH_LENGTH_OFFSET] + 3);
}
else
{
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, (uint8_t *)g_pEm9304PatchesHCICmd[index],
g_pEm9304PatchesHCICmd[index][PATCH_LENGTH_OFFSET] + 3);
}
if (EM9304_INIT_STATUS_SUCCESS != waitEM9304Response())
{
am_util_debug_printf("No Response to EM9304 Patch Write\n");
return EM9304_INIT_STATUS_ERROR;
}
if ((g_pEm9304PatchesHCICmd[index][0] == 0x27) &&
((bytePtr[EM_PATCH_STATUS_OFFSET] != EM_PATCH_CONTINUE) &&
(bytePtr[EM_PATCH_STATUS_OFFSET] != EM_PATCH_APPLIED)))
{
am_util_debug_printf("Error Response (%d)to EM9304 Patch Write\n", bytePtr[EM_PATCH_STATUS_OFFSET]);
return EM9304_INIT_STATUS_ERROR;
}
else if (g_pEm9304PatchesHCICmd[index][0] == 0x28)
{
if (((index + 1) == g_pEm9304Patches[patch].endingPatch) && (bytePtr[EM_PATCH_STATUS_OFFSET] != EM_PATCH_APPLIED))
{
am_util_debug_printf("Error Response to EM9304 Patch Continue (next to last patch segment)\n");
return EM9304_INIT_STATUS_ERROR;
}
else if (((index + 1) < g_pEm9304Patches[patch].endingPatch) && (bytePtr[EM_PATCH_STATUS_OFFSET] != EM_PATCH_CONTINUE))
{
am_util_debug_printf("Error Response to EM9304 Patch Continue (last patch segment)\n");
return EM9304_INIT_STATUS_ERROR;
}
}
}
}
}
return EM9304_INIT_STATUS_SUCCESS;
}
//*****************************************************************************
//
// Configure the necessary pins and start the EM9304 radio.
//
//*****************************************************************************
uint32_t
initEM9304(void)
{
if (validEM9304Patches())
{
//
// Query the EM9304 for patches
//
if (EM9304_INIT_STATUS_SUCCESS == queryEM9304Patches())
{
//
// Apply the patches not already in the EM9304
//
if (EM9304_INIT_STATUS_SUCCESS != applyEM9304Patches(EM9304_PATCHES_DEST_MEMORY, 0))
{
am_util_debug_printf("EM9304 Patch Application Failed\n");
}
}
else
{
am_util_debug_printf("EM9304 Patching Query Failed. Patch update not applied\n");
}
}
// Send EM_CpuReset HCI command.
am_devices_em9304_block_write(&g_sEm9304, HCI_CMD_TYPE, g_pui8EM_CpuReset, sizeof(g_pui8EM_CpuReset));
// HCI Respone should return in 1-2 messages at most, but driver returns
// 0 bytes when nothing is available, so wait up to 10msec.
for (uint32_t attempts = 0; attempts < EM9304_MAX_ATTEMPTS; attempts++)
{
uint32_t numBytesRx;
numBytesRx = am_devices_em9304_block_read(&g_sEm9304, g_pui32HCIRXBuffer, 0);
if ((numBytesRx == 7) && (0x0000FC32 == (g_pui32HCIRXBuffer[1] & 0x0000FFFF)))
{
am_util_debug_printf("EM9304 CPU Reset Successfully\n");
break;
}
am_util_delay_ms(EM9304_ATTEMPT_DELAY_MS);
}
return EM9304_PATCHES_DEST_MEMORY;
}