Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Cannot exit deep power down mode (QSPI flash)

We designed a board based on the nRF52840, and are using nrf5 SDK 17.1.0 with S140 SoftDevice. We are using a QSPI flash, MX25U51245G (https://www.macronix.com/Lists/Datasheet/Attachments/8736/MX25U51245G%2054,%201.8V,%20512Mb,%20v1.2.pdf), we are using the 8-WSON version with no Reset pin

I have followed the QSPI example and the information provided here  Exiting QSPI DPM mode when opening QSPI (memory has been put into DPM previously before QSPI was closed) I can successfully access the QSPI flash, and I can put it in deep power down mode using the code provided, or sending the deep power down command as a QSPI command (CMD_DEEP_PWRDOWN 0xB9).

The problem is that this chip, unlike the one in the DK, does not exit deep power down by holding CS high for the proper time, but requires a command (RDP = 0xAB), see 10-4. Release from Deep Power-down (RDP). When I re-initialize the QSPI peripheral with the flash chip in power down, I get a NRF_ERROR_TIMEOUT because the chip doesn't seem to respond to whatever command the QSPI peripheral is using to wake up the device. nrfx_qspi_init fails when executing  NRFX_WAIT_FOR(nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY), QSPI_DEF_WAIT_ATTEMPTS, QSPI_DEF_WAIT_TIME_US, result);

I'm not sure I understand what nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); is doing, but when my flash chip is in deep power down, that never completes and blocks the QSPI peripheral preventing to send any other command

Is there a way to send the 0xAB command to release my flash chip from deep sleep?

Parents
  • Hello,

    I don't have the same flash chip that you are using, but have you tried to use a logic analyzer and capture a logic trace of when the NRF_QSPI_TASKS_ACTIVATE is called? Does it generate any data on the QSPI pins? Have you tried to increase the QSPI_DEF_WAIT_TIME_US from 10 to a higher number? Just for debugging purposes. 

    Also, is it possible to upload the application that you are using to reproduce this issue? 

    Best regards,

    Edvin

  • Thanks for looking into my issue.

    I'm using this code, a small variation of the code linked in the original question

    #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 CMD_DEEP_PWRDOWN    0xB9
    
    #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;
        NRF_QSPI->DPMDUR = (0x3 << 16) | 0x3;
        
        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;
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_DEEP_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);          // send deep power down instruction
        APP_ERROR_CHECK(err_code);
    
        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);      // NRF_ERROR_TIMEOUT error here
        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 code works for any SPI NOR flash chip that can be released from deep power down using the CS line. For most NOR flash chips, the datasheet says “After CS# goes high, there is a delay of tRDP before the device transitions from Deep Power-down mode back to Stand-by mode.”

    In the newer 512 Mbit chips (all of them, not just mine), the device doesn't exit deep power down only using the CS line, it explicitly requires a 0xAB instruction to release. Problem is, the QSPI peripheral won't let me send anything, can't even complete the init function.

    I haven't connected a logic analyzer yet, because it would require removing the chip and soldering wires. But from everything I have seen, when initializing the QSPI peripheral, it sends 0x05 to the chip to verify that a chip is connected. In my case, the chip ignores the 0x05 because the only instruction it recognizes is 0xAB. So QSPI times out (I changed QSPI_DEF_WAIT_TIME_US  but it makes no difference, given it's not a real timeout issue)

    I need a way to either change the QSPI peripheral initialization to send 0xAB instead of 0x05, or a way to "trick" the QSPI peripheral to initialize even if the flash chip doesn't reply, so that I can send 0xAB either as a custom instruction or a nrfx_qspi_cinstr_xfer()

    Let me try to convey why this is more than just a minor issue for a specific case: all new 256/512Mbit chips cannot exit deep power down only by using CS, as far as I can tell. They all need the 0xAB instruction (it's done to improve security and avoid exiting low power by  mistake). As such, none of those chips can be properly used by the nRF52840 QSPI when extreme low power is needed.

  • Once again, I appreciate everything you are doing. And, funny you should ask, but I came to the exact same conclusion: we were flying blind and it was unfair for me to ask you to help without a logic analyzer trace. So I bit the bullet, desoldered the chip and create a horrible rat's nest of bodge wire.

    My logic analyzer channels are: 0 - clock, 1 - DIO0, 2 - DIO1, 3 - DIO2, 4 - DIO3, 5 - CS

    Here's what happens when nrf_drv_qspi_init() runs. The QSPI peripheral actually sends 0x05 (Read Status Register), the EEPROM replies

    Then we have configure_memory. As you can see, every instruction is preceded by sending once more 0x05. It looks as if every instruction starts with 0x05 again, but I'm not sure I understand the 0x06 (Write Enable) before 0x66 (Reset Enable), 0x99 (Reset) and the change to the configuration register 0x01 0x40

    Using your code, the instructions in line 5-12 look like this, i.e. sends yet another 0x05, followed by 0xB9 (deep power down)

    Incidentally, absolutely the same as this code

        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_DEEP_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);

    as you can see here

    Good, so now we are in deep sleep.

    Then we get to lines 16-21 in  your code snippet, which is supposed to wake up the EEPROM. As you can see, it simply tries to toggle CS as per the "old" EEPROM convention to exit DPM using only CS  (I needed to use 2 screengrabs due to the different zoom levels, look at the timing on top to make sense of them)

    The device is not out of DPM, though.

    I'm seeing inconsistent results between the device I bodge-wired and another, and I need to do a bit more soldering, but wanted to start capturing some traces and share them.

    Also, if I read the spec sheet correctly, this device should work like mine, and exit DPM only when receiving 0xAB. But I haven't tested it https://www.adafruit.com/product/5634

    To be honest, dead-bug soldering eight 35AWG wires directly the bottom of the WSON-8 version of MX25U51245G is not too bad, so I'm not entirely sure you need a breakout board for the tests

    EDIT: please let me know what traces you need. I'm seeing a slightly different behavior between the board with a dead bug soldered device and the unmodified one. I'm tired now, though, and I'm not sure if I'm doing something else wrong.

    But if there were a way to send 0xAB instead of 0x05 during the qspi init call ,that would solve all problems. Otherwise it looks as if the init returns timeout because there's no reply from the EEPROM

  • wow! This is great! 

    robca said:
    To be honest, dead-bug soldering eight 35AWG wires directly the bottom of the WSON-8 version of MX25U51245G is not too bad,

    I agree that it is doable, but I don't have the chip-only either, so to test properly, I need some HW either way. And the breakout board is easier to re-purpose for different tests that may pop up later. I am sorry, but there is a lot to do, because many of our staff has started their summer holidays. I have modified my DK to be able to analyze the QSPI pins, but I have not yet been able to capture the trace. I will set it up, and see if I get time tonight. 

    I will get back to you with more updates. 

    Best regards,

    Edvin

  • I see the same 0x05 as you see:

    Out of curiousity, does it work if you don't uninit the QSPI? But send the 0xAB like you did with the CMD_DEEP_PWRDOWN packet? I don't know the consequences of this yet, but it is worth a shot just to check that everything else is working as intended.

    edit: Or since we are already at this stage, what happens if you comment out the qspi_ready_wait() in nrfx_qspi_init(), and then send the 0xAB packet?

    Best regards,

    Edvin

  • Problem is: I need to save power, so I need to deinitialize the QSPI peripheral when in low power. Also, it will be disabled anyway when the device goes into sd_power_system_off(), and I need to initialize the QSPI to start sending commands.

    I already tried disabling qspi_ready_wait() in nrfx_qspi_init() (or ignoring the error timeout), but the QSPI peripheral remains in error mode, and doesn't send anything else. Is there a way to clear a pending error?

    The most concerning thing for me now is that I noticed an inconsistency between the board with the dead bug soldering and another board. So I used a PCBite setup on the 4 SPI pins of the QSPI interface, for the non-working board (I only have 4 PCBite probes). Then that board started working, too (DPM and RDP). In the end, I verified that if I have a logic analyzer probe on DIO1, the QSPI peripheral never times out (even when sending 0x05 to a sleeping EEPROM and not receiving 0x40 as a response. Just DIO1, all other pins make no difference.

    I think we used the recommended QSPI pins, minus P0.18 which in the past gave us problems with PINRESET. And those same pins work just fine for everything else, only recover from DPM is a problem.

    #define BSP_QSPI_CSN_PIN P0_16
    #define BSP_QSPI_SCK_PIN P0_19
    #define BSP_QSPI_IO0_PIN P0_21
    #define BSP_QSPI_IO1_PIN P0_22
    #define BSP_QSPI_IO2_PIN P0_23
    #define BSP_QSPI_IO3_PIN P1_00

    My logic analyzer (a DSlogic Plus) has never caused problems before, so I'm a a loss to understand how it could make such a huge difference for QSPI initialization. And I tried with an oscilloscope probe, which also is enough to solve the problem. So there's some hair-trigger logic situation happening (on all boards, also checked)

    Of course if we could clear the QSPI error and send 0xAB after the error, it shouldn't matter

    EDIT:

    And I just found a horrible hack that seems to work. By editing the nrfx_qspi_init() and nrfx_qspi_cinstr_xfer() functions to configure the DIO1 pin as input pulldown (instead of input nopull), the init function returns success, and so does sending 0xAB. Basically, forcing DIO1 low seems to prevent the QSPI peripheral from noticing that the QSPI chip is not responding, until it sends 0xAB and the chip responds again.

    I created a clone of init and xfer, where I define the DIO1 pin as input pulldown on entry and redefine as input no pull on exit. The rest of the library is unchanged. I mean, I guess it could work, but I really don't like this Slight smile. It's "too clever by half" and those things tend to come back and bite.

  • I decided to test with a modified nrfx_qspi.c implementation. I cloned nrfx_qspi_init() into nrfx_qspi_init_rdp() and added the code to configure DIO1 with pulldown, init the QSPI peripheral, then within that same function call nrfx_qspi_cinstr_xfer() with the 0xAB CMD_RELEASE_PWRDOWN command, finally redefining DIO1 as the other QSPI pins. 

    Basically QSPI init for EEPROM devices in Deep Power Down Mode that require 0xAB to exit. I confirmed that when the peripheral sends the first 0x05 with the EEPROM in DPM, the status register reads 0x00 (no data), and as soon as the 0xAB is sent, the EEPROM status reads 0x40. And the QSPI doesn't timeout, even if there's nothing on DIO1. Here's the trace of the nrfx_qspi_init_rdp() call followed by nrfx_qspi_mem_busy_check() (which reads 0x40 as expected)

    I'm posting the modified files for reference. Other people might find this ticket when dealing with modern NOR flash chips. Can't promise it will work in all case, it's not fully tested yet

    nrf_drv_qspi.h

    /**
     * Copyright (c) 2016 - 2021, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    
    #include <nrfx.h>
    
    #if NRFX_CHECK(NRFX_QSPI_ENABLED)
    
    #include <nrfx_qspi.h>
    #include <hal/nrf_gpio.h>
    
    /** @brief Command byte used to read status register. */
    #define QSPI_STD_CMD_RDSR 0x05
    #define CMD_RELEASE_PWRDOWN 0xAB
    
    /** @brief Byte used to mask status register and retrieve the write-in-progess bit. */
    #define QSPI_MEM_STATUSREG_WIP_Pos 0x01
    
    /** @brief Default time used in timeout function. */
    #define QSPI_DEF_WAIT_TIME_US 10
    
    /** @brief Default number of tries in timeout function. */
    #define QSPI_DEF_WAIT_ATTEMPTS 100
    
    /**
     * @brief Macro for initializing a QSPI pin.
     *
     * QSPI peripheral expects high drive pin strength.
     */
    #define QSPI_PIN_INIT(_pin) nrf_gpio_cfg((_pin),                        \
                                             NRF_GPIO_PIN_DIR_INPUT,        \
                                             NRF_GPIO_PIN_INPUT_DISCONNECT, \
                                             NRF_GPIO_PIN_NOPULL,           \
                                             NRF_GPIO_PIN_H0H1,             \
                                             NRF_GPIO_PIN_NOSENSE)
    
    #define QSPI_PIN_INITPD(_pin) nrf_gpio_cfg((_pin),                        \
                                             NRF_GPIO_PIN_DIR_INPUT,        \
                                             NRF_GPIO_PIN_INPUT_DISCONNECT, \
                                             NRF_GPIO_PIN_PULLDOWN,           \
                                             NRF_GPIO_PIN_H0H1,             \
                                             NRF_GPIO_PIN_NOSENSE)
    
    /** @brief Control block - driver instance local data. */
    typedef struct
    {
        nrfx_qspi_handler_t handler;   /**< Handler. */
        nrfx_drv_state_t    state;     /**< Driver state. */
        volatile bool       is_busy;   /**< Flag indicating that an operation is currently being performed. */
        void *              p_context; /**< Driver context used in interrupt. */
    } qspi_control_block_t;
    
    static qspi_control_block_t m_cb;
    
    static nrfx_err_t qspi_task_perform(nrf_qspi_task_t task)
    {
        // Wait for peripheral
        if (m_cb.is_busy)
        {
            return NRFX_ERROR_BUSY;
        }
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        if (m_cb.handler)
        {
            m_cb.is_busy = true;
            nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
        }
    
        nrf_qspi_task_trigger(NRF_QSPI, task);
    
        if (m_cb.handler == NULL)
        {
            while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY))
            {};
        }
        return NRFX_SUCCESS;
    }
    
    static bool qspi_pins_configure(nrf_qspi_pins_t const * p_config)
    {
        // Check if the user set meaningful values to struct fields. If not, return false.
        if ((p_config->sck_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
            (p_config->csn_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
            (p_config->io0_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
            (p_config->io1_pin == NRF_QSPI_PIN_NOT_CONNECTED))
        {
            return false;
        }
    
        QSPI_PIN_INIT(p_config->sck_pin);
        QSPI_PIN_INIT(p_config->csn_pin);
        QSPI_PIN_INIT(p_config->io0_pin);
        QSPI_PIN_INIT(p_config->io1_pin);
        if (p_config->io2_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            QSPI_PIN_INIT(p_config->io2_pin);
        }
        if (p_config->io3_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            QSPI_PIN_INIT(p_config->io3_pin);
        }
    
        nrf_qspi_pins_set(NRF_QSPI, p_config);
    
        return true;
    }
    
    static void qspi_pins_deconfigure(void)
    {
        nrf_qspi_pins_t pins;
        nrf_qspi_pins_get(NRF_QSPI, &pins);
    
        nrf_gpio_cfg_default(pins.sck_pin);
        nrf_gpio_cfg_default(pins.csn_pin);
        nrf_gpio_cfg_default(pins.io0_pin);
        nrf_gpio_cfg_default(pins.io1_pin);
        if (pins.io2_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            nrf_gpio_cfg_default(pins.io2_pin);
        }
        if (pins.io3_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            nrf_gpio_cfg_default(pins.io3_pin);
        }
    }
    
    static nrfx_err_t qspi_ready_wait(void)
    {
        bool result;
        NRFX_WAIT_FOR(nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY),
                                           QSPI_DEF_WAIT_ATTEMPTS,
                                           QSPI_DEF_WAIT_TIME_US,
                                           result);
        if (!result)
        {
            return NRFX_ERROR_TIMEOUT;
        }
    
        return NRFX_SUCCESS;
    }
    
    nrfx_err_t nrfx_qspi_init(nrfx_qspi_config_t const * p_config,
                              nrfx_qspi_handler_t        handler,
                              void *                     p_context)
    {
        NRFX_ASSERT(p_config);
        if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
        {
            return NRFX_ERROR_INVALID_STATE;
        }
    
        if (!qspi_pins_configure(&p_config->pins))
        {
            return NRFX_ERROR_INVALID_PARAM;
        }
    
        nrf_qspi_xip_offset_set(NRF_QSPI, p_config->xip_offset);
        nrf_qspi_ifconfig0_set(NRF_QSPI, &p_config->prot_if);
        nrf_qspi_ifconfig1_set(NRF_QSPI, &p_config->phy_if);
    
        m_cb.is_busy = false;
        m_cb.handler = handler;
        m_cb.p_context = p_context;
    
        /* QSPI interrupt is disabled because the device should be enabled in polling mode (wait for activate
           task event ready)*/
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        if (handler)
        {
            NRFX_IRQ_PRIORITY_SET(QSPI_IRQn, p_config->irq_priority);
            NRFX_IRQ_ENABLE(QSPI_IRQn);
        }
    
        m_cb.state = NRFX_DRV_STATE_INITIALIZED;
    
        nrf_qspi_enable(NRF_QSPI);
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
        nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE);
    
        // Waiting for the peripheral to activate
    
        return qspi_ready_wait();
    }
    
    nrfx_err_t nrfx_qspi_init_rdp(nrfx_qspi_config_t const * p_config,
                              nrfx_qspi_handler_t        handler,
                              void *                     p_context)
    {
        NRFX_ASSERT(p_config);
        if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
        {
            return NRFX_ERROR_INVALID_STATE;
        }
    
        if (!qspi_pins_configure(&p_config->pins))
        {
            return NRFX_ERROR_INVALID_PARAM;
        }
    
        nrf_qspi_pins_t const *ppin = &p_config->pins;    // hack to prevent error wwhen NOR Flash chip is in deep poeer down mode;
        QSPI_PIN_INITPD(ppin->io1_pin);                     // DIO1 low is seen by QSPI peripheral as NOR Flash present
    
        nrf_qspi_xip_offset_set(NRF_QSPI, p_config->xip_offset);
        nrf_qspi_ifconfig0_set(NRF_QSPI, &p_config->prot_if);
        nrf_qspi_ifconfig1_set(NRF_QSPI, &p_config->phy_if);
    
        m_cb.is_busy = false;
        m_cb.handler = handler;
        m_cb.p_context = p_context;
    
        /* QSPI interrupt is disabled because the device should be enabled in polling mode (wait for activate
           task event ready)*/
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        if (handler)
        {
            NRFX_IRQ_PRIORITY_SET(QSPI_IRQn, p_config->irq_priority);
            NRFX_IRQ_ENABLE(QSPI_IRQn);
        }
    
        m_cb.state = NRFX_DRV_STATE_INITIALIZED;
    
        nrf_qspi_enable(NRF_QSPI);
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
        nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE);
    
        // Waiting for the peripheral to activate
        nrfx_err_t err_code= qspi_ready_wait();   
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_RELEASE_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        
        QSPI_PIN_INIT(ppin->io1_pin);   // revert DIO1 back to normal
    
        return err_code;
    }
    
    nrfx_err_t nrfx_qspi_cinstr_xfer(nrf_qspi_cinstr_conf_t const * p_config,
                                     void const *                   p_tx_buffer,
                                     void *                         p_rx_buffer)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
    
        if (m_cb.is_busy)
        {
            return NRFX_ERROR_BUSY;
        }
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
        /* In some cases, only opcode should be sent. To prevent execution, set function code is
         * surrounded by an if.
         */
        if (p_tx_buffer)
        {
            nrf_qspi_cinstrdata_set(NRF_QSPI, p_config->length, p_tx_buffer);
        }
    
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        nrf_qspi_cinstr_transfer_start(NRF_QSPI, p_config);
    
        if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
        {
            // This timeout should never occur when WIPWAIT is not active, since in this
            // case the QSPI peripheral should send the command immediately, without any
            // waiting for previous write to complete.
            NRFX_ASSERT(p_config->wipwait);
    
            return NRFX_ERROR_TIMEOUT;
        }
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        if (p_rx_buffer)
        {
            nrf_qspi_cinstrdata_get(NRF_QSPI, p_config->length, p_rx_buffer);
        }
    
        return NRFX_SUCCESS;
    }
    
    nrfx_err_t nrfx_qspi_cinstr_quick_send(uint8_t               opcode,
                                           nrf_qspi_cinstr_len_t length,
                                           void const *          p_tx_buffer)
    {
        nrf_qspi_cinstr_conf_t config = NRFX_QSPI_DEFAULT_CINSTR(opcode, length);
        return nrfx_qspi_cinstr_xfer(&config, p_tx_buffer, NULL);
    }
    
    nrfx_err_t nrfx_qspi_lfm_start(nrf_qspi_cinstr_conf_t const * p_config)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(!(nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI)));
        NRFX_ASSERT(p_config->length == NRF_QSPI_CINSTR_LEN_1B);
    
        if (m_cb.is_busy)
        {
            return NRFX_ERROR_BUSY;
        }
    
        nrf_qspi_cinstr_long_transfer_start(NRF_QSPI, p_config);
    
        if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
        {
            /* In case of error, abort long frame mode */
            nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
            return NRFX_ERROR_TIMEOUT;
        }
    
        m_cb.is_busy = true;
        return NRFX_SUCCESS;
    }
    
    nrfx_err_t nrfx_qspi_lfm_xfer(void const * p_tx_buffer,
                                  void *       p_rx_buffer,
                                  size_t       transfer_length,
                                  bool         finalize)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI));
    
        nrfx_err_t status = NRFX_SUCCESS;
    
        /* Perform transfers in packets of 8 bytes. Last transfer may be shorter. */
        nrf_qspi_cinstr_len_t length = NRF_QSPI_CINSTR_LEN_9B;
        for (uint32_t curr_byte = 0; curr_byte < transfer_length; curr_byte += 8)
        {
            uint32_t remaining_bytes = transfer_length - curr_byte;
            if (remaining_bytes < 8)
            {
                length = (nrf_qspi_cinstr_len_t)(remaining_bytes + 1);
            }
    
            if (p_tx_buffer)
            {
                nrf_qspi_cinstrdata_set(NRF_QSPI,
                                        length,
                                        &((uint8_t const *)p_tx_buffer)[curr_byte]);
            }
    
            nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
            if (remaining_bytes <= 8)
            {
                nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, length, finalize);
            }
            else
            {
                nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, length, false);
            }
    
            if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
            {
                /* In case of error, abort long frame mode */
                nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
                status = NRFX_ERROR_TIMEOUT;
                break;
            }
    
            if (p_rx_buffer)
            {
                nrf_qspi_cinstrdata_get(NRF_QSPI,
                                        length,
                                        &((uint8_t *)p_rx_buffer)[curr_byte]);
            }
        }
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        if ((finalize) || (status == NRFX_ERROR_TIMEOUT))
        {
            m_cb.is_busy = false;
        }
    
        return status;
    }
    
    nrfx_err_t nrfx_qspi_mem_busy_check(void)
    {
        nrfx_err_t ret_code;
        uint8_t status_value = 0;
    
        nrf_qspi_cinstr_conf_t const config =
            NRFX_QSPI_DEFAULT_CINSTR(QSPI_STD_CMD_RDSR,
                                     NRF_QSPI_CINSTR_LEN_2B);
        ret_code = nrfx_qspi_cinstr_xfer(&config, &status_value, &status_value);
    
        if (ret_code != NRFX_SUCCESS)
        {
            return ret_code;
        }
    
        if ((status_value & QSPI_MEM_STATUSREG_WIP_Pos) != 0x00)
        {
            return NRFX_ERROR_BUSY;
        }
    
        return NRFX_SUCCESS;
    }
    
    void nrfx_qspi_uninit(void)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
    
        if (nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI))
        {
            nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
        }
    
        NRFX_IRQ_DISABLE(QSPI_IRQn);
    
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_DEACTIVATE);
    
        nrf_qspi_disable(NRF_QSPI);
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        qspi_pins_deconfigure();
    
        m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
    }
    
    nrfx_err_t nrfx_qspi_write(void const * p_tx_buffer,
                               size_t       tx_buffer_length,
                               uint32_t     dst_address)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(p_tx_buffer != NULL);
    
        if (!nrfx_is_in_ram(p_tx_buffer) || !nrfx_is_word_aligned(p_tx_buffer))
        {
            return NRFX_ERROR_INVALID_ADDR;
        }
    
        nrf_qspi_write_buffer_set(NRF_QSPI, p_tx_buffer, tx_buffer_length, dst_address);
        return qspi_task_perform(NRF_QSPI_TASK_WRITESTART);
    }
    
    nrfx_err_t nrfx_qspi_read(void *   p_rx_buffer,
                              size_t   rx_buffer_length,
                              uint32_t src_address)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(p_rx_buffer != NULL);
    
        if (!nrfx_is_in_ram(p_rx_buffer) || !nrfx_is_word_aligned(p_rx_buffer))
        {
            return NRFX_ERROR_INVALID_ADDR;
        }
    
        nrf_qspi_read_buffer_set(NRF_QSPI, p_rx_buffer, rx_buffer_length, src_address);
        return qspi_task_perform(NRF_QSPI_TASK_READSTART);
    }
    
    nrfx_err_t nrfx_qspi_erase(nrf_qspi_erase_len_t length,
                               uint32_t             start_address)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
    
        if (!nrfx_is_word_aligned((void const *)start_address))
        {
            return NRFX_ERROR_INVALID_ADDR;
        }
    
        nrf_qspi_erase_ptr_set(NRF_QSPI, start_address, length);
        return qspi_task_perform(NRF_QSPI_TASK_ERASESTART);
    }
    
    nrfx_err_t nrfx_qspi_chip_erase(void)
    {
        return nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_ALL, 0);
    }
    
    void nrfx_qspi_irq_handler(void)
    {
        // Catch Event ready interrupts
        if (nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY))
        {
            m_cb.is_busy = false;
            nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
            m_cb.handler(NRFX_QSPI_EVENT_DONE, m_cb.p_context);
        }
    }
    
    #endif // NRFX_CHECK(NRFX_QSPI_ENABLED)
    
    nrfx_qspi.h

Reply
  • I decided to test with a modified nrfx_qspi.c implementation. I cloned nrfx_qspi_init() into nrfx_qspi_init_rdp() and added the code to configure DIO1 with pulldown, init the QSPI peripheral, then within that same function call nrfx_qspi_cinstr_xfer() with the 0xAB CMD_RELEASE_PWRDOWN command, finally redefining DIO1 as the other QSPI pins. 

    Basically QSPI init for EEPROM devices in Deep Power Down Mode that require 0xAB to exit. I confirmed that when the peripheral sends the first 0x05 with the EEPROM in DPM, the status register reads 0x00 (no data), and as soon as the 0xAB is sent, the EEPROM status reads 0x40. And the QSPI doesn't timeout, even if there's nothing on DIO1. Here's the trace of the nrfx_qspi_init_rdp() call followed by nrfx_qspi_mem_busy_check() (which reads 0x40 as expected)

    I'm posting the modified files for reference. Other people might find this ticket when dealing with modern NOR flash chips. Can't promise it will work in all case, it's not fully tested yet

    nrf_drv_qspi.h

    /**
     * Copyright (c) 2016 - 2021, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    
    #include <nrfx.h>
    
    #if NRFX_CHECK(NRFX_QSPI_ENABLED)
    
    #include <nrfx_qspi.h>
    #include <hal/nrf_gpio.h>
    
    /** @brief Command byte used to read status register. */
    #define QSPI_STD_CMD_RDSR 0x05
    #define CMD_RELEASE_PWRDOWN 0xAB
    
    /** @brief Byte used to mask status register and retrieve the write-in-progess bit. */
    #define QSPI_MEM_STATUSREG_WIP_Pos 0x01
    
    /** @brief Default time used in timeout function. */
    #define QSPI_DEF_WAIT_TIME_US 10
    
    /** @brief Default number of tries in timeout function. */
    #define QSPI_DEF_WAIT_ATTEMPTS 100
    
    /**
     * @brief Macro for initializing a QSPI pin.
     *
     * QSPI peripheral expects high drive pin strength.
     */
    #define QSPI_PIN_INIT(_pin) nrf_gpio_cfg((_pin),                        \
                                             NRF_GPIO_PIN_DIR_INPUT,        \
                                             NRF_GPIO_PIN_INPUT_DISCONNECT, \
                                             NRF_GPIO_PIN_NOPULL,           \
                                             NRF_GPIO_PIN_H0H1,             \
                                             NRF_GPIO_PIN_NOSENSE)
    
    #define QSPI_PIN_INITPD(_pin) nrf_gpio_cfg((_pin),                        \
                                             NRF_GPIO_PIN_DIR_INPUT,        \
                                             NRF_GPIO_PIN_INPUT_DISCONNECT, \
                                             NRF_GPIO_PIN_PULLDOWN,           \
                                             NRF_GPIO_PIN_H0H1,             \
                                             NRF_GPIO_PIN_NOSENSE)
    
    /** @brief Control block - driver instance local data. */
    typedef struct
    {
        nrfx_qspi_handler_t handler;   /**< Handler. */
        nrfx_drv_state_t    state;     /**< Driver state. */
        volatile bool       is_busy;   /**< Flag indicating that an operation is currently being performed. */
        void *              p_context; /**< Driver context used in interrupt. */
    } qspi_control_block_t;
    
    static qspi_control_block_t m_cb;
    
    static nrfx_err_t qspi_task_perform(nrf_qspi_task_t task)
    {
        // Wait for peripheral
        if (m_cb.is_busy)
        {
            return NRFX_ERROR_BUSY;
        }
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        if (m_cb.handler)
        {
            m_cb.is_busy = true;
            nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
        }
    
        nrf_qspi_task_trigger(NRF_QSPI, task);
    
        if (m_cb.handler == NULL)
        {
            while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY))
            {};
        }
        return NRFX_SUCCESS;
    }
    
    static bool qspi_pins_configure(nrf_qspi_pins_t const * p_config)
    {
        // Check if the user set meaningful values to struct fields. If not, return false.
        if ((p_config->sck_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
            (p_config->csn_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
            (p_config->io0_pin == NRF_QSPI_PIN_NOT_CONNECTED) ||
            (p_config->io1_pin == NRF_QSPI_PIN_NOT_CONNECTED))
        {
            return false;
        }
    
        QSPI_PIN_INIT(p_config->sck_pin);
        QSPI_PIN_INIT(p_config->csn_pin);
        QSPI_PIN_INIT(p_config->io0_pin);
        QSPI_PIN_INIT(p_config->io1_pin);
        if (p_config->io2_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            QSPI_PIN_INIT(p_config->io2_pin);
        }
        if (p_config->io3_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            QSPI_PIN_INIT(p_config->io3_pin);
        }
    
        nrf_qspi_pins_set(NRF_QSPI, p_config);
    
        return true;
    }
    
    static void qspi_pins_deconfigure(void)
    {
        nrf_qspi_pins_t pins;
        nrf_qspi_pins_get(NRF_QSPI, &pins);
    
        nrf_gpio_cfg_default(pins.sck_pin);
        nrf_gpio_cfg_default(pins.csn_pin);
        nrf_gpio_cfg_default(pins.io0_pin);
        nrf_gpio_cfg_default(pins.io1_pin);
        if (pins.io2_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            nrf_gpio_cfg_default(pins.io2_pin);
        }
        if (pins.io3_pin != NRF_QSPI_PIN_NOT_CONNECTED)
        {
            nrf_gpio_cfg_default(pins.io3_pin);
        }
    }
    
    static nrfx_err_t qspi_ready_wait(void)
    {
        bool result;
        NRFX_WAIT_FOR(nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY),
                                           QSPI_DEF_WAIT_ATTEMPTS,
                                           QSPI_DEF_WAIT_TIME_US,
                                           result);
        if (!result)
        {
            return NRFX_ERROR_TIMEOUT;
        }
    
        return NRFX_SUCCESS;
    }
    
    nrfx_err_t nrfx_qspi_init(nrfx_qspi_config_t const * p_config,
                              nrfx_qspi_handler_t        handler,
                              void *                     p_context)
    {
        NRFX_ASSERT(p_config);
        if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
        {
            return NRFX_ERROR_INVALID_STATE;
        }
    
        if (!qspi_pins_configure(&p_config->pins))
        {
            return NRFX_ERROR_INVALID_PARAM;
        }
    
        nrf_qspi_xip_offset_set(NRF_QSPI, p_config->xip_offset);
        nrf_qspi_ifconfig0_set(NRF_QSPI, &p_config->prot_if);
        nrf_qspi_ifconfig1_set(NRF_QSPI, &p_config->phy_if);
    
        m_cb.is_busy = false;
        m_cb.handler = handler;
        m_cb.p_context = p_context;
    
        /* QSPI interrupt is disabled because the device should be enabled in polling mode (wait for activate
           task event ready)*/
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        if (handler)
        {
            NRFX_IRQ_PRIORITY_SET(QSPI_IRQn, p_config->irq_priority);
            NRFX_IRQ_ENABLE(QSPI_IRQn);
        }
    
        m_cb.state = NRFX_DRV_STATE_INITIALIZED;
    
        nrf_qspi_enable(NRF_QSPI);
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
        nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE);
    
        // Waiting for the peripheral to activate
    
        return qspi_ready_wait();
    }
    
    nrfx_err_t nrfx_qspi_init_rdp(nrfx_qspi_config_t const * p_config,
                              nrfx_qspi_handler_t        handler,
                              void *                     p_context)
    {
        NRFX_ASSERT(p_config);
        if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
        {
            return NRFX_ERROR_INVALID_STATE;
        }
    
        if (!qspi_pins_configure(&p_config->pins))
        {
            return NRFX_ERROR_INVALID_PARAM;
        }
    
        nrf_qspi_pins_t const *ppin = &p_config->pins;    // hack to prevent error wwhen NOR Flash chip is in deep poeer down mode;
        QSPI_PIN_INITPD(ppin->io1_pin);                     // DIO1 low is seen by QSPI peripheral as NOR Flash present
    
        nrf_qspi_xip_offset_set(NRF_QSPI, p_config->xip_offset);
        nrf_qspi_ifconfig0_set(NRF_QSPI, &p_config->prot_if);
        nrf_qspi_ifconfig1_set(NRF_QSPI, &p_config->phy_if);
    
        m_cb.is_busy = false;
        m_cb.handler = handler;
        m_cb.p_context = p_context;
    
        /* QSPI interrupt is disabled because the device should be enabled in polling mode (wait for activate
           task event ready)*/
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        if (handler)
        {
            NRFX_IRQ_PRIORITY_SET(QSPI_IRQn, p_config->irq_priority);
            NRFX_IRQ_ENABLE(QSPI_IRQn);
        }
    
        m_cb.state = NRFX_DRV_STATE_INITIALIZED;
    
        nrf_qspi_enable(NRF_QSPI);
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
        nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE);
    
        // Waiting for the peripheral to activate
        nrfx_err_t err_code= qspi_ready_wait();   
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = CMD_RELEASE_PWRDOWN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true, // High level during transmission
            .io3_level = true, // High level during transmission
            .wipwait   = true,
            .wren      = false};
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        
        QSPI_PIN_INIT(ppin->io1_pin);   // revert DIO1 back to normal
    
        return err_code;
    }
    
    nrfx_err_t nrfx_qspi_cinstr_xfer(nrf_qspi_cinstr_conf_t const * p_config,
                                     void const *                   p_tx_buffer,
                                     void *                         p_rx_buffer)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
    
        if (m_cb.is_busy)
        {
            return NRFX_ERROR_BUSY;
        }
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
        /* In some cases, only opcode should be sent. To prevent execution, set function code is
         * surrounded by an if.
         */
        if (p_tx_buffer)
        {
            nrf_qspi_cinstrdata_set(NRF_QSPI, p_config->length, p_tx_buffer);
        }
    
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        nrf_qspi_cinstr_transfer_start(NRF_QSPI, p_config);
    
        if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
        {
            // This timeout should never occur when WIPWAIT is not active, since in this
            // case the QSPI peripheral should send the command immediately, without any
            // waiting for previous write to complete.
            NRFX_ASSERT(p_config->wipwait);
    
            return NRFX_ERROR_TIMEOUT;
        }
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        if (p_rx_buffer)
        {
            nrf_qspi_cinstrdata_get(NRF_QSPI, p_config->length, p_rx_buffer);
        }
    
        return NRFX_SUCCESS;
    }
    
    nrfx_err_t nrfx_qspi_cinstr_quick_send(uint8_t               opcode,
                                           nrf_qspi_cinstr_len_t length,
                                           void const *          p_tx_buffer)
    {
        nrf_qspi_cinstr_conf_t config = NRFX_QSPI_DEFAULT_CINSTR(opcode, length);
        return nrfx_qspi_cinstr_xfer(&config, p_tx_buffer, NULL);
    }
    
    nrfx_err_t nrfx_qspi_lfm_start(nrf_qspi_cinstr_conf_t const * p_config)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(!(nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI)));
        NRFX_ASSERT(p_config->length == NRF_QSPI_CINSTR_LEN_1B);
    
        if (m_cb.is_busy)
        {
            return NRFX_ERROR_BUSY;
        }
    
        nrf_qspi_cinstr_long_transfer_start(NRF_QSPI, p_config);
    
        if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
        {
            /* In case of error, abort long frame mode */
            nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
            return NRFX_ERROR_TIMEOUT;
        }
    
        m_cb.is_busy = true;
        return NRFX_SUCCESS;
    }
    
    nrfx_err_t nrfx_qspi_lfm_xfer(void const * p_tx_buffer,
                                  void *       p_rx_buffer,
                                  size_t       transfer_length,
                                  bool         finalize)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI));
    
        nrfx_err_t status = NRFX_SUCCESS;
    
        /* Perform transfers in packets of 8 bytes. Last transfer may be shorter. */
        nrf_qspi_cinstr_len_t length = NRF_QSPI_CINSTR_LEN_9B;
        for (uint32_t curr_byte = 0; curr_byte < transfer_length; curr_byte += 8)
        {
            uint32_t remaining_bytes = transfer_length - curr_byte;
            if (remaining_bytes < 8)
            {
                length = (nrf_qspi_cinstr_len_t)(remaining_bytes + 1);
            }
    
            if (p_tx_buffer)
            {
                nrf_qspi_cinstrdata_set(NRF_QSPI,
                                        length,
                                        &((uint8_t const *)p_tx_buffer)[curr_byte]);
            }
    
            nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
            if (remaining_bytes <= 8)
            {
                nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, length, finalize);
            }
            else
            {
                nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, length, false);
            }
    
            if (qspi_ready_wait() == NRFX_ERROR_TIMEOUT)
            {
                /* In case of error, abort long frame mode */
                nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
                status = NRFX_ERROR_TIMEOUT;
                break;
            }
    
            if (p_rx_buffer)
            {
                nrf_qspi_cinstrdata_get(NRF_QSPI,
                                        length,
                                        &((uint8_t *)p_rx_buffer)[curr_byte]);
            }
        }
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        if ((finalize) || (status == NRFX_ERROR_TIMEOUT))
        {
            m_cb.is_busy = false;
        }
    
        return status;
    }
    
    nrfx_err_t nrfx_qspi_mem_busy_check(void)
    {
        nrfx_err_t ret_code;
        uint8_t status_value = 0;
    
        nrf_qspi_cinstr_conf_t const config =
            NRFX_QSPI_DEFAULT_CINSTR(QSPI_STD_CMD_RDSR,
                                     NRF_QSPI_CINSTR_LEN_2B);
        ret_code = nrfx_qspi_cinstr_xfer(&config, &status_value, &status_value);
    
        if (ret_code != NRFX_SUCCESS)
        {
            return ret_code;
        }
    
        if ((status_value & QSPI_MEM_STATUSREG_WIP_Pos) != 0x00)
        {
            return NRFX_ERROR_BUSY;
        }
    
        return NRFX_SUCCESS;
    }
    
    void nrfx_qspi_uninit(void)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
    
        if (nrf_qspi_cinstr_long_transfer_is_ongoing(NRF_QSPI))
        {
            nrf_qspi_cinstr_long_transfer_continue(NRF_QSPI, NRF_QSPI_CINSTR_LEN_1B, true);
        }
    
        NRFX_IRQ_DISABLE(QSPI_IRQn);
    
        nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK);
    
        nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_DEACTIVATE);
    
        nrf_qspi_disable(NRF_QSPI);
    
        nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
    
        qspi_pins_deconfigure();
    
        m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
    }
    
    nrfx_err_t nrfx_qspi_write(void const * p_tx_buffer,
                               size_t       tx_buffer_length,
                               uint32_t     dst_address)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(p_tx_buffer != NULL);
    
        if (!nrfx_is_in_ram(p_tx_buffer) || !nrfx_is_word_aligned(p_tx_buffer))
        {
            return NRFX_ERROR_INVALID_ADDR;
        }
    
        nrf_qspi_write_buffer_set(NRF_QSPI, p_tx_buffer, tx_buffer_length, dst_address);
        return qspi_task_perform(NRF_QSPI_TASK_WRITESTART);
    }
    
    nrfx_err_t nrfx_qspi_read(void *   p_rx_buffer,
                              size_t   rx_buffer_length,
                              uint32_t src_address)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
        NRFX_ASSERT(p_rx_buffer != NULL);
    
        if (!nrfx_is_in_ram(p_rx_buffer) || !nrfx_is_word_aligned(p_rx_buffer))
        {
            return NRFX_ERROR_INVALID_ADDR;
        }
    
        nrf_qspi_read_buffer_set(NRF_QSPI, p_rx_buffer, rx_buffer_length, src_address);
        return qspi_task_perform(NRF_QSPI_TASK_READSTART);
    }
    
    nrfx_err_t nrfx_qspi_erase(nrf_qspi_erase_len_t length,
                               uint32_t             start_address)
    {
        NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
    
        if (!nrfx_is_word_aligned((void const *)start_address))
        {
            return NRFX_ERROR_INVALID_ADDR;
        }
    
        nrf_qspi_erase_ptr_set(NRF_QSPI, start_address, length);
        return qspi_task_perform(NRF_QSPI_TASK_ERASESTART);
    }
    
    nrfx_err_t nrfx_qspi_chip_erase(void)
    {
        return nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_ALL, 0);
    }
    
    void nrfx_qspi_irq_handler(void)
    {
        // Catch Event ready interrupts
        if (nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY))
        {
            m_cb.is_busy = false;
            nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY);
            m_cb.handler(NRFX_QSPI_EVENT_DONE, m_cb.p_context);
        }
    }
    
    #endif // NRFX_CHECK(NRFX_QSPI_ENABLED)
    
    nrfx_qspi.h

Children
Related