Skip to content

Commit 8c91052

Browse files
sumpfralleacassis
authored andcommitted
boards/arm/rp2040: allow flash write operation on rp2040 in SMP mode
Previously the function "up_cpu_pause" was used for preventing all other CPUs from executing code from flash. The above function was removed in d8cb775. Now flash operations work on rp2040 in SMP mode by blocking all but the current CPU for the duration of the critical function (write or erase). Closes: apache#16203 Signed-off-by: Lars Kruse <devel@sumpfralle.de>
1 parent 35d1aaa commit 8c91052

1 file changed

Lines changed: 218 additions & 16 deletions

File tree

arch/arm/src/rp2040/rp2040_flash_mtd.c

Lines changed: 218 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <nuttx/arch.h>
5050
#include <nuttx/irq.h>
5151
#include <nuttx/mutex.h>
52+
#include <nuttx/spinlock.h>
5253

5354
#include "rp2040_flash_mtd.h"
5455
#include "rp2040_rom.h"
@@ -94,10 +95,6 @@
9495
#define FLASH_SECTOR_COUNT (CONFIG_RP2040_FLASH_LENGTH - FLASH_START_OFFSET)\
9596
/ FLASH_SECTOR_SIZE
9697

97-
#ifdef CONFIG_SMP
98-
# define OTHER_CPU (this_cpu() == 0 ? 1 : 0)
99-
#endif
100-
10198
/****************************************************************************
10299
* Private Types
103100
****************************************************************************/
@@ -116,6 +113,28 @@ typedef void (*flash_range_program_f)(uint32_t, const uint8_t *, size_t);
116113
typedef void (*flash_flush_cache_f)(void);
117114
typedef void (*flash_enable_xip_f)(void);
118115

116+
#ifdef CONFIG_SMP
117+
/* The locks are used for coordinating "pause" and "resume" with the handler
118+
* used for blocking a CPU.
119+
*/
120+
121+
struct smp_isolation_data_s
122+
{
123+
volatile spinlock_t cpu_wait;
124+
volatile spinlock_t cpu_pause;
125+
volatile spinlock_t cpu_resume;
126+
struct smp_call_data_s call_data;
127+
};
128+
129+
struct smp_isolation_s
130+
{
131+
/* The id of the single CPU which is exempted from pausing. */
132+
133+
int isolated_cpuid;
134+
struct smp_isolation_data_s cpu_data[CONFIG_SMP_NCPUS];
135+
};
136+
#endif
137+
119138
/****************************************************************************
120139
* Private Function Prototypes
121140
****************************************************************************/
@@ -191,6 +210,179 @@ void *boot_2_copy = NULL;
191210
* Private Functions
192211
****************************************************************************/
193212

213+
#ifdef CONFIG_SMP
214+
/****************************************************************************
215+
* Name: pause_cpu_handler
216+
*
217+
* Description:
218+
* Busy-wait for "leave_smp_isolation" to release our lock.
219+
* "enter_smp_isolation" triggers the execution of this function on all
220+
* CPUs to be blocked temporarily.
221+
*
222+
****************************************************************************/
223+
224+
static int pause_cpu_handler(void *const context)
225+
{
226+
struct smp_isolation_data_s *const cpu_data =
227+
(struct smp_isolation_data_s *)context;
228+
229+
/* Reserve the resumption lock. To be released after resuming. */
230+
231+
spin_lock(&cpu_data->cpu_resume);
232+
233+
/* Notify "enter_smp_isolation", that we are pausing now. */
234+
235+
spin_unlock(&cpu_data->cpu_pause);
236+
237+
/* Wait for "leave_smp_isolation". */
238+
239+
spin_lock(&cpu_data->cpu_wait);
240+
spin_unlock(&cpu_data->cpu_wait);
241+
242+
/* Notify "leave_smp_isolation", that we have resumed. */
243+
244+
spin_unlock(&cpu_data->cpu_resume);
245+
246+
return OK;
247+
}
248+
249+
/****************************************************************************
250+
* Name: init_smp_isolation
251+
*
252+
* Description:
253+
* Initialize an SMP isolation environment.
254+
*
255+
* Input Parameters:
256+
* data - SMP isolation environment.
257+
*
258+
* Returned Value:
259+
* None.
260+
*
261+
****************************************************************************/
262+
263+
static void init_smp_isolation(struct smp_isolation_s *const data)
264+
{
265+
struct smp_isolation_data_s *cpu_data;
266+
267+
for (int cpuid = 0; cpuid < CONFIG_SMP_NCPUS; cpuid++)
268+
{
269+
cpu_data = &data->cpu_data[cpuid];
270+
spin_lock_init(&cpu_data->cpu_wait);
271+
spin_lock_init(&cpu_data->cpu_pause);
272+
spin_lock_init(&cpu_data->cpu_resume);
273+
}
274+
}
275+
276+
/****************************************************************************
277+
* Name: enter_smp_isolation
278+
*
279+
* Description:
280+
* Force all CPUs except for the currently active one to pause execution
281+
* via a busy loop.
282+
* Scheduling of the current CPU is disabled until the isolation is lifted
283+
* again by calling "leave_smp_isolation".
284+
*
285+
* Input Parameters:
286+
* data - SMP isolation environment.
287+
*
288+
* Returned Value:
289+
* None.
290+
*
291+
****************************************************************************/
292+
293+
static void enter_smp_isolation(struct smp_isolation_s *const data)
294+
{
295+
struct smp_isolation_data_s *cpu_data;
296+
297+
/* TODO: remove "sched_lock" after "nxsched_smp_call_async" does not try to
298+
* run the handler directly anymore, if the current CPU is part of the
299+
* given cpuset.
300+
*/
301+
302+
sched_lock();
303+
304+
data->isolated_cpuid = this_cpu();
305+
306+
/* Pause all other CPUs. */
307+
308+
for (int other_cpuid = 0; other_cpuid < CONFIG_SMP_NCPUS; other_cpuid++)
309+
{
310+
cpu_data = &data->cpu_data[other_cpuid];
311+
312+
if (other_cpuid != data->isolated_cpuid)
313+
{
314+
spin_lock(&cpu_data->cpu_wait);
315+
spin_lock(&cpu_data->cpu_pause);
316+
spin_unlock(&cpu_data->cpu_resume);
317+
}
318+
319+
nxsched_smp_call_init(&cpu_data->call_data, pause_cpu_handler,
320+
cpu_data);
321+
nxsched_smp_call_single_async(other_cpuid, &cpu_data->call_data);
322+
}
323+
324+
/* Wait until all other CPUs are paused. */
325+
326+
for (int other_cpuid = 0; other_cpuid < CONFIG_SMP_NCPUS; other_cpuid++)
327+
{
328+
cpu_data = &data->cpu_data[other_cpuid];
329+
330+
if (other_cpuid != data->isolated_cpuid)
331+
{
332+
spin_lock(&cpu_data->cpu_pause);
333+
spin_unlock(&cpu_data->cpu_pause);
334+
}
335+
}
336+
}
337+
338+
/****************************************************************************
339+
* Name: leave_smp_isolation
340+
*
341+
* Description:
342+
* Release all blocked CPUs.
343+
* Call this function as early as possible after "enter_smp_isolation".
344+
*
345+
* Input Parameters:
346+
* data - SMP isolation environment.
347+
*
348+
* Returned Value:
349+
* None.
350+
*
351+
****************************************************************************/
352+
353+
static void leave_smp_isolation(struct smp_isolation_s *const data)
354+
{
355+
struct smp_isolation_data_s *cpu_data;
356+
357+
/* Tell all other CPUs to resume. */
358+
359+
for (int other_cpuid = 0; other_cpuid < CONFIG_SMP_NCPUS; other_cpuid++)
360+
{
361+
cpu_data = &data->cpu_data[other_cpuid];
362+
363+
if (other_cpuid != data->isolated_cpuid)
364+
{
365+
spin_unlock(&cpu_data->cpu_wait);
366+
}
367+
}
368+
369+
/* Wait until all other CPUs have resumed. */
370+
371+
for (int other_cpuid = 0; other_cpuid < CONFIG_SMP_NCPUS; other_cpuid++)
372+
{
373+
cpu_data = &data->cpu_data[other_cpuid];
374+
375+
if (other_cpuid != data->isolated_cpuid)
376+
{
377+
spin_lock(&cpu_data->cpu_resume);
378+
spin_unlock(&cpu_data->cpu_resume);
379+
}
380+
}
381+
382+
sched_unlock();
383+
}
384+
#endif
385+
194386
/****************************************************************************
195387
* Name: do_erase
196388
****************************************************************************/
@@ -261,27 +453,32 @@ static int rp2040_flash_erase(struct mtd_dev_s *dev,
261453
nblocks,
262454
FLASH_BLOCK_SIZE * nblocks);
263455

456+
#ifdef CONFIG_SMP
457+
struct smp_isolation_s smp_isolation;
458+
init_smp_isolation(&smp_isolation);
459+
#endif
460+
264461
ret = nxmutex_lock(&rp_dev->lock);
265462
if (ret < 0)
266463
{
267464
return ret;
268465
}
269466

270-
flags = enter_critical_section();
271-
272467
#ifdef CONFIG_SMP
273-
up_cpu_pause(OTHER_CPU);
468+
enter_smp_isolation(&smp_isolation);
274469
#endif
275470

471+
flags = enter_critical_section();
472+
276473
do_erase(FLASH_BLOCK_SIZE * startblock + FLASH_START_OFFSET,
277474
FLASH_BLOCK_SIZE * nblocks);
278475

476+
leave_critical_section(flags);
477+
279478
#ifdef CONFIG_SMP
280-
up_cpu_resume(OTHER_CPU);
479+
leave_smp_isolation(&smp_isolation);
281480
#endif
282481

283-
leave_critical_section(flags);
284-
285482
ret = nblocks;
286483

287484
nxmutex_unlock(&rp_dev->lock);
@@ -344,28 +541,33 @@ static ssize_t rp2040_flash_block_write(struct mtd_dev_s *dev,
344541
irqstate_t flags;
345542
int ret;
346543

544+
#ifdef CONFIG_SMP
545+
struct smp_isolation_s smp_isolation;
546+
init_smp_isolation(&smp_isolation);
547+
#endif
548+
347549
ret = nxmutex_lock(&rp_dev->lock);
348550
if (ret < 0)
349551
{
350552
return ret;
351553
}
352554

353-
flags = enter_critical_section();
354-
355555
#ifdef CONFIG_SMP
356-
up_cpu_pause(OTHER_CPU);
556+
enter_smp_isolation(&smp_isolation);
357557
#endif
358558

559+
flags = enter_critical_section();
560+
359561
do_write(FLASH_SECTOR_SIZE * startblock + FLASH_START_OFFSET,
360562
buffer,
361563
FLASH_SECTOR_SIZE * nblocks);
362564

565+
leave_critical_section(flags);
566+
363567
#ifdef CONFIG_SMP
364-
up_cpu_resume(OTHER_CPU);
568+
leave_smp_isolation(&smp_isolation);
365569
#endif
366570

367-
leave_critical_section(flags);
368-
369571
finfo("FLASH: write sector: %8u (0x%08x) count:%5u\n",
370572
(unsigned)(startblock),
371573
(unsigned)(FLASH_SECTOR_SIZE * startblock + FLASH_START_OFFSET),

0 commit comments

Comments
 (0)