Welcome to mirror list, hosted at ThFree Co, Russian Federation.

flash_driver.c « Src « Core « BLE_Ota « BLE « Applications « NUCLEO-WB15CC « Projects - github.com/Flipper-Zero/STM32CubeWB.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: dcd415b18e55dddc05d358ac39fc335045937cb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/**
 ******************************************************************************
 * File Name          : flash_driver.c
 * Description        : Dual core Flash driver
 *
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under Ultimate Liberty license
 * SLA0044, the "License"; You may not use this file except in compliance with
 * the License. You may obtain a copy of the License at:
 *                             www.st.com/SLA0044
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "app_common.h"

#include "flash_driver.h"
#include "shci.h"
#include "utilities_conf.h"

/* Private typedef -----------------------------------------------------------*/
typedef enum
{
  SEM_LOCK_SUCCESSFUL,
  SEM_LOCK_BUSY,
}SemStatus_t;

typedef enum
{
  FLASH_ERASE,
  FLASH_WRITE,
}FlashOperationType_t;

/* Private defines -----------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Global variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
static SingleFlashOperationStatus_t ProcessSingleFlashOperation(FlashOperationType_t FlashOperationType,
                                                                uint32_t SectorNumberOrDestAddress,
                                                                uint64_t Data);
/* Public functions ----------------------------------------------------------*/
uint32_t FD_EraseSectors(uint32_t FirstSector, uint32_t NbrOfSectors)
{
  uint32_t loop_flash;
  uint32_t return_value;
  SingleFlashOperationStatus_t single_flash_operation_status;

  single_flash_operation_status = SINGLE_FLASH_OPERATION_DONE;

  /**
   *  Take the semaphore to take ownership of the Flash IP
   */
  while(LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID));

  HAL_FLASH_Unlock();

  /**
   *  Notify the CPU2 that some flash erase activity may be executed
   *  On reception of this command, the CPU2 enables the BLE timing protection versus flash erase processing
   *  The Erase flash activity will be executed only when the BLE RF is idle for at least 25ms
   *  The CPU2 will prevent all flash activity (write or erase) in all cases when the BL RF Idle is shorter than 25ms.
   */
  SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON);

  for(loop_flash = 0; (loop_flash < NbrOfSectors) && (single_flash_operation_status ==  SINGLE_FLASH_OPERATION_DONE) ; loop_flash++)
  {
    single_flash_operation_status = FD_EraseSingleSector(FirstSector+loop_flash);
  }

  if(single_flash_operation_status != SINGLE_FLASH_OPERATION_DONE)
  {
    return_value = NbrOfSectors - loop_flash + 1;
  }
  else
  {
    /**
     *  Notify the CPU2 there will be no request anymore to erase the flash
     *  On reception of this command, the CPU2 disables the BLE timing protection versus flash erase processing
     */
    SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF);

    HAL_FLASH_Lock();

    /**
     *  Release the ownership of the Flash IP
     */
    LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0);

    return_value = 0;
  }

  return return_value;
}

uint32_t FD_WriteData(uint32_t DestAddress, uint64_t * pSrcBuffer, uint32_t NbrOfData)
{
  uint32_t loop_flash;
  uint32_t return_value;
  SingleFlashOperationStatus_t single_flash_operation_status;

  single_flash_operation_status = SINGLE_FLASH_OPERATION_DONE;

  /**
   *  Take the semaphore to take ownership of the Flash IP
   */
  while(LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID));

  HAL_FLASH_Unlock();

  for(loop_flash = 0; (loop_flash < NbrOfData) && (single_flash_operation_status ==  SINGLE_FLASH_OPERATION_DONE) ; loop_flash++)
  {
    single_flash_operation_status = FD_WriteSingleData(DestAddress+(8*loop_flash), *(pSrcBuffer+loop_flash));
  }

  if(single_flash_operation_status != SINGLE_FLASH_OPERATION_DONE)
  {
    return_value = NbrOfData - loop_flash + 1;
  }
  else
  {
    HAL_FLASH_Lock();

    /**
     *  Release the ownership of the Flash IP
     */
    LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0);

    return_value = 0;
  }

  return return_value;
}

SingleFlashOperationStatus_t FD_EraseSingleSector(uint32_t SectorNumber)
{
  SingleFlashOperationStatus_t return_value;

  /* The last parameter is unused in that case and set to 0 */
  return_value =  ProcessSingleFlashOperation(FLASH_ERASE, SectorNumber, 0);

  return return_value;
}

SingleFlashOperationStatus_t FD_WriteSingleData(uint32_t DestAddress, uint64_t Data)
{
  SingleFlashOperationStatus_t return_value;

  return_value =  ProcessSingleFlashOperation(FLASH_WRITE, DestAddress, Data);

  return return_value;
}

/*************************************************************
 *
 * LOCAL FUNCTIONS
 *
 *************************************************************/
static SingleFlashOperationStatus_t ProcessSingleFlashOperation(FlashOperationType_t FlashOperationType,
                                                                uint32_t SectorNumberOrDestAddress,
                                                                uint64_t Data)
{
  SemStatus_t cpu1_sem_status;
  SemStatus_t cpu2_sem_status;
  WaitedSemStatus_t waited_sem_status;
  SingleFlashOperationStatus_t return_status;

  uint32_t page_error;
  FLASH_EraseInitTypeDef p_erase_init;

  waited_sem_status = WAITED_SEM_FREE;

  p_erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
  p_erase_init.NbPages = 1;
  p_erase_init.Page = SectorNumberOrDestAddress;

  do
  {
    /**
     * When the PESD bit mechanism is used by CPU2 to protect its timing, the PESD bit should be polled here.
     * If the PESD is set, the CPU1 will be stalled when reading literals from an ISR that may occur after
     * the flash processing has been requested but suspended due to the PESD bit.
     *
     * Note: This code is required only when the PESD mechanism is used to protect the CPU2 timing.
     * However, keeping that code make it compatible with the two mechanisms.
     */
    while(LL_FLASH_IsActiveFlag_OperationSuspended());

    UTILS_ENTER_CRITICAL_SECTION();

    /**
     *  Depending on the application implementation, in case a multitasking is possible with an OS,
     *  it should be checked here if another task in the application disallowed flash processing to protect
     *  some latency in critical code execution
     *  When flash processing is ongoing, the CPU cannot access the flash anymore.
     *  Trying to access the flash during that time stalls the CPU.
     *  The only way for CPU1 to disallow flash processing is to take CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID.
     */
    cpu1_sem_status = (SemStatus_t)LL_HSEM_GetStatus(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID);
    if(cpu1_sem_status == SEM_LOCK_SUCCESSFUL)
    {
      /**
       *  Check now if the CPU2 disallows flash processing to protect its timing.
       *  If the semaphore is locked, the CPU2 does not allow flash processing
       *
       *  Note: By default, the CPU2 uses the PESD mechanism to protect its timing,
       *  therefore, it is useless to get/release the semaphore.
       *
       *  However, keeping that code make it compatible with the two mechanisms.
       *  The protection by semaphore is enabled on CPU2 side with the command SHCI_C2_SetFlashActivityControl()
       *
       */
      cpu2_sem_status = (SemStatus_t)LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID);
      if(cpu2_sem_status == SEM_LOCK_SUCCESSFUL)
      {
        /**
         * When CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID is taken, it is allowed to only erase one sector or
         * write one single 64bits data
         * When either several sectors need to be erased or several 64bits data need to be written,
         * the application shall first exit from the critical section and try again.
         */
        if(FlashOperationType == FLASH_ERASE)
        {
          HAL_FLASHEx_Erase(&p_erase_init, &page_error);
        }
        else
        {
          HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, SectorNumberOrDestAddress, Data);
        }
        /**
         *  Release the semaphore to give the opportunity to CPU2 to protect its timing versus the next flash operation
         *  by taking this semaphore.
         *  Note that the CPU2 is polling on this semaphore so CPU1 shall release it as fast as possible.
         *  This is why this code is protected by a critical section.
         */
        LL_HSEM_ReleaseLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, 0);
      }
    }

    UTILS_EXIT_CRITICAL_SECTION();

    if(cpu1_sem_status != SEM_LOCK_SUCCESSFUL)
    {
      /**
       * To avoid looping in ProcessSingleFlashOperation(), FD_WaitForSemAvailable() should implement a mechanism to
       * continue only when CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID is free
       */
      waited_sem_status = FD_WaitForSemAvailable(WAIT_FOR_SEM_BLOCK_FLASH_REQ_BY_CPU1);
    }
    else if(cpu2_sem_status != SEM_LOCK_SUCCESSFUL)
    {
      /**
       * To avoid looping in ProcessSingleFlashOperation(), FD_WaitForSemAvailable() should implement a mechanism to
       * continue only when CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID is free
       */
      waited_sem_status = FD_WaitForSemAvailable(WAIT_FOR_SEM_BLOCK_FLASH_REQ_BY_CPU2);
    }
  }
  while( ((cpu2_sem_status != SEM_LOCK_SUCCESSFUL) || (cpu1_sem_status != SEM_LOCK_SUCCESSFUL))
      && (waited_sem_status != WAITED_SEM_BUSY) );

  /**
   * In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms)
   * However, it could be that for some marginal application, this time is longer.
   * In that case either HAL_FLASHEx_Erase() or HAL_FLASH_Program() will exit with FLASH_TIMEOUT_VALUE value.
   * This is not a failing case and there is no other way than waiting the operation to be completed.
   * If for any reason this test is never passed, this means there is a failure in the system and there is no other
   * way to recover than applying a device reset.
   *
   * Note: This code is required only when the PESD mechanism is used to protect the CPU2 timing.
   * However, keeping that code make it compatible with the two mechanisms.
   */
  while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_CFGBSY));

  if(waited_sem_status != WAITED_SEM_BUSY)
  {
    /**
     * The flash processing has been done. It has not been checked whether it has been successful or not.
     * The only commitment is that it is possible to request a new flash processing
     */
    return_status = SINGLE_FLASH_OPERATION_DONE;
  }
  else
  {
    /**
     * The flash processing has not been executed due to timing protection from either the CPU1 or the CPU2.
     * This status is reported up to the user that should retry after checking that each CPU do not
     * protect its timing anymore.
     */
    return_status = SINGLE_FLASH_OPERATION_NOT_EXECUTED;
  }

  return return_status;
}

/*************************************************************
 *
 * WEAK FUNCTIONS
 *
 *************************************************************/
__WEAK WaitedSemStatus_t FD_WaitForSemAvailable(WaitedSemId_t WaitedSemId)
{
  /**
   * The timing protection is enabled by either CPU1 or CPU2. It should be decided here if the driver shall
   * keep trying to erase/write the flash until successful or if it shall exit ans report to the user that the action
   * has not been executed.
   * WAITED_SEM_BUSY returns to the user
   * WAITED_SEM_FREE keep looping in the driver until the action is executed. This will result in the current tack looping
   * until this is done. In a bare metal implementation, only the code within interrupt handler can be executed. With an OS,
   * only task with higher priority can be processed
   *
   */
  return WAITED_SEM_BUSY;
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/