This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Exiting QSPI DPM mode when opening QSPI (memory has been put into DPM previously before QSPI was closed)

Using the nrfx_qspi driver, it is possible to put a QSPI memory into DPM to save power, this all works fine and well. We want to keep the memory in DPM when uninitiating the QSPI hardware on the nRF52840 which seems to work OK until we attempt to re-open the QSPI driver later and it causes a module reset because the nrfx_qspi_init function returns a timeout. Even toggling the CS line from high to low and back to high before opening doesn't seem to resolve the issue. The memory being used is a Macronix MX25R6435F (same as used on the nRF52840 development board). Is there a way to prevent this from failing?

  • Yes, I fitted the wires after seeing this issue. Change the code in the sample app in examples\peripheral\qspi\main.c (using SDK v15.2.0) to this:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include "nrf_drv_qspi.h"
    #include "nrf_delay.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "sdk_config.h"
    
    #define QSPI_STD_CMD_WRSR   0x01
    #define QSPI_STD_CMD_RSTEN  0x66
    #define QSPI_STD_CMD_RST    0x99
    
    #define QSPI_TEST_DATA_SIZE 256
    
    #define WAIT_FOR_PERIPH() do { \
            while (!m_finished) {} \
            m_finished = false;    \
        } while (0)
    
    static volatile bool m_finished = false;
    static uint8_t m_buffer_tx[QSPI_TEST_DATA_SIZE];
    static uint8_t m_buffer_rx[QSPI_TEST_DATA_SIZE];
    
    static void qspi_handler(nrf_drv_qspi_evt_t event, void * p_context)
    {
        UNUSED_PARAMETER(event);
        UNUSED_PARAMETER(p_context);
        m_finished = true;
    }
    
    static void configure_memory()
    {
        uint8_t temporary = 0x40;
        uint32_t err_code;
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = QSPI_STD_CMD_RSTEN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };
    
        // Send reset enable
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        // Send reset command
        cinstr_cfg.opcode = QSPI_STD_CMD_RST;
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        // Switch to qspi mode
        cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
        cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, &temporary, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    int main(void)
    {
        uint32_t i;
        uint32_t err_code;
    
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO(""
                     "QSPI write and read example using 24bit addressing mode");
    
        srand(0);
        for (i = 0; i < QSPI_TEST_DATA_SIZE; ++i)
        {
            m_buffer_tx[i] = (uint8_t)rand();
        }
    
        nrf_drv_qspi_config_t config = NRF_DRV_QSPI_DEFAULT_CONFIG;
    
        config.phy_if.dpmen = false;
        config.prot_if.dpmconfig = true;
        err_code = nrf_drv_qspi_init(&config, qspi_handler, NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_INFO("QSPI example started.");
    
        configure_memory();
    
        m_finished = false;
    
        err_code = nrf_drv_qspi_read(m_buffer_rx, QSPI_TEST_DATA_SIZE, 0);
        WAIT_FOR_PERIPH();
        NRF_LOG_INFO("Data read");
    
        uint32_t nConfigOld = NRF_QSPI->IFCONFIG1;
        uint32_t nConfig = NRF_QSPI->IFCONFIG1;
        nConfig |= 1U << QSPI_IFCONFIG1_DPMEN_Pos;
        NRF_QSPI->IFCONFIG1 = nConfig;
    
        nrf_drv_qspi_uninit();
        NRF_LOG_INFO("QSPI closed in DPM.");
        nrf_delay_ms(500);
    
        NRF_QSPI->IFCONFIG1 = nConfigOld;
    
        err_code = nrf_drv_qspi_init(&config, qspi_handler, NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_INFO("QSPI opened again.");
    
        configure_memory();
    
        err_code = nrf_drv_qspi_read(m_buffer_rx, QSPI_TEST_DATA_SIZE, 0);
        WAIT_FOR_PERIPH();
        NRF_LOG_INFO("Data read 2");
    
        for (;;)
        {
        }
    }
    
    /** @} */
    

    This is the output I get:

    <info> app: QSPI write and read example using 24bit addressing mode

    <info> app: QSPI example started.

    <info> app: Data read

    <info> app: QSPI closed in DPM.

    <error> app: Fatal error

    <warning> app: System reset

  • I think I've found the problem, the timing requirements for DPM were not set. 

    Add 'NRF_QSPI->DPMDUR = (0x3 << 16) | 0x3;' before the first nrf_drv_qspi_init.

    This sets the time it takes for the external memory to enter/exit DPM. From its spec i gather that

    tDP + tDPDD = 35µs and tRDP = 30µs. Closest viable value of DPMDUR.ENTER and .EXIT will be 0x3. See chapter 10-24 Deep Power-down and Table 17. AC Charatceristics, see also DPMDUR

    Strangely there's no HAL API for setting this register. I'll ask the devs what's up with that. 

Related