1 //*****************************************************************************
2 //
3 //! @file am_util_ble_cooper.c
4 //!
5 //! @brief Cooper BLE functions not covered by the HAL.
6 //!
7 //! This file contains functions for interacting with the Apollo4 BLE hardware
8 //! that are not already covered by the HAL. Most of these commands either
9 //! adjust RF settings or facilitate RF testing operations.
10 //!
11 //! @addtogroup ble_cooper Cooper - BLE Functions
12 //! @ingroup utils
13 //! @{
14 //
15 //*****************************************************************************
16
17 //*****************************************************************************
18 //
19 // Copyright (c) 2023, Ambiq Micro, Inc.
20 // All rights reserved.
21 //
22 // Redistribution and use in source and binary forms, with or without
23 // modification, are permitted provided that the following conditions are met:
24 //
25 // 1. Redistributions of source code must retain the above copyright notice,
26 // this list of conditions and the following disclaimer.
27 //
28 // 2. Redistributions in binary form must reproduce the above copyright
29 // notice, this list of conditions and the following disclaimer in the
30 // documentation and/or other materials provided with the distribution.
31 //
32 // 3. Neither the name of the copyright holder nor the names of its
33 // contributors may be used to endorse or promote products derived from this
34 // software without specific prior written permission.
35 //
36 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
37 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
40 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
44 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 // POSSIBILITY OF SUCH DAMAGE.
47 //
48 // This is part of revision release_sdk_4_4_0-3c5977e664 of the AmbiqSuite Development Package.
49 //
50 //*****************************************************************************
51
52 #include <stdint.h>
53 #include <stdbool.h>
54 #include <string.h>
55 #include "am_util_delay.h"
56 #include "am_mcu_apollo.h"
57 #include "am_devices_cooper.h"
58
59 #define COOPER_INFO0_BASE (0x60000000)
60 #define COOPER_INFO0_TRIM_VER_OFFSET (0x60)
61 #define COOPER_INOF0_RETV_TRIM_VER_OFFSET (0X64)
62
63 //*****************************************************************************
64 //
65 // Statics
66 //
67 //*****************************************************************************
68
69 //*****************************************************************************
70 //
71 // Globals
72 //
73 //*****************************************************************************
74 //*****************************************************************************
75 //
76 // Read a register value from the BLE core.
77 //
78 //*****************************************************************************
79 uint32_t
am_util_ble_plf_reg_read(void * pHandle,uint32_t ui32Address,uint32_t * pui32Value)80 am_util_ble_plf_reg_read(void* pHandle, uint32_t ui32Address, uint32_t* pui32Value)
81 {
82 //
83 // Fill the buffer with the specific command we want to write, and send it.
84 //
85 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_REG_RD_CMD_LENGTH)] =
86 HCI_VSC_CMD(HCI_VSC_REG_RD,
87 UINT32_TO_BYTE0(ui32Address), UINT32_TO_BYTE1(ui32Address), UINT32_TO_BYTE2(ui32Address), UINT32_TO_BYTE3(ui32Address));
88 //
89 // Make a buffer big enough to hold the register write command, and a
90 // second one big enough to hold the response.
91 //
92 uint32_t ui32ErrorStatus = AM_DEVICES_COOPER_STATUS_SUCCESS;
93 uint32_t ui32BytesNum = 0;
94 am_devices_cooper_buffer(32) sResponse = {0};
95 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum);
96 *pui32Value = (((sResponse.words[2] & 0xFF000000) >> 24) |
97 ((sResponse.words[3] & 0x00FFFFFF) << 8));
98 return ui32ErrorStatus;
99 }
100
101 //*****************************************************************************
102 //
103 // Write a register value to the BLE core.
104 //
105 //*****************************************************************************
106 uint32_t
am_util_ble_plf_reg_write(void * pHandle,uint32_t ui32Address,uint32_t ui32Value)107 am_util_ble_plf_reg_write(void* pHandle, uint32_t ui32Address, uint32_t ui32Value)
108 {
109 //
110 // Fill the buffer with the specific command we want to write, and send it.
111 //
112 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_REG_WR_CMD_LENGTH)] =
113 HCI_VSC_CMD(HCI_VSC_REG_WR,
114 UINT32_TO_BYTE0(ui32Address), UINT32_TO_BYTE1(ui32Address), UINT32_TO_BYTE2(ui32Address), UINT32_TO_BYTE3(ui32Address),
115 UINT32_TO_BYTE0(ui32Value), UINT32_TO_BYTE1(ui32Value), UINT32_TO_BYTE2(ui32Value), UINT32_TO_BYTE3(ui32Value));
116 //
117 // Make a buffer big enough to hold the register write command, and a
118 // second one big enough to hold the response.
119 //
120 uint32_t ui32BytesNum = 0;
121 am_devices_cooper_buffer(16) sResponse = {0};
122 return (am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum));
123 }
124
125 //*****************************************************************************
126 //
127 // Manually enable/disable transmitter to output carrier signal
128 // set ui8TxChannel as 0 to 0x27 for each transmit channel, 0xFF back to normal modulate mode
129 //
130 //*****************************************************************************
131 uint32_t
am_util_ble_hci_reset(void * pHandle)132 am_util_ble_hci_reset(void *pHandle)
133 {
134 //
135 // Fill the buffer with the specific command we want to write, and send it.
136 //
137 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(0)] = HCI_RAW_CMD(0x0C03, 0);
138
139 //
140 // Make a buffer big enough to hold the register write command, and a
141 // second one big enough to hold the response.
142 //
143 uint32_t ui32BytesNum = 0;
144 am_devices_cooper_buffer(16) sResponse = {0};
145
146 return (am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum));
147 }
148
149 //*****************************************************************************
150 //
151 // Set BLE sleep enable/disable for the BLE core.
152 // enable = 'true' set sleep enable, enable = 'false' set sleep disable
153 //
154 //*****************************************************************************
155 uint32_t
am_util_ble_sleep_set(void * pHandle,bool enable)156 am_util_ble_sleep_set(void* pHandle, bool enable)
157 {
158 uint32_t ui32BytesNum = 0;
159 am_devices_cooper_buffer(16) sResponse = {0};
160 uint32_t ui32ErrorStatus = AM_DEVICES_COOPER_STATUS_SUCCESS;
161
162 if ( enable )
163 {
164 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_UPDATE_NVDS_CFG_CMD_LENGTH)] = HCI_VSC_CMD(HCI_VSC_UPDATE_NVDS_CFG, NVDS_PARAMETER_MAGIC_NUMBER, NVDS_PARAMETER_SLEEP_ENABLE);
165 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum);
166 }
167 else
168 {
169 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_UPDATE_NVDS_CFG_CMD_LENGTH)] = HCI_VSC_CMD(HCI_VSC_UPDATE_NVDS_CFG, NVDS_PARAMETER_MAGIC_NUMBER, NVDS_PARAMETER_SLEEP_DISABLE);
170 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum);
171 }
172
173 if ( ui32ErrorStatus != AM_DEVICES_COOPER_STATUS_SUCCESS )
174 {
175 return ui32ErrorStatus;
176 }
177
178 am_util_delay_ms(1);
179 am_util_ble_hci_reset(pHandle);
180
181 return ui32ErrorStatus;
182 }
183
184 //*****************************************************************************
185 //
186 // set the tx power of BLE
187 // values.
188 // ui32TxPower: enum txPowerLevel_t defined in hci_drv_cooper.h
189 //
190 //*****************************************************************************
191 uint32_t
am_util_ble_tx_power_set(void * pHandle,uint8_t ui32TxPower)192 am_util_ble_tx_power_set(void* pHandle, uint8_t ui32TxPower)
193 {
194 //
195 // Fill the buffer with the specific command we want to write, and send it.
196 //
197 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_SET_TX_POWER_LEVEL_CFG_CMD_LENGTH)] = HCI_VSC_CMD(HCI_VSC_SET_TX_POWER_LEVEL_CFG, ui32TxPower);
198
199 //
200 // Make a buffer big enough to hold the register write command, and a
201 // second one big enough to hold the response.
202 //
203 uint32_t ui32BytesNum = 0;
204 am_devices_cooper_buffer(16) sResponse = {0};
205 return (am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum));
206 }
207
208 //*****************************************************************************
209 //
210 // Write NVDS parameters to the BLE core.
211 //
212 //*****************************************************************************
213 uint32_t
am_util_ble_nvds_set(void * pHandle,uint8_t * pui8NVDS,uint8_t ui8Length)214 am_util_ble_nvds_set(void *pHandle, uint8_t* pui8NVDS, uint8_t ui8Length)
215 {
216 uint32_t ui32BytesNum = 0;
217 am_devices_cooper_buffer(16) sResponse = {0};
218 uint32_t ui32ErrorStatus = AM_DEVICES_COOPER_STATUS_SUCCESS;
219 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_UPDATE_NVDS_CFG_CMD_LENGTH)] = HCI_VSC_CMD(HCI_VSC_UPDATE_NVDS_CFG, NVDS_PARAMETER_MAGIC_NUMBER);
220 memcpy(&write_cmd[HCI_VSC_UPDATE_NVDS_CFG_CMD_OFFSET], pui8NVDS, ui8Length);
221
222 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum);
223 if ( ui32ErrorStatus != AM_DEVICES_COOPER_STATUS_SUCCESS )
224 {
225 return ui32ErrorStatus;
226 }
227
228 am_util_delay_ms(1);
229 am_util_ble_hci_reset(pHandle);
230
231 return ui32ErrorStatus;
232 }
233
234 //*****************************************************************************
235 //
236 // Write update signature to the BLE core.
237 //
238 //*****************************************************************************
239 #if defined(AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4L) || defined(AM_PART_APOLLO4P)
240 uint32_t
am_util_ble_update_sign_set(void * pHandle,uint32_t ui32Sign)241 am_util_ble_update_sign_set(void *pHandle, uint32_t ui32Sign)
242 {
243 am_devices_cooper_t *pBle = (am_devices_cooper_t *)pHandle;
244 uint32_t ui32ErrorStatus = AM_DEVICES_COOPER_STATUS_SUCCESS;
245 uint32_t ui32BytesNum = 0;
246 am_devices_cooper_buffer(16) sResponse = {0};
247
248 //
249 // Fill the buffer with the specific command we want to write, and send it.
250 //
251 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(HCI_VSC_UPDATE_FW_CFG_CMD_LENGTH)] = \
252 HCI_VSC_CMD(HCI_VSC_UPDATE_FW_CFG,
253 UINT32_TO_BYTE0(ui32Sign),
254 UINT32_TO_BYTE1(ui32Sign),
255 UINT32_TO_BYTE2(ui32Sign),
256 UINT32_TO_BYTE3(ui32Sign));
257
258 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum);
259 if ( ui32ErrorStatus != AM_DEVICES_COOPER_STATUS_SUCCESS )
260 {
261 return ui32ErrorStatus;
262 }
263
264 //
265 // There's different signature writing process for versions lower than 1.14, need to wait flash writing finishes
266 //
267 if ( pBle->ui32Firmver < 0x10E )
268 {
269 am_util_delay_ms(2000);
270 }
271 return (sResponse.bytes[ui32BytesNum - 1]);
272 }
273 #endif
274
275 //*****************************************************************************
276 //
277 // to do directly output modulation signal. change channel ranges from 0 to 0x27, pattern from 0 to 7.
278 //
279 //*****************************************************************************
280 uint32_t
am_util_ble_trasmitter_test_ex(void * pHandle,uint8_t channel,uint8_t pattern)281 am_util_ble_trasmitter_test_ex(void *pHandle, uint8_t channel, uint8_t pattern)
282 {
283 //
284 // Fill the buffer with the specific command we want to write, and send it.
285 //
286 uint8_t write_cmd[HCI_VSC_CMD_LENGTH(3)] = HCI_RAW_CMD(0x201E, 3, channel, 0x25, pattern);
287
288 //
289 // Make a buffer big enough to hold the register write command, and a
290 // second one big enough to hold the response.
291 //
292 uint32_t ui32BytesNum = 0;
293 am_devices_cooper_buffer(16) sResponse = {0};
294
295 return (am_devices_cooper_command_write(pHandle, (uint32_t*)write_cmd, sizeof(write_cmd), sResponse.words, &ui32BytesNum));
296 }
297
298 //*****************************************************************************
299 //
300 // to do directly receiver test. change channel ranges from 0 to 0x27, return received packets in 100ms.
301 //
302 //*****************************************************************************
303 uint32_t
am_util_ble_receiver_test_ex(void * pHandle,uint8_t channel,uint32_t * recvpackets)304 am_util_ble_receiver_test_ex(void *pHandle, uint8_t channel, uint32_t *recvpackets)
305 {
306 //
307 // Fill the buffer with the specific command we want to write, and send it.
308 //
309 uint32_t ui32ErrorStatus = AM_DEVICES_COOPER_STATUS_SUCCESS;
310 uint8_t start_cmd[HCI_VSC_CMD_LENGTH(1)] = HCI_RAW_CMD(0x201D, 1, channel);
311 uint8_t end_cmd[HCI_VSC_CMD_LENGTH(0)] = HCI_RAW_CMD(0x201F, 0);
312
313 //
314 // Make a buffer big enough to hold the register write command, and a
315 // second one big enough to hold the response.
316 //
317 uint32_t ui32BytesNum = 0;
318 am_devices_cooper_buffer(16) sResponse = {0};
319
320 //
321 // issue the HCI command with to init for the channel 1
322 //
323 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)start_cmd, sizeof(start_cmd), sResponse.words, &ui32BytesNum);
324 if ( ui32ErrorStatus != AM_DEVICES_COOPER_STATUS_SUCCESS )
325 {
326 return ui32ErrorStatus;
327 }
328
329 am_util_delay_ms(100);
330
331 //
332 // issue the HCI command with to stop test for the channel 1
333 //
334 ui32ErrorStatus = am_devices_cooper_command_write(pHandle, (uint32_t*)end_cmd, sizeof(end_cmd), sResponse.words, &ui32BytesNum);
335 *recvpackets = (sResponse.bytes[8] << 8) + sResponse.bytes[7];
336
337 return ui32ErrorStatus;
338 }
339
340 //*****************************************************************************
341 //
342 // Dump info0 of BLE controller for debug use.
343 //
344 //*****************************************************************************
345 uint32_t
am_util_ble_info0_dump(void * pHandle)346 am_util_ble_info0_dump(void *pHandle)
347 {
348 uint32_t i = 0;
349 uint32_t start_addr = 0x60000000;
350
351 for ( i = 0; i < 66; i++ )
352 {
353 uint32_t reg_value;
354 am_util_ble_plf_reg_read(pHandle, start_addr, ®_value);
355 am_util_stdio_printf("0x%04x = 0x%08x\r\n", start_addr, reg_value);
356 start_addr += 4;
357 }
358
359 return AM_DEVICES_COOPER_STATUS_SUCCESS;
360 }
361
362 //*****************************************************************************
363 //
364 // get cooper TRIM version.
365 //
366 // return Status code.
367 //
368 // pui32TrimVer: The uint32_t that will receive the trim version number.
369 //
370 //*****************************************************************************
371 uint32_t
am_util_ble_trim_version_get(void * pHandle,uint32_t * pui32TrimVer,uint32_t * pui32RetvTrimVer)372 am_util_ble_trim_version_get(void* pHandle, uint32_t *pui32TrimVer, uint32_t *pui32RetvTrimVer)
373 {
374 uint32_t ui32ErrorStatus, reg_value;
375 uint32_t start_addr = COOPER_INFO0_BASE + COOPER_INFO0_TRIM_VER_OFFSET;
376
377 ui32ErrorStatus = am_util_ble_plf_reg_read(pHandle, start_addr, ®_value);
378 am_util_stdio_printf("Cooper trim version: 0x%04x = 0x%08x\r\n", start_addr, reg_value);
379
380 if ( ui32ErrorStatus == AM_DEVICES_COOPER_STATUS_SUCCESS )
381 {
382 if ( pui32TrimVer )
383 {
384 *pui32TrimVer = reg_value;
385 }
386 }
387 else
388 {
389 return ui32ErrorStatus;
390 }
391
392 start_addr = COOPER_INFO0_BASE + COOPER_INOF0_RETV_TRIM_VER_OFFSET;
393 ui32ErrorStatus = am_util_ble_plf_reg_read(pHandle, start_addr, ®_value);
394 am_util_stdio_printf("Cooper retention voltage trim version: 0x%04x = 0x%08x\r\n", start_addr, reg_value);
395
396 if ( ui32ErrorStatus == AM_DEVICES_COOPER_STATUS_SUCCESS )
397 {
398 if ( pui32RetvTrimVer )
399 {
400 *pui32RetvTrimVer = reg_value;
401 }
402 }
403
404 return ui32ErrorStatus;
405 }
406
407 //*****************************************************************************
408 //
409 // API to disable the BLE controller's firmware rollback version (Enabled in default)
410 // Should be called as the very last step during manufacturing, after it done,
411 // the BLE controller will reset.
412 //
413 //*****************************************************************************
414 uint32_t
am_util_ble_disable_rollback(void * pHandle,void * pDevConfig)415 am_util_ble_disable_rollback(void* pHandle, void* pDevConfig)
416 {
417 const unsigned char info_1_enc_patch_disable_ver_rollBack_bin[] =
418 {
419 0x80, 0x00, 0x30, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x94, 0xfd, 0x5c, 0x6c,
420 0x3b, 0x83, 0x36, 0x18, 0x73, 0x08, 0xa0, 0x95, 0xf5, 0x0b, 0xb4, 0xcd,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
423 0x35, 0x4c, 0x47, 0x0f, 0x16, 0xa3, 0x4d, 0xb7, 0x71, 0xf7, 0x6c, 0x8d,
424 0x92, 0x63, 0x20, 0xac, 0xf5, 0x3e, 0xfd, 0xd6, 0xea, 0x3f, 0xcc, 0x4d,
425 0x8a, 0xf6, 0x4e, 0xd6, 0x7f, 0x01, 0x74, 0xce, 0x0d, 0x95, 0x9a, 0x35,
426 0xcc, 0xf2, 0x41, 0x3c, 0x42, 0x46, 0x78, 0x2d, 0x61, 0xaa, 0x87, 0xcb,
427 0x1e, 0x5e, 0x22, 0x54, 0x5a, 0xcd, 0xee, 0x49, 0xab, 0xf6, 0xb9, 0x9e,
428 0x16, 0xb4, 0x09, 0x08, 0x93, 0xe6, 0x41, 0x05, 0x41, 0x7a, 0xd4, 0x1d,
429 0xf1, 0x9c, 0x72, 0xb2, 0x57, 0xfc, 0x25, 0xb7
430 };
431
432 am_devices_cooper_sbl_update_data_t sBLEInfo1Image =
433 {
434 (uint8_t*)& info_1_enc_patch_disable_ver_rollBack_bin,
435 sizeof(info_1_enc_patch_disable_ver_rollBack_bin),
436 AM_DEVICES_COOPER_SBL_UPDATE_IMAGE_TYPE_INFO_1,
437 0
438 };
439
440 uint32_t ui32ErrorStatus;
441
442 //
443 // Update info1 image information to SBL
444 //
445 am_devices_cooper_get_info1_patch(&sBLEInfo1Image);
446
447 //
448 // For info 1 patching
449 // write HCI command to trigger Cooper to reboot for SBL to do download.
450 //
451 ui32ErrorStatus = am_util_ble_update_sign_set(pHandle, COOPER_INFO1_UPDATE_SIGN);
452 if ( ui32ErrorStatus != AM_DEVICES_COOPER_STATUS_SUCCESS )
453 {
454 am_util_stdio_printf("Write signature to BLE Controller failed\n");
455 }
456 else
457 {
458 //
459 // reset Cooper to get SBL to update info1
460 //
461 am_util_stdio_printf("Reset Cooper for info1 updating\r\n");
462 ui32ErrorStatus = am_devices_cooper_reset_with_sbl_check(pHandle, (am_devices_cooper_config_t*)pDevConfig);
463 }
464
465 return ui32ErrorStatus;
466 }
467
468 //*****************************************************************************
469 //
470 // End Doxygen group.
471 //! @}
472 //
473 //*****************************************************************************
474
475