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, &reg_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, &reg_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, &reg_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