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"
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);
116113typedef void (* flash_flush_cache_f )(void );
117114typedef 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