This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 8770fbea0f8 rp23xx: Add external QSPI PSRAM (APS6404) support for the 
Pimoroni Pico Plus 2.
8770fbea0f8 is described below

commit 8770fbea0f801d1ee0f5ee44b82339239fdc8cfb
Author: Marco Casaroli <[email protected]>
AuthorDate: Sat Jul 25 01:33:27 2026 +0200

    rp23xx: Add external QSPI PSRAM (APS6404) support for the Pimoroni Pico 
Plus 2.
    
    Adds a driver for the external QSPI PSRAM hanging off QMI chip select 1,
    such as the 8 MiB APS6404 fitted on the Pimoroni Pico Plus 2.
    
    rp23xx_psramconfig() assigns the CS1 pin, reads the device ID over the QMI
    direct interface to confirm an APS6404-family part (KGD 0x5D), resets it
    into quad mode and programs the QMI M1 timing/read/write formats so the
    region at 0x11000000 becomes directly addressable and writable.  Because
    driving the QMI in direct mode stalls execute-in-place from the flash, the
    detection and (re)configuration code runs from RAM (.time_critical) with
    interrupts disabled; the command bytes are selected with immediates rather
    than a .rodata table for the same reason.  The register values follow the
    Raspberry Pi Pico SDK setup_psram().
    
    The detection routine only keeps the QMI DIRECT_CSR synchronization that is
    strictly necessary.  Each candidate busy-wait was removed one at a time and
    re-verified on hardware: the post-enable "cooldown" waits, the READ_ID
    TXEMPTY wait (redundant with the BUSY wait after it), and a fixed nop delay
    proved unnecessary and are omitted.  The three BUSY waits that remain -- 
after
    the quad-mode nudge, after each READ_ID byte, and after each 
reset/quad-enable
    command -- are required and carry a comment explaining that the frame must
    finish shifting before the chip select is deasserted, and what breaks
    otherwise (garbage ID reads, or the shared QMI bus wedging).
    
    rp23xx_psram_restore() re-applies the M1 configuration and is meant to be
    called from the flash write path, which goes through the bootrom and
    reconfigures the shared QMI for chip select 0.
    
    rp23xx_heaps.c is wired into the build and now uses the detected size, so
    the PSRAM is exposed to the memory manager (added to the main heap, used as
    a separate heap, or as the user heap) and is skipped cleanly when no PSRAM
    is present.
    
    Enabled by default on the pimoroni-pico-2-plus:nsh configuration, where it
    adds the 8 MiB to the main heap.  A documentation page for the Pimoroni Pico
    Plus 2 is added alongside the other rp23xx boards, covering its serial
    console, LED, the external PSRAM and how it is exposed to the heap, the
    supported capabilities and the available configurations, with a photo of the
    board.
    
    Tested on Pimoroni Pico Plus 2 hardware: detection reports the 8 MiB
    APS6404, the region is read/write across its whole range (ramtest word,
    half-word and byte passes, marching/pattern/address-in-address), and the
    heap reports the extra 8 MiB.
    
    Assisted-by: Claude Code:claude-opus-4-8
    Signed-off-by: Marco Casaroli <[email protected]>
---
 .../rp23xx/boards/pimoroni-pico-2-plus/index.rst   | 172 ++++++++++
 .../boards/pimoroni-pico-2-plus/pico-plus-2.jpg    | Bin 0 -> 328014 bytes
 arch/arm/src/rp23xx/CMakeLists.txt                 |   4 +
 arch/arm/src/rp23xx/Kconfig                        |  45 +++
 arch/arm/src/rp23xx/Make.defs                      |   5 +
 arch/arm/src/rp23xx/hardware/rp23xx_qmi.h          |   4 +
 arch/arm/src/rp23xx/rp23xx_heaps.c                 |  32 +-
 arch/arm/src/rp23xx/rp23xx_psram.c                 | 346 +++++++++++++++++++++
 arch/arm/src/rp23xx/rp23xx_psram.h                 |  89 ++++++
 .../pimoroni-pico-2-plus/configs/nsh/defconfig     |   2 +
 10 files changed, 691 insertions(+), 8 deletions(-)

diff --git 
a/Documentation/platforms/arm/rp23xx/boards/pimoroni-pico-2-plus/index.rst 
b/Documentation/platforms/arm/rp23xx/boards/pimoroni-pico-2-plus/index.rst
new file mode 100644
index 00000000000..486c4f2c844
--- /dev/null
+++ b/Documentation/platforms/arm/rp23xx/boards/pimoroni-pico-2-plus/index.rst
@@ -0,0 +1,172 @@
+===============================
+Pimoroni Pico Plus 2
+===============================
+
+.. tags:: chip:rp2350
+
+The `Pimoroni Pico Plus 2 
<https://shop.pimoroni.com/products/pimoroni-pico-plus-2>`_
+is an RP2350B based board in the Raspberry Pi Pico form factor, with a
+USB-C connector, a large 16 MiB of flash and 8 MiB of external QSPI PSRAM.
+
+.. figure:: pico-plus-2.jpg
+   :align: center
+
+Features
+========
+
+* RP2350B microcontroller chip (QFN-80 variant)
+* Dual-core Arm Cortex-M33 processor running up to 150 MHz
+* 520 KiB of internal SRAM
+* 16 MiB of on-board QSPI flash memory
+* 8 MiB of on-board QSPI PSRAM (APS6404) on chip select 1
+* USB-C connector, USB 1.1 host and device support
+* Qwiic / STEMMA QT (I2C) connector
+* SP/CE debug and expansion connectors
+* Reset and BOOTSEL buttons
+* 48 multi-function GPIO pins broken out on castellated and through-hole pads
+* Drag & drop programming using mass storage over USB
+* 12 Programmable IO (PIO) state machines for custom peripheral support
+
+Serial Console
+==============
+
+By default a serial console appears on GPIO0 (TX) and GPIO1 (RX).  This
+console runs at 115200-8N1.
+
+The board can be configured to use the USB connection as the serial console.
+See the ``usbnsh`` configuration.
+
+Buttons and LEDs
+================
+
+The user LED is controlled by GPIO25 and is configured as autoled by default.
+
+A BOOTSEL button, which if held down when power is first applied to the board,
+will cause the RP2350 to boot into programming mode and appear as a storage
+device to the computer connected via USB.  Saving a ``.uf2`` file to this
+device will replace the flash contents.  A separate RESET button is also
+provided.
+
+PSRAM
+=====
+
+The distinguishing feature of the Pimoroni Pico Plus 2 is the 8 MiB of
+external QSPI PSRAM (an APS6404 part) hanging off the QMI on chip select 1
+(GPIO47).  NuttX detects the PSRAM at boot, switches it to quad mode and
+maps it read/write at ``0x11000000``.
+
+PSRAM support is enabled by default in the ``nsh`` configuration
+(``CONFIG_RP23XX_PSRAM``), which adds the 8 MiB to the main heap.  The
+chip select pin is configured with ``CONFIG_RP23XX_PSRAM_CS1_GPIO`` (47 on
+this board) and the way the PSRAM is exposed to the memory manager (single
+heap, separate heap, or user heap) is selectable.  See the
+:doc:`RP23xx PSRAM section <../../index>` for the details of each mode.
+
+.. note::
+
+   The PSRAM is considerably slower than the internal SRAM.  The QMI is
+   shared with the flash: writing to the flash goes through the bootrom,
+   which reconfigures the QMI for chip select 0 and disturbs the PSRAM
+   configuration on chip select 1.  ``rp23xx_psram_restore()`` re-applies it
+   and is intended to be called from the flash write path after each erase or
+   program.
+
+Supported Capabilities
+======================
+
+NuttX supports the following Pico Plus 2 capabilities:
+
+* UART (console port)
+
+  * GPIO 0 (UART0 TX) and GPIO 1 (UART0 RX) are used for the console.
+
+* External QSPI PSRAM (8 MiB)
+* I2C
+* SPI (master only)
+* DMAC
+* PWM
+* ADC
+* Watchdog
+* USB device
+
+  * MSC, CDC/ACM serial and composite devices are supported.
+  * CDC/ACM serial device can be used for the console.
+
+* PIO (RP2350 Programmable I/O)
+* Flash ROM Boot
+* SRAM Boot
+
+  * If the Pico SDK is available, a ``nuttx.uf2`` file which can be used in
+    BOOTSEL mode will be created.
+
+* Persistent flash filesystem in unused flash ROM
+
+Installation
+============
+
+1. Download and install the toolchain and, optionally, the Raspberry Pi
+   Pico SDK (needed to generate the ``nuttx.uf2`` image).
+
+.. code-block:: console
+
+  $ git clone -b 2.2.0 --recurse-submodules 
https://github.com/raspberrypi/pico-sdk.git
+  $ export PICO_SDK_PATH=<absolute_path_to_pico-sdk_directory>
+
+2. Configure and build NuttX
+
+.. code-block:: console
+
+  $ git clone https://github.com/apache/nuttx.git nuttx
+  $ git clone https://github.com/apache/nuttx-apps.git apps
+  $ cd nuttx
+  $ make distclean
+  $ ./tools/configure.sh pimoroni-pico-2-plus:nsh
+  $ make V=1
+
+3. Connect the board to the USB port while pressing BOOTSEL.  The board will
+   be detected as a USB Mass Storage Device.  Then copy ``nuttx.uf2`` into
+   the device (the same way as standard Pico SDK applications).
+
+4. To access the console, connect GPIO 0 and 1 to a device such as a
+   USB-serial converter, or use the ``usbnsh`` configuration for a console
+   over USB CDC/ACM.
+
+Configurations
+==============
+
+nsh
+---
+
+Basic NuttShell configuration (console enabled on UART0, at 115200 bps) with
+the external PSRAM added to the main heap.
+
+usbnsh
+------
+
+Basic NuttShell configuration (console enabled via USB CDC/ACM).
+
+smp
+---
+
+Basic NuttShell configuration (console enabled on UART0, at 115200 bps) with
+both Arm cores enabled.
+
+audiopack
+---------
+
+Configuration for the Pimoroni Pico Audio Pack.
+
+composite
+---------
+
+USB composite device configuration.
+
+usbmsc
+------
+
+USB mass storage device configuration.
+
+userled
+-------
+
+Configuration demonstrating user-controlled LED access.
diff --git 
a/Documentation/platforms/arm/rp23xx/boards/pimoroni-pico-2-plus/pico-plus-2.jpg
 
b/Documentation/platforms/arm/rp23xx/boards/pimoroni-pico-2-plus/pico-plus-2.jpg
new file mode 100644
index 00000000000..d12129fd105
Binary files /dev/null and 
b/Documentation/platforms/arm/rp23xx/boards/pimoroni-pico-2-plus/pico-plus-2.jpg
 differ
diff --git a/arch/arm/src/rp23xx/CMakeLists.txt 
b/arch/arm/src/rp23xx/CMakeLists.txt
index 6036cb04ebf..814a1007859 100644
--- a/arch/arm/src/rp23xx/CMakeLists.txt
+++ b/arch/arm/src/rp23xx/CMakeLists.txt
@@ -105,4 +105,8 @@ if(CONFIG_RP23XX_FLASH_MTD)
   list(APPEND SRCS rp23xx_flash_mtd.c)
 endif()
 
+if(CONFIG_RP23XX_PSRAM)
+  list(APPEND SRCS rp23xx_psram.c rp23xx_heaps.c)
+endif()
+
 target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig
index cb71ef9f529..0713e4f8721 100644
--- a/arch/arm/src/rp23xx/Kconfig
+++ b/arch/arm/src/rp23xx/Kconfig
@@ -9,6 +9,51 @@ config RP23XX_RP2350B
        bool "Use RP2350B variant (QFN-80)"
        default n
 
+config RP23XX_PSRAM
+       bool "External QSPI PSRAM"
+       default n
+       ---help---
+               Enable support for external QSPI PSRAM connected to QMI chip
+               select 1, such as the 8 MiB APS6404 fitted on the Pimoroni Pico
+               Plus 2.  The chip is detected and switched to quad mode at boot,
+               after which it is directly addressable and writable at
+               0x11000000.  Configure the chip-select pin under the Board
+               Selection menu (RP23XX_PSRAM_CS1_GPIO).
+
+if RP23XX_PSRAM
+
+choice
+       prompt "PSRAM heap usage"
+       default RP23XX_PSRAM_HEAP_SINGLE
+       ---help---
+               How the detected PSRAM is exposed to the memory manager.
+
+config RP23XX_PSRAM_HEAP_SINGLE
+       bool "Add PSRAM to the main heap"
+       depends on !MM_KERNEL_HEAP
+       ---help---
+               Add the PSRAM region to the main heap.  Requires
+               CONFIG_MM_REGIONS greater than 1 so the extra region can be
+               registered.
+
+config RP23XX_PSRAM_HEAP_USER
+       bool "PSRAM as the user heap"
+       depends on MM_KERNEL_HEAP
+       ---help---
+               Use the PSRAM as the (user) heap and the internal SRAM as the
+               kernel heap.
+
+config RP23XX_PSRAM_HEAP_SEPARATE
+       bool "Separate PSRAM heap"
+       select ARCH_HAVE_EXTRA_HEAPS
+       ---help---
+               Expose the PSRAM as a separate named heap ("psram") rather than
+               adding it to the main heap.
+
+endchoice
+
+endif # RP23XX_PSRAM
+
 config RP23XX_DMAC
        bool "DMAC support"
        default y
diff --git a/arch/arm/src/rp23xx/Make.defs b/arch/arm/src/rp23xx/Make.defs
index 8f59e6aac6d..040f23ff868 100644
--- a/arch/arm/src/rp23xx/Make.defs
+++ b/arch/arm/src/rp23xx/Make.defs
@@ -108,3 +108,8 @@ endif
 ifeq ($(CONFIG_RP23XX_FLASH_MTD),y)
 CHIP_CSRCS += rp23xx_flash_mtd.c
 endif
+
+ifeq ($(CONFIG_RP23XX_PSRAM),y)
+CHIP_CSRCS += rp23xx_psram.c
+CHIP_CSRCS += rp23xx_heaps.c
+endif
diff --git a/arch/arm/src/rp23xx/hardware/rp23xx_qmi.h 
b/arch/arm/src/rp23xx/hardware/rp23xx_qmi.h
index 0030d723a13..2be63cccbae 100644
--- a/arch/arm/src/rp23xx/hardware/rp23xx_qmi.h
+++ b/arch/arm/src/rp23xx/hardware/rp23xx_qmi.h
@@ -33,6 +33,10 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* The QMI registers share the XIP-QMI peripheral base. */
+
+#define RP23XX_QMI_BASE                 RP23XX_XIP_QMI_BASE
+
 /* Register offsets *********************************************************/
 
 #define RP23XX_QMI_DIRECT_CSR_OFFSET    0x00000000
diff --git a/arch/arm/src/rp23xx/rp23xx_heaps.c 
b/arch/arm/src/rp23xx/rp23xx_heaps.c
index 725aeac6ad2..a2b57df4f10 100644
--- a/arch/arm/src/rp23xx/rp23xx_heaps.c
+++ b/arch/arm/src/rp23xx/rp23xx_heaps.c
@@ -30,15 +30,21 @@
 #include <nuttx/mm/mm.h>
 
 #include "arm_internal.h"
+#include "rp23xx_psram.h"
 
 #if defined(CONFIG_RP23XX_PSRAM)
 
 /****************************************************************************
- * Private Data
+ * Pre-processor Definitions
  ****************************************************************************/
 
-static void * const psram_start = (void *)0x11000000ul;
-static const size_t psram_size = 8 * 1024 * 1024;
+/* The PSRAM base address is fixed; the size is whatever rp23xx_psramconfig()
+ * detected at boot (0 if no PSRAM is fitted).  psramconfig() runs from
+ * rp23xx_boardinitialize(), before any of the heap hooks below, so the
+ * detected size is always available here.
+ */
+
+#define PSRAM_START ((void *)RP23XX_PSRAM_BASE)
 
 /****************************************************************************
  * Public Functions
@@ -56,9 +62,14 @@ static struct mm_heap_s *g_psramheap;
 #if CONFIG_MM_REGIONS > 1
 void arm_addregion(void)
 {
-  /* Add the PSRAM region to main heap */
+  size_t psram_size = rp23xx_psram_size();
+
+  /* Add the detected PSRAM region to the main heap */
 
-  kumm_addregion(psram_start, psram_size);
+  if (psram_size > 0)
+    {
+      kumm_addregion(PSRAM_START, psram_size);
+    }
 }
 #endif
 
@@ -85,8 +96,8 @@ void up_allocate_kheap(void **heap_start, size_t *heap_size)
 
 void up_allocate_heap(void **heap_start, size_t *heap_size)
 {
-  *heap_start = psram_start;
-  *heap_size = psram_size;
+  *heap_start = PSRAM_START;
+  *heap_size  = rp23xx_psram_size();
 }
 
 #elif defined (CONFIG_RP23XX_PSRAM_HEAP_SEPARATE)
@@ -97,7 +108,12 @@ void up_allocate_heap(void **heap_start, size_t *heap_size)
 
 void up_extraheaps_init(void)
 {
-    g_psramheap = mm_initialize("psram", psram_start, psram_size);
+  size_t psram_size = rp23xx_psram_size();
+
+  if (psram_size > 0)
+    {
+      g_psramheap = mm_initialize("psram", PSRAM_START, psram_size);
+    }
 }
 #endif
 
diff --git a/arch/arm/src/rp23xx/rp23xx_psram.c 
b/arch/arm/src/rp23xx/rp23xx_psram.c
new file mode 100644
index 00000000000..1aa155c1fd3
--- /dev/null
+++ b/arch/arm/src/rp23xx/rp23xx_psram.c
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * arch/arm/src/rp23xx/rp23xx_psram.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ *
+ * Driver for the external QSPI PSRAM (an APS6404-family part) fitted on
+ * boards such as the Pimoroni Pico Plus 2.  It hangs off QMI chip select 1
+ * and, once configured, is directly addressable and writable at
+ * RP23XX_PSRAM_BASE (0x11000000).
+ *
+ * The QMI is the same serial interface that serves execute-in-place from the
+ * flash on chip select 0.  Reprogramming it uses direct (non-XIP) mode,
+ * which stalls all XIP fetches, so the detection and configuration code must
+ * run from RAM with interrupts disabled -- hence the .time_critical section,
+ * which the board linker scripts copy to RAM.  The sequence follows the
+ * Raspberry Pi Pico SDK's setup_psram().
+ *
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <nuttx/irq.h>
+
+#include "arm_internal.h"
+#include "rp23xx_gpio.h"
+#include "rp23xx_psram.h"
+#include "hardware/rp23xx_qmi.h"
+#include "hardware/rp23xx_xip.h"
+#include "hardware/rp23xx_memorymap.h"
+
+#ifdef CONFIG_RP23XX_PSRAM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Detection and (re)configuration touch the QMI in direct mode, which stalls
+ * execute-in-place from the flash.  The code that does so must therefore run
+ * from RAM: the board linker scripts copy the .time_critical section there.
+ */
+
+#define RP23XX_PSRAM_RAMFUNC \
+  __attribute__((section(".time_critical.rp23xx_psram"), noinline))
+
+/* GPIO function select for the XIP chip-select-1 line (PSRAM). */
+
+#define RP23XX_GPIO_FUNC_XIP_CS1  9
+
+/* PSRAM interface width for direct-mode transfers (quad). */
+
+#define QMI_DIRECT_TX_IWIDTH_Q    (2 << RP23XX_QMI_DIRECT_TX_IWIDTH_SHIFT)
+
+/* Final QMI M1 register values for the APS6404 in quad mode.  Computed from
+ * the field layout in the RP2350 datasheet to match the Pico SDK:
+ *
+ * TIMING: COOLDOWN=1, PAGEBREAK=1024, SELECT_HOLD=3, MAX_SELECT=16,
+ *         MIN_DESELECT=7, RXDELAY=1, CLKDIV=2.
+ * RFMT:   quad prefix/addr/suffix/dummy/data, 8-bit prefix, 24 dummy bits.
+ * RCMD:   read prefix 0xeb.
+ * WFMT:   quad prefix/addr/suffix/dummy/data, 8-bit prefix, no dummy.
+ * WCMD:   write prefix 0x38.
+ */
+
+#define RP23XX_PSRAM_M1_TIMING    0x61a07102
+#define RP23XX_PSRAM_M1_RFMT      0x000612aa
+#define RP23XX_PSRAM_M1_RCMD      0x000000eb
+#define RP23XX_PSRAM_M1_WFMT      0x000012aa
+#define RP23XX_PSRAM_M1_WCMD      0x00000038
+
+/* APS6404 SPI opcodes used during detection and mode switching. */
+
+#define PSRAM_CMD_READ_ID         0x9f
+#define PSRAM_CMD_RSTEN           0x66
+#define PSRAM_CMD_RST             0x99
+#define PSRAM_CMD_QUAD_ENABLE     0x35
+
+#define PSRAM_KGD_PASS            0x5d
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static size_t g_psram_size;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp23xx_psram_apply_format
+ *
+ * Description:
+ *   Program the QMI M1 timing/format/command registers and mark the region
+ *   writable.  Shared by the initial configuration and the post-flash
+ *   restore.  Runs from RAM.
+ *
+ ****************************************************************************/
+
+static void RP23XX_PSRAM_RAMFUNC
+rp23xx_psram_apply_format(void)
+{
+  putreg32(RP23XX_PSRAM_M1_TIMING, RP23XX_QMI_M1_TIMING);
+  putreg32(RP23XX_PSRAM_M1_RFMT,   RP23XX_QMI_M1_RFMT);
+  putreg32(RP23XX_PSRAM_M1_RCMD,   RP23XX_QMI_M1_RCMD);
+  putreg32(RP23XX_PSRAM_M1_WFMT,   RP23XX_QMI_M1_WFMT);
+  putreg32(RP23XX_PSRAM_M1_WCMD,   RP23XX_QMI_M1_WCMD);
+
+  /* Allow the memory-mapped window to accept writes. */
+
+  putreg32(getreg32(RP23XX_XIP_CTRL_BASE) | RP23XX_XIP_CTRL_WRITABLE_M1,
+           RP23XX_XIP_CTRL_BASE);
+}
+
+/****************************************************************************
+ * Name: rp23xx_psram_detect
+ *
+ * Description:
+ *   Read the PSRAM device ID over the QMI direct interface and return the
+ *   detected size in bytes, or 0 if the part does not respond correctly.
+ *   Runs from RAM with the caller holding interrupts disabled.
+ *
+ ****************************************************************************/
+
+static size_t RP23XX_PSRAM_RAMFUNC
+rp23xx_psram_detect(void)
+{
+  uint8_t kgd = 0;
+  uint8_t eid = 0;
+  size_t size;
+  size_t i;
+
+  /* Enable direct mode at a conservative clock divisor.  No BUSY wait is
+   * needed for the previous XIP transfer's cooldown: it drains before the
+   * first chip-select assertion below (verified on hardware).
+   */
+
+  putreg32((30 << RP23XX_QMI_DIRECT_CSR_CLKDIV_SHIFT) |
+           RP23XX_QMI_DIRECT_CSR_EN, RP23XX_QMI_DIRECT_CSR);
+
+  /* Nudge the part out of any quad-continuation mode left by a prior init by
+   * clocking one quad byte with CS asserted.
+   */
+
+  putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) |
+           RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N, RP23XX_QMI_DIRECT_CSR);
+  putreg32(RP23XX_QMI_DIRECT_TX_OE | QMI_DIRECT_TX_IWIDTH_Q | 0xf5,
+           RP23XX_QMI_DIRECT_TX);
+
+  /* This BUSY wait is required: the frame must finish shifting before the
+   * chip select is deasserted, else the nudge is truncated, the part stays
+   * in quad-continuation mode, and the ID read below returns garbage so
+   * detection fails (verified on hardware).
+   */
+
+  while ((getreg32(RP23XX_QMI_DIRECT_CSR) & RP23XX_QMI_DIRECT_CSR_BUSY) != 0)
+    {
+    }
+
+  (void)getreg32(RP23XX_QMI_DIRECT_RX);
+  putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) &
+           ~RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N, RP23XX_QMI_DIRECT_CSR);
+
+  /* Read the identification: command 0x9f followed by six bytes.  The
+   * Known-Good-Die marker is byte 5 and the size code is byte 6.
+   */
+
+  putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) |
+           RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N, RP23XX_QMI_DIRECT_CSR);
+  for (i = 0; i < 7; i++)
+    {
+      putreg32(i == 0 ? PSRAM_CMD_READ_ID : 0xff, RP23XX_QMI_DIRECT_TX);
+
+      /* This BUSY wait is required: the byte must finish shifting in before
+       * DIRECT_RX is read, else the KGD/EID bytes are sampled early as
+       * garbage and detection fails (verified on hardware).  A TXEMPTY wait
+       * ahead of it is redundant -- BUSY already covers the whole transfer.
+       */
+
+      while ((getreg32(RP23XX_QMI_DIRECT_CSR) &
+              RP23XX_QMI_DIRECT_CSR_BUSY) != 0)
+        {
+        }
+
+      if (i == 5)
+        {
+          kgd = (uint8_t)getreg32(RP23XX_QMI_DIRECT_RX);
+        }
+      else if (i == 6)
+        {
+          eid = (uint8_t)getreg32(RP23XX_QMI_DIRECT_RX);
+        }
+      else
+        {
+          (void)getreg32(RP23XX_QMI_DIRECT_RX);
+        }
+    }
+
+  putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) &
+           ~(RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N | RP23XX_QMI_DIRECT_CSR_EN),
+           RP23XX_QMI_DIRECT_CSR);
+
+  if (kgd != PSRAM_KGD_PASS)
+    {
+      return 0;
+    }
+
+  /* Reset the device and place it in quad mode: RSTEN, RST, quad-enable. */
+
+  putreg32((30 << RP23XX_QMI_DIRECT_CSR_CLKDIV_SHIFT) |
+           RP23XX_QMI_DIRECT_CSR_EN, RP23XX_QMI_DIRECT_CSR);
+
+  for (i = 0; i < 3; i++)
+    {
+      /* The command byte is selected with a ternary rather than a lookup
+       * table on purpose: a "static const" array is emitted to .rodata in
+       * flash, and reading it here -- while the QMI is in direct mode and
+       * flash is inaccessible -- would fault.  An immediate keeps it in RAM.
+       */
+
+      uint8_t cmd = (i == 0) ? PSRAM_CMD_RSTEN :
+                    (i == 1) ? PSRAM_CMD_RST   :
+                               PSRAM_CMD_QUAD_ENABLE;
+
+      putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) |
+               RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N, RP23XX_QMI_DIRECT_CSR);
+      putreg32(cmd, RP23XX_QMI_DIRECT_TX);
+
+      /* This BUSY wait is required: the command must finish shifting before
+       * the chip select is deasserted.  Cutting it short truncates the
+       * RSTEN/RST/quad-enable commands, so the part is not reset into quad
+       * mode and later accesses fail or wedge the shared QMI bus (verified
+       * on hardware).  No extra settling delay is needed after CS deassert:
+       * the QMI's own minimum-deselect timing covers it.
+       */
+
+      while ((getreg32(RP23XX_QMI_DIRECT_CSR) &
+              RP23XX_QMI_DIRECT_CSR_BUSY) != 0)
+        {
+        }
+
+      putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) &
+               ~RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N, RP23XX_QMI_DIRECT_CSR);
+
+      (void)getreg32(RP23XX_QMI_DIRECT_RX);
+    }
+
+  putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) &
+           ~(RP23XX_QMI_DIRECT_CSR_ASSERT_CS1N | RP23XX_QMI_DIRECT_CSR_EN),
+           RP23XX_QMI_DIRECT_CSR);
+
+  /* Decode the size from the EID byte (APS6404 family). */
+
+  size = 1024 * 1024;
+  if (eid == 0x26 || (eid >> 5) == 2)
+    {
+      size *= 8;
+    }
+  else if ((eid >> 5) == 0)
+    {
+      size *= 2;
+    }
+  else if ((eid >> 5) == 1)
+    {
+      size *= 4;
+    }
+
+  return size;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp23xx_psramconfig
+ ****************************************************************************/
+
+size_t RP23XX_PSRAM_RAMFUNC
+rp23xx_psramconfig(void)
+{
+  irqstate_t flags;
+  size_t size;
+
+  rp23xx_gpio_set_function(CONFIG_RP23XX_PSRAM_CS1_GPIO,
+                           RP23XX_GPIO_FUNC_XIP_CS1);
+
+  flags = up_irq_save();
+
+  size = rp23xx_psram_detect();
+  if (size != 0)
+    {
+      rp23xx_psram_apply_format();
+    }
+
+  up_irq_restore(flags);
+
+  g_psram_size = size;
+  return size;
+}
+
+/****************************************************************************
+ * Name: rp23xx_psram_size
+ ****************************************************************************/
+
+size_t rp23xx_psram_size(void)
+{
+  return g_psram_size;
+}
+
+/****************************************************************************
+ * Name: rp23xx_psram_restore
+ ****************************************************************************/
+
+void RP23XX_PSRAM_RAMFUNC
+rp23xx_psram_restore(void)
+{
+  if (g_psram_size != 0)
+    {
+      rp23xx_psram_apply_format();
+    }
+}
+
+#endif /* CONFIG_RP23XX_PSRAM */
diff --git a/arch/arm/src/rp23xx/rp23xx_psram.h 
b/arch/arm/src/rp23xx/rp23xx_psram.h
new file mode 100644
index 00000000000..58d5ce907d2
--- /dev/null
+++ b/arch/arm/src/rp23xx/rp23xx_psram.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+ * arch/arm/src/rp23xx/rp23xx_psram.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_RP23XX_RP23XX_PSRAM_H
+#define __ARCH_ARM_SRC_RP23XX_RP23XX_PSRAM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stddef.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The QSPI PSRAM is mapped through QMI chip select 1 at this address */
+
+#define RP23XX_PSRAM_BASE  0x11000000
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp23xx_psramconfig
+ *
+ * Description:
+ *   Detect and initialise the external QSPI PSRAM on QMI chip select 1.
+ *   Assigns the CS1 function to CONFIG_RP23XX_PSRAM_CS1_GPIO, reads the
+ *   device ID to confirm an APS6404-family part is present, switches it to
+ *   quad mode and programs the QMI M1 read/write formats so the region at
+ *   RP23XX_PSRAM_BASE becomes directly addressable (and writable).
+ *
+ * Returned Value:
+ *   The size of the detected PSRAM in bytes, or 0 if no PSRAM was found.
+ *
+ ****************************************************************************/
+
+size_t rp23xx_psramconfig(void);
+
+/****************************************************************************
+ * Name: rp23xx_psram_size
+ *
+ * Description:
+ *   Return the size in bytes detected by the last rp23xx_psramconfig(), or 0
+ *   if PSRAM is absent or has not been configured.
+ *
+ ****************************************************************************/
+
+size_t rp23xx_psram_size(void);
+
+/****************************************************************************
+ * Name: rp23xx_psram_restore
+ *
+ * Description:
+ *   Re-apply the QMI M1 (PSRAM) format and timing registers.  Programming
+ *   the flash goes through the bootrom, which reconfigures the shared QMI
+ *   interface for chip select 0; this restores the CS1 configuration
+ *   afterwards so the memory-mapped PSRAM keeps working across a flash
+ *   erase or program.  Runs from RAM and does nothing when no PSRAM was
+ *   detected.
+ *
+ ****************************************************************************/
+
+void rp23xx_psram_restore(void);
+
+#endif /* __ARCH_ARM_SRC_RP23XX_RP23XX_PSRAM_H */
diff --git a/boards/arm/rp23xx/pimoroni-pico-2-plus/configs/nsh/defconfig 
b/boards/arm/rp23xx/pimoroni-pico-2-plus/configs/nsh/defconfig
index 36d7894cfb3..61b813f3dda 100644
--- a/boards/arm/rp23xx/pimoroni-pico-2-plus/configs/nsh/defconfig
+++ b/boards/arm/rp23xx/pimoroni-pico-2-plus/configs/nsh/defconfig
@@ -28,12 +28,14 @@ CONFIG_EXAMPLES_HELLO=y
 CONFIG_FS_PROCFS=y
 CONFIG_FS_PROCFS_REGISTER=y
 CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_MM_REGIONS=2
 CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
 CONFIG_NSH_BUILTIN_APPS=y
 CONFIG_NSH_READLINE=y
 CONFIG_RAM_SIZE=532480
 CONFIG_RAM_START=0x20000000
 CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP23XX_PSRAM=y
 CONFIG_RR_INTERVAL=200
 CONFIG_SCHED_WAITPID=y
 CONFIG_START_DAY=9

Reply via email to