| /**************************************************************************** |
| * 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/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 |
| |
| /* QSPI memory synchronization */ |
| |
| #define MEMORY_SYNC() do { ARM_DSB(); ARM_ISB(); } while (0) |
| |
| /* Ensure that the DMA buffers are word-aligned. */ |
| |
| #define ALIGN_SHIFT 2 |
| #define ALIGN_MASK 3 |
| #define ALIGN_UP(n) (((n)+ALIGN_MASK) & ~ALIGN_MASK) |
| #define IS_ALIGNED(n) (((uint32_t)(n) & ALIGN_MASK) == 0) |
| |
| /**************************************************************************** |
| * 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) |
| { |
| return kmm_malloc(ALIGN_UP(buflen)); |
| } |
| |
| /**************************************************************************** |
| * 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; |
| } |