//***************************************************************************** // //! @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 #include #include #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; }