blob: 701d4eb4a3f212e66675fe4de35c4927dccbf3cf [file] [log] [blame]
/****************************************************************************
* arch/arm/src/s32k3xx/s32k3xx_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.
*
****************************************************************************/
/* Copyright 2022 NXP */
/****************************************************************************
* Included Files
****************************************************************************/
#include "s32k3xx_qspi.h"
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arch/barriers.h>
#include <arch/board/board.h>
#include <nuttx/arch.h>
#include <nuttx/wdog.h>
#include <nuttx/clock.h>
#include <nuttx/cache.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/nuttx.h>
#include <nuttx/spi/qspi.h>
#include "arm_internal.h"
#ifdef CONFIG_S32K3XX_QSPI_DMA
#include "hardware/s32k3xx_dmamux.h"
#include "s32k3xx_edma.h"
#endif
#include "hardware/s32k3xx_qspi.h"
#include "hardware/s32k344_pinmux.h"
#ifdef CONFIG_S32K3XX_QSPI
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* LUT entries used for various command sequences */
#define QSPI_LUT_READ 0U /* Quad Output read */
#define QSPI_LUT_WRITE 1U /* Quad write */
#define QSPI_LUT_SHARE_TYPE1 2U /* Shared Lut */
#define QSPI_LUT_SHARE_TYPE2 3U /* Shared Lut */
#define QSPI_LUT_CMD_STOP 0U /* End of sequence */
#define QSPI_LUT_CMD_CMD 1U /* Command */
#define QSPI_LUT_CMD_ADDR 2U /* Address */
#define QSPI_LUT_CMD_DUMMY 3U /* Dummy cycles */
#define QSPI_LUT_CMD_MODE 4U /* 8-bit mode */
#define QSPI_LUT_CMD_MODE2 5U /* 2-bit mode */
#define QSPI_LUT_CMD_MODE4 6U /* 4-bit mode */
#define QSPI_LUT_CMD_READ 7U /* Read data */
#define QSPI_LUT_CMD_WRITE 8U /* Write data */
#define QSPI_LUT_CMD_JMP_ON_CS 9U /* Jump on chip select deassert */
#define QSPI_TRANSFER_TYPE_SYNC 0U /* Synchronous transfer using polling */
#define QSPI_TRANSFER_TYPE_ASYNC_INT 1U /* Interrupt-based asynchronous transfer */
#define QSPI_TRANSFER_TYPE_ASYNC_DMA 2U /* DMA-based asynchronous transfer */
#define QSPI_RX_BUF_SIZE 128U
/* Debug ********************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/* The state of the QSPI controller.
*
* NOTE: the S32K3XX supports only a single QSPI peripheral. Logic here is
* designed to support multiple QSPI peripherals.
*/
struct s32k3xx_qspidev_s
{
struct qspi_dev_s qspi; /* Externally visible part of the QSPI interface */
uint32_t base; /* QSPI controller register base address */
uint32_t frequency; /* Requested clock frequency */
uint32_t actual; /* Actual clock frequency */
uint8_t mode; /* Mode 0,3 */
uint8_t nbits; /* Width of word in bits (8 to 32) */
uint8_t intf; /* QSPI controller number (0) */
bool initialized; /* TRUE: Controller has been initialized */
mutex_t lock; /* Assures mutually exclusive access to QSPI */
bool memmap; /* TRUE: Controller is in memory mapped mode */
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
xcpt_t handler; /* Interrupt handler */
uint8_t irq; /* Interrupt number */
sem_t op_sem; /* Block until complete */
#endif
#ifdef CONFIG_S32K3XX_QSPI_DMA
volatile uint32_t rxresult; /* Result of the RX DMA */
volatile uint32_t txresult; /* Result of the TX DMA */
const uint16_t rxch; /* The RX DMA channel number */
const uint16_t txch; /* The TX DMA channel number */
DMACH_HANDLE rxdma; /* DMA channel handle for RX transfers */
DMACH_HANDLE txdma; /* DMA channel handle for TX transfers */
sem_t rxsem; /* Wait for RX DMA to complete */
sem_t txsem; /* Wait for TX DMA to complete */
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Helpers */
static void qspi_resetregisters(void);
#if defined(CONFIG_DEBUG_SPI_INFO) && defined(CONFIG_DEBUG_GPIO)
static void qspi_dumpgpioconfig(const char *msg);
#else
# define qspi_dumpgpioconfig(msg)
#endif
static inline uint32_t qspi_isbusy(void);
static inline uint32_t qspi_rxdataevent(void);
static inline uint32_t qspi_rxfill(void);
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
static int qspi_interrupt(int irq, void *context, void *arg);
#endif
/* DMA support */
#ifdef CONFIG_S32K3XX_QSPI_DMA
#error DMA not supported
static int qspi_dmarxwait(struct s32k3xx_qspidev_s *priv);
static int qspi_dmatxwait(struct s32k3xx_qspidev_s *priv);
static inline void qspi_dmarxwakeup(struct s32k3xx_qspidev_s *priv);
static inline void qspi_dmatxwakeup(struct s32k3xx_qspidev_s *priv);
static void qspi_dmarxcallback(DMACH_HANDLE handle, void *arg,
bool done, int result);
static void qspi_dmatxcallback(DMACH_HANDLE handle, void *arg,
bool done, int result);
static inline void qspi_dmarxstart(struct s32k3xx_qspidev_s *priv);
static inline void qspi_dmatxstart(struct s32k3xx_qspidev_s *priv);
#endif
/* QSPI methods */
static int qspi_lock(struct qspi_dev_s *dev, bool lock);
static uint32_t qspi_setfrequency(struct qspi_dev_s *dev,
uint32_t frequency);
static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode);
static void qspi_setbits(struct qspi_dev_s *dev, int nbits);
static int qspi_command(struct qspi_dev_s *dev,
struct qspi_cmdinfo_s *cmdinfo);
static int qspi_memory(struct qspi_dev_s *dev,
struct qspi_meminfo_s *meminfo);
static void *qspi_alloc(struct qspi_dev_s *dev, size_t buflen);
static void qspi_free(struct qspi_dev_s *dev, void *buffer);
/* Initialization */
static int qspi_hw_initialize(struct s32k3xx_qspidev_s *priv);
/****************************************************************************
* Private Data
****************************************************************************/
/* QSPI0 driver operations */
static const struct qspi_ops_s g_qspi0ops =
{
.lock = qspi_lock,
.setfrequency = qspi_setfrequency,
.setmode = qspi_setmode,
.setbits = qspi_setbits,
#ifdef CONFIG_QSPI_HWFEATURES
.hwfeatures = NULL,
#endif
.command = qspi_command,
.memory = qspi_memory,
.alloc = qspi_alloc,
.free = qspi_free,
};
/* This is the overall state of the QSPI0 controller */
static struct s32k3xx_qspidev_s g_qspi0dev =
{
.qspi =
{
.ops = &g_qspi0ops,
},
.base = S32K3XX_QSPI_BASE,
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
.handler = qspi_interrupt,
.irq = S32K3XX_IRQ_QSPI,
.op_sem = SEM_INITIALIZER(0),
#endif
.intf = 0,
#ifdef CONFIG_S32K3XX_QSPI_DMA
.rxch = DMA_REQ_QSPI_RX,
.txch = DMA_REQ_QSPI_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: qspi_dumpregs
*
* Description:
* Dump the contents of all QSPI registers
*
* Input Parameters:
* priv - The QSPI controller to dump
* msg - Message to print before the register data
*
* Returned Value:
* None
*
****************************************************************************/
static void qspi_resetregisters(void)
{
uint8_t i;
putreg32(0x000f404cul, S32K3XX_QSPI_MCR);
putreg32(0x0, S32K3XX_QSPI_IPCR);
putreg32(QSPI_FLSHCR_TCSS(3) | QSPI_FLSHCR_TCSH(3), S32K3XX_QSPI_FLSHCR);
putreg32(QSPI_BUFCR_MSTRID(3), S32K3XX_QSPI_BUF0CR);
putreg32(QSPI_BUFCR_MSTRID(2), S32K3XX_QSPI_BUF1CR);
putreg32(QSPI_BUFCR_MSTRID(1), S32K3XX_QSPI_BUF2CR);
putreg32(QSPI_BUFCR_MSTRID(0), S32K3XX_QSPI_BUF3CR);
putreg32(0x0, S32K3XX_QSPI_BFGENCR);
putreg32(0x0, S32K3XX_QSPI_SOCCR);
putreg32(0x0, S32K3XX_QSPI_BUF0IND);
putreg32(0x0, S32K3XX_QSPI_BUF1IND);
putreg32(0x0, S32K3XX_QSPI_BUF2IND);
putreg32(0x01200000ul, S32K3XX_QSPI_DLLCRA);
putreg32(0x0, S32K3XX_QSPI_SFAR);
putreg32(0xff000000ul, S32K3XX_QSPI_SMPR);
putreg32(0x0, S32K3XX_QSPI_RBCT);
putreg32(0x0, S32K3XX_QSPI_TBDR);
putreg32(0x0, S32K3XX_QSPI_TBCT);
putreg32(0x0c8378c1ul, S32K3XX_QSPI_FR);
putreg32(0x0, S32K3XX_QSPI_RSER);
putreg32(QSPI_SPTRCLR_BFPTRC | QSPI_SPTRCLR_IPPTRC, S32K3XX_QSPI_SPTRCLR);
putreg32(QSPI_SFAD_TPAD(0x1c0000), S32K3XX_QSPI_SFA1AD);
putreg32(QSPI_SFAD_TPAD(0x1c0000), S32K3XX_QSPI_SFA2AD);
putreg32(QSPI_SFAD_TPAD(0x1c0000), S32K3XX_QSPI_SFB1AD);
putreg32(QSPI_SFAD_TPAD(0x1c0000), S32K3XX_QSPI_SFB2AD);
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_UNLOCK, S32K3XX_QSPI_LCKCR);
putreg32(QSPI_LUT_OPRND0(0x3) | QSPI_LUT_INSTR0(QSPI_LUT_CMD_CMD)
| QSPI_LUT_OPRND1(0x18) | QSPI_LUT_INSTR1(0x2),
S32K3XX_QSPI_LUT0);
putreg32(QSPI_LUT_OPRND0(0x8) | QSPI_LUT_INSTR0(0x7)
| QSPI_LUT_INSTR1(0x9), S32K3XX_QSPI_LUT1);
putreg32(QSPI_LUT_OPRND0(0x38) | QSPI_LUT_INSTR0(QSPI_LUT_CMD_CMD)
| QSPI_LUT_OPRND1(24) | QSPI_LUT_INSTR1(QSPI_LUT_CMD_ADDR),
S32K3XX_QSPI_LUT5);
putreg32(QSPI_LUT_OPRND0(0X10) | QSPI_LUT_INSTR0(QSPI_LUT_CMD_WRITE)
| QSPI_LUT_INSTR1(QSPI_LUT_CMD_STOP), S32K3XX_QSPI_LUT6);
for (i = 7; i < S32K3XX_QSPI_LUT_COUNT; i++)
{
putreg32(0x0, S32K3XX_QSPI_LUT(i));
}
}
#if defined(CONFIG_DEBUG_SPI_INFO) && defined(CONFIG_DEBUG_GPIO)
static void qspi_dumpgpioconfig(const char *msg)
{
uint32_t regval;
spiinfo("%s:\n", msg);
}
#endif
static inline uint32_t qspi_isbusy(void)
{
return (getreg32(S32K3XX_QSPI_SR) & QSPI_SR_BUSY);
}
static inline uint32_t qspi_rxdataevent(void)
{
return (getreg32(S32K3XX_QSPI_SR) & QSPI_SR_RXWE);
}
static inline uint32_t qspi_rxfill(void)
{
return (getreg32(S32K3XX_QSPI_RBSR) & QSPI_RBSR_RDBFL_MASK);
}
static void qspi_ipwrite(uint8_t seqid, uint32_t addr, uint8_t * data,
uint32_t size)
{
uint32_t regval;
uint8_t bytecnt;
/* Set write address */
putreg32(QSPI_AMBA_BASE + addr, S32K3XX_QSPI_SFAR);
/* Reset Tx queue */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_TXF;
putreg32(regval, S32K3XX_QSPI_MCR);
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* clear leftover flags from previous transfers */
putreg32(QSPI_FR_TFF, S32K3XX_QSPI_FR);
/* enable end of transfer interrupt for asynchronous transfers */
regval = getreg32(S32K3XX_QSPI_RSER);
regval |= QSPI_RSER_TFIE;
putreg32(regval, S32K3XX_QSPI_RSER);
#endif
regval = 0;
for (bytecnt = 0; bytecnt < size; bytecnt++)
{
regval += ((uint32_t)(data[bytecnt]) << (8 * bytecnt));
}
putreg32(regval, S32K3XX_QSPI_TBDR);
/* Trigger IP command with specified sequence and size */
putreg32((QSPICR_SEQID(seqid) | QSPICR_IDATSZ(size)), S32K3XX_QSPI_IPCR);
#ifndef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* Wait for command to be completed */
while (qspi_isbusy())
{
}
/* FIXME Check for errors reported by the QuadSPI */
#endif
}
static void qspi_ipread(uint8_t seqid, uint32_t addr, uint8_t * dataread,
uint32_t size)
{
uint32_t regval;
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* clear leftover flags from previous transfers */
putreg32(QSPI_FR_TFF, S32K3XX_QSPI_FR);
/* enable end of transfer interrupt for asynchronous transfers */
regval = getreg32(S32K3XX_QSPI_RSER);
regval |= QSPI_RSER_TFIE;
putreg32(regval, S32K3XX_QSPI_RSER);
#endif
/* Reset Rx queue */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_RXF;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Set read address */
putreg32(QSPI_AMBA_BASE + addr, S32K3XX_QSPI_SFAR);
/* Trigger IP command with specified sequence and size */
putreg32((QSPICR_SEQID(seqid) | QSPICR_IDATSZ(size)), S32K3XX_QSPI_IPCR);
#ifndef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* Wait for command to be completed */
while (qspi_isbusy() || (qspi_rxfill() != 0U))
{
if (qspi_rxdataevent())
{
for (regval = 0; regval < size; regval += 4)
{
((uint32_t *)dataread)[regval] =
*(uint32_t *)S32K3XX_QSPI_RBDR(0);
/* Perform a POP operation on the Rx buffer,
* removing Rx_watermark entries
*/
putreg32(QSPI_FR_RBDF, S32K3XX_QSPI_FR);
}
}
}
/* FIXME Check for errors reported by the QuadSPI */
#endif
}
static void qspi_setluttype1(uint8_t command, uint8_t instr1, uint8_t oprnd1)
{
/* Unlock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_UNLOCK, S32K3XX_QSPI_LCKCR);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_CMD) |
QSPI_LUT_PAD0_1 |
QSPI_LUT_OPRND0(command) |
QSPI_LUT_INSTR1(instr1) |
QSPI_LUT_PAD1_1 |
QSPI_LUT_OPRND1(oprnd1),
S32K3XX_QSPI_LUT10);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_STOP) |
QSPI_LUT_PAD0_1 |
QSPI_LUT_OPRND0(0) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_STOP) |
QSPI_LUT_PAD1_1 |
QSPI_LUT_OPRND1(0),
S32K3XX_QSPI_LUT11);
/* Lock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_LOCK, S32K3XX_QSPI_LCKCR);
}
static inline void qspi_setluttype2(uint8_t command)
{
/* Unlock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_UNLOCK, S32K3XX_QSPI_LCKCR);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_CMD) |
QSPI_LUT_PAD0_1 |
QSPI_LUT_OPRND0(command) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_STOP) |
QSPI_LUT_PAD1_1 |
QSPI_LUT_OPRND1(0),
S32K3XX_QSPI_LUT15);
/* Lock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_LOCK, S32K3XX_QSPI_LCKCR);
}
/****************************************************************************
* Name: qspi_setup_lut_cmd
*
* Description:
* Setup our transaction descriptor from a command info structure
*
* Input Parameters:
* xctn - the transaction descriptor we setup
* cmdinfo - the command info (originating from the MTD device)
*
* Returned Value:
* OK, or -errno if invalid
*
****************************************************************************/
static int qspi_setup_lut_cmd(const struct qspi_cmdinfo_s *cmdinfo)
{
int type = -1;
#ifdef CONFIG_DEBUG_SPI_INFO
spiinfo("Transfer:\n");
spiinfo(" flags: %02x\n", cmdinfo->flags);
spiinfo(" cmd: %04x\n", cmdinfo->cmd);
if (QSPICMD_ISADDRESS(cmdinfo->flags))
{
spiinfo(" address/length: %08lx/%d\n",
(unsigned long)cmdinfo->addr, cmdinfo->addrlen);
}
if (QSPICMD_ISDATA(cmdinfo->flags))
{
spiinfo(" %s Data:\n",
QSPICMD_ISWRITE(cmdinfo->flags) ? "Write" : "Read");
spiinfo(" buffer/length: %p/%d\n",
cmdinfo->buffer, cmdinfo->buflen);
}
#endif
DEBUGASSERT(cmdinfo->cmd < 256);
/* Specify the instruction as per command info */
if (QSPICMD_ISDATA(cmdinfo->flags))
{
if (QSPICMD_ISREAD(cmdinfo->flags))
{
qspi_setluttype1(cmdinfo->cmd,
QSPI_LUT_CMD_READ, cmdinfo->buflen);
}
else if (QSPICMD_ISWRITE(cmdinfo->flags))
{
qspi_setluttype1(cmdinfo->cmd,
QSPI_LUT_CMD_WRITE, cmdinfo->buflen);
}
type = QSPI_LUT_SHARE_TYPE1;
}
else if (QSPICMD_ISADDRESS(cmdinfo->flags))
{
qspi_setluttype1(cmdinfo->cmd,
QSPI_LUT_CMD_ADDR, cmdinfo->addrlen * 8);
type = QSPI_LUT_SHARE_TYPE1;
}
else if (cmdinfo->flags == 0)
{
qspi_setluttype2(cmdinfo->cmd);
type = QSPI_LUT_SHARE_TYPE2;
}
return type;
}
#if defined(CONFIG_S32K3XX_QSPI_INTERRUPTS)
/****************************************************************************
* Name: qspi_interrupt
*
* Description:
* Interrupt handler; we handle all QSPI cases -- reads, writes,
* automatic status polling, etc.
*
* Input Parameters:
* irq -
* context -
*
* Returned Value:
* OK means we handled it
*
****************************************************************************/
static int qspi_interrupt(int irq, void *context, void *arg)
{
uint32_t regval;
regval = getreg32(S32K3XX_QSPI_FR);
if (regval & QSPI_FR_TFF)
{
nxsem_post(&g_qspi0dev.op_sem);
/* Disable transfer interrupt */
regval = getreg32(S32K3XX_QSPI_RSER);
regval &= ~(QSPI_RSER_TFIE);
putreg32(regval, S32K3XX_QSPI_RSER);
}
if (regval & QSPI_FR_RBDF)
{
nxsem_post(&g_qspi0dev.op_sem);
/* Disable rx interrupt */
regval = getreg32(S32K3XX_QSPI_RSER);
regval &= ~(QSPI_RSER_RBDIE);
putreg32(regval, S32K3XX_QSPI_RSER);
}
if (regval & QSPI_FR_TBFF)
{
nxsem_post(&g_qspi0dev.op_sem);
/* Disable transfer interrupt */
regval = getreg32(S32K3XX_QSPI_RSER);
regval &= ~(QSPI_RSER_TBFIE);
putreg32(regval, S32K3XX_QSPI_RSER);
}
return 0;
}
#endif
static inline void qspi_setlut_read(uint8_t command, uint8_t length)
{
/* Unlock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_UNLOCK, S32K3XX_QSPI_LCKCR);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_CMD) |
QSPI_LUT_PAD0_1 |
QSPI_LUT_OPRND0(command) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_ADDR) |
QSPI_LUT_PAD1_4 |
QSPI_LUT_OPRND1(24),
S32K3XX_QSPI_LUT0);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_MODE) |
QSPI_LUT_PAD0_4 |
QSPI_LUT_OPRND0(0) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_DUMMY) |
QSPI_LUT_PAD1_4 |
QSPI_LUT_OPRND1(4),
S32K3XX_QSPI_LUT1);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_READ) |
QSPI_LUT_PAD0_4 |
QSPI_LUT_OPRND0(length) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_STOP),
S32K3XX_QSPI_LUT2);
/* Lock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_LOCK, S32K3XX_QSPI_LCKCR);
}
#if 1
/****************************************************************************
* Name: qspi_receive_blocking
*
* Description:
* Do common data receive in a blocking (status polling) way
*
* Input Parameters:
* priv - The QSPI controller to dump
* xctn - the transaction descriptor
*
* Returned Value:
* OK, or -errno on error
*
****************************************************************************/
static int qspi_receive_blocking(struct s32k3xx_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
uint32_t readlen;
uint32_t remaining = meminfo->buflen;
uint8_t *data = meminfo->buffer;
uint32_t regval;
int ret = 0;
readlen = MIN(128, remaining);
/* Copy sequence in LUT registers */
qspi_setlut_read(meminfo->cmd, readlen);
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* clear leftover flags from previous transfers */
putreg32(QSPI_FR_RBDF, S32K3XX_QSPI_FR);
#endif
while (remaining > 0)
{
/* Clear RX fifo */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_RXF;
putreg32(regval, S32K3XX_QSPI_MCR);
readlen = MIN(128, remaining);
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* enable end of transfer interrupt for asynchronous transfers */
regval = getreg32(S32K3XX_QSPI_RSER);
regval |= QSPI_RSER_RBDIE;
putreg32(regval, S32K3XX_QSPI_RSER);
#endif
/* Set read address */
putreg32(QSPI_AMBA_BASE +
meminfo->addr + (meminfo->buflen - remaining),
S32K3XX_QSPI_SFAR);
/* Trigger IP command with specified sequence and size */
putreg32((QSPICR_SEQID(QSPI_LUT_READ) | QSPICR_IDATSZ(readlen)),
S32K3XX_QSPI_IPCR);
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
nxsem_wait(&priv->op_sem);
#endif
/* Wait until the command is sent */
while (qspi_isbusy())
{
}
/* Flush the contents of the modified RXBDR into physical
* memory.
*/
up_clean_dcache((uintptr_t)S32K3XX_QSPI_RBDR(0),
(uintptr_t)S32K3XX_QSPI_RBDR(0) + readlen);
UP_MB();
memcpy(data, (void *)S32K3XX_QSPI_RBDR(0), readlen);
data += readlen;
remaining -= readlen;
}
return ret;
}
#endif
#ifdef CONFIG_S32K3XX_QSPI_DMA
/****************************************************************************
* Name: qspi_receive
*
* Description:
* Do common data receive using DMA
*
* Input Parameters:
* priv - The QSPI controller to dump
* meminfo - the transaction descriptor
*
* Returned Value:
* OK, or -errno on error
*
****************************************************************************/
static int qspi_receive(struct s32k3xx_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
uint32_t regval;
int ret = 0;
/* Copy sequence in LUT registers */
qspi_setlut_read(meminfo->cmd, meminfo->buflen);
putreg32(QSPI_RBCT_WMRK(3), S32K3XX_QSPI_RBCT);
/* Clear RX fifo */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_RXF;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Set read address */
putreg32(QSPI_AMBA_BASE + meminfo->addr, S32K3XX_QSPI_SFAR);
/* Set up the DMA */
struct s32k3xx_edma_xfrconfig_s config;
config.saddr = S32K3XX_QSPI_RBDR(0);
config.daddr = (uint32_t) (meminfo->buffer);
config.soff = 0;
config.doff = 16;
config.iter = meminfo->buflen / 16;
config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE;
config.ssize = EDMA_16BYTE;
config.dsize = EDMA_16BYTE;
config.nbytes = 16;
#ifdef CONFIG_KINETIS_EDMA_ELINK
config.linkch = NULL;
#endif
config.bwc = 0;
s32k3xx_dmach_xfrsetup(priv->rxdma, &config);
/* Invoke DMA mode */
putreg32(QSPI_RSER_RBDDE, S32K3XX_QSPI_RSER);
/* Start the DMA */
qspi_dmarxstart(priv);
/* Trigger IP command with specified sequence and size */
putreg32((QSPICR_SEQID(QSPI_LUT_READ) | QSPICR_IDATSZ(meminfo->buflen)),
S32K3XX_QSPI_IPCR);
ret = qspi_dmarxwait(priv);
putreg32(0, S32K3XX_QSPI_RSER);
putreg32(QSPI_RBCT_WMRK(0), S32K3XX_QSPI_RBCT);
up_clean_dcache((uintptr_t)meminfo->addr,
(uintptr_t)meminfo->addr + meminfo->buflen);
return ret;
}
#endif
static inline void qspi_setlut_write(uint8_t command)
{
/* Unlock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_UNLOCK, S32K3XX_QSPI_LCKCR);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_CMD) |
QSPI_LUT_PAD0_1 |
QSPI_LUT_OPRND0(command) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_ADDR) |
QSPI_LUT_PAD1_4 |
QSPI_LUT_OPRND1(24),
S32K3XX_QSPI_LUT5);
putreg32(QSPI_LUT_INSTR0(QSPI_LUT_CMD_WRITE) |
QSPI_LUT_PAD0_4 |
QSPI_LUT_OPRND0(16) |
QSPI_LUT_INSTR1(QSPI_LUT_CMD_STOP),
S32K3XX_QSPI_LUT6);
/* Lock LUT */
putreg32(QSPI_LUTKEY_KEY, S32K3XX_QSPI_LUTKEY);
putreg32(QSPI_LKCR_LOCK, S32K3XX_QSPI_LCKCR);
}
#ifndef CONFIG_S32K3XX_QSPI_DMA
/****************************************************************************
* Name: qspi_transmit_blocking
*
* Description:
* Do common data transmit in a blocking (status polling) way
*
* Input Parameters:
* priv - The QSPI controller to dump
* xctn - the transaction descriptor
*
* Returned Value:
* OK, or -errno on error
*
****************************************************************************/
static int qspi_transmit_blocking(struct s32k3xx_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
int32_t remaining = meminfo->buflen;
uint32_t regval;
uint32_t count = UINT32_MAX;
uint32_t *data = (uint32_t *)meminfo->buffer;
uint32_t write_cycle = MIN(32, ((uint32_t)remaining) >> 2U);
uint32_t timeout = 1000;
int ret = 0;
/* Copy sequence in LUT registers */
qspi_setlut_write(meminfo->cmd);
/* Reset serial flash and AHB */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD;
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~(QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD);
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Set read address */
putreg32(QSPI_AMBA_BASE + meminfo->addr, S32K3XX_QSPI_SFAR);
/* Clear TX fifo */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_TXF;
putreg32(regval, S32K3XX_QSPI_MCR);
do /* Wait for fifo clear otherwise timeout */
{
timeout--;
}
while ((getreg32(S32K3XX_QSPI_TBSR) & QSPI_TBSR_TRBLF_MASK) != 0
&& timeout > 0);
if (timeout == 0)
{
return -ETIMEDOUT;
}
spiinfo("Transmit: %" PRIu32 " size: %" PRIu32 "\n",
meminfo->addr, meminfo->buflen);
for (count = 0U; count < write_cycle; count++)
{
putreg32(*data, S32K3XX_QSPI_TBDR);
data++;
remaining -= 4U;
}
/* Trigger IP command with specified sequence and size */
putreg32((QSPICR_SEQID(QSPI_LUT_WRITE) | QSPICR_IDATSZ(meminfo->buflen)),
S32K3XX_QSPI_IPCR);
while (remaining > 0)
{
while ((getreg32(S32K3XX_QSPI_FR) & QSPI_FR_TBFF) != QSPI_FR_TBFF)
{
}
putreg32(*data, S32K3XX_QSPI_TBDR);
data++;
remaining -= 4U;
}
/* Wait until the command is sent */
while (qspi_isbusy())
{
}
return ret;
}
#endif
#ifdef CONFIG_S32K3XX_QSPI_DMA
/****************************************************************************
* Name: qspi_transmit
*
* Description:
* Do common data transmit in a blocking (status polling) way
*
* Input Parameters:
* priv - The QSPI controller to dump
* xctn - the transaction descriptor
*
* Returned Value:
* OK, or -errno on error
*
****************************************************************************/
static int qspi_transmit(struct s32k3xx_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
int32_t remaining = meminfo->buflen;
uint32_t regval;
uint32_t count = UINT32_MAX;
int ret = 0;
/* Copy sequence in LUT registers */
qspi_setlut_write(meminfo->cmd);
/* Reset serial flash and AHB */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD;
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~(QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD);
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Clear TX fifo */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_TXF;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Set read address */
putreg32(QSPI_AMBA_BASE + meminfo->addr, S32K3XX_QSPI_SFAR);
up_clean_dcache((uintptr_t)meminfo->buffer,
(uintptr_t)meminfo->buffer + meminfo->buflen);
/* Set up the DMA */
uint32_t adjust = 1;
struct s32k3xx_edma_xfrconfig_s config;
config.saddr = (uint32_t) (meminfo->buffer);
config.daddr = S32K3XX_QSPI_TBDR;
config.soff = 4;
config.doff = 0;
config.iter = meminfo->buflen / 4;
config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE;
config.ssize = EDMA_32BIT;
config.dsize = EDMA_32BIT;
config.nbytes = 4;
#ifdef CONFIG_KINETIS_EDMA_ELINK
config.linkch = NULL;
#endif
config.bwc = 0x2;
s32k3xx_dmach_xfrsetup(priv->txdma, &config);
/* Invoke DMA mode */
putreg32(QSPI_RSER_TBFDE, S32K3XX_QSPI_RSER);
/* Start the DMA */
qspi_dmatxstart(priv);
/* Trigger IP command with specified sequence and size */
putreg32((QSPICR_SEQID(QSPI_LUT_WRITE) | QSPICR_IDATSZ(meminfo->buflen)),
S32K3XX_QSPI_IPCR);
ret = qspi_dmatxwait(priv);
putreg32(0, S32K3XX_QSPI_RSER);
return ret;
}
#endif
/****************************************************************************
* Name: 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 qspi_lock(struct qspi_dev_s *dev, bool lock)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_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: 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 qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_qspidev_s *)dev;
uint32_t actual = 0;
uint32_t prescaler;
(void)prescaler;
(void)priv;
/* FIXME add support for frequency switching,
* typically reads can be higher the nwrites
*/
return actual;
}
/****************************************************************************
* Name: qspi_setmode
*
* Description:
* Set the QSPI mode. Optional. See enum qspi_mode_e for mode definitions.
* NOTE: the S32K3XX QSPI supports only modes 0 and 3.
*
* Input Parameters:
* dev - Device-specific state data
* mode - The QSPI mode requested
*
* Returned Value:
* none
*
****************************************************************************/
static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_qspidev_s *)dev;
uint32_t regval;
if (priv->memmap)
{
/* XXX we have no better return here, but the caller will find out
* in their subsequent calls.
*/
return;
}
spiinfo("mode=%d\n", mode);
/* Has the mode changed? */
if (mode != priv->mode)
{
/* Yes... Set the mode appropriately:
*
* QSPI CPOL CPHA
* MODE
* 0 0 0
* 1 0 1
* 2 1 0
* 3 1 1
*/
switch (mode)
{
case QSPIDEV_MODE0: /* CPOL=0; CPHA=0 */
break;
case QSPIDEV_MODE3: /* CPOL=1; CPHA=1 */
break;
case QSPIDEV_MODE1: /* CPOL=0; CPHA=1 */
case QSPIDEV_MODE2: /* CPOL=1; CPHA=0 */
spiinfo("unsupported mode=%d\n", mode);
default:
DEBUGASSERT(FALSE);
return;
}
spiinfo("DCR=%08" PRIx32 "\n", regval);
/* Save the mode so that subsequent re-configurations will be faster */
priv->mode = mode;
}
}
/****************************************************************************
* Name: qspi_setbits
*
* Description:
* Set the number if bits per word.
* NOTE: the S32K3XX 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 qspi_setbits(struct qspi_dev_s *dev, int nbits)
{
/* Not meaningful for the S32K3XXx6 */
if (8 != nbits)
{
spiinfo("unsupported nbits=%d\n", nbits);
DEBUGASSERT(FALSE);
}
}
/****************************************************************************
* Name: qspi_command
*
* Description:
* Perform one QSPI data transfer
*
* 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 qspi_command(struct qspi_dev_s *dev,
struct qspi_cmdinfo_s *cmdinfo)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_qspidev_s *)dev;
int ret;
/* We assume cmd buflen should not be greater then 128 */
DEBUGASSERT(cmdinfo->buflen < 128);
/* Reject commands issued while in memory mapped mode, which will
* automatically cancel the memory mapping. You must exit the
* memory mapped mode first.
*/
if (priv->memmap)
{
return -EBUSY;
}
/* Setup LUT sequence */
ret = qspi_setup_lut_cmd(cmdinfo);
if (ret < 0)
{
return -1;
}
/* Prepare for transaction */
if (QSPICMD_ISWRITE(cmdinfo->flags))
{
qspi_ipwrite(ret, 0, cmdinfo->buffer, cmdinfo->buflen);
}
else if(QSPICMD_ISADDRESS(cmdinfo->flags))
{
qspi_ipread(ret, cmdinfo->addr, cmdinfo->buffer, 1);
}
else
{
qspi_ipread(ret, 0, cmdinfo->buffer, cmdinfo->buflen);
}
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* Wait for the interrupt routine to finish it's magic */
nxsem_wait(&priv->op_sem);
UP_MB();
if (QSPICMD_ISREAD(cmdinfo->flags))
{
uint32_t regval;
for (regval = 0; regval < cmdinfo->buflen; regval += 4)
{
((uint32_t *)cmdinfo->buffer)[regval] =
*(uint32_t *)S32K3XX_QSPI_RBDR(regval);
}
/* Reset Rx queue */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_CLR_RXF;
putreg32(regval, S32K3XX_QSPI_MCR);
}
#endif
return OK;
}
/****************************************************************************
* Name: 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 qspi_memory(struct qspi_dev_s *dev,
struct qspi_meminfo_s *meminfo)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_qspidev_s *)dev;
int ret;
/* Reject commands issued while in memory mapped mode, which will
* automatically cancel the memory mapping. You must exit the
* memory mapped mode first.
*/
if (priv->memmap)
{
return -EBUSY;
}
if (QSPIMEM_ISWRITE(meminfo->flags))
{
#ifdef CONFIG_S32K3XX_QSPI_DMA
ret = qspi_transmit(priv, meminfo);
#else
ret = qspi_transmit_blocking(priv, meminfo);
#endif
}
else
{
#ifdef CONFIG_S32K3XX_QSPI_DMA
ret = qspi_receive(priv, meminfo);
#else
ret = qspi_receive_blocking(priv, meminfo);
#endif
}
UP_MB();
return ret;
}
/****************************************************************************
* Name: 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 *qspi_alloc(struct qspi_dev_s *dev, size_t buflen)
{
/* Here we exploit the carnal knowledge the kmm_malloc() will return memory
* aligned to 64-bit addresses. The buffer length must be large enough to
* hold the rested buflen in units a 32-bits.
* Ensure that the DMA buffers are word-aligned.
*/
return kmm_malloc(ALIGN_UP(buflen, 4));
}
/****************************************************************************
* Name: 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 qspi_free(struct qspi_dev_s *dev, void *buffer)
{
if (buffer)
{
kmm_free(buffer);
}
}
static void qspi_setmemmapsizea(uint32_t sizea1, uint32_t sizea2)
{
putreg32(QSPI_AMBA_BASE + sizea1, S32K3XX_QSPI_SFA1AD);
putreg32(QSPI_AMBA_BASE + sizea1 + sizea2, S32K3XX_QSPI_SFA2AD);
}
/****************************************************************************
* Name: 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 qspi_hw_initialize(struct s32k3xx_qspidev_s *priv)
{
uint32_t regval;
/* Disable the QSPI */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Set all registers to reset value */
qspi_resetregisters();
/* Configure QSPI controller */
qspi_setmemmapsizea(8388608U, 0);
/* Sample register */
putreg32(QSPI_SMPR_DLLFSMPFA(4), S32K3XX_QSPI_SMPR);
/* CS Hold & Setup time & DLL tapselect */
putreg32(QSPI_FLSHCR_TCSS(4) | QSPI_FLSHCR_TCSH(4),
S32K3XX_QSPI_FLSHCR);
/* Buffer configuration IP/AHB */
regval = getreg32(S32K3XX_QSPI_RBCT);
regval |= QSPI_RBCT_RXBRD_IP;
putreg32(regval, S32K3XX_QSPI_RBCT);
/* TX watermark */
regval = getreg32(S32K3XX_QSPI_TBCT);
regval |= QSPI_TBCT_WMRK(1);
putreg32(regval, S32K3XX_QSPI_TBCT);
/* RX watermark */
regval = getreg32(S32K3XX_QSPI_RBCT);
regval |= QSPI_RBCT_WMRK(0);
putreg32(regval, S32K3XX_QSPI_RBCT);
/* Set DQS Source */
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~QSPI_MCR_DQS_FA_SEL_MASK;
regval |= QSPI_MCR_DQS_FA_SEL_LOOPBACK;
putreg32(regval, S32K3XX_QSPI_MCR);
/* SCK pin S32K3XX pin options */
putreg32(QSPI_SOCCR_IBE | QSPI_SOCCR_OBE | QSPI_SOCCR_DSE,
S32K3XX_QSPI_SOCCR);
/* AHB configuration */
putreg32(QSPI_BUFCR_ADATSZ(64U >> 3)
| QSPI_BUFCR_MSTRID(0), S32K3XX_QSPI_BUF0CR);
putreg32(QSPI_BUFCR_ADATSZ(64U >> 3)
| QSPI_BUFCR_MSTRID(1), S32K3XX_QSPI_BUF1CR);
putreg32(QSPI_BUFCR_ADATSZ(64U >> 3)
| QSPI_BUFCR_MSTRID(2), S32K3XX_QSPI_BUF2CR);
putreg32(QSPI_BUFCR_ADATSZ(64U >> 3) | QSPI_BUFCR_MSTRID(3)
| QSPI_BUF3CR_ALLMST, S32K3XX_QSPI_BUF3CR);
putreg32(64U, S32K3XX_QSPI_BUF0IND);
putreg32(128U, S32K3XX_QSPI_BUF1IND);
putreg32(192U, S32K3XX_QSPI_BUF2IND);
/* Enable QSPI */
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Reset serial flash and AHB */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD;
putreg32(regval, S32K3XX_QSPI_MCR);
/* FIXME maybe add delay here */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~(QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD);
putreg32(regval, S32K3XX_QSPI_MCR);
regval = getreg32(S32K3XX_QSPI_MCR);
regval &= ~QSPI_MCR_MDIS;
putreg32(regval, S32K3XX_QSPI_MCR);
/* FIXME maybe add delay here */
/* Configure DLL */
regval = getreg32(S32K3XX_QSPI_DLLCRA);
regval |= (QSPI_DLLCRA_SLV_EN | QSPI_DLLCRA_SLV_DLL_BYPASS |
QSPI_DLLCRA_SLV_DLY(0) | QSPI_DLLCRA_SLV_DLY_COARSE (4));
putreg32(regval, S32K3XX_QSPI_DLLCRA);
regval = getreg32(S32K3XX_QSPI_DLLCRA);
regval |= QSPI_DLLCRA_SLV_UPD;
putreg32(regval, S32K3XX_QSPI_DLLCRA);
return OK;
}
/****************************************************************************
* Name: spi_dmarxwait
*
* Description:
* Wait for DMA to complete.
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static int qspi_dmarxwait(struct s32k3xx_qspidev_s *priv)
{
int ret;
/* Take the semaphore (perhaps waiting). If the result is zero, then the
* DMA must not really have completed.
*/
do
{
ret = nxsem_wait_uninterruptible(&priv->rxsem);
/* The only expected error is ECANCELED which would occur if the
* calling thread were canceled.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (priv->rxresult == 0 && ret == OK);
return ret;
}
#endif
/****************************************************************************
* Name: spi_dmatxwait
*
* Description:
* Wait for DMA to complete.
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static int qspi_dmatxwait(struct s32k3xx_qspidev_s *priv)
{
int ret;
/* Take the semaphore (perhaps waiting). If the result is zero, then the
* DMA must not really have completed.
*/
do
{
ret = nxsem_wait_uninterruptible(&priv->txsem);
/* The only expected error is ECANCELED which would occur if the
* calling thread were canceled.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (priv->txresult == 0 && ret == OK);
return ret;
}
#endif
/****************************************************************************
* Name: spi_dmarxwakeup
*
* Description:
* Signal that DMA is complete
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static inline void qspi_dmarxwakeup(struct s32k3xx_qspidev_s *priv)
{
nxsem_post(&priv->rxsem);
}
#endif
/****************************************************************************
* Name: spi_dmatxwakeup
*
* Description:
* Signal that DMA is complete
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static inline void qspi_dmatxwakeup(struct s32k3xx_qspidev_s *priv)
{
nxsem_post(&priv->txsem);
}
#endif
/****************************************************************************
* Name: spi_dmarxcallback
*
* Description:
* Called when the RX DMA completes
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static void qspi_dmarxcallback(DMACH_HANDLE handle, void *arg, bool done,
int result)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_qspidev_s *)arg;
priv->rxresult = result | 0x80000000; /* assure non-zero */
qspi_dmarxwakeup(priv);
}
#endif
/****************************************************************************
* Name: spi_dmatxcallback
*
* Description:
* Called when the RX DMA completes
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static void qspi_dmatxcallback(DMACH_HANDLE handle, void *arg, bool done,
int result)
{
struct s32k3xx_qspidev_s *priv = (struct s32k3xx_qspidev_s *)arg;
/* Wake-up the SPI driver */
priv->txresult = result | 0x80000000; /* assure non-zero */
qspi_dmatxwakeup(priv);
}
#endif
/****************************************************************************
* Name: spi_dmarxstart
*
* Description:
* Start RX DMA
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static inline void qspi_dmarxstart(struct s32k3xx_qspidev_s *priv)
{
priv->rxresult = 0;
s32k3xx_dmach_start(priv->rxdma, qspi_dmarxcallback, priv);
}
#endif
/****************************************************************************
* Name: spi_dmatxstart
*
* Description:
* Start TX DMA
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_QSPI_DMA
static inline void qspi_dmatxstart(struct s32k3xx_qspidev_s *priv)
{
priv->txresult = 0;
s32k3xx_dmach_start(priv->txdma, qspi_dmatxcallback, priv);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: s32k3xx_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 *s32k3xx_qspi_initialize(int intf)
{
struct s32k3xx_qspidev_s *priv;
uint32_t regval;
int ret;
/* The S32K3XX has only a single QSPI port */
spiinfo("intf: %d\n", intf);
DEBUGASSERT(intf == 0);
/* Select the QSPI interface */
if (intf == 0)
{
/* If this function is called multiple times, the following operations
* will be performed multiple times.
*/
/* Select QSPI0 */
priv = &g_qspi0dev;
/* Reset the QSPI peripheral */
regval = getreg32(S32K3XX_QSPI_MCR);
regval |= QSPI_MCR_SWRSTSD | QSPI_MCR_SWRSTHD;
putreg32(regval, S32K3XX_QSPI_MCR);
/* Configure multiplexed pins as connected on the board. */
s32k3xx_pinconfig(PIN_QSPI_PCSFA | PIN_OUTPUT_HIGHDRIVE);
s32k3xx_pinconfig(PIN_QSPI_IOFA0 | PIN_OUTPUT_HIGHDRIVE);
s32k3xx_pinconfig(PIN_QSPI_IOFA1 | PIN_OUTPUT_HIGHDRIVE);
s32k3xx_pinconfig(PIN_QSPI_IOFA2 | PIN_OUTPUT_HIGHDRIVE);
s32k3xx_pinconfig(PIN_QSPI_IOFA3 | PIN_OUTPUT_HIGHDRIVE);
s32k3xx_pinconfig(PIN_QSPI_SCKFA | PIN_OUTPUT_HIGHDRIVE);
}
else
{
spierr("ERROR: QSPI%d not supported\n", intf);
return NULL;
}
/* Has the QSPI hardware been initialized? */
if (!priv->initialized)
{
/* Now perform one time initialization. */
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
/* Attach the interrupt handler */
ret = irq_attach(priv->irq, priv->handler, NULL);
if (ret < 0)
{
spierr("ERROR: Failed to attach irq %d\n", priv->irq);
}
#endif
/* Perform hardware initialization. Puts the QSPI into an active
* state.
*/
ret = qspi_hw_initialize(priv);
if (ret < 0)
{
spierr("ERROR: Failed to initialize QSPI hardware\n");
}
/* Enable interrupts at the NVIC */
priv->initialized = true;
priv->memmap = false;
#ifdef CONFIG_S32K3XX_QSPI_INTERRUPTS
up_enable_irq(priv->irq);
#endif
#ifdef CONFIG_S32K3XX_QSPI_DMA
/* Initialize the QSPI dma channel. */
if (priv->rxch && priv->txch)
{
if (priv->txdma == NULL && priv->rxdma == NULL)
{
priv->txdma = s32k3xx_dmach_alloc(priv->txch
| DMAMUX_CHCFG_ENBL, 0);
priv->rxdma = s32k3xx_dmach_alloc(priv->rxch
| DMAMUX_CHCFG_ENBL, 0);
DEBUGASSERT(priv->rxdma && priv->txdma);
}
}
else
{
priv->rxdma = NULL;
priv->txdma = NULL;
}
#endif
}
return &priv->qspi;
}
#endif /* CONFIG_S32K3XX_QSPI */