3075 lines
89 KiB
C
3075 lines
89 KiB
C
|
//*****************************************************************************
|
||
|
//
|
||
|
//! @file am_hal_ble.c
|
||
|
//!
|
||
|
//! @brief HAL functions for the BLE interface.
|
||
|
//!
|
||
|
//! @addtogroup
|
||
|
//! @ingroup
|
||
|
//! @{
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// 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_hal_ble_patch_b0.h"
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Globals
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
am_hal_ble_state_t g_sBLEState[AM_REG_BLEIF_NUM_MODULES];
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Helper macros for rev B0 parts.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
#define BLEIF_INTSTAT_BLECSSTATN_Msk BLEIF_INTSTAT_B2MSHUTDN_Msk
|
||
|
#define BLEIF_INTSTAT_BLECIRQN_Msk BLEIF_INTSTAT_B2MACTIVE_Msk
|
||
|
|
||
|
#define SKIP_FALLING_EDGES 0
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// SPI "options"
|
||
|
//
|
||
|
// These values affect the behavior of the BLE HAL in regards to the SPI bus,
|
||
|
// but end users aren't likely to need to modify them. They are collected here
|
||
|
// for testing and debugging purposes.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
// The amount of extra delay to add between successive SPI TX packets (in
|
||
|
// microseconds).
|
||
|
#define AM_BLE_TX_PACKET_SPACING_US 1
|
||
|
|
||
|
// The BLE core takes a little while to wake up from a fresh boot, which means
|
||
|
// that the patch_apply function might time-out on the first few tries. Set
|
||
|
// this variable to let it try again for a finite number of trials.
|
||
|
#define AM_BLE_NUM_PATCH_TRIALS 5000
|
||
|
|
||
|
// Patch complete can also take some time.
|
||
|
#define AM_BLE_NUM_PATCH_CMP_TRIALS 5000
|
||
|
|
||
|
// How long the MCU should wait for SPI_STATUS before assuming the BLE core is
|
||
|
// busy (measured in 10 us increments).
|
||
|
#define AM_BLE_STATUS_TIMEOUT 300
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Private types.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
#define AM_HAL_MAGIC_BLE 0x775230
|
||
|
|
||
|
#define AM_HAL_BLE_CHK_HANDLE(h) \
|
||
|
((h) && ((am_hal_handle_prefix_t *)(h))->s.bInit \
|
||
|
&& (((am_hal_handle_prefix_t *)(h))->s.magic == AM_HAL_MAGIC_BLE))
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// BLE Core maximum patch packet size.
|
||
|
//
|
||
|
// Specified as part of the protocol.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
#define MAX_PATCH_PACKET_LEN 0x80
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Some of the NationZ register addresses are different between A1/A2 and B0.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
|
||
|
#define AM_HAL_BLE_IP_RAM_32K_CLOCK_ADDR_A1 0x20006054
|
||
|
#define AM_HAL_BLE_IP_RAM_MODEX_TRIM_ADDR_A1 0x20006070
|
||
|
#define AM_HAL_BLE_IP_RAM_POWER_LEVEL_ADDR_A1 0x20006038
|
||
|
#define AM_HAL_BLE_IP_RAM_SLEEP_ENABLE_ADDR_A1 (0x200067b8 + 0x0c)
|
||
|
|
||
|
#define AM_HAL_BLE_IP_RAM_32K_CLOCK_ADDR_B0 0x20006858
|
||
|
#define AM_HAL_BLE_IP_RAM_MODEX_TRIM_ADDR_B0 0x20006874
|
||
|
#define AM_HAL_BLE_IP_RAM_POWER_LEVEL_ADDR_B0 0x20006838
|
||
|
#define AM_HAL_BLE_IP_RAM_SLEEP_ENABLE_ADDR_B0 (0x20006e0c + 0x0c)
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Static function prototypes.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static bool am_hal_ble_bus_lock(am_hal_ble_state_t *pBle);
|
||
|
static void am_hal_ble_bus_release(am_hal_ble_state_t *pBle);
|
||
|
static uint32_t am_hal_ble_fifo_drain(void *pHandle);
|
||
|
static void am_hal_ble_fifo_read(void *pHandle, uint32_t *pui32Data, uint32_t ui32NumBytes);
|
||
|
static bool am_hal_ble_check_status(am_hal_ble_state_t *pBle);
|
||
|
static bool am_hal_ble_check_irq(am_hal_ble_state_t *pBle);
|
||
|
static uint32_t am_hal_ble_cmd_write(void *pHandle, am_hal_ble_transfer_t *psTransfer);
|
||
|
static uint32_t am_hal_ble_load_modex_trim_set(void *pHandle);
|
||
|
static uint32_t nonblocking_write(am_hal_ble_state_t *pBle, am_hal_ble_transfer_t *psTransfer);
|
||
|
static uint32_t nonblocking_read(am_hal_ble_state_t *pBle, am_hal_ble_transfer_t *psTransfer);
|
||
|
static uint8_t am_hal_ble_read_trimdata_from_info1(void);
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Look up Table for NZ CRC16 generation
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static const uint16_t ccitt_table[] =
|
||
|
{
|
||
|
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
|
||
|
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
|
||
|
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
|
||
|
0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
|
||
|
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
|
||
|
0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
|
||
|
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
|
||
|
0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
|
||
|
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
|
||
|
0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
|
||
|
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
|
||
|
0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
|
||
|
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
|
||
|
0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
|
||
|
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
|
||
|
0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
|
||
|
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
|
||
|
0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
|
||
|
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
|
||
|
0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
|
||
|
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
|
||
|
0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
|
||
|
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
|
||
|
0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
|
||
|
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
|
||
|
0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
|
||
|
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
|
||
|
0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
|
||
|
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
|
||
|
0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
|
||
|
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
|
||
|
0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
|
||
|
};
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Helper macros for delays.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
#define delay_ms(ms) am_hal_flash_delay(FLASH_CYCLES_US(1000 * (ms)))
|
||
|
#define delay_us(us) am_hal_flash_delay(FLASH_CYCLES_US(us))
|
||
|
|
||
|
#define WHILE_TIMEOUT_MS(expr, timeout, error) \
|
||
|
{ \
|
||
|
uint32_t ui32Timeout = 0; \
|
||
|
while (expr) \
|
||
|
{ \
|
||
|
if (ui32Timeout == (timeout * 1000)) \
|
||
|
{ \
|
||
|
return error; \
|
||
|
} \
|
||
|
\
|
||
|
delay_us(1); \
|
||
|
ui32Timeout++; \
|
||
|
} \
|
||
|
}
|
||
|
|
||
|
#define WHILE_TIMEOUT_MS_BREAK(expr, timeout, error) \
|
||
|
{ \
|
||
|
uint32_t ui32Timeout = 0; \
|
||
|
while (expr) \
|
||
|
{ \
|
||
|
if (ui32Timeout == (timeout * 1000)) \
|
||
|
{ \
|
||
|
break; \
|
||
|
} \
|
||
|
\
|
||
|
delay_us(1); \
|
||
|
ui32Timeout++; \
|
||
|
} \
|
||
|
}
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Helper function for checking BLE data.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static bool
|
||
|
buffer_compare(void *b1, void *b2, uint32_t len)
|
||
|
{
|
||
|
uint8_t *p1 = b1;
|
||
|
uint8_t *p2 = b2;
|
||
|
|
||
|
for (uint32_t i = 0; i < len; i++)
|
||
|
{
|
||
|
if (p1[i] != p2[i])
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Helper function for CRC caculation of BLE patch.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static uint16_t
|
||
|
am_hal_ble_crc_nz(uint8_t *pui8Data, uint32_t len)
|
||
|
{
|
||
|
uint16_t ui16CurValue = 0;
|
||
|
uint32_t i;
|
||
|
|
||
|
for (i = 0; i < len; i++)
|
||
|
{
|
||
|
ui16CurValue = ccitt_table[(((uint8_t)(ui16CurValue >> 8)) ^ pui8Data[i]) & 0xFF] ^ (ui16CurValue << 8);
|
||
|
}
|
||
|
|
||
|
return ((ui16CurValue ^ 0) & ((1 << 16) - 1));
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Default options for the BLE module.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
const am_hal_ble_config_t am_hal_ble_default_config =
|
||
|
{
|
||
|
// Configure the HCI interface clock for 6 MHz
|
||
|
.ui32SpiClkCfg = AM_HAL_BLE_HCI_CLK_DIV8,
|
||
|
|
||
|
// Set HCI read and write thresholds to 32 bytes each.
|
||
|
.ui32ReadThreshold = 32,
|
||
|
.ui32WriteThreshold = 32,
|
||
|
|
||
|
// The MCU will supply the clock to the BLE core.
|
||
|
.ui32BleClockConfig = AM_HAL_BLE_CORE_MCU_CLK,
|
||
|
|
||
|
// Apply the default patches when am_hal_ble_boot() is called.
|
||
|
.bUseDefaultPatches = true,
|
||
|
};
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Function for controlling the WAKE signal.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_wakeup_set(void *pHandle, uint32_t ui32Mode)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBle = pHandle;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if ( !AM_HAL_BLE_CHK_HANDLE(pHandle) )
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
// am_hal_debug_gpio_set(BLE_DEBUG_TRACE_08);
|
||
|
|
||
|
if ( ui32Mode )
|
||
|
{
|
||
|
BLEIFn(pBle->ui32Module)->BLECFG_b.WAKEUPCTL = BLEIF_BLECFG_WAKEUPCTL_ON;
|
||
|
am_hal_debug_gpio_set(BLE_DEBUG_TRACE_08);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifndef AM_DISABLE_BLE_SLEEP
|
||
|
BLEIFn(pBle->ui32Module)->BLECFG_b.WAKEUPCTL = BLEIF_BLECFG_WAKEUPCTL_OFF;
|
||
|
am_hal_debug_gpio_clear(BLE_DEBUG_TRACE_08);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
|
||
|
// am_hal_debug_gpio_clear(BLE_DEBUG_TRACE_08);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Buffer for patch data.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
am_hal_ble_buffer(128 + 4) g_psPatchBuffer;
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Initialize the global variables associated with a BLE module, and return its
|
||
|
// handle.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_initialize(uint32_t ui32Module, void **ppHandle)
|
||
|
{
|
||
|
//
|
||
|
// Check the arguments.
|
||
|
//
|
||
|
if (ui32Module >= AM_REG_BLEIF_NUM_MODULES)
|
||
|
{
|
||
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
||
|
}
|
||
|
|
||
|
if (!ppHandle)
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_ARG;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if the handle is unallocated.
|
||
|
//
|
||
|
if (g_sBLEState[ui32Module].prefix.s.bInit)
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the handle.
|
||
|
//
|
||
|
memset(&g_sBLEState[ui32Module].sCurrentTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
memset(&g_sBLEState[ui32Module].sSavedTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
|
||
|
g_sBLEState[ui32Module].prefix.s.bInit = true;
|
||
|
g_sBLEState[ui32Module].prefix.s.magic = AM_HAL_MAGIC_BLE;
|
||
|
g_sBLEState[ui32Module].ui32Module = ui32Module;
|
||
|
g_sBLEState[ui32Module].ui32TransferIndex = 0;
|
||
|
g_sBLEState[ui32Module].bPatchComplete = 0;
|
||
|
g_sBLEState[ui32Module].bContinuePacket = 0;
|
||
|
g_sBLEState[ui32Module].bSavedPacket = 0;
|
||
|
g_sBLEState[ui32Module].bBusy = 0;
|
||
|
g_sBLEState[ui32Module].bCmdComplete = 0;
|
||
|
g_sBLEState[ui32Module].bDmaComplete = 0;
|
||
|
g_sBLEState[ui32Module].bFlowControlComplete = 0;
|
||
|
g_sBLEState[ui32Module].bUseDefaultPatches = false;
|
||
|
|
||
|
//
|
||
|
// Pass the handle back to the caller.
|
||
|
//
|
||
|
*ppHandle = &g_sBLEState[ui32Module];
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_initialize()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Initialize the global variables associated with a BLE module, and return its
|
||
|
// handle.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_deinitialize(void *pHandle)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = (am_hal_ble_state_t *)pHandle;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the handle.
|
||
|
//
|
||
|
memset(&(pBLE->sCurrentTransfer), 0, sizeof(am_hal_ble_transfer_t));
|
||
|
|
||
|
pBLE->prefix.s.bInit = false;
|
||
|
pBLE->prefix.s.magic = 0;
|
||
|
pBLE->ui32Module = 0;
|
||
|
pBLE->ui32TransferIndex = 0;
|
||
|
pBLE->bPatchComplete = 0;
|
||
|
pBLE->bContinuePacket = 0;
|
||
|
pBLE->bSavedPacket = 0;
|
||
|
pBLE->bBusy = 0;
|
||
|
pBLE->bCmdComplete = 0;
|
||
|
pBLE->bDmaComplete = 0;
|
||
|
pBLE->bFlowControlComplete = 0;
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_deinitialize()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Configuration function.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_config(void *pHandle, const am_hal_ble_config_t *psConfig)
|
||
|
{
|
||
|
uint32_t ui32Module;
|
||
|
uint32_t ui32BleClkConfig;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Configure the SPI.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->MSPICFG = 0x3;
|
||
|
BLEIFn(ui32Module)->MSPICFG_b.RDFC = 0;
|
||
|
BLEIFn(ui32Module)->MSPICFG_b.WTFC = 0;
|
||
|
BLEIFn(ui32Module)->MSPICFG_b.WTFCPOL = 1;
|
||
|
BLEIFn(ui32Module)->FIFOTHR_b.FIFOWTHR = psConfig->ui32WriteThreshold;
|
||
|
BLEIFn(ui32Module)->FIFOTHR_b.FIFORTHR = psConfig->ui32ReadThreshold;
|
||
|
BLEIFn(ui32Module)->FIFOCTRL |= BLEIF_FIFOCTRL_POPWR_Msk;
|
||
|
|
||
|
//
|
||
|
// Clock configuration register writes need to be combined to a single
|
||
|
// operation.
|
||
|
//
|
||
|
ui32BleClkConfig = _VAL2FLD(BLEIF_CLKCFG_FSEL, psConfig->ui32SpiClkCfg);
|
||
|
ui32BleClkConfig |= _VAL2FLD(BLEIF_CLKCFG_IOCLKEN, 1);
|
||
|
|
||
|
if (psConfig->ui32BleClockConfig == AM_HAL_BLE_CORE_MCU_CLK)
|
||
|
{
|
||
|
ui32BleClkConfig |= _VAL2FLD(BLEIF_CLKCFG_CLK32KEN, 1);
|
||
|
}
|
||
|
|
||
|
BLEIFn(ui32Module)->CLKCFG = ui32BleClkConfig;
|
||
|
|
||
|
//
|
||
|
// Save the addresses to the patches we intend to use.
|
||
|
//
|
||
|
g_sBLEState[ui32Module].bUseDefaultPatches = psConfig->bUseDefaultPatches;
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_config()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Enable BLE
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_power_control(void *pHandle, uint32_t ui32PowerState)
|
||
|
{
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// BLE buck is shared by Burst as well
|
||
|
// Enable the BLE buck trim values if in use
|
||
|
//
|
||
|
am_hal_pwrctrl_blebuck_trim();
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if ( !AM_HAL_BLE_CHK_HANDLE(pHandle) )
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
if (ui32PowerState == AM_HAL_BLE_POWER_ACTIVE)
|
||
|
{
|
||
|
//
|
||
|
// Don't run this initialization if the BLE is already enabled.
|
||
|
//
|
||
|
if ( PWRCTRL->DEVPWRSTATUS_b.BLEL == 0)
|
||
|
{
|
||
|
MCUCTRL->FEATUREENABLE |= 1;
|
||
|
WHILE_TIMEOUT_MS ( ((MCUCTRL->FEATUREENABLE & 0x7) != 0x7), 100,
|
||
|
AM_HAL_BLE_FEATURE_DISABLED );
|
||
|
|
||
|
//
|
||
|
// Enable the BLE module.
|
||
|
//
|
||
|
if (am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_BLEL) !=
|
||
|
AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return AM_HAL_BLE_REGULATOR_FAILED;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the BLE module RESET, start the "power state machine", and
|
||
|
// enable the clocks.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->CLKCFG = _VAL2FLD(BLEIF_CLKCFG_CLK32KEN, 1);
|
||
|
BLEIFn(ui32Module)->BLEDBG_b.DBGDATA = 1 << 14;
|
||
|
|
||
|
//
|
||
|
// The reset bit is different between A0 and subsequent revisions.
|
||
|
//
|
||
|
if ( APOLLO3_GE_A1 )
|
||
|
{
|
||
|
MCUCTRL->MISCCTRL_b.BLE_RESETN = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AM_REGVAL(0x40020198) = 0x1 << 2;
|
||
|
}
|
||
|
|
||
|
delay_ms(5);
|
||
|
BLEIFn(ui32Module)->BLECFG_b.PWRSMEN = 1;
|
||
|
|
||
|
//
|
||
|
// Wait for indication that the power is on.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->BSTATUS_b.PWRST != 3, 1000,
|
||
|
AM_HAL_BLE_POWERUP_INCOMPLETE );
|
||
|
}
|
||
|
}
|
||
|
else if (ui32PowerState == AM_HAL_BLE_POWER_OFF)
|
||
|
{
|
||
|
//
|
||
|
// Reverse of power-up. Disable clocks, set reset, then disable power.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->CLKCFG = 0;
|
||
|
BLEIF->BLEDBG_b.DBGDATA = 0;
|
||
|
|
||
|
if ( APOLLO3_GE_A1 )
|
||
|
{
|
||
|
MCUCTRL->MISCCTRL_b.BLE_RESETN = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AM_REGVAL(0x40020198) &= ~(0x1 << 2);
|
||
|
}
|
||
|
|
||
|
BLEIF->BLECFG_b.PWRSMEN = 0;
|
||
|
|
||
|
if (am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_BLEL) !=
|
||
|
AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return AM_HAL_BLE_SHUTDOWN_FAILED;
|
||
|
}
|
||
|
|
||
|
delay_us(100);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_power_control()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Perform all of the operations necessary to prepare the BLE controller for
|
||
|
// HCI operation.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_boot(void *pHandle)
|
||
|
{
|
||
|
uint32_t ui32Status;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The handle is good, so we can access it as a structure.
|
||
|
//
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
|
||
|
if (pBLE->bUseDefaultPatches)
|
||
|
{
|
||
|
//
|
||
|
// The B0 silicon patching method is slightly different from A1. B0 silicon
|
||
|
// does not require the Copy Patch method introduced for A1 silicon.
|
||
|
//
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
//
|
||
|
// Apply the BLE trim value
|
||
|
//
|
||
|
ui32Status = am_hal_ble_default_trim_set_ramcode(pHandle);
|
||
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Apply the NVDS patch.
|
||
|
//
|
||
|
ui32Status = am_hal_ble_default_patch_apply(pHandle);
|
||
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Complete the patching step
|
||
|
//
|
||
|
ui32Status = am_hal_ble_patch_complete(pHandle);
|
||
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32Status;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (am_hal_ble_check_32k_clock(pBLE) == AM_HAL_STATUS_FAIL)
|
||
|
{
|
||
|
return AM_HAL_BLE_32K_CLOCK_UNSTABLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
}
|
||
|
} // am_hal_ble_boot()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Apply a patch.
|
||
|
//
|
||
|
// Returns 0 for success or a numerical error code for failures.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_patch_apply(void *pHandle, am_hal_ble_patch_t *psPatch)
|
||
|
{
|
||
|
uint8_t pui8ExpectedResponse[32];
|
||
|
uint32_t ui32ErrorStatus;
|
||
|
uint32_t ui32Trial;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
uint32_t ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
am_hal_ble_transfer_t sTransfer;
|
||
|
am_hal_ble_buffer(16) psPatchBuffer;
|
||
|
|
||
|
//
|
||
|
// Send a header packet.
|
||
|
//
|
||
|
psPatchBuffer.bytes[0] = 0x01;
|
||
|
psPatchBuffer.bytes[1] = psPatch->ui32Type;
|
||
|
psPatchBuffer.bytes[2] = 0xF1;
|
||
|
psPatchBuffer.bytes[3] = 0x02;
|
||
|
psPatchBuffer.bytes[4] = (psPatch->ui32Length & 0xFF);
|
||
|
psPatchBuffer.bytes[5] = ((psPatch->ui32Length >> 8) & 0xFF);
|
||
|
|
||
|
//
|
||
|
// This first packet might take a few tries.
|
||
|
//
|
||
|
for ( ui32Trial = 0; ui32Trial < AM_BLE_NUM_PATCH_TRIALS; ui32Trial++)
|
||
|
{
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_hci_write(pHandle,
|
||
|
AM_HAL_BLE_RAW,
|
||
|
psPatchBuffer.words,
|
||
|
6);
|
||
|
|
||
|
if ( ui32ErrorStatus == AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ui32ErrorStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wait for the header response. It should be 5 bytes long.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 1000,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE );
|
||
|
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_READ;
|
||
|
sTransfer.pui32Data = psPatchBuffer.words;
|
||
|
sTransfer.ui16Length = 5;
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
pui8ExpectedResponse[0] = 0x04;
|
||
|
pui8ExpectedResponse[1] = psPatch->ui32Type;
|
||
|
pui8ExpectedResponse[2] = 0xF1;
|
||
|
pui8ExpectedResponse[3] = 0x01;
|
||
|
pui8ExpectedResponse[4] = 0x00;
|
||
|
|
||
|
if (!buffer_compare(psPatchBuffer.words, pui8ExpectedResponse, 5))
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Send all of the data, including the acknowledgements.
|
||
|
//
|
||
|
uint32_t ui32RemainingBytes = psPatch->ui32Length;
|
||
|
uint32_t ui32Index = 0;
|
||
|
|
||
|
while (ui32RemainingBytes)
|
||
|
{
|
||
|
//
|
||
|
// Figure out how many bytes to send in the next packet.
|
||
|
//
|
||
|
uint32_t ui32TransferSize = (ui32RemainingBytes > MAX_PATCH_PACKET_LEN ?
|
||
|
MAX_PATCH_PACKET_LEN : ui32RemainingBytes);
|
||
|
|
||
|
//
|
||
|
// Send a data header.
|
||
|
//
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_WRITE;
|
||
|
sTransfer.pui32Data = g_psPatchBuffer.words;
|
||
|
sTransfer.ui16Length = ui32TransferSize + 4;
|
||
|
sTransfer.bContinue = false;
|
||
|
|
||
|
g_psPatchBuffer.bytes[0] = 0x01;
|
||
|
g_psPatchBuffer.bytes[1] = psPatch->ui32Type;
|
||
|
g_psPatchBuffer.bytes[2] = 0xF2;
|
||
|
g_psPatchBuffer.bytes[3] = ui32TransferSize;
|
||
|
|
||
|
// copy data into buffer
|
||
|
memcpy(&g_psPatchBuffer.bytes[4], (uint8_t *)&(psPatch->pui32Data[ui32Index / 4]), ui32TransferSize);
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read the acknowledgement.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 1000,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE);
|
||
|
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_READ;
|
||
|
sTransfer.pui32Data = psPatchBuffer.words;
|
||
|
sTransfer.ui16Length = 5;
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
pui8ExpectedResponse[0] = 0x04;
|
||
|
pui8ExpectedResponse[1] = psPatch->ui32Type;
|
||
|
pui8ExpectedResponse[2] = 0xF2;
|
||
|
pui8ExpectedResponse[3] = 0x01;
|
||
|
pui8ExpectedResponse[4] = 0x00;
|
||
|
|
||
|
if (!buffer_compare(psPatchBuffer.words, pui8ExpectedResponse, 5))
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the tracking variables
|
||
|
//
|
||
|
ui32RemainingBytes -= ui32TransferSize;
|
||
|
ui32Index += ui32TransferSize;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Send the CRC, and make sure we got it right.
|
||
|
//
|
||
|
psPatchBuffer.bytes[0] = 0x01;
|
||
|
psPatchBuffer.bytes[1] = psPatch->ui32Type;
|
||
|
psPatchBuffer.bytes[2] = 0xF3;
|
||
|
psPatchBuffer.bytes[3] = 0x02;
|
||
|
psPatchBuffer.bytes[4] = (psPatch->ui32CRC & 0xFF);
|
||
|
psPatchBuffer.bytes[5] = ((psPatch->ui32CRC >> 8) & 0xFF);
|
||
|
|
||
|
if (am_hal_ble_blocking_hci_write(pHandle, AM_HAL_BLE_RAW, psPatchBuffer.words, 6) !=
|
||
|
AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wait for the header response. It should be 5 bytes long.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 1000,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE );
|
||
|
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_READ;
|
||
|
sTransfer.pui32Data = psPatchBuffer.words;
|
||
|
sTransfer.ui16Length = 5;
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
pui8ExpectedResponse[0] = 0x04;
|
||
|
pui8ExpectedResponse[1] = psPatch->ui32Type;
|
||
|
pui8ExpectedResponse[2] = 0xF3;
|
||
|
pui8ExpectedResponse[3] = 0x01;
|
||
|
pui8ExpectedResponse[4] = 0x00;
|
||
|
|
||
|
if (!buffer_compare(psPatchBuffer.words, pui8ExpectedResponse, 5))
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
}
|
||
|
} // am_hal_ble_patch_apply()
|
||
|
|
||
|
uint32_t
|
||
|
am_hal_ble_patch_copy_end_apply(void *pHandle)
|
||
|
{
|
||
|
uint8_t pui8ExpectedResponse[32];
|
||
|
uint32_t ui32ErrorStatus;
|
||
|
uint32_t ui32Trial;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
uint32_t ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
am_hal_ble_transfer_t sTransfer;
|
||
|
am_hal_ble_buffer(16) psPatchBuffer;
|
||
|
|
||
|
//
|
||
|
// Send a header packet.
|
||
|
//
|
||
|
psPatchBuffer.bytes[0] = 0x01;
|
||
|
psPatchBuffer.bytes[1] = 0xEE;
|
||
|
psPatchBuffer.bytes[2] = 0xF1;
|
||
|
psPatchBuffer.bytes[3] = 0x02;
|
||
|
psPatchBuffer.bytes[4] = 0x00;
|
||
|
psPatchBuffer.bytes[5] = 0x00;
|
||
|
|
||
|
//
|
||
|
// This first packet might take a few tries.
|
||
|
//
|
||
|
for ( ui32Trial = 0; ui32Trial < AM_BLE_NUM_PATCH_TRIALS; ui32Trial++)
|
||
|
{
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_hci_write(pHandle,
|
||
|
AM_HAL_BLE_RAW,
|
||
|
psPatchBuffer.words,
|
||
|
6);
|
||
|
|
||
|
if ( ui32ErrorStatus == AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ui32ErrorStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wait for the header response. It should be 5 bytes long.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 1000, AM_HAL_BLE_NO_HCI_RESPONSE);
|
||
|
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_READ;
|
||
|
sTransfer.pui32Data = psPatchBuffer.words;
|
||
|
sTransfer.ui16Length = 5;
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
pui8ExpectedResponse[0] = 0x04;
|
||
|
pui8ExpectedResponse[1] = 0xEE;
|
||
|
pui8ExpectedResponse[2] = 0xF1;
|
||
|
pui8ExpectedResponse[3] = 0x01;
|
||
|
pui8ExpectedResponse[4] = 0x00;
|
||
|
|
||
|
if (!buffer_compare(psPatchBuffer.words, pui8ExpectedResponse, 5))
|
||
|
{
|
||
|
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
return 0;
|
||
|
} // am_hal_ble_patch_copy_end_apply()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Apply the default patch.
|
||
|
//
|
||
|
// Returns 0 for success or a numerical error code for failures.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_default_patch_apply(void *pHandle)
|
||
|
{
|
||
|
uint32_t ui32Status, i = 0;
|
||
|
uint16_t ui16Crc;
|
||
|
uint32_t ui32NumPatches;
|
||
|
am_hal_ble_patch_t **psDefaultPatches;
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
ui32NumPatches = am_hal_ble_num_default_patches_b0;
|
||
|
psDefaultPatches = am_hal_ble_default_patches_b0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
for ( i = 0; i < ui32NumPatches; i++ )
|
||
|
{
|
||
|
ui16Crc = am_hal_ble_crc_nz((uint8_t*)(psDefaultPatches[i]->pui32Data), psDefaultPatches[i]->ui32Length);
|
||
|
psDefaultPatches[i]->ui32CRC = ui16Crc;
|
||
|
ui32Status = am_hal_ble_patch_apply(pHandle, psDefaultPatches[i]);
|
||
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32Status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_default_patch_apply()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Complete the patching process
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_patch_complete(void *pHandle)
|
||
|
{
|
||
|
uint32_t ui32ErrorStatus;
|
||
|
am_hal_ble_transfer_t sTransfer;
|
||
|
am_hal_ble_buffer(12) sTxBuffer;
|
||
|
am_hal_ble_buffer(12) sRxBuffer;
|
||
|
uint32_t ui32Trial;
|
||
|
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
uint32_t ui32Module = pBLE->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Write the "patch complete" command.
|
||
|
//
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_WRITE;
|
||
|
sTransfer.pui32Data = sTxBuffer.words;
|
||
|
sTransfer.ui16Length = 6;
|
||
|
|
||
|
sTxBuffer.bytes[0] = 0x01;
|
||
|
sTxBuffer.bytes[1] = 0xEE;
|
||
|
sTxBuffer.bytes[2] = 0xF1;
|
||
|
sTxBuffer.bytes[3] = 0x02;
|
||
|
sTxBuffer.bytes[4] = 0x00;
|
||
|
sTxBuffer.bytes[5] = 0x00;
|
||
|
|
||
|
for ( ui32Trial = 0; ui32Trial < AM_BLE_NUM_PATCH_CMP_TRIALS; ui32Trial++)
|
||
|
{
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus == AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 100,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE );
|
||
|
|
||
|
//
|
||
|
// Read back the response.
|
||
|
//
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_READ;
|
||
|
sTransfer.pui32Data = sRxBuffer.words;
|
||
|
sTransfer.ui16Length = 2;
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see which format the response came back in. If it doesn't have
|
||
|
// a 2-byte length header, we need to manually override the length, and
|
||
|
// continue on to adjust the HCI format in the next packet. Otherwise, we
|
||
|
// can just return from here.
|
||
|
//
|
||
|
if ( sRxBuffer.bytes[1] == 0xEE )
|
||
|
{
|
||
|
sTransfer.ui16Length = 3;
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sTransfer.ui16Length = (sRxBuffer.bytes[0] + (sRxBuffer.bytes[1] << 8));
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure to remember that we've sent the "patch complete" packet.
|
||
|
//
|
||
|
pBLE->bPatchComplete = true;
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we made it here, we need to tell the radio that we need two-byte
|
||
|
// headers prepended to each HCI packet it sends us.
|
||
|
//
|
||
|
memset(&sTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_WRITE;
|
||
|
sTransfer.pui32Data = sTxBuffer.words;
|
||
|
sTransfer.ui16Length = 5;
|
||
|
|
||
|
sTxBuffer.bytes[0] = 0x01;
|
||
|
sTxBuffer.bytes[1] = 0x04;
|
||
|
sTxBuffer.bytes[2] = 0xFD;
|
||
|
sTxBuffer.bytes[3] = 0x01;
|
||
|
sTxBuffer.bytes[4] = 0x01;
|
||
|
|
||
|
for ( ui32Trial = 0; ui32Trial < AM_BLE_NUM_PATCH_CMP_TRIALS; ui32Trial++)
|
||
|
{
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus == AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ui32ErrorStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 100,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE );
|
||
|
|
||
|
sTransfer.ui8Command = AM_HAL_BLE_READ;
|
||
|
sTransfer.pui32Data = sRxBuffer.words;
|
||
|
sTransfer.ui16Length = 9;
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &sTransfer);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now that we're done patching, we can let the radio sleep.
|
||
|
//
|
||
|
am_hal_ble_wakeup_set(pBLE, 0);
|
||
|
|
||
|
//
|
||
|
// Make sure to remember that we've sent the "patch complete" packet.
|
||
|
//
|
||
|
pBLE->bPatchComplete = true;
|
||
|
|
||
|
//
|
||
|
// Delay to give the BLE core time to take the patch (assuming a patch was sent).
|
||
|
//
|
||
|
delay_ms(500);
|
||
|
|
||
|
//
|
||
|
// Load the modex trim data to the BLE controller.
|
||
|
//
|
||
|
am_hal_ble_load_modex_trim_set(pBLE);
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_patch_complete()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Set one of the trim values for the BLE core.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_trim_set(void *pHandle, uint32_t ui32BleCoreAddress, uint32_t ui32TrimValue, uint32_t ui32TrimMask)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint32_t ui32TrimValueSwapped, ui32LockValue, ui32ReadVal, ui32WriteVal;
|
||
|
|
||
|
ui32TrimValueSwapped = (((ui32TrimValue & 0x000000FF) << 24) |
|
||
|
((ui32TrimValue & 0x0000FF00) << 8) |
|
||
|
((ui32TrimValue & 0x00FF0000) >> 8) |
|
||
|
((ui32TrimValue & 0xFF000000) >> 24));
|
||
|
|
||
|
if (ui32TrimValue != 0xFFFFFFFF)
|
||
|
{
|
||
|
//
|
||
|
// Unlock the BLE registers and save the "lock register" value.
|
||
|
//
|
||
|
am_hal_ble_plf_reg_read(pBLE, 0x43000004, &ui32LockValue);
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x43000004, 0xFFFFFFFF);
|
||
|
|
||
|
//
|
||
|
// Check to see if we need a bitfield mask. If not, we can just write
|
||
|
// directly.
|
||
|
//
|
||
|
if (ui32TrimMask == 0xFFFFFFFF)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_write(pBLE, ui32BleCoreAddress, ui32TrimValueSwapped);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// If we do need a mask, read the register, mask out the old bits,
|
||
|
// OR in the new, and write the new value back.
|
||
|
//
|
||
|
am_hal_ble_plf_reg_read(pBLE, ui32BleCoreAddress, &ui32ReadVal);
|
||
|
ui32WriteVal = ((ui32ReadVal & (~ui32TrimMask)) | ui32TrimValueSwapped);
|
||
|
|
||
|
am_hal_ble_plf_reg_write(pBLE, ui32BleCoreAddress, ui32WriteVal);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unlock the BLE register.
|
||
|
//
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x43000004, ui32LockValue);
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_trim_set()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Set the bandgap voltage, bandgap current, and retention LDO output values
|
||
|
// based on the tested values stored in non-volatile memory.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_default_trim_set_ramcode(void *pHandle)
|
||
|
{
|
||
|
uint32_t ui32TrimValue;
|
||
|
uint32_t ui32TrimValueSwapped;
|
||
|
uint32_t *pRamCode;
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
pRamCode = (uint32_t *) (am_ble_performance_patch_b0.pui32Data);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the bandgap voltage and current.
|
||
|
//
|
||
|
//ui32TrimValue = (AM_REGVAL(0x50023800) | (0x0F000000)) & (0xEFFFFFFF);
|
||
|
ui32TrimValue = AM_REGVAL(0x50023800);
|
||
|
ui32TrimValueSwapped = (((ui32TrimValue & 0x000000FF) << 24) |
|
||
|
((ui32TrimValue & 0x0000FF00) << 8) |
|
||
|
((ui32TrimValue & 0x00FF0000) >> 8) |
|
||
|
((ui32TrimValue & 0xFF000000) >> 24));
|
||
|
|
||
|
if (ui32TrimValueSwapped != 0xFFFFFFFF)
|
||
|
{
|
||
|
pRamCode[2] = ui32TrimValueSwapped;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the retention LDO voltage.
|
||
|
//
|
||
|
ui32TrimValue = AM_REGVAL(0x50023804);
|
||
|
if (ui32TrimValue != 0xFFFFFFFF)
|
||
|
{
|
||
|
// 0xFFFFFFFF means the part has not been trimed.
|
||
|
ui32TrimValue += 0x40000000; // Increase the retention voltage to > 0.75v
|
||
|
}
|
||
|
ui32TrimValueSwapped = (((ui32TrimValue & 0x000000FF) << 24) |
|
||
|
((ui32TrimValue & 0x0000FF00) << 8) |
|
||
|
((ui32TrimValue & 0x00FF0000) >> 8) |
|
||
|
((ui32TrimValue & 0xFF000000) >> 24));
|
||
|
|
||
|
if ( ui32TrimValueSwapped != 0xFFFFFFFF )
|
||
|
{
|
||
|
pRamCode[3] = ((pRamCode[3] & (~0x1F0)) | ui32TrimValueSwapped);
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_default_trim_set_ramcode()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Builds a vendor-specific BLE command.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_vs_command_build(uint32_t *pui32Command, uint32_t ui32OpCode,
|
||
|
uint32_t ui32TotalLength, uint8_t *pui8Parameters)
|
||
|
{
|
||
|
uint8_t *pui8Dest = (uint8_t *) pui32Command;
|
||
|
|
||
|
//
|
||
|
// Build the header portion of the command from the given argments.
|
||
|
//
|
||
|
pui8Dest[0] = 0x01;
|
||
|
pui8Dest[1] = ui32OpCode & 0xFF;
|
||
|
pui8Dest[2] = (ui32OpCode >> 8) & 0xFF;
|
||
|
pui8Dest[3] = (ui32TotalLength - 4) & 0xFF;
|
||
|
|
||
|
//
|
||
|
// Finish filling the array with any parameters that may be required.
|
||
|
//
|
||
|
for (uint32_t i = 4; i < ui32TotalLength; i++)
|
||
|
{
|
||
|
pui8Dest[i] = pui8Parameters[i - 4];
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_vs_command_build()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Returns the number of bytes written.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_blocking_hci_write(void *pHandle, uint8_t ui8Type,
|
||
|
uint32_t *pui32Data, uint32_t ui32NumBytes)
|
||
|
{
|
||
|
uint32_t ui32ErrorStatus;
|
||
|
|
||
|
am_hal_ble_transfer_t HciWrite =
|
||
|
{
|
||
|
.pui32Data = pui32Data,
|
||
|
.pui8Offset = {ui8Type, 0x0, 0x0},
|
||
|
.ui8OffsetLen = 0,
|
||
|
.ui16Length = ui32NumBytes,
|
||
|
.ui8Command = AM_HAL_BLE_WRITE,
|
||
|
.ui8RepeatCount = 0,
|
||
|
.bContinue = false,
|
||
|
.pfnTransferCompleteCB = 0x0,
|
||
|
.pvContext = 0x0,
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Fix up the offset length based on the packet type, and send the bytes.
|
||
|
//
|
||
|
if (ui8Type != AM_HAL_BLE_RAW)
|
||
|
{
|
||
|
HciWrite.ui8OffsetLen = 1;
|
||
|
}
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &HciWrite);
|
||
|
if (ui32ErrorStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_blocking_hci_write()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Returns the number of bytes received.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_blocking_hci_read(void *pHandle, uint32_t *pui32Data, uint32_t *pui32BytesReceived)
|
||
|
{
|
||
|
uint32_t ui32Module, ui32NumBytes, ui32ErrorStatus;
|
||
|
|
||
|
am_hal_ble_buffer(2) sLengthBytes;
|
||
|
|
||
|
am_hal_ble_transfer_t HciRead =
|
||
|
{
|
||
|
.pui32Data = sLengthBytes.words,
|
||
|
.pui8Offset = {0x0, 0x0, 0x0},
|
||
|
.ui8OffsetLen = 0,
|
||
|
.ui16Length = 2,
|
||
|
.ui8Command = AM_HAL_BLE_READ,
|
||
|
.ui8RepeatCount = 0,
|
||
|
.bContinue = false,
|
||
|
.pfnTransferCompleteCB = 0x0,
|
||
|
.pvContext = 0x0,
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Make sure the IRQ signal is set.
|
||
|
//
|
||
|
if ( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ )
|
||
|
{
|
||
|
//
|
||
|
// Read the length bytes.
|
||
|
//
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &HciRead);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read the rest of the packet.
|
||
|
//
|
||
|
HciRead.pui32Data = pui32Data;
|
||
|
HciRead.ui16Length = (sLengthBytes.bytes[0] +
|
||
|
(sLengthBytes.bytes[1] << 8));
|
||
|
|
||
|
//
|
||
|
// Check if the length is not out of the boundary
|
||
|
//
|
||
|
// Fixme: it is assumed here all the sizes of the buffer are 256
|
||
|
if (HciRead.ui16Length > 256)
|
||
|
{
|
||
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
||
|
}
|
||
|
|
||
|
ui32ErrorStatus = am_hal_ble_blocking_transfer(pHandle, &HciRead);
|
||
|
if ( ui32ErrorStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
return ui32ErrorStatus;
|
||
|
}
|
||
|
|
||
|
ui32NumBytes = HciRead.ui16Length;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ui32NumBytes = 0;
|
||
|
}
|
||
|
|
||
|
if (pui32BytesReceived)
|
||
|
{
|
||
|
*pui32BytesReceived = ui32NumBytes;
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_blocking_hci_read()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Returns the number of bytes written.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_nonblocking_hci_write(void *pHandle, uint8_t ui8Type,
|
||
|
uint32_t *pui32Data, uint32_t ui32NumBytes,
|
||
|
am_hal_ble_transfer_complete_cb_t pfnCallback,
|
||
|
void *pvContext)
|
||
|
{
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
am_hal_ble_transfer_t HciWrite =
|
||
|
{
|
||
|
.pui32Data = pui32Data,
|
||
|
.pui8Offset = {ui8Type, 0x0, 0x0},
|
||
|
.ui8OffsetLen = 0,
|
||
|
.ui16Length = ui32NumBytes,
|
||
|
.ui8Command = AM_HAL_BLE_WRITE,
|
||
|
.ui8RepeatCount = 0,
|
||
|
.bContinue = false,
|
||
|
.pfnTransferCompleteCB = pfnCallback,
|
||
|
.pvContext = pvContext,
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Fix up the offset length based on the packet type, and send the bytes.
|
||
|
//
|
||
|
if (ui8Type != AM_HAL_BLE_RAW)
|
||
|
{
|
||
|
HciWrite.ui8OffsetLen = 1;
|
||
|
}
|
||
|
|
||
|
uint32_t ui32Status = am_hal_ble_nonblocking_transfer(pHandle, &HciWrite);
|
||
|
|
||
|
return ui32Status;
|
||
|
} // am_hal_ble_nonblocking_hci_write()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Returns the number of bytes received.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_nonblocking_hci_read(void *pHandle, uint32_t *pui32Data,
|
||
|
am_hal_ble_transfer_complete_cb_t pfnCallback,
|
||
|
void *pvContext)
|
||
|
{
|
||
|
uint32_t ui32Status;
|
||
|
am_hal_ble_state_t *pBle = pHandle;
|
||
|
|
||
|
am_hal_ble_buffer(2) sLengthBytes;
|
||
|
|
||
|
am_hal_ble_transfer_t HciRead =
|
||
|
{
|
||
|
.pui32Data = sLengthBytes.words,
|
||
|
.pui8Offset = {0x0, 0x0, 0x0},
|
||
|
.ui8OffsetLen = 0,
|
||
|
.ui16Length = 2,
|
||
|
.ui8Command = AM_HAL_BLE_READ,
|
||
|
.ui8RepeatCount = 0,
|
||
|
.bContinue = false,
|
||
|
.pfnTransferCompleteCB = pfnCallback,
|
||
|
.pvContext = pvContext,
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if ( !AM_HAL_BLE_CHK_HANDLE(pHandle) )
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure the IRQ signal is set.
|
||
|
//
|
||
|
if ( am_hal_ble_check_irq(pBle) )
|
||
|
{
|
||
|
//
|
||
|
// Read the length bytes.
|
||
|
//
|
||
|
ui32Status = am_hal_ble_blocking_transfer(pHandle, &HciRead);
|
||
|
|
||
|
if ( ui32Status != AM_HAL_STATUS_SUCCESS )
|
||
|
{
|
||
|
return ui32Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read the rest of the packet.
|
||
|
//
|
||
|
HciRead.pfnTransferCompleteCB = pfnCallback;
|
||
|
HciRead.pui32Data = pui32Data;
|
||
|
HciRead.ui16Length = (sLengthBytes.bytes[0] +
|
||
|
(sLengthBytes.bytes[1] << 8));
|
||
|
|
||
|
return am_hal_ble_nonblocking_transfer(pHandle, &HciRead);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we get here, return fail.
|
||
|
//
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
} // am_hal_ble_nonblocking_hci_read()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Return true if BSTATUS is high.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static bool
|
||
|
am_hal_ble_check_status(am_hal_ble_state_t *pBle)
|
||
|
{
|
||
|
//
|
||
|
// We need to make a special exception for "continue" packets, since the
|
||
|
// BLE radio may deassert the STATUS signal mid-packet.
|
||
|
//
|
||
|
if (pBle->bContinuePacket)
|
||
|
{
|
||
|
pBle->bContinuePacket = false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( BLEIFn(0)->BSTATUS_b.SPISTATUS == 0)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} // am_hal_ble_check_status()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Return true if IRQ is high.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static bool
|
||
|
am_hal_ble_check_irq(am_hal_ble_state_t *pBle)
|
||
|
{
|
||
|
if ( BLEIFn(pBle->ui32Module)->BSTATUS_b.BLEIRQ )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
} // am_hal_ble_check_irq()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Return true if we recently received a BSTATUS edge.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static bool
|
||
|
am_hal_ble_check_status_edge(am_hal_ble_state_t *pBle)
|
||
|
{
|
||
|
//
|
||
|
// We need to make a special exception for "continue" packets, since the
|
||
|
// BLE radio may deassert the STATUS signal mid-packet.
|
||
|
//
|
||
|
if (pBle->bContinuePacket)
|
||
|
{
|
||
|
pBle->bContinuePacket = false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (pBle->bPatchComplete == false)
|
||
|
{
|
||
|
return am_hal_ble_check_status(pBle);
|
||
|
}
|
||
|
|
||
|
if ( BLEIFn(0)->INTSTAT_b.BLECSSTAT == 0)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} // am_hal_ble_check_status_edge()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Blocking write to the BLE module.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_blocking_transfer(void *pHandle, am_hal_ble_transfer_t *psTransfer)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBle = pHandle;
|
||
|
uint32_t ui32IntEnable;
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
//
|
||
|
// If the transfer doesn't have any bytes in it, just return success.
|
||
|
//
|
||
|
if (psTransfer->ui16Length == 0)
|
||
|
{
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure we don't get any interrupts that might interfere with this
|
||
|
// operation. We will save the interrupt enable register state so we can
|
||
|
// restore it later. Also, make sure "command complete" is clear, so we can
|
||
|
// detect the end of the transaction.
|
||
|
//
|
||
|
ui32IntEnable = BLEIFn(ui32Module)->INTEN;
|
||
|
BLEIFn(ui32Module)->INTEN_b.BLECIRQ = 0;
|
||
|
BLEIFn(ui32Module)->INTEN_b.BLECSSTAT = 0;
|
||
|
BLEIFn(ui32Module)->INTEN_b.CMDCMP = 0;
|
||
|
BLEIFn(ui32Module)->INTEN_b.THR = 0;
|
||
|
BLEIFn(ui32Module)->INTCLR_b.CMDCMP = 1;
|
||
|
BLEIFn(ui32Module)->INTCLR_b.BLECSSTAT = 1;
|
||
|
|
||
|
//
|
||
|
// If we're writing, we need to lock down the bus now. Set the wakeup
|
||
|
// signal, and start monitoring STATUS. If STATUS isn't high within our
|
||
|
// configured timeout, we have to assume that the BLE core is unresponsive
|
||
|
// and report an error back to the caller.
|
||
|
//
|
||
|
if (psTransfer->ui8Command == AM_HAL_BLE_WRITE)
|
||
|
{
|
||
|
uint32_t ui32SpiStatus = false;
|
||
|
|
||
|
if ( pBle->bLastPacketWasTX == true)
|
||
|
{
|
||
|
//
|
||
|
// wait some time to give the controller more time to consume
|
||
|
// the last TX packet
|
||
|
//
|
||
|
if (!pBle->bPatchComplete)
|
||
|
{
|
||
|
delay_ms(3);
|
||
|
}
|
||
|
pBle->bLastPacketWasTX = false;
|
||
|
}
|
||
|
|
||
|
if (pBle->bPatchComplete)
|
||
|
{
|
||
|
uint32_t statusTimeout = 0;
|
||
|
while (am_hal_ble_check_status(pBle) == true)
|
||
|
{
|
||
|
statusTimeout++;
|
||
|
delay_us(10);
|
||
|
if (statusTimeout > 300)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure the IO clock for the STATUS signal is on.
|
||
|
//
|
||
|
BLEIFn(0)->BLEDBG_b.IOCLKON = 1;
|
||
|
delay_us(5);
|
||
|
|
||
|
//
|
||
|
// Set WAKE, and wait for a positive edge on the STATUS signal.
|
||
|
//
|
||
|
am_hal_ble_wakeup_set(pBle, 1);
|
||
|
|
||
|
//
|
||
|
// If we don't see an edge on STATUS in X ms, assume it's not coming
|
||
|
// and return with an AM_HAL_BLE_STATUS_SPI_NOT_READY error.
|
||
|
//
|
||
|
uint32_t ui32Timeout = 0;
|
||
|
uint32_t ui32TimeoutLimit = AM_BLE_STATUS_TIMEOUT;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
if (am_hal_ble_check_status_edge(pBle) == true)
|
||
|
{
|
||
|
if (am_hal_ble_bus_lock(pBle))
|
||
|
{
|
||
|
ui32SpiStatus = AM_HAL_STATUS_SUCCESS;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if ((ui32Timeout == ui32TimeoutLimit) ||
|
||
|
(BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ))
|
||
|
{
|
||
|
ui32SpiStatus = AM_HAL_BLE_STATUS_SPI_NOT_READY;
|
||
|
am_hal_ble_wakeup_set(pBle, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ui32Timeout++;
|
||
|
delay_us(10);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Disable IOCLK
|
||
|
//
|
||
|
BLEIFn(0)->BLEDBG_b.IOCLKON = 0;
|
||
|
|
||
|
if (ui32SpiStatus != AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
//
|
||
|
// Restore the interrupt state.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->INTEN = ui32IntEnable;
|
||
|
am_hal_ble_wakeup_set(pBle, 0);
|
||
|
return ui32SpiStatus;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0)
|
||
|
{
|
||
|
//
|
||
|
// Restore the interrupt state.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->INTEN = ui32IntEnable;
|
||
|
return AM_HAL_BLE_STATUS_IRQ_LOW;
|
||
|
}
|
||
|
|
||
|
if (!am_hal_ble_bus_lock(pBle))
|
||
|
{
|
||
|
//
|
||
|
// Restore the interrupt state.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->INTEN = ui32IntEnable;
|
||
|
return AM_HAL_BLE_STATUS_BUS_BUSY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (psTransfer->bContinue)
|
||
|
{
|
||
|
pBle->bContinuePacket = true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the current transfer, and clear the command complete interrupt so we
|
||
|
// can tell when the next command completes.
|
||
|
//
|
||
|
memcpy(&pBle->sCurrentTransfer, psTransfer, sizeof(am_hal_ble_transfer_t));
|
||
|
|
||
|
//
|
||
|
// Critical section to protect the gap between command and data.
|
||
|
//
|
||
|
AM_CRITICAL_BEGIN;
|
||
|
|
||
|
//
|
||
|
// Write the command word.
|
||
|
//
|
||
|
am_hal_ble_cmd_write(pHandle, psTransfer);
|
||
|
|
||
|
//
|
||
|
// Now we need to manage the fifos based on the type of transfer. In either
|
||
|
// case, we will keep draining or refilling the FIFO until the full
|
||
|
// transaction is complete.
|
||
|
//
|
||
|
if (psTransfer->ui8Command == AM_HAL_BLE_WRITE)
|
||
|
{
|
||
|
bool bCmdCmp = false;
|
||
|
uint32_t numWait = 0;
|
||
|
// Adjust the byte count to be sent/received for repeat count
|
||
|
uint32_t ui32Bytes = pBle->sCurrentTransfer.ui16Length;
|
||
|
|
||
|
uint32_t ui32FifoRem;
|
||
|
uint32_t *pui32Buffer = pBle->sCurrentTransfer.pui32Data;
|
||
|
|
||
|
//
|
||
|
// Write the command word.
|
||
|
//
|
||
|
am_hal_ble_cmd_write(pHandle, psTransfer);
|
||
|
|
||
|
//
|
||
|
// Keep looping until we're out of bytes to send or command complete (error).
|
||
|
//
|
||
|
while (ui32Bytes)
|
||
|
{
|
||
|
//
|
||
|
// Limit the wait to reasonable limit - instead of blocking forever
|
||
|
//
|
||
|
numWait = 0;
|
||
|
while ((ui32FifoRem = BLEIFn(ui32Module)->FIFOPTR_b.FIFO0REM) < 4)
|
||
|
{
|
||
|
bCmdCmp = BLEIFn(ui32Module)->INTSTAT_b.CMDCMP;
|
||
|
if (bCmdCmp || (numWait++ >= AM_HAL_IOM_MAX_BLOCKING_WAIT))
|
||
|
{
|
||
|
//
|
||
|
// FIFO not expected to change any more - get out
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
am_hal_flash_delay( FLASH_CYCLES_US(1) );
|
||
|
}
|
||
|
}
|
||
|
if (bCmdCmp || (ui32FifoRem < 4))
|
||
|
{
|
||
|
//
|
||
|
// Something went wrong - bail out
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
while ((ui32FifoRem >= 4) && ui32Bytes)
|
||
|
{
|
||
|
BLEIFn(ui32Module)->FIFOPUSH = *pui32Buffer++;
|
||
|
ui32FifoRem -= 4;
|
||
|
if (ui32Bytes >= 4)
|
||
|
{
|
||
|
ui32Bytes -= 4;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ui32Bytes = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
WHILE_TIMEOUT_MS_BREAK ( BLEIFn(ui32Module)->INTSTAT_b.CMDCMP == 0, 2,
|
||
|
AM_HAL_BLE_HCI_PACKET_INCOMPLETE );
|
||
|
am_hal_ble_wakeup_set(pBle, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
while (pBle->ui32TransferIndex < pBle->sCurrentTransfer.ui16Length)
|
||
|
{
|
||
|
am_hal_ble_fifo_drain(pHandle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// End the critical section.
|
||
|
//
|
||
|
AM_CRITICAL_END; //fixme moved further down to cover am_hal_ble_bus_release();
|
||
|
|
||
|
//
|
||
|
// Wait for the transaction to complete, and clear out any interrupts that
|
||
|
// may have come up.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->INTSTAT_b.CMDCMP == 0, 10,
|
||
|
AM_HAL_BLE_HCI_PACKET_INCOMPLETE );
|
||
|
BLEIFn(ui32Module)->INTCLR_b.CMDCMP = 1;
|
||
|
BLEIFn(ui32Module)->INTCLR_b.THR = 1;
|
||
|
|
||
|
//
|
||
|
// Clear out the current transfer. We're done.
|
||
|
//
|
||
|
memset(&pBle->sCurrentTransfer, 0, sizeof(am_hal_ble_transfer_t));
|
||
|
pBle->ui32TransferIndex = 0;
|
||
|
|
||
|
//
|
||
|
// Let the radio go back to sleep.
|
||
|
//
|
||
|
if (psTransfer->ui8Command == AM_HAL_BLE_WRITE)
|
||
|
{
|
||
|
am_hal_ble_wakeup_set(pBle, 0);
|
||
|
pBle->bLastPacketWasTX = true;
|
||
|
}
|
||
|
|
||
|
if ((psTransfer->ui8Command == AM_HAL_BLE_READ) &&
|
||
|
(pBle->bPatchComplete == true))
|
||
|
{
|
||
|
pBle->bLastPacketWasTX = false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Restore the interrupt state.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->INTEN = ui32IntEnable;
|
||
|
|
||
|
//
|
||
|
// Release the bus.
|
||
|
//
|
||
|
am_hal_ble_bus_release(pBle);
|
||
|
|
||
|
//
|
||
|
// End the critical section.
|
||
|
//
|
||
|
// AM_CRITICAL_END; //fixme moved further down to cover am_hal_ble_bus_release();
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_blocking_transfer()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Nonblocking write to the BLE module.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_nonblocking_transfer(void *pHandle, am_hal_ble_transfer_t *psTransfer)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBle = pHandle;
|
||
|
uint32_t ui32Status;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if this is a write or a read.
|
||
|
//
|
||
|
if (psTransfer->ui8Command == AM_HAL_BLE_WRITE)
|
||
|
{
|
||
|
ui32Status = nonblocking_write(pBle, psTransfer);
|
||
|
}
|
||
|
else // AM_HAL_BLE_READ case.
|
||
|
{
|
||
|
ui32Status = nonblocking_read(pBle, psTransfer);
|
||
|
}
|
||
|
|
||
|
return ui32Status;
|
||
|
} // am_hal_ble_nonblocking_transfer()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Function for performing non-blocking writes to the HCI interface.
|
||
|
//
|
||
|
// This function will start a BLE write on the physical bus. The caller should
|
||
|
// have already set WAKEUP and received a STATUS interrupt before they call
|
||
|
// this function. When the write operation is complete, the MCU will receive a
|
||
|
// command complete interrupt.
|
||
|
//
|
||
|
// Before calling this function, the caller is responsible for ensuring that
|
||
|
// STATUS is high, that BLEIRQ is low, and the the bus isn't already in use. If
|
||
|
// any of these problems exists when this function is called, it will simply
|
||
|
// return with an error status.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static uint32_t
|
||
|
nonblocking_write(am_hal_ble_state_t *pBle, am_hal_ble_transfer_t *psTransfer)
|
||
|
{
|
||
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
||
|
uint32_t ui32Module = pBle->ui32Module;
|
||
|
|
||
|
//
|
||
|
// This function goes in a critical section to make sure that the operation
|
||
|
// isn't interrupted or started again.
|
||
|
//
|
||
|
AM_CRITICAL_BEGIN;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
//
|
||
|
// Check for any of the various reasons that we might not be able to
|
||
|
// perform a write right now. If the bus is busy, if the BLE core requires
|
||
|
// a READ operation, or if the BLE core simply isn't ready yet, stop here
|
||
|
// and throw an error.
|
||
|
//
|
||
|
if ( pBle->bBusy )
|
||
|
{
|
||
|
ui32Status = AM_HAL_BLE_STATUS_BUS_BUSY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( am_hal_ble_check_irq(pBle) )
|
||
|
{
|
||
|
ui32Status = AM_HAL_BLE_REQUESTING_READ;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !am_hal_ble_check_status(pBle) )
|
||
|
{
|
||
|
ui32Status = AM_HAL_BLE_STATUS_SPI_NOT_READY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (psTransfer->ui16Length == 0)
|
||
|
{
|
||
|
ui32Status = AM_HAL_STATUS_SUCCESS;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// With the obvious error cases out of the way, we can claim the bus and
|
||
|
// start the transaction.
|
||
|
//
|
||
|
if ( pBle->bLastPacketWasTX == true )
|
||
|
{
|
||
|
delay_us(AM_BLE_TX_PACKET_SPACING_US);
|
||
|
}
|
||
|
|
||
|
pBle->bBusy = true;
|
||
|
pBle->bLastPacketWasTX = true;
|
||
|
|
||
|
//
|
||
|
// Save the current transfer.
|
||
|
//
|
||
|
memcpy(&pBle->sCurrentTransfer, psTransfer, sizeof(am_hal_ble_transfer_t));
|
||
|
|
||
|
//
|
||
|
// Prepare the DMA.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->DMATARGADDR = (uint32_t)pBle->sCurrentTransfer.pui32Data;
|
||
|
BLEIFn(ui32Module)->DMATOTCOUNT = pBle->sCurrentTransfer.ui16Length;
|
||
|
BLEIFn(ui32Module)->DMATRIGEN = BLEIF_DMATRIGEN_DTHREN_Msk;
|
||
|
BLEIFn(ui32Module)->DMACFG =
|
||
|
(_VAL2FLD(BLEIF_DMACFG_DMADIR, BLEIF_DMACFG_DMADIR_M2P) |
|
||
|
_VAL2FLD(BLEIF_DMACFG_DMAPRI, BLEIF_DMACFG_DMAPRI_HIGH));
|
||
|
|
||
|
//
|
||
|
// Write the command word, and enable the DMA.
|
||
|
//
|
||
|
ui32Status = am_hal_ble_cmd_write(pBle, &pBle->sCurrentTransfer);
|
||
|
|
||
|
BLEIFn(ui32Module)->DMACFG |= _VAL2FLD(BLEIF_DMACFG_DMAEN, BLEIF_DMACFG_DMAEN_EN);
|
||
|
|
||
|
//
|
||
|
// Make sure WAKE goes low as quickly as possible after starting the write.
|
||
|
//
|
||
|
if (ui32Status == AM_HAL_STATUS_SUCCESS)
|
||
|
{
|
||
|
am_hal_ble_wakeup_set(pBle, 0);
|
||
|
}
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
//
|
||
|
// No matter what happened above, the function should end here. We'll end
|
||
|
// the critical section and alert the caller of our status.
|
||
|
//
|
||
|
AM_CRITICAL_END;
|
||
|
return ui32Status;
|
||
|
} // nonblocking_write()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// This function performs a nonblocking read from the BLE core.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static uint32_t
|
||
|
nonblocking_read(am_hal_ble_state_t *pBle, am_hal_ble_transfer_t *psTransfer)
|
||
|
{
|
||
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
||
|
uint32_t ui32Module = pBle->ui32Module;
|
||
|
|
||
|
//
|
||
|
// This function goes in a critical section to make sure that the operation
|
||
|
// isn't interrupted or started again.
|
||
|
//
|
||
|
AM_CRITICAL_BEGIN;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if ( pBle->bBusy )
|
||
|
{
|
||
|
ui32Status = AM_HAL_BLE_STATUS_BUS_BUSY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !am_hal_ble_check_irq(pBle) )
|
||
|
{
|
||
|
ui32Status = AM_HAL_BLE_STATUS_IRQ_LOW;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (psTransfer->ui16Length == 0)
|
||
|
{
|
||
|
ui32Status = AM_HAL_STATUS_SUCCESS;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// With the obvious error cases out of the way, we can claim the bus and
|
||
|
// start the transaction.
|
||
|
//
|
||
|
if ( pBle->bLastPacketWasTX == true )
|
||
|
{
|
||
|
delay_us(AM_BLE_TX_PACKET_SPACING_US);
|
||
|
}
|
||
|
|
||
|
pBle->bBusy = true;
|
||
|
pBle->bLastPacketWasTX = false;
|
||
|
|
||
|
//
|
||
|
// Set the current transfer.
|
||
|
//
|
||
|
memcpy(&pBle->sCurrentTransfer, psTransfer, sizeof(am_hal_ble_transfer_t));
|
||
|
|
||
|
BLEIFn(ui32Module)->DMATARGADDR = (uint32_t) pBle->sCurrentTransfer.pui32Data;
|
||
|
BLEIFn(ui32Module)->DMATOTCOUNT = pBle->sCurrentTransfer.ui16Length;
|
||
|
BLEIFn(ui32Module)->DMATRIGEN = (BLEIF_DMATRIGEN_DTHREN_Msk | BLEIF_INTCLR_CMDCMP_Msk);
|
||
|
BLEIFn(ui32Module)->DMACFG =
|
||
|
(_VAL2FLD(BLEIF_DMACFG_DMADIR, BLEIF_DMACFG_DMADIR_P2M) |
|
||
|
_VAL2FLD(BLEIF_DMACFG_DMAPRI, BLEIF_DMACFG_DMAPRI_HIGH));
|
||
|
|
||
|
//
|
||
|
// Write the command word, and enable the DMA.
|
||
|
//
|
||
|
ui32Status = am_hal_ble_cmd_write(pBle, &pBle->sCurrentTransfer);
|
||
|
BLEIFn(ui32Module)->DMACFG |= _VAL2FLD(BLEIF_DMACFG_DMAEN, BLEIF_DMACFG_DMAEN_EN);
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
//
|
||
|
// No matter what happened above, the function should end here. We'll end
|
||
|
// the critical section and alert the caller of our status.
|
||
|
//
|
||
|
AM_CRITICAL_END;
|
||
|
return ui32Status;
|
||
|
} // nonblocking_read()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Mark the BLE interface busy so it doesn't get used by more than one
|
||
|
// interface.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static bool
|
||
|
am_hal_ble_bus_lock(am_hal_ble_state_t *pBle)
|
||
|
{
|
||
|
bool bLockObtained;
|
||
|
|
||
|
//
|
||
|
// In one atomic sweep, check to see if the bus is busy, and reserve it if
|
||
|
// it isn't.
|
||
|
//
|
||
|
AM_CRITICAL_BEGIN;
|
||
|
|
||
|
if (pBle->bBusy == false)
|
||
|
{
|
||
|
am_hal_debug_gpio_set(BLE_DEBUG_TRACE_11);
|
||
|
pBle->bBusy = true;
|
||
|
bLockObtained = true;
|
||
|
pBle->bCmdComplete = 0;
|
||
|
pBle->bDmaComplete = 0;
|
||
|
pBle->bFlowControlComplete = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bLockObtained = false;
|
||
|
}
|
||
|
|
||
|
AM_CRITICAL_END;
|
||
|
|
||
|
//
|
||
|
// Tell the caller if we successfully locked the bus.
|
||
|
//
|
||
|
return bLockObtained;
|
||
|
} // am_hal_ble_bus_lock()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Release the bus so someone else can use it.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static void
|
||
|
am_hal_ble_bus_release(am_hal_ble_state_t *pBle)
|
||
|
{
|
||
|
pBle->bBusy = false;
|
||
|
am_hal_debug_gpio_clear(BLE_DEBUG_TRACE_11);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Pull data out of the fifo for reads.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static uint32_t
|
||
|
am_hal_ble_fifo_drain(void *pHandle)
|
||
|
{
|
||
|
uint32_t ui32Module;
|
||
|
uint32_t ui32ReadSize, ui32RxDataLen, ui32BytesLeft;
|
||
|
uint32_t *pDest;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Rename some pointers for convenience.
|
||
|
//
|
||
|
am_hal_ble_state_t *pBle = pHandle;
|
||
|
am_hal_ble_transfer_t *pTransfer = &pBle->sCurrentTransfer;
|
||
|
|
||
|
//
|
||
|
// Check to see how much data there is in the FIFO, and also how many
|
||
|
// bytes are remaining in the transfer.
|
||
|
//
|
||
|
ui32RxDataLen = BLEIFn(ui32Module)->FIFOPTR_b.FIFO1SIZ;
|
||
|
ui32BytesLeft = (pTransfer->ui16Length - pBle->ui32TransferIndex);
|
||
|
|
||
|
//
|
||
|
// Calculate how much we can drain the fifo.
|
||
|
//
|
||
|
if (ui32RxDataLen < 4)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else if (ui32RxDataLen >= pTransfer->ui16Length)
|
||
|
{
|
||
|
ui32ReadSize = ui32BytesLeft;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ui32ReadSize = ui32RxDataLen & (~0x3);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Calculate the place where we last left off, feed the FIFO starting from
|
||
|
// that location, and update the index to match.
|
||
|
//
|
||
|
pDest = &pTransfer->pui32Data[pBle->ui32TransferIndex / 4];
|
||
|
|
||
|
am_hal_ble_fifo_read(pHandle, pDest, ui32ReadSize);
|
||
|
|
||
|
pBle->ui32TransferIndex += ui32ReadSize;
|
||
|
|
||
|
//
|
||
|
// Return the number of bytes we wrote.
|
||
|
//
|
||
|
return ui32ReadSize;
|
||
|
} // am_hal_ble_fifo_drain()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Write the command word for a BLE transfer.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_cmd_write(void *pHandle, am_hal_ble_transfer_t *psTransfer)
|
||
|
{
|
||
|
uint32_t ui32CmdWord, ui32OffsetHigh;
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Figure out the command word and the offset register. Then write them.
|
||
|
//
|
||
|
switch (psTransfer->ui8OffsetLen)
|
||
|
{
|
||
|
case 0:
|
||
|
ui32CmdWord = 0;
|
||
|
ui32OffsetHigh = 0;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
ui32CmdWord = _VAL2FLD(BLEIF_CMD_OFFSETLO, psTransfer->pui8Offset[0]);
|
||
|
ui32OffsetHigh = 0;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
ui32CmdWord = _VAL2FLD(BLEIF_CMD_OFFSETLO, psTransfer->pui8Offset[1]);
|
||
|
ui32OffsetHigh = psTransfer->pui8Offset[0];
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
ui32CmdWord = _VAL2FLD(BLEIF_CMD_OFFSETLO, psTransfer->pui8Offset[2]);
|
||
|
ui32OffsetHigh = ((psTransfer->pui8Offset[1]) |
|
||
|
(psTransfer->pui8Offset[0] << 8));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// Offset length was incorrect.
|
||
|
return AM_HAL_STATUS_INVALID_ARG;
|
||
|
}
|
||
|
|
||
|
ui32CmdWord |= (_VAL2FLD(BLEIF_CMD_OFFSETCNT, psTransfer->ui8OffsetLen) |
|
||
|
_VAL2FLD(BLEIF_CMD_TSIZE, psTransfer->ui16Length) |
|
||
|
_VAL2FLD(BLEIF_CMD_CONT, psTransfer->bContinue) |
|
||
|
psTransfer->ui8Command);
|
||
|
|
||
|
BLEIFn(ui32Module)->OFFSETHI = ui32OffsetHigh;
|
||
|
BLEIFn(ui32Module)->CMD = ui32CmdWord;
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_cmd_write()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Read ui32NumBytes from the RX FIFO.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
static void
|
||
|
am_hal_ble_fifo_read(void *pHandle, uint32_t *pui32Data, uint32_t ui32NumBytes)
|
||
|
{
|
||
|
uint32_t ui32Index;
|
||
|
uint32_t ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
for (ui32Index = 0; (ui32Index * 4) < ui32NumBytes; ui32Index++)
|
||
|
{
|
||
|
pui32Data[ui32Index] = BLEIFn(ui32Module)->FIFOPOP;
|
||
|
|
||
|
#ifndef AM_HAL_BLE_NO_FIFO_PROTECTION
|
||
|
BLEIFn(ui32Module)->FIFOPOP = 0;
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
} // am_hal_ble_fifo_read()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Call the appropriate callbacks when DMA transfers complete.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_int_service(void *pHandle, uint32_t ui32Status)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBle = pHandle;
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Track each of the interrupts signaling the end of an HCI transfer.
|
||
|
//
|
||
|
if ( ui32Status & BLEIF_INTSTAT_CMDCMP_Msk )
|
||
|
{
|
||
|
pBle->bCmdComplete = true;
|
||
|
}
|
||
|
|
||
|
if ( ui32Status & BLEIF_INTSTAT_DCMP_Msk )
|
||
|
{
|
||
|
pBle->bDmaComplete = true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For B0 parts, we can detect when key flow control signals from the BLE
|
||
|
// core are de-asserted.
|
||
|
//
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
//
|
||
|
// Check for falling IRQ
|
||
|
//
|
||
|
if ( (ui32Status & BLEIF_INTSTAT_BLECIRQN_Msk) &&
|
||
|
(pBle->sCurrentTransfer.ui8Command == AM_HAL_BLE_READ) )
|
||
|
{
|
||
|
pBle->bFlowControlComplete = true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check for falling status.
|
||
|
//
|
||
|
if ( (ui32Status & BLEIF_INTSTAT_BLECSSTATN_Msk ) &&
|
||
|
(pBle->sCurrentTransfer.ui8Command == AM_HAL_BLE_WRITE) )
|
||
|
{
|
||
|
pBle->bFlowControlComplete = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we get a command complete, we need to release the wake signal,
|
||
|
// disable the DMA, release the bus, and call any callback that might
|
||
|
// exist.
|
||
|
//
|
||
|
// For revision A parts, "command complete" means that the DMA operation
|
||
|
// and the BLE SPI interface have both finished their operations. For rev B
|
||
|
// parts, we will also wait for the flow control signal (either STATUS or
|
||
|
// IRQ) to be removed.
|
||
|
//
|
||
|
if ( pBle->bCmdComplete && pBle->bDmaComplete &&
|
||
|
((pBle->bFlowControlComplete) || (!APOLLO3_GE_B0) || SKIP_FALLING_EDGES) )
|
||
|
{
|
||
|
//
|
||
|
// Clean up our state flags.
|
||
|
//
|
||
|
pBle->bCmdComplete = false;
|
||
|
pBle->bDmaComplete = false;
|
||
|
pBle->bFlowControlComplete = false;
|
||
|
|
||
|
//
|
||
|
// If our FIFOs aren't empty right now, either the DMA didn't finish,
|
||
|
// or this interrupt handler is somehow being called incorrectly.
|
||
|
//
|
||
|
if ( BLEIFn(ui32Module)->FIFOPTR != 0x20002000 )
|
||
|
{
|
||
|
return AM_HAL_BLE_FIFO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Drop the wake request if we had one, and make sure we remember if
|
||
|
// the last packet was a transmit packet.
|
||
|
//
|
||
|
if ((pBle->sCurrentTransfer.ui8Command == AM_HAL_BLE_WRITE) &&
|
||
|
(pBle->bPatchComplete == true))
|
||
|
{
|
||
|
pBle->bLastPacketWasTX = true;
|
||
|
am_hal_ble_wakeup_set(pBle, 0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If this was a read packet, remember that it wasn't a TX packet.
|
||
|
//
|
||
|
if (pBle->sCurrentTransfer.ui8Command == AM_HAL_BLE_READ)
|
||
|
{
|
||
|
pBle->bLastPacketWasTX = false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Disable the DMA
|
||
|
//
|
||
|
BLEIFn(ui32Module)->DMACFG = 0;
|
||
|
|
||
|
am_hal_ble_bus_release(pBle);
|
||
|
|
||
|
if ( pBle->sCurrentTransfer.pfnTransferCompleteCB )
|
||
|
{
|
||
|
am_hal_ble_transfer_complete_cb_t pfnCallback;
|
||
|
uint32_t ui32Length;
|
||
|
uint8_t *pui8Data;
|
||
|
void *pvContext;
|
||
|
|
||
|
pfnCallback = pBle->sCurrentTransfer.pfnTransferCompleteCB;
|
||
|
pui8Data = (uint8_t * ) pBle->sCurrentTransfer.pui32Data;
|
||
|
ui32Length = pBle->sCurrentTransfer.ui16Length;
|
||
|
pvContext = pBle->sCurrentTransfer.pvContext;
|
||
|
|
||
|
pfnCallback(pui8Data, ui32Length, pvContext);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_int_service()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Interrupt Enable
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_int_enable(void *pHandle, uint32_t ui32InterruptMask)
|
||
|
{
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
AM_CRITICAL_BEGIN
|
||
|
BLEIFn(ui32Module)->INTEN |= ui32InterruptMask;
|
||
|
AM_CRITICAL_END
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_int_enable()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Interrupt Enable
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_int_disable(void *pHandle, uint32_t ui32InterruptMask)
|
||
|
{
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if (!AM_HAL_BLE_CHK_HANDLE(pHandle))
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
AM_CRITICAL_BEGIN
|
||
|
BLEIFn(ui32Module)->INTEN &= ~ui32InterruptMask;
|
||
|
AM_CRITICAL_END
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_int_disable()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Check the status of the interrupts.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_int_status(void *pHandle, bool bEnabledOnly)
|
||
|
{
|
||
|
uint32_t ui32Module = ((am_hal_ble_state_t *) pHandle)->ui32Module;
|
||
|
|
||
|
if (bEnabledOnly)
|
||
|
{
|
||
|
uint32_t ui32IntEn = BLEIFn(ui32Module)->INTEN;
|
||
|
return ( BLEIFn(ui32Module)->INTSTAT & ui32IntEn );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return BLEIFn(ui32Module)->INTSTAT;
|
||
|
}
|
||
|
} // am_hal_ble_int_status()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Clear the interrupt status.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_int_clear(void *pHandle, uint32_t ui32InterruptMask)
|
||
|
{
|
||
|
uint32_t ui32Module;
|
||
|
|
||
|
//
|
||
|
// Check the handle.
|
||
|
//
|
||
|
if ( !AM_HAL_BLE_CHK_HANDLE(pHandle) )
|
||
|
{
|
||
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle is good, so get the module number.
|
||
|
//
|
||
|
ui32Module = ((am_hal_ble_state_t *)pHandle)->ui32Module;
|
||
|
|
||
|
BLEIFn(ui32Module)->INTCLR = ui32InterruptMask;
|
||
|
|
||
|
//
|
||
|
// Return the status.
|
||
|
//
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_int_clear()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// check 32768Hz clock is ready.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_check_32k_clock(void *pHandle)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint32_t rc32k_clock = 0xFFFFFFFF;
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_read(pBLE, AM_HAL_BLE_IP_RAM_32K_CLOCK_ADDR_B0, &rc32k_clock);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
// Normal 32KHz clock is about 0x8000
|
||
|
if ( (rc32k_clock > 0x8200) || (rc32k_clock < 0x7B00) )
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
}
|
||
|
} // am_hal_ble_check_32k_clock()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Read a register value from the BLE core.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_plf_reg_read(void *pHandle, uint32_t ui32Address, uint32_t *pui32Value)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint8_t pui8Parameter[4];
|
||
|
uint32_t ui32IntEnable;
|
||
|
|
||
|
uint32_t ui32Module = pBLE->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Make a buffer big enough to hold the register write command, and a
|
||
|
// second one big enough to hold the response.
|
||
|
//
|
||
|
am_hal_ble_buffer(AM_HAL_BLE_PLF_REGISTER_READ_LENGTH) sWriteCommand;
|
||
|
am_hal_ble_buffer(32) sResponse;
|
||
|
|
||
|
//
|
||
|
// Prepare our register write value.
|
||
|
//
|
||
|
pui8Parameter[0] = ui32Address;
|
||
|
pui8Parameter[1] = (ui32Address >> 8);
|
||
|
pui8Parameter[2] = (ui32Address >> 16);
|
||
|
pui8Parameter[3] = (ui32Address >> 24);
|
||
|
|
||
|
sResponse.words[0] = 0;
|
||
|
sResponse.words[1] = 0;
|
||
|
sResponse.words[2] = 0;
|
||
|
|
||
|
//
|
||
|
// Fill the buffer with the specific command we want to write, and send it.
|
||
|
//
|
||
|
am_hal_ble_vs_command_build(sWriteCommand.words,
|
||
|
AM_HAL_BLE_PLF_REGISTER_READ_OPCODE,
|
||
|
AM_HAL_BLE_PLF_REGISTER_READ_LENGTH,
|
||
|
pui8Parameter);
|
||
|
|
||
|
//
|
||
|
// Temporarily disable BLE interrupts.
|
||
|
//
|
||
|
ui32IntEnable = BLEIFn(ui32Module)->INTEN;
|
||
|
BLEIFn(ui32Module)->INTEN = 0;
|
||
|
|
||
|
am_hal_ble_blocking_hci_write(pBLE,
|
||
|
AM_HAL_BLE_RAW,
|
||
|
sWriteCommand.words,
|
||
|
AM_HAL_BLE_PLF_REGISTER_READ_LENGTH);
|
||
|
|
||
|
//
|
||
|
// Make sure the IO clock for the STATUS signal is on.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->BLEDBG_b.IOCLKON = 1;
|
||
|
|
||
|
//
|
||
|
// Wait for the response, and return it to the caller via our variable.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 500,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE );
|
||
|
|
||
|
am_hal_ble_blocking_hci_read(pBLE, sResponse.words, 0);
|
||
|
|
||
|
*pui32Value = (((sResponse.words[1] & 0xFF000000) >> 24) |
|
||
|
((sResponse.words[2] & 0x00FFFFFF) << 8));
|
||
|
|
||
|
//
|
||
|
// Re-enable BLE interrupts.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->INTCLR = ui32IntEnable;
|
||
|
BLEIFn(ui32Module)->INTEN = ui32IntEnable;
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
|
||
|
} // am_hal_ble_plf_reg_read()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Write a register value to the BLE core.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_plf_reg_write(void *pHandle, uint32_t ui32Address, uint32_t ui32Value)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint8_t pui8Parameter[8];
|
||
|
uint32_t ui32IntEnable;
|
||
|
|
||
|
uint32_t ui32Module = pBLE->ui32Module;
|
||
|
|
||
|
//
|
||
|
// Make a buffer big enough to hold the register write command, and a
|
||
|
// second one big enough to hold the response.
|
||
|
//
|
||
|
am_hal_ble_buffer(AM_HAL_BLE_PLF_REGISTER_WRITE_LENGTH) sWriteCommand;
|
||
|
am_hal_ble_buffer(16) sResponse;
|
||
|
|
||
|
//
|
||
|
// Prepare our register write value.
|
||
|
//
|
||
|
pui8Parameter[0] = ui32Address;
|
||
|
pui8Parameter[1] = (ui32Address >> 8);
|
||
|
pui8Parameter[2] = (ui32Address >> 16);
|
||
|
pui8Parameter[3] = (ui32Address >> 24);
|
||
|
pui8Parameter[4] = ui32Value;
|
||
|
pui8Parameter[5] = (ui32Value >> 8);
|
||
|
pui8Parameter[6] = (ui32Value >> 16);
|
||
|
pui8Parameter[7] = (ui32Value >> 24);
|
||
|
|
||
|
//
|
||
|
// Fill the buffer with the specific command we want to write, and send it.
|
||
|
//
|
||
|
am_hal_ble_vs_command_build(sWriteCommand.words,
|
||
|
AM_HAL_BLE_PLF_REGISTER_WRITE_OPCODE,
|
||
|
AM_HAL_BLE_PLF_REGISTER_WRITE_LENGTH,
|
||
|
pui8Parameter);
|
||
|
|
||
|
//
|
||
|
// Temporarily disable BLE interrupts.
|
||
|
//
|
||
|
ui32IntEnable = BLEIFn(ui32Module)->INTEN;
|
||
|
BLEIFn(ui32Module)->INTEN = 0;
|
||
|
|
||
|
am_hal_ble_blocking_hci_write(pBLE,
|
||
|
AM_HAL_BLE_RAW,
|
||
|
sWriteCommand.words,
|
||
|
AM_HAL_BLE_PLF_REGISTER_WRITE_LENGTH);
|
||
|
|
||
|
//
|
||
|
// Make sure the IO clock for the STATUS signal is on.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->BLEDBG_b.IOCLKON = 1;
|
||
|
|
||
|
//
|
||
|
// Wait for the response.
|
||
|
//
|
||
|
WHILE_TIMEOUT_MS ( BLEIFn(ui32Module)->BSTATUS_b.BLEIRQ == 0, 50,
|
||
|
AM_HAL_BLE_NO_HCI_RESPONSE );
|
||
|
|
||
|
am_hal_ble_blocking_hci_read(pBLE, sResponse.words, 0);
|
||
|
|
||
|
//
|
||
|
// Re-enable BLE interrupts.
|
||
|
//
|
||
|
BLEIFn(ui32Module)->INTCLR = ui32IntEnable;
|
||
|
BLEIFn(ui32Module)->INTEN = ui32IntEnable;
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
|
||
|
} // am_hal_ble_plf_reg_write()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Set the modulation frequency offset from INFO1,
|
||
|
// based on the tested values stored in non-volatile memory.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_load_modex_trim_set(void *pHandle)
|
||
|
{
|
||
|
uint8_t ui8TrimValue;
|
||
|
//
|
||
|
// load the modex trim data from info1.
|
||
|
//
|
||
|
ui8TrimValue = am_hal_ble_read_trimdata_from_info1();
|
||
|
if ( ui8TrimValue )
|
||
|
{
|
||
|
am_hal_ble_transmitter_modex_set(pHandle, ui8TrimValue);
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
} // am_hal_ble_load_modex_trim_set()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Load the modulation frequency offset from INFO1,
|
||
|
// based on the tested values stored in non-volatile memory.
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint8_t
|
||
|
am_hal_ble_read_trimdata_from_info1(void)
|
||
|
{
|
||
|
uint32_t ui32TrimValue = 0, temp = 0;
|
||
|
uint8_t TrimData = 0;
|
||
|
|
||
|
temp = ui32TrimValue = AM_REGVAL(0x50023808);
|
||
|
temp &= 0xffffff00;
|
||
|
|
||
|
if ( temp == 0x18240600 )
|
||
|
{
|
||
|
TrimData = ui32TrimValue & 0xFF;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TrimData = 0;
|
||
|
}
|
||
|
|
||
|
if ( (TrimData > 0x50) || (TrimData < 0x20) ) // change from 0x40 to 0x50 for improving the FT2 yield.
|
||
|
{
|
||
|
TrimData = 0;
|
||
|
}
|
||
|
|
||
|
return TrimData;
|
||
|
} // am_hal_ble_read_trimdata_from_info1()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Manually set modulation characteristic
|
||
|
// based on the tested values at customer side.
|
||
|
// manually set frequency offset for 10101010 or 01010101 pattern
|
||
|
// parameter default value is 0x34, increase to get larger frequency offset
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_transmitter_modex_set(void *pHandle, uint8_t ui8ModFrqOffset)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint32_t RegValueMCGR, RegValueBACKCR, RegValueSTCR, RegValueDACSPICR, temp = 0;
|
||
|
|
||
|
ui8ModFrqOffset &= 0x7F;
|
||
|
|
||
|
am_hal_ble_plf_reg_read(pBLE, 0x43000004, &RegValueMCGR);
|
||
|
|
||
|
//
|
||
|
// Unlock the BLE registers.
|
||
|
//
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x43000004, 0xFFFFFFFF);
|
||
|
am_hal_ble_plf_reg_read(pBLE, 0x52000008, &temp);
|
||
|
temp |= 0x08;
|
||
|
am_hal_ble_plf_reg_read(pBLE, 0x52000000, &RegValueSTCR);
|
||
|
RegValueSTCR |= (1 << 10);
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x52000000, RegValueSTCR);
|
||
|
|
||
|
am_hal_ble_plf_reg_read(pBLE, 0x45800070, &RegValueBACKCR);
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x45800070, (RegValueBACKCR | 0x8));
|
||
|
RegValueDACSPICR = (ui8ModFrqOffset << 1) | 0x1;
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x52000014, RegValueDACSPICR);
|
||
|
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x52000008, temp);
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_write(pBLE, AM_HAL_BLE_IP_RAM_MODEX_TRIM_ADDR_B0, ui8ModFrqOffset);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x43000004, RegValueMCGR);
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_transmitter_modex_set()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Set BLE sleep enable/disable for the BLE core.
|
||
|
// enable = 'true' set sleep enable, enable = 'false' set sleep disable
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_sleep_set(void *pHandle, bool enable)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint32_t sleepenable = 0;
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_read(pBLE, AM_HAL_BLE_IP_RAM_SLEEP_ENABLE_ADDR_B0, &sleepenable);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
sleepenable &= 0xffff0100;
|
||
|
|
||
|
if ( enable )
|
||
|
{
|
||
|
sleepenable |= 0x0101;
|
||
|
}
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_write(pBLE, AM_HAL_BLE_IP_RAM_SLEEP_ENABLE_ADDR_B0, sleepenable);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_sleep_set()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// Get current sleep enable status
|
||
|
// return 'true' = sleep enable , 'false' = sleep disable
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
bool
|
||
|
am_hal_ble_sleep_get(void *pHandle)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint32_t sleepenable = 0;
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_read(pBLE, AM_HAL_BLE_IP_RAM_SLEEP_ENABLE_ADDR_B0, &sleepenable);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
if ( (sleepenable & 0xFFFF) > 0 )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
} // am_hal_ble_sleep_get()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// set the tx power of BLE
|
||
|
// values.
|
||
|
// ui32TxPower: 0x03->-20dBm 0x04->-10dBm 0x05->-5dBm 0x08->0dBm 0x0F->3dBm
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
uint32_t
|
||
|
am_hal_ble_tx_power_set(void *pHandle, uint8_t ui32TxPower)
|
||
|
{
|
||
|
am_hal_ble_state_t *pBLE = pHandle;
|
||
|
uint32_t RegValueMCGR, tempreg = 0;
|
||
|
uint32_t ui32PowerValue = 0x00000008;
|
||
|
ui32PowerValue |= (ui32TxPower & 0xF) << 16;
|
||
|
|
||
|
am_hal_ble_plf_reg_read(pBLE, 0x43000004, &RegValueMCGR);
|
||
|
|
||
|
//
|
||
|
// Unlock the BLE registers.
|
||
|
//
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x43000004, 0xFFFFFFFF);
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_read(pBLE, AM_HAL_BLE_IP_RAM_POWER_LEVEL_ADDR_B0, &tempreg);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
tempreg &= 0xffffff00;
|
||
|
tempreg |= ui32TxPower;
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x52400018, ui32PowerValue);
|
||
|
|
||
|
if (APOLLO3_GE_B0)
|
||
|
{
|
||
|
am_hal_ble_plf_reg_write(pBLE, AM_HAL_BLE_IP_RAM_POWER_LEVEL_ADDR_B0, tempreg);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AM_HAL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
am_hal_ble_plf_reg_write(pBLE, 0x43000004, RegValueMCGR);
|
||
|
|
||
|
return AM_HAL_STATUS_SUCCESS;
|
||
|
} // am_hal_ble_tx_power_set()
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// End Doxygen group.
|
||
|
//! @}
|
||
|
//
|
||
|
//*****************************************************************************
|