/****************************************************************************
 * arch/arm/src/nrf53/nrf53_qspi.c
 *
 * 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
 ****************************************************************************/

#include <nuttx/config.h>

#include <debug.h>

#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/nuttx.h>
#include <nuttx/semaphore.h>
#include <arch/board/board.h>

#include "arm_internal.h"

#include "hardware/nrf53_qspi.h"

#include "nrf53_clockconfig.h"
#include "nrf53_gpio.h"
#include "nrf53_qspi.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#if ((NRF53_PCLK192M_FREQ != 192000000) && (NRF53_PCLK192M_FREQ != 96000000))
#  error not supported PCLK192M frequency
#endif

/* QSPI frequency limits */

#define NRF53_QSPI_FREQMAX (NRF53_PCLK192M_FREQ / (2 * 1))
#define NRF53_QSPI_FREQMIN (NRF53_PCLK192M_FREQ / (2 * 16))

/* Instruction handled by the NRF53 QSPI peripheral */

#define QSPI_SECTOR_ERASE 0x20
#define QSPI_BLOCK_ERASE  0xd8
#define QSPI_ALL_ERASE    0xc7

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct nrf53_qspidev_s
{
  struct qspi_dev_s qspi;           /* Externally visible part of the QSPI interface */
  mutex_t           lock;           /* Assures mutually exclusive access to QSPI */
  uint32_t          base;           /* QSPI base address */
  uint32_t          actual;         /* Actual clock frequency */
  uint32_t          frequency;      /* Requested clock frequency */
  bool              initialized;    /* TRUE: Controller has been initialized */
  uint8_t           intf;           /* QSPI controller number (0) */
  uint8_t           mode;           /* Mode 0,3 */
  sem_t             op_sem;         /* Block until complete */
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Helpers */

static inline void nrf53_qspi_putreg(struct nrf53_qspidev_s *priv,
                                     uint32_t offset,
                                     uint32_t value);
static inline uint32_t nrf53_qspi_getreg(struct nrf53_qspidev_s *priv,
                                         uint32_t offset);
static void nrf53_qspi_cinstrdata_get(struct nrf53_qspidev_s *priv,
                                      struct qspi_cmdinfo_s *cmdinfo);
static void nrf53_qspi_cinstrdata_put(struct nrf53_qspidev_s *priv,
                                      struct qspi_cmdinfo_s *cmdinfo);

/* QSPI operations */

static int nrf53_qspi_lock(struct qspi_dev_s *dev, bool lock);

static int nrf53_qspi_lock(struct qspi_dev_s *dev, bool lock);
static uint32_t nrf53_qspi_setfrequency(struct qspi_dev_s *dev,
                                        uint32_t frequency);
static void nrf53_qspi_setmode(struct qspi_dev_s *dev,
                               enum qspi_mode_e mode);
static void nrf53_qspi_setbits(struct qspi_dev_s *dev, int nbits);
static int nrf53_qspi_command(struct qspi_dev_s *dev,
                             struct qspi_cmdinfo_s *cmdinfo);
static int nrf53_qspi_memory(struct qspi_dev_s *dev,
                             struct qspi_meminfo_s *meminfo);
static void *nrf53_qspi_alloc(struct qspi_dev_s *dev, size_t buflen);
static void nrf53_qspi_free(struct qspi_dev_s *dev, void *buffer);

static int nrf53_qspi_interrupt(int irq, void *context, void *arg);
static int nrf53_qspi_hw_initialize(struct nrf53_qspidev_s *priv);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct qspi_ops_s g_qspi_ops =
{
  .lock              = nrf53_qspi_lock,
  .setfrequency      = nrf53_qspi_setfrequency,
  .setmode           = nrf53_qspi_setmode,
  .setbits           = nrf53_qspi_setbits,
#ifdef CONFIG_QSPI_HWFEATURES
  .hwfeatures        = NULL,
#endif
  .command           = nrf53_qspi_command,
  .memory            = nrf53_qspi_memory,
  .alloc             = nrf53_qspi_alloc,
  .free              = nrf53_qspi_free,
};

static struct nrf53_qspidev_s g_qspi0_dev =
{
  .qspi      =
  {
    .ops     = &g_qspi_ops,
  },
  .lock      = NXMUTEX_INITIALIZER,
  .op_sem    = SEM_INITIALIZER(0),
  .base      = NRF53_QSPI_BASE,
  .intf      = 0
};

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Name: nrf53_qspi_putreg
 *
 * Description:
 *   Put a 32-bit register value by offset
 *
 ****************************************************************************/

static inline void nrf53_qspi_putreg(struct nrf53_qspidev_s *priv,
                                     uint32_t offset,
                                     uint32_t value)
{
  putreg32(value, priv->base + offset);
}

/****************************************************************************
 * Name: nrf53_qspi_getreg
 *
 * Description:
 *   Get a 32-bit register value by offset
 *
 ****************************************************************************/

static inline uint32_t nrf53_qspi_getreg(struct nrf53_qspidev_s *priv,
                                        uint32_t offset)
{
  return getreg32(priv->base + offset);
}

/****************************************************************************
 * Name: nrf53_qspi_lock
 *
 * Description:
 *   On QSPI buses where there are multiple devices, it will be necessary to
 *   lock QSPI to have exclusive access to the buses for a sequence of
 *   transfers.  The bus should be locked before the chip is selected. After
 *   locking the QSPI bus, the caller should then also call the setfrequency,
 *   setbits, and setmode methods to make sure that the QSPI is properly
 *   configured for the device.  If the QSPI bus is being shared, then it
 *   may have been left in an incompatible state.
 *
 * Input Parameters:
 *   dev  - Device-specific state data
 *   lock - true: Lock QSPI bus, false: unlock QSPI bus
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static int nrf53_qspi_lock(struct qspi_dev_s *dev, bool lock)
{
  struct nrf53_qspidev_s *priv = (struct nrf53_qspidev_s *)dev;
  int ret;

  spiinfo("lock=%d\n", lock);
  if (lock)
    {
      ret = nxmutex_lock(&priv->lock);
    }
  else
    {
      ret = nxmutex_unlock(&priv->lock);
    }

  return ret;
}

/****************************************************************************
 * Name: nrf53_qspi_setfrequency
 *
 * Description:
 *   Set the QSPI frequency.
 *
 * Input Parameters:
 *   dev -       Device-specific state data
 *   frequency - The QSPI frequency requested
 *
 * Returned Value:
 *   Returns the actual frequency selected
 *
 ****************************************************************************/

static uint32_t nrf53_qspi_setfrequency(struct qspi_dev_s *dev,
                                        uint32_t frequency)
{
  struct nrf53_qspidev_s *priv    = (struct nrf53_qspidev_s *)dev;
  uint32_t                sckfreq = 0;
  uint32_t                actual  = 0;
  uint32_t                regval  = 0;

  spiinfo("frequency=%ld\n", frequency);
  DEBUGASSERT(priv);

  /* Check if the requested frequency is the same as the frequency
   * selection
   */

  if (priv->frequency == frequency)
    {
      /* We are already at this frequency.  Return the actual. */

      return priv->actual;
    }

  /* Get prescaler */

  if (frequency <= NRF53_QSPI_FREQMIN)
    {
      sckfreq = 15;
    }
  else if (frequency <= NRF53_QSPI_FREQMAX)
    {
      sckfreq = ((NRF53_PCLK192M_FREQ / 2) / frequency) - 1;
    }
  else
    {
      sckfreq = 0;
    }

  /* Modify register */

  regval = nrf53_qspi_getreg(priv, NRF53_QSPI_IFCONFIG1_OFFSET);
  regval &= ~QSPI_IFCONFIG1_SCKFREQ_MASK;
  regval |= sckfreq << QSPI_IFCONFIG1_SCKFREQ_SHIFT;
  nrf53_qspi_putreg(priv, NRF53_QSPI_IFCONFIG1_OFFSET, regval);

  /* Calculate the new actual frequency */

  actual = NRF53_PCLK192M_FREQ / (2 * (sckfreq + 1));

  /* Save the frequency setting */

  priv->frequency = frequency;
  priv->actual    = actual;

  spiinfo("Frequency %ld->%ld\n", frequency, actual);

  return actual;
}

/****************************************************************************
 * Name: nrf53_qspi_setmode
 *
 * Description:
 *   Set the QSPI mode. Optional.  See enum qspi_mode_e for mode definitions.
 *   NOTE:  the NRF53 QSPI supports only modes 0 and 3.
 *
 * Input Parameters:
 *   dev -  Device-specific state data
 *   mode - The QSPI mode requested
 *
 * Returned Value:
 *   none
 *
 ****************************************************************************/

static void nrf53_qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode)
{
  struct nrf53_qspidev_s *priv    = (struct nrf53_qspidev_s *)dev;
  uint32_t                regval  = 0;

  spiinfo("mode=%d\n", mode);

  /* Has the mode changed? */

  if (mode != priv->mode)
    {
      regval = nrf53_qspi_getreg(priv, NRF53_QSPI_IFCONFIG1_OFFSET);

      switch (mode)
        {
          case QSPIDEV_MODE0:
            {
              regval |= (QSPI_IFCONFIG1_SPIMODE_0);
              break;
            }

          case QSPIDEV_MODE3:
            {
              regval |= (QSPI_IFCONFIG1_SPIMODE_3);
              break;
            }

          case QSPIDEV_MODE1:
          case QSPIDEV_MODE2:
            {
              spiinfo("unsupported mode=%d\n", mode);

              /* No break here */
            }
          default:
            {
              DEBUGASSERT(0);
              return;
            }
        }

      /* Write new mode */

      nrf53_qspi_putreg(priv, NRF53_QSPI_IFCONFIG1_OFFSET, regval);

      /* Save the mode so that subsequent re-configurations will be faster */

      priv->mode = mode;
    }
}

/****************************************************************************
 * Name: nrf53_qspi_setbits
 *
 * Description:
 *   Set the number of bits per word.
 *   NOTE:  the NRF53 QSPI only supports 8 bits, so this does nothing.
 *
 * Input Parameters:
 *   dev -  Device-specific state data
 *   nbits - The number of bits requests
 *
 * Returned Value:
 *   none
 *
 ****************************************************************************/

static void nrf53_qspi_setbits(struct qspi_dev_s *dev, int nbits)
{
  if (nbits != 8)
    {
      spiinfo("unsupported nbits=%d\n", nbits);
      DEBUGASSERT(FALSE);
    }
}

/****************************************************************************
 * Name: nrf53_qspi_cinstrdata_get
 *
 * Description:
 *   Get command data
 *
 * Input Parameters:
 *   priv - Device state structure.
 *   cmdinfo - Describes the command transfer to be performed.
 *
 * Returned Value:
 *   Zero (OK) on SUCCESS, a negated errno on value of failure
 *
 ****************************************************************************/

static void nrf53_qspi_cinstrdata_get(struct nrf53_qspidev_s *priv,
                                      struct qspi_cmdinfo_s *cmdinfo)
{
  uint32_t regval = 0;
  int      i      = 0;
  uint8_t *buffer = cmdinfo->buffer;

  DEBUGASSERT(cmdinfo->buflen <= 8);

  /* Get Bytes 0-3 */

  regval = nrf53_qspi_getreg(priv, NRF53_QSPI_CINSTRDAT0_OFFSET);

  if (cmdinfo->buflen > 0)
    {
      for (i = 0; (i < 4) && (i < cmdinfo->buflen); i += 1)
        {
          buffer[i] = (regval >> (i * 8)) & 0xff;
        }
    }

  /* Write Bytes 4-7 */

  regval = nrf53_qspi_getreg(priv, NRF53_QSPI_CINSTRDAT1_OFFSET);

  if (cmdinfo->buflen > 4)
    {
      for (i = 4; (i < 8) && (i < cmdinfo->buflen); i += 1)
        {
          buffer[i] = (regval >> ((i - 4) * 8)) & 0xff;
        }
    }
}

/****************************************************************************
 * Name: nrf53_qspi_cinstrdata_put
 *
 * Description:
 *   Put command data
 *
 * Input Parameters:
 *   priv - Device state structure.
 *   cmdinfo - Describes the command transfer to be performed.
 *
 * Returned Value:
 *   Zero (OK) on SUCCESS, a negated errno on value of failure
 *
 ****************************************************************************/

static void nrf53_qspi_cinstrdata_put(struct nrf53_qspidev_s *priv,
                                      struct qspi_cmdinfo_s *cmdinfo)
{
  uint32_t regval = 0;
  int      i      = 0;
  uint8_t *buffer = cmdinfo->buffer;

  DEBUGASSERT(cmdinfo->buflen <= 8);

  regval = 0;

  if (cmdinfo->buflen > 0)
    {
      for (i = 0; (i < 4) && (i < cmdinfo->buflen); i += 1)
        {
          regval |= (buffer[i] << (i * 8));
        }
    }

  /* Write Bytes 0-3 */

  nrf53_qspi_putreg(priv, NRF53_QSPI_CINSTRDAT0_OFFSET, regval);

  regval = 0;
  if (cmdinfo->buflen > 4)
    {
      for (i = 4; (i < 8) && (i < cmdinfo->buflen); i += 1)
        {
          regval |= (buffer[i] << ((i - 4) * 8));
        }
    }

  /* Write Bytes 4-7 */

  nrf53_qspi_putreg(priv, NRF53_QSPI_CINSTRDAT1_OFFSET, regval);
}

/****************************************************************************
 * Name: nrf53_qspi_command
 *
 * Description:
 *   Perform one QSPI data transfer
 *
 *   TODO: long frame mode not supported
 *
 * Input Parameters:
 *   dev     - Device-specific state data
 *   cmdinfo - Describes the command transfer to be performed.
 *
 * Returned Value:
 *   Zero (OK) on SUCCESS, a negated errno on value of failure
 *
 ****************************************************************************/

static int nrf53_qspi_command(struct qspi_dev_s *dev,
                              struct qspi_cmdinfo_s *cmdinfo)
{
  struct nrf53_qspidev_s *priv   = (struct nrf53_qspidev_s *)dev;
  uint32_t                regval = 0;

  DEBUGASSERT(cmdinfo->cmd < 256);

  if (QSPICMD_ISADDRESS(cmdinfo->flags))
    {
      /* Only ERASE commands supported */

      switch (cmdinfo->cmd)
        {
          case QSPI_SECTOR_ERASE:
            {
              regval = QSPI_ERASE_SECTOR;
              break;
            }

          case QSPI_BLOCK_ERASE:
            {
              regval = QSPI_ERASE_PAGE;
              break;
            }

          case QSPI_ALL_ERASE:
            {
              regval = QSPI_ERASE_ALL;
              break;
            }

          default:
            {
              /* Not supported addressed command */

              DEBUGASSERT(0);
              return -EINVAL;
            }
        }

      /* Configure erase length */

      nrf53_qspi_putreg(priv, NRF53_QSPI_ERASE_LEN_OFFSET, regval);

      /* Configure erase address */

      regval = cmdinfo->addr;
      nrf53_qspi_putreg(priv, NRF53_QSPI_ERASE_PTR_OFFSET, regval);

      /* Start erase operation */

      nrf53_qspi_putreg(priv, NRF53_QSPI_TASKS_ERASESTART_OFFSET, 1);

      /* Wait for the READY event.
       * TODO: add timeout.
       *
       * NOTE: READ event only signals that the erase operation
       *       has been started.
       */

      nxsem_wait(&priv->op_sem);

      return OK;
    }

  if (QSPICMD_ISWRITE(cmdinfo->flags))
    {
      /* Write data to CINSTRDAT registers */

      nrf53_qspi_cinstrdata_put(priv, cmdinfo);
    }

  /* Configure custom instruction */

  regval = cmdinfo->cmd << QSPI_ADDRCONF_OPCODE_SHIFT;
  regval |= QSPI_CINSTRCONF_LENGTH(cmdinfo->buflen + 1);

  if (QSPICMD_ISWRITE(cmdinfo->flags))
    {
      /* Write request */

      regval |= QSPI_CINSTRCONF_WREN;

      /* Wait for write complete before sending command */

      regval |= QSPI_CINSTRCONF_WIPWAIT;
    }

  /* IO2 and IO3 high during transmission of custom instruction */

  regval |= QSPI_CINSTRCONF_LIO2 | QSPI_CINSTRCONF_LIO3;

  /* Write CINSTRCONF register to initiate transfer */

  nrf53_qspi_putreg(priv, NRF53_QSPI_CINSTRCONF_OFFSET, regval);

  /* Wait for the READY event.
   * TODO: add timeout.
   */

  nxsem_wait(&priv->op_sem);

  if (QSPICMD_ISREAD(cmdinfo->flags))
    {
      /* Get response */

      nrf53_qspi_cinstrdata_get(priv, cmdinfo);
    }

  return OK;
}

/****************************************************************************
 * Name: nrf53_qspi_memory
 *
 * Description:
 *   Perform one QSPI memory transfer
 *
 * Input Parameters:
 *   dev     - Device-specific state data
 *   meminfo - Describes the memory transfer to be performed.
 *
 * Returned Value:
 *   Zero (OK) on SUCCESS, a negated errno on value of failure
 *
 ****************************************************************************/

static int nrf53_qspi_memory(struct qspi_dev_s *dev,
                             struct qspi_meminfo_s *meminfo)
{
  struct nrf53_qspidev_s *priv   = (struct nrf53_qspidev_s *)dev;

  DEBUGASSERT(meminfo->buffer != NULL && meminfo->buflen > 0);

  if (QSPIMEM_ISWRITE(meminfo->flags))
    {
      /* Configure data transfer */

      nrf53_qspi_putreg(priv, NRF53_QSPI_WRITE_SRC_OFFSET,
                        meminfo->addr);
      nrf53_qspi_putreg(priv, NRF53_QSPI_WRITE_DST_OFFSET,
                        (uint32_t) meminfo->buffer);
      nrf53_qspi_putreg(priv, NRF53_QSPI_WRITE_CNT_OFFSET,
                        meminfo->buflen);

      /* Start WRITE task */

      nrf53_qspi_putreg(priv, NRF53_QSPI_TASKS_WRITESTART_OFFSET, 1);
    }
  else
    {
      /* Configure data transfer */

      nrf53_qspi_putreg(priv, NRF53_QSPI_READ_SRC_OFFSET,
                        meminfo->addr);
      nrf53_qspi_putreg(priv, NRF53_QSPI_READ_DST_OFFSET,
                        (uint32_t) meminfo->buffer);
      nrf53_qspi_putreg(priv, NRF53_QSPI_READ_CNT_OFFSET,
                        meminfo->buflen);

      /* Start READ task */

      nrf53_qspi_putreg(priv, NRF53_QSPI_TASKS_READSTART_OFFSET, 1);
    }

  /* Wait for the READY event.
   * TODO: add timeout.
   */

  nxsem_wait(&priv->op_sem);

  return OK;
}

/****************************************************************************
 * Name: nrf53_qspi_alloc
 *
 * Description:
 *   Allocate a buffer suitable for DMA data transfer
 *
 * Input Parameters:
 *   dev    - Device-specific state data
 *   buflen - Buffer length to allocate in bytes
 *
 * Returned Value:
 *   Address of the allocated memory on success; NULL is returned on any
 *   failure.
 *
 ****************************************************************************/

static void *nrf53_qspi_alloc(struct qspi_dev_s *dev, size_t buflen)
{
  /* Ensure that the DMA buffers are word-aligned. */

  return kmm_malloc(ALIGN_UP(buflen, 4));
}

/****************************************************************************
 * Name: nrf53_qspi_free
 *
 * Description:
 *   Free memory returned by QSPI_ALLOC
 *
 * Input Parameters:
 *   dev    - Device-specific state data
 *   buffer - Buffer previously allocated via QSPI_ALLOC
 *
 * Returned Value:
 *   None.
 *
 ****************************************************************************/

static void nrf53_qspi_free(struct qspi_dev_s *dev, void *buffer)
{
  if (buffer)
    {
      kmm_free(buffer);
    }
}

/****************************************************************************
 * Name: nrf53_qspi_interrupt
 *
 * Description:
 *   Interrupt handler; we handle all QSPI cases -- reads, writes,
 *   automatic status polling, etc.
 *
 * Input Parameters:
 *   irq  -
 *   context  -
 *   qrg  -
 *
 * Returned Value:
 *   OK means we handled it
 *
 ****************************************************************************/

static int nrf53_qspi_interrupt(int irq, void *context, void *arg)
{
  struct nrf53_qspidev_s *priv = arg;

  /* Clear READY event */

  nrf53_qspi_putreg(priv, NRF53_QSPI_EVENTS_READY_OFFSET, 0);

  /* Signal TASK complete */

  nxsem_post(&g_qspi0_dev.op_sem);

  return OK;
}

/****************************************************************************
 * Name: nrf53_qspi_hw_initialize
 *
 * Description:
 *   Initialize the QSPI peripheral from hardware reset.
 *
 * Input Parameters:
 *   priv - Device state structure.
 *
 * Returned Value:
 *   Zero (OK) on SUCCESS, a negated errno on value of failure
 *
 ****************************************************************************/

static int nrf53_qspi_hw_initialize(struct nrf53_qspidev_s *priv)
{
  uint32_t regval = 0;
  int      pin    = 0;
  int      port   = 0;
  int      ret    = 0;

  /* Only for QSPI0 */

  DEBUGASSERT(priv->intf == 0);

  /* Attach the interrupt handler */

  ret = irq_attach(NRF53_IRQ_QSPI, nrf53_qspi_interrupt, priv);
  if (ret < 0)
    {
      spierr("ERROR: Failed to attach QSPI irq\n");
      return ret;
    }

  /* SCK pin */

  nrf53_gpio_config(NRF53_QSPI0_SCK_PIN | GPIO_DRIVE_H0H1);
  pin = GPIO_PIN_DECODE(NRF53_QSPI0_SCK_PIN);
  port = GPIO_PORT_DECODE(NRF53_QSPI0_SCK_PIN);
  regval = (pin << QSPI_PSEL_PIN_SHIFT) | (port << QSPI_PSEL_PORT_SHIFT);
  nrf53_qspi_putreg(priv, NRF53_QSPI_PSEL_SCK_OFFSET, regval);

  /* CSN pin */

  nrf53_gpio_config(NRF53_QSPI0_CSN_PIN | GPIO_DRIVE_H0H1);
  pin = GPIO_PIN_DECODE(NRF53_QSPI0_CSN_PIN);
  port = GPIO_PORT_DECODE(NRF53_QSPI0_CSN_PIN);
  regval = (pin << QSPI_PSEL_PIN_SHIFT) | (port << QSPI_PSEL_PORT_SHIFT);
  nrf53_qspi_putreg(priv, NRF53_QSPI_PSEL_CSN_OFFSET, regval);

  /* IO0 pin */

  nrf53_gpio_config(NRF53_QSPI0_IO0_PIN | GPIO_DRIVE_H0H1);
  pin = GPIO_PIN_DECODE(NRF53_QSPI0_IO0_PIN);
  port = GPIO_PORT_DECODE(NRF53_QSPI0_IO0_PIN);
  regval = (pin << QSPI_PSEL_PIN_SHIFT) | (port << QSPI_PSEL_PORT_SHIFT);
  nrf53_qspi_putreg(priv, NRF53_QSPI_PSEL_IO0_OFFSET, regval);

  /* IO1 pin */

  nrf53_gpio_config(NRF53_QSPI0_IO1_PIN | GPIO_DRIVE_H0H1);
  pin = GPIO_PIN_DECODE(NRF53_QSPI0_IO1_PIN);
  port = GPIO_PORT_DECODE(NRF53_QSPI0_IO1_PIN);
  regval = (pin << QSPI_PSEL_PIN_SHIFT) | (port << QSPI_PSEL_PORT_SHIFT);
  nrf53_qspi_putreg(priv, NRF53_QSPI_PSEL_IO1_OFFSET, regval);

  /* IO2 pin */

  nrf53_gpio_config(NRF53_QSPI0_IO2_PIN | GPIO_DRIVE_H0H1);
  pin = GPIO_PIN_DECODE(NRF53_QSPI0_IO2_PIN);
  port = GPIO_PORT_DECODE(NRF53_QSPI0_IO2_PIN);
  regval = (pin << QSPI_PSEL_PIN_SHIFT) | (port << QSPI_PSEL_PORT_SHIFT);
  nrf53_qspi_putreg(priv, NRF53_QSPI_PSEL_IO2_OFFSET, regval);

  /* IO3 pin */

  nrf53_gpio_config(NRF53_QSPI0_IO3_PIN | GPIO_DRIVE_H0H1);
  pin = GPIO_PIN_DECODE(NRF53_QSPI0_IO3_PIN);
  port = GPIO_PORT_DECODE(NRF53_QSPI0_IO3_PIN);
  regval = (pin << QSPI_PSEL_PIN_SHIFT) | (port << QSPI_PSEL_PORT_SHIFT);
  nrf53_qspi_putreg(priv, NRF53_QSPI_PSEL_IO3_OFFSET, regval);

  /* Configure quad data line SPI */

  regval = (QSPI_IFCONFIG0_READOC_READ4IO | QSPI_IFCONFIG0_WRITEOC_PP4IO);
  regval |= QSPI_IFCONFIG0_PPSIZE_512;
  nrf53_qspi_putreg(priv, NRF53_QSPI_IFCONFIG0_OFFSET, regval);

  /* Enable READY interrupt */

  nrf53_qspi_putreg(priv, NRF53_QSPI_INTENSET_OFFSET, QSPI_INT_READY);

  /* Configure RX delay */

  nrf53_qspi_putreg(priv, NRF53_QSPI_IFTIMING_OFFSET,
                    QSPI_IFTIMING_RXDELAY(CONFIG_NRF53_QSPI_RXDELAY));

  /* Enable QSPI interrupts */

  up_enable_irq(NRF53_IRQ_QSPI);

  /* Enable QSPI */

  nrf53_qspi_putreg(priv, NRF53_QSPI_ENABLE_OFFSET, 1);

  /* Activate QSPI */

  nrf53_qspi_putreg(priv, NRF53_QSPI_TASKS_ACTIVATE_OFFSET, 1);

  /* Wait for READY event.
   * TODO: add timeout.
   */

  nxsem_wait(&priv->op_sem);

  return ret;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: nrf53_qspi_initialize
 *
 * Description:
 *   Initialize the selected QSPI port in master mode
 *
 * Input Parameters:
 *   intf - Interface number(must be zero)
 *
 * Returned Value:
 *   Valid QSPI device structure reference on success; a NULL on failure
 *
 ****************************************************************************/

struct qspi_dev_s *nrf53_qspi_initialize(int intf)
{
  struct nrf53_qspidev_s *priv = NULL;
  int                     ret  = OK;

  /* The NRF53 has only a single QSPI port */

  spiinfo("intf: %d\n", intf);
  DEBUGASSERT(intf == 0);

  /* Select the QSPI interface */

  if (intf == 0)
    {
      /* Select QSPI0 */

      priv = &g_qspi0_dev;
    }
  else
    {
      spierr("ERROR: QSPI%d not supported\n", intf);
      return NULL;
    }

  /* Has the QSPI hardware been initialized? */

  if (!priv->initialized)
    {
      /* Perform hardware initialization.  Puts the QSPI into an active
       * state.
       */

      ret = nrf53_qspi_hw_initialize(priv);
      if (ret < 0)
        {
          spierr("ERROR: Failed to initialize QSPI hardware\n");
          irq_detach(NRF53_IRQ_QSPI);
          return NULL;
        }

      /* Enable interrupts at the NVIC */

      priv->initialized = true;
    }

  return &priv->qspi;
}