From 03064b970172ddde16779fe71f4f8eb34a2a50af Mon Sep 17 00:00:00 2001 From: Janne Rosberg Date: Tue, 7 Nov 2023 18:04:11 +0200 Subject: [PATCH] sama5: add support for QSPI --- arch/arm/include/sama5/chip.h | 1 + arch/arm/src/sama5/Kconfig | 68 +- arch/arm/src/sama5/Make.defs | 8 + arch/arm/src/sama5/hardware/sam_qspi.h | 266 ++++ arch/arm/src/sama5/sam_qspi.c | 1907 ++++++++++++++++++++++++ arch/arm/src/sama5/sam_qspi.h | 90 ++ 6 files changed, 2339 insertions(+), 1 deletion(-) create mode 100644 arch/arm/src/sama5/hardware/sam_qspi.h create mode 100644 arch/arm/src/sama5/sam_qspi.c create mode 100644 arch/arm/src/sama5/sam_qspi.h diff --git a/arch/arm/include/sama5/chip.h b/arch/arm/include/sama5/chip.h index 68b377042f..38620d8545 100644 --- a/arch/arm/include/sama5/chip.h +++ b/arch/arm/include/sama5/chip.h @@ -105,6 +105,7 @@ #endif # define SAM_NDMAC 2 /* (2) XDMA controllers */ # define SAM_NDMACHAN 16 /* (16) DMA channels per XDMA controller */ +# define SAM_NQSPI 2 /* (2) QuadSPI controllers */ /* SAMA5D3 Family * diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index f128387f36..078d031ef6 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -149,6 +149,14 @@ config SAMA5_HAVE_PIOE bool default n +config SAMA5_HAVE_QSPI0 + bool + default n + +config SAMA5_HAVE_QSPI1 + bool + default n + config SAMA5_HAVE_SAIC bool default n @@ -250,7 +258,8 @@ config ARCH_CHIP_SAMA5D2 select SAMA5_HAVE_FLEXCOM2 select SAMA5_HAVE_FLEXCOM3 select SAMA5_HAVE_FLEXCOM4 - select SAMA5_HAVE_QSPI + select SAMA5_HAVE_QSPI0 + select SAMA5_HAVE_QSPI1 select SAMA5_HAVE_XDMA select SAMA5_HAVE_SAIC select SAMA5_HAVE_SFC @@ -651,6 +660,16 @@ config SAMA5_SPI2 default n depends on SAMA5_HAVE_SPI2 +config SAMA5_QSPI0 + bool "Quad SPI interface 0 (QSPI0)" + default n + depends on SAMA5_HAVE_QSPI0 + +config SAMA5_QSPI1 + bool "Quad SPI interface 1 (QSPI1)" + default n + depends on SAMA5_HAVE_QSPI1 + config SAMA5_TC0 bool "Timer Counter 0 (ch. 0, 1, 2) (TC0)" default n @@ -3459,6 +3478,53 @@ config SAMA5_FLEXCOM_SPI_DMADEBUG endmenu # Flexcom SPI device driver options endif # SAMA5_FLEXCOM0_SPI || SAMA5_FLEXCOM1_SPI || SAMA5_FLEXCOM2_SPI || SAMA5_FLEXCOM3_SPI || SAMA5_FLEXCOM4_SPI +menu "QSPI Device Driver Configuration" + depends on SAMA5_QSPI0 || SAMA5_QSPI1 + +config SAMA5_QSPI_DLYBS + int "Delay Before QSCK (nsec)" + default 0 + +config SAMA5_QSPI_DLYBCT + int "Delay Between Consecutive Transfers (nsec)" + default 0 + +config SAMA5_QSPI_DMA + bool "QSPI DMA" + default n + depends on SAMA5_XDMAC0 || SAMA5_XDMAC1 + ---help--- + Use XDMA to improve SPI transfer performance. + +config SAMA5_QSPI_DMATHRESHOLD + int "QSPI DMA threshold" + default 4 + depends on SAMA5_QSPI_DMA + ---help--- + When QSPI (X)DMA is enabled, small DMA transfers will still be performed + by polling logic. But we need a threshold value to determine what + is small. That value is provided by SAMA5_QSPI_DMATHRESHOLD. + +config SAMA5_QSPI_DMADEBUG + bool "QSPI DMA transfer debug" + depends on SAMA5_QSPI_DMA && DEBUG_FEATURES && DEBUG_DMA + default n + ---help--- + Enable special debug instrumentation analyze QSPI DMA data transfers. + This logic is as non-invasive as possible: It samples DMA + registers at key points in the data transfer and then dumps all of + the registers at the end of the transfer. + +config SAMA5_QSPI_REGDEBUG + bool "QSPI Register level debug" + depends on DEBUG_SPI_INFO + default n + ---help--- + Output detailed register-level QSPI device debug information. + Requires also CONFIG_DEBUG_SPI_INFO. + +endmenu # QSPI Device Driver Configuration + if SAMA5_TWI0 || SAMA5_TWI1 || SAMA5_TWI2 || SAMA5_TWI3 || SAMA5_FLEXCOM_TWI menu "TWI device driver options" diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index 3b67d9f5cf..63e7f3f5ee 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -93,6 +93,14 @@ else endif endif +ifeq ($(CONFIG_SAMA5_QSPI0),y) +CHIP_CSRCS += sam_qspi.c +else +ifeq ($(CONFIG_SAMA5_QSPI1),y) +CHIP_CSRCS += sam_qspi.c +endif +endif + ifeq ($(CONFIG_SAMA5D2_CLASSD), y) CHIP_CSRCS += sam_classd.c endif diff --git a/arch/arm/src/sama5/hardware/sam_qspi.h b/arch/arm/src/sama5/hardware/sam_qspi.h new file mode 100644 index 0000000000..79a17e5768 --- /dev/null +++ b/arch/arm/src/sama5/hardware/sam_qspi.h @@ -0,0 +1,266 @@ +/**************************************************************************** + * arch/arm/src/sama5/hardware/sam_qspi.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_QSPI_H +#define __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_QSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "hardware/sam_memorymap.h" + +#if SAM_NQSPI > 0 || SAM_NQSPI_SPI > 0 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* General Characteristics **************************************************/ + +#define SAM_QSPI_MINBITS 8 /* Minimum word width */ +#define SAM_QSPI_MAXBITS 16 /* Maximum word width */ + +/* QSPI register offsets ****************************************************/ + +#define SAM_QSPI_CR_OFFSET 0x0000 /* Control Register */ +#define SAM_QSPI_MR_OFFSET 0x0004 /* Mode Register */ +#define SAM_QSPI_RDR_OFFSET 0x0008 /* Receive Data Register */ +#define SAM_QSPI_TDR_OFFSET 0x000c /* Transmit Data Register */ +#define SAM_QSPI_SR_OFFSET 0x0010 /* Status Register */ +#define SAM_QSPI_IER_OFFSET 0x0014 /* Interrupt Enable Register */ +#define SAM_QSPI_IDR_OFFSET 0x0018 /* Interrupt Disable Register */ +#define SAM_QSPI_IMR_OFFSET 0x001c /* Interrupt Mask Register */ +#define SAM_QSPI_SCR_OFFSET 0x0020 /* Serial Clock Register */ +#define SAM_QSPI_IAR_OFFSET 0x0030 /* Instruction Address Register */ +#define SAM_QSPI_ICR_OFFSET 0x0034 /* Instruction Code Register */ +#define SAM_QSPI_IFR_OFFSET 0x0038 /* Instruction Frame Register */ + /* 0x003c Reserved */ +#define SAM_QSPI_SMR_OFFSET 0x0040 /* Scrambling Mode Register */ +#define SAM_QSPI_SKR_OFFSET 0x0044 /* Scrambling Key Register */ + /* 0x0048–0x00e0 Reserved */ +#define SAM_QSPI_WPCR_OFFSET 0x00e4 /* Write Protection Control Register */ +#define SAM_QSPI_WPSR_OFFSET 0x00e8 /* Write Protection Status Register */ + /* 0xec-0xfc: Reserved */ + +/* QSPI register addresses **************************************************/ + +#define SAM_QSPI0_CR (SAM_QSPI0_BASE+SAM_QSPI_CR_OFFSET) /* Control Register */ +#define SAM_QSPI0_MR (SAM_QSPI0_BASE+SAM_QSPI_MR_OFFSET) /* Mode Register */ +#define SAM_QSPI0_RDR (SAM_QSPI0_BASE+SAM_QSPI_RDR_OFFSET) /* Receive Data Register */ +#define SAM_QSPI0_TDR (SAM_QSPI0_BASE+SAM_QSPI_TDR_OFFSET) /* Transmit Data Register */ +#define SAM_QSPI0_SR (SAM_QSPI0_BASE+SAM_QSPI_SR_OFFSET) /* Status Register */ +#define SAM_QSPI0_IER (SAM_QSPI0_BASE+SAM_QSPI_IER_OFFSET) /* Interrupt Enable Register */ +#define SAM_QSPI0_IDR (SAM_QSPI0_BASE+SAM_QSPI_IDR_OFFSET) /* Interrupt Disable Register */ +#define SAM_QSPI0_IMR (SAM_QSPI0_BASE+SAM_QSPI_IMR_OFFSET) /* Interrupt Mask Register */ +#define SAM_QSPI0_SCR (SAM_QSPI0_BASE+SAM_QSPI_SCR_OFFSET) /* Serial Clock Register */ +#define SAM_QSPI0_IAR (SAM_QSPI0_BASE+SAM_QSPI_IAR_OFFSET) /* Instruction Address Register */ +#define SAM_QSPI0_ICR (SAM_QSPI0_BASE+SAM_QSPI_ICR_OFFSET) /* Instruction Code Register */ +#define SAM_QSPI0_IFR (SAM_QSPI0_BASE+SAM_QSPI_IFR_OFFSET) /* Instruction Frame Register */ +#define SAM_QSPI0_SMR (SAM_QSPI0_BASE+SAM_QSPI_SMR_OFFSET) /* Scrambling Mode Register */ +#define SAM_QSPI0_SKR (SAM_QSPI0_BASE+SAM_QSPI_SKR_OFFSET) /* Scrambling Key Register */ +#define SAM_QSPI0_WPCR (SAM_QSPI0_BASE+SAM_QSPI_WPCR_OFFSET) /* Write Protection Control Register */ +#define SAM_QSPI0_WPSR (SAM_QSPI0_BASE+SAM_QSPI_WPSR_OFFSET) /* Write Protection Status Register */ + +#if SAM_NQSPI > 1 +# define SAM_QSPI1_CR (SAM_QSPI1_BASE+SAM_QSPI_CR_OFFSET) /* Control Register */ +# define SAM_QSPI1_MR (SAM_QSPI1_BASE+SAM_QSPI_MR_OFFSET) /* Mode Register */ +# define SAM_QSPI1_RDR (SAM_QSPI1_BASE+SAM_QSPI_RDR_OFFSET) /* Receive Data Register */ +# define SAM_QSPI1_TDR (SAM_QSPI1_BASE+SAM_QSPI_TDR_OFFSET) /* Transmit Data Register */ +# define SAM_QSPI1_SR (SAM_QSPI1_BASE+SAM_QSPI_SR_OFFSET) /* Status Register */ +# define SAM_QSPI1_IER (SAM_QSPI1_BASE+SAM_QSPI_IER_OFFSET) /* Interrupt Enable Register */ +# define SAM_QSPI1_IDR (SAM_QSPI1_BASE+SAM_QSPI_IDR_OFFSET) /* Interrupt Disable Register */ +# define SAM_QSPI1_IMR (SAM_QSPI1_BASE+SAM_QSPI_IMR_OFFSET) /* Interrupt Mask Register */ +# define SAM_QSPI1_SCR (SAM_QSPI1_BASE+SAM_QSPI_SCR_OFFSET) /* Serial Clock Register */ +# define SAM_QSPI1_IAR (SAM_QSPI1_BASE+SAM_QSPI_IAR_OFFSET) /* Instruction Address Register */ +# define SAM_QSPI1_ICR (SAM_QSPI1_BASE+SAM_QSPI_ICR_OFFSET) /* Instruction Code Register */ +# define SAM_QSPI1_IFR (SAM_QSPI1_BASE+SAM_QSPI_IFR_OFFSET) /* Instruction Frame Register */ +# define SAM_QSPI1_SMR (SAM_QSPI1_BASE+SAM_QSPI_SMR_OFFSET) /* Scrambling Mode Register */ +# define SAM_QSPI1_SKR (SAM_QSPI1_BASE+SAM_QSPI_SKR_OFFSET) /* Scrambling Key Register */ +# define SAM_QSPI1_WPCR (SAM_QSPI1_BASE+SAM_QSPI_WPCR_OFFSET) /* Write Protection Control Register */ +# define SAM_QSPI1_WPSR (SAM_QSPI1_BASE+SAM_QSPI_WPSR_OFFSET) /* Write Protection Status Register */ +#endif + +/* QSPI register bit definitions ********************************************/ + +/* QSPI Control Register */ + +#define QSPI_CR_QSPIEN (1 << 0) /* Bit 0: QSPI Enable */ +#define QSPI_CR_QSPIDIS (1 << 1) /* Bit 1: QSPI Disable */ +#define QSPI_CR_SWRST (1 << 7) /* Bit 7: QSPI Software Reset */ +#define QSPI_CR_LASTXFER (1 << 24) /* Bit 24: Last Transfer */ + +/* QSPI Mode Register */ + +#define QSPI_MR_SPI (0 << 0) /* Bit 0: SPI Master Mode */ +#define QSPI_MR_SMM (1 << 0) /* Bit 0: Serial Memory Mode */ +#define QSPI_MR_LLB (1 << 1) /* Bit 1: Local Loopback Enable */ +#define QSPI_MR_WDRBT (1 << 2) /* Bit 2: Wait Data Read Before Transfer */ +#define QSPI_MR_CSMODE_SHIFT (4) /* Bits 4-5: Chip Select Mode */ +#define QSPI_MR_CSMODE_MASK (3 << QSPI_MR_CSMODE_SHIFT) +# define QSPI_MR_CSMODE_NRELOAD (0 << QSPI_MR_CSMODE_SHIFT) /* CS deasserted if TD not reloaded */ +# define QSPI_MR_CSMODE_LASTXFER (1 << QSPI_MR_CSMODE_SHIFT) /* CS deasserted when LASTXFER transferred */ +# define QSPI_MR_CSMODE_SYSTEM (2 << QSPI_MR_CSMODE_SHIFT) /* CS deasserted after each transfer */ + +#define QSPI_MR_NBBITS_SHIFT (8) /* Bits 8-11: Number Of Bits Per Transfer */ +#define QSPI_MR_NBBITS_MASK (15 << QSPI_MR_NBBITS_SHIFT) +# define QSPI_MR_NBBITS(n) ((uint32_t)((n)-SAM_QSPI_MINBITS) << QSPI_MR_NBBITS_SHIFT) +# define QSPI_MR_NBBITS_8BIT (0 << QSPI_MR_NBBITS_SHIFT) /* 8 bits for transfer */ +# define QSPI_MR_NBBITS_9BIT (1 << QSPI_MR_NBBITS_SHIFT) /* 9 bits for transfer */ +# define QSPI_MR_NBBITS_10BIT (2 << QSPI_MR_NBBITS_SHIFT) /* 10 bits for transfer */ +# define QSPI_MR_NBBITS_11BIT (3 << QSPI_MR_NBBITS_SHIFT) /* 11 bits for transfer */ +# define QSPI_MR_NBBITS_12BIT (4 << QSPI_MR_NBBITS_SHIFT) /* 12 bits for transfer */ +# define QSPI_MR_NBBITS_13BIT (5 << QSPI_MR_NBBITS_SHIFT) /* 13 bits for transfer */ +# define QSPI_MR_NBBITS_14BIT (6 << QSPI_MR_NBBITS_SHIFT) /* 14 bits for transfer */ +# define QSPI_MR_NBBITS_15BIT (7 << QSPI_MR_NBBITS_SHIFT) /* 15 bits for transfer */ +# define QSPI_MR_NBBITS_16BIT (8 << QSPI_MR_NBBITS_SHIFT) /* 16 bits for transfer */ + +#define QSPI_MR_DLYBCT_SHIFT (16) /* Bits 16-23: Delay Between Consecutive Transfers */ +#define QSPI_MR_DLYBCT_MASK (0xff << QSPI_MR_DLYBCT_SHIFT) +# define QSPI_MR_DLYBCT(n) ((uint32_t)(n) << QSPI_MR_DLYBCT_SHIFT) +#define QSPI_MR_DLYCS_SHIFT (24) /* Bits 24-31: Minimum Inactive QCS Delay */ +#define QSPI_MR_DLYCS_MASK (0xff << QSPI_MR_DLYCS_SHIFT) +# define QSPI_MR_DLYCS(n) ((uint32_t)(n) << QSPI_MR_DLYCS_SHIFT) + +/* QSPI Receive Data Register */ + +#define QSPI_RDR_RD_SHIFT (0) /* Bits 0-15: Receive Data */ +#define QSPI_RDR_RD_MASK (0xffff << QSPI_RDR_RD_SHIFT) + +/* QSPI Transmit Data Register */ + +#define QSPI_TDR_TD_SHIFT (0) /* Bits 0-15: Transmit Data */ +#define QSPI_TDR_TD_MASK (0xffff << QSPI_TDR_TD_SHIFT) + +/* QSPI Status Register, QSPI Interrupt Enable Register, + * QSPI Interrupt Disable Register, + * and QSPI Interrupt Mask Register (common bit fields) + */ + +#define QSPI_INT_RDRF (1 << 0) /* Bit 0: Receive Data Register Full Interrupt */ +#define QSPI_INT_TDRE (1 << 1) /* Bit 1: Transmit Data Register Empty Interrupt */ +#define QSPI_INT_TXEMPTY (1 << 2) /* Bit 2: Transmission Registers Empty Interrupt */ +#define QSPI_INT_OVRES (1 << 3) /* Bit 3: Overrun Error Interrupt */ +#define QSPI_INT_CSR (1 << 8) /* Bit 8: Chip Select Rise Interrupt */ +#define QSPI_SR_CSS (1 << 9) /* Bit 9: Chip Select Status Interrupt */ +#define QSPI_SR_INSTRE (1 << 10) /* Bit 10: Instruction End Status Interrupt */ +#define QSPI_SR_QSPIENS (1 << 24) /* Bit 24: QSPI Enable Status (SR only) */ + +#define QSPI_INT_ALL (0x0000070f) + +/* Serial Clock Register */ + +#define QSPI_SCR_CPOL (1 << 0) /* Bit 0: Clock Polarity */ +#define QSPI_SCR_CPHA (1 << 1) /* Bit 1: Clock Phase */ +#define QSPI_SCR_SCBR_SHIFT (8) /* Bits 8-15: Serial Clock Baud Rate */ +#define QSPI_SCR_SCBR_MASK (0xff << QSPI_SCR_SCBR_SHIFT) +# define QSPI_SCR_SCBR(n) ((uint32_t)(n) << QSPI_SCR_SCBR_SHIFT) +#define QSPI_SCR_DLYBS_SHIFT (16) /* Bits 16-23: Delay Before QSCK */ +#define QSPI_SCR_DLYBS_MASK (0xff << QSPI_SCR_DLYBS_SHIFT) +# define QSPI_SCR_DLYBS(n) ((uint32_t)(n) << QSPI_SCR_DLYBS_SHIFT) + +/* Instruction Address Register (32-bit value) */ + +/* Instruction Code Register */ + +#define QSPI_ICR_INST_SHIFT (0) /* Bits 0-7: Instruction Code */ +#define QSPI_ICR_INST_MASK (0xff << QSPI_ICR_INST_SHIFT) +# define QSPI_ICR_INST(n) ((uint32_t)(n) << QSPI_ICR_INST_SHIFT) +#define QSPI_ICR_OPT_SHIFT (16) /* Bits 16-23: Option Code */ +#define QSPI_ICR_OPT_MASK (0xff << QSPI_ICR_OPT_SHIFT) +# define QSPI_ICR_OPT(n) ((uint32_t)(n) << QSPI_ICR_OPT_SHIFT) + +/* Instruction Frame Register */ + +#define QSPI_IFR_WIDTH_SHIFT (0) /* Bits 0-2: Width of Instruction Code, + * Address, Option Code and Data */ +#define QSPI_IFR_WIDTH_MASK (7 << QSPI_IFR_WIDTH_SHIFT) + /* Instruction Address-Option Data */ +# define QSPI_IFR_WIDTH_SINGLE (0 << QSPI_IFR_WIDTH_SHIFT) /* Single-bit Single-bit Single-bit */ +# define QSPI_IFR_WIDTH_DUALOUT (1 << QSPI_IFR_WIDTH_SHIFT) /* Single-bit Single-bit Dual */ +# define QSPI_IFR_WIDTH_QUADOUT (2 << QSPI_IFR_WIDTH_SHIFT) /* Single-bit Single-bit Quad */ +# define QSPI_IFR_WIDTH_DUALIO (3 << QSPI_IFR_WIDTH_SHIFT) /* Single-bit Dual Dual */ +# define QSPI_IFR_WIDTH_QUADIO (4 << QSPI_IFR_WIDTH_SHIFT) /* Single-bit Quad Quad */ +# define QSPI_IFR_WIDTH_DUALCMD (5 << QSPI_IFR_WIDTH_SHIFT) /* Dual Dual Dual */ +# define QSPI_IFR_WIDTH_QUADCMD (6 << QSPI_IFR_WIDTH_SHIFT) /* Quad Quad Quad */ + +#define QSPI_IFR_INSTEN (1 << 4) /* Bit 4: Instruction Enable */ +#define QSPI_IFR_ADDREN (1 << 5) /* Bit 5: Address Enable */ +#define QSPI_IFR_OPTEN (1 << 6) /* Bit 6: Option Enable */ +#define QSPI_IFR_DATAEN (1 << 7) /* Bit 7: Data Enable */ +#define QSPI_IFR_OPTL_SHIFT (8) /* Bits 8-9: Option Code Length */ +#define QSPI_IFR_OPTL_MASK (3 << QSPI_IFR_OPTL_SHIFT) +# define QSPI_IFR_OPTL_1BIT (0 << QSPI_IFR_OPTL_SHIFT) /* Option is 1 bit */ +# define QSPI_IFR_OPTL_2BIT (1 << QSPI_IFR_OPTL_SHIFT) /* Option is 2 bits */ +# define QSPI_IFR_OPTL_4BIT (2 << QSPI_IFR_OPTL_SHIFT) /* Option is 4 bits */ +# define QSPI_IFR_OPTL_8BIT (3 << QSPI_IFR_OPTL_SHIFT) /* Option is 8 bits */ + +#define QSPI_IFR_ADDRL (1 << 10) /* Bit 10: Address Length */ +# define QSPI_IFR_ADDRL_24BIT (0 << 10) /* 0=24-bit */ +# define QSPI_IFR_ADDRL_32BIT (1 << 10) /* 1=32-bit */ +#define QSPI_IFR_TFRTYP_SHIFT (12) /* Bits 12-13: Data Transfer Type */ +#define QSPI_IFR_TFRTYP_MASK (3 << QSPI_IFR_TFRTYP_SHIFT) +# define QSPI_IFR_TFRTYP_READ (0 << QSPI_IFR_TFRTYP_SHIFT) /* Read transfer from serial memory */ +# define QSPI_IFR_TFRTYP_RDMEM (1 << QSPI_IFR_TFRTYP_SHIFT) /* Read data transfer from serial memory */ +# define QSPI_IFR_TFRTYP_WRITE (2 << QSPI_IFR_TFRTYP_SHIFT) /* Write transfer into serial memory */ +# define QSPI_IFR_TFRTYP_WRMEM (3 << QSPI_IFR_TFRTYP_SHIFT) /* Write data transfer the serial memory */ + +#define QSPI_IFR_CRM (1 << 14) /* Bit 14: Continuous Read Mode */ +#define QSPI_IFR_NBDUM_SHIFT (16) /* Bits 16-20: Number Of Dummy Cycles */ +#define QSPI_IFR_NBDUM_MASK (31 << QSPI_IFR_NBDUM_SHIFT) +# define QSPI_IFR_NBDUM(n) ((uint32_t)(n) << QSPI_IFR_NBDUM_SHIFT) + +/* Scrambling Mode Register */ + +#define QSPI_SMR_SCREN (1 << 0) /* Bit 0: Scrambling/Unscrambling Enable */ +#define QSPI_SMR_RVDIS (1 << 1) /* Bit 1: Scrambling/Unscrambling Random Value Disable */ + +/* Scrambling Key Register (32-bit value) */ + +/* QSPI Write Protection Control Register */ + +#define QSPI_WPCR_WPEN (1 << 0) /* Bit 0: QSPI Write Protection Enable */ +#define QSPI_WPCR_WPKEY_SHIFT (8) /* Bits 8-31: QSPI Write Protection Key Password */ +#define QSPI_WPCR_WPKEY_MASK (0x00ffffff << QSPI_WPCR_WPKEY_SHIFT) +# define QSPI_WPCR_WPKEY (0x00515350 << QSPI_WPCR_WPKEY_SHIFT) + +/* QSPI Write Protection Status Register */ + +#define QSPI_WPSR_WPVS (1 << 0) /* Bit 0: QSPI Write Protection Violation Status */ +#define QSPI_WPSR_WPVSRC_SHIFT (8) /* Bits 8-15: QSPI Write Protection Violation Source */ +#define QSPI_WPSR_WPVSRC_MASK (0xff << QSPI_WPSR_WPVSRC_SHIFT) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#endif /* SAM_NQSPI > 0 */ +#endif /* __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_QSPI_H */ diff --git a/arch/arm/src/sama5/sam_qspi.c b/arch/arm/src/sama5/sam_qspi.c new file mode 100644 index 0000000000..8fb6c7871e --- /dev/null +++ b/arch/arm/src/sama5/sam_qspi.c @@ -0,0 +1,1907 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "arm_internal.h" +#include "barriers.h" + +#include "sam_pio.h" +#include "sam_dmac.h" +#include "sam_periphclks.h" +#include "sam_qspi.h" +#include "hardware/sam_pmc.h" +#include "hardware/sam_xdmac.h" +#include "hardware/sam_qspi.h" +#include "hardware/sam_pinmap.h" + +#if (defined(CONFIG_SAMA5_QSPI0) || (defined(CONFIG_SAMA5_QSPI1))) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_SAMA5_QSPI_DLYBS +# define CONFIG_SAMA5_QSPI_DLYBS 0 +#endif + +#ifndef CONFIG_SAMA5_QSPI_DLYBCT +# define CONFIG_SAMA5_QSPI_DLYBCT 0 +#endif + +#ifndef CONFIG_DEBUG_SPI_INFO +# undef CONFIG_SAMA5_QSPI_REGDEBUG +#endif + +/* When QSPI DMA is enabled, small DMA transfers will still be performed by + * polling logic. But we need a threshold value to determine what is small. + * That value is provided by CONFIG_SAMA5_QSPI_DMATHRESHOLD. + */ + +#ifndef CONFIG_SAMA5_QSPI_DMATHRESHOLD +# define CONFIG_SAMA5_QSPI_DMATHRESHOLD 4 +#endif + +#ifndef CONFIG_SAMA5_XDMAC0 +# undef CONFIG_SAMA5_QSPI_DMA +#endif + +#ifdef CONFIG_SAMA5_QSPI_DMA +# define SAMA5_QSPI0_DMA true +#endif + +#ifndef CONFIG_SAMA5_QSPI_DMA +# undef CONFIG_SAMA5_QSPI_DMADEBUG +#endif + +/* QSPI interrupts are not used */ + +#undef QSPI_USE_INTERRUPTS + +/* Clocking *****************************************************************/ + +/* The QSPI Baud rate clock is generated by dividing the peripheral clock by + * a value between 1 and 255 + */ + +#define SAM_QSPI_CLOCK BOARD_MCK_FREQUENCY /* Frequency of the main clock */ + +/* DMA timeout. The value is not critical; we just don't want the system to + * hang in the event that a DMA does not finish. This is set to + */ + +#define DMA_TIMEOUT_MS (800) +#define DMA_TIMEOUT_TICKS MSEC2TICK(DMA_TIMEOUT_MS) + +/* QSPI memory synchronization */ + +#define MEMORY_SYNC() do { ARM_DSB(); ARM_ISB(); } while (0) + +/* The SAMA5x QSPI driver insists that transfers be performed in multiples + * of 32-bits. The alignment requirement only applies to RX DMA data. + */ + +#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) + +/* Debug ********************************************************************/ + +/* Check if QSPI debug is enabled */ + +#ifndef CONFIG_DEBUG_DMA +# undef CONFIG_SAMA5_QSPI_DMADEBUG +#endif + +#define DMA_INITIAL 0 +#define DMA_AFTER_SETUP 1 +#define DMA_AFTER_START 2 +#define DMA_CALLBACK 3 +#define DMA_TIMEOUT 3 +#define DMA_END_TRANSFER 4 +#define DMA_NSAMPLES 5 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The state of the QSPI controller. */ + +struct sam_qspidev_s +{ + struct qspi_dev_s qspi; /* Externally visible part of the QSPI interface */ +#ifdef QSPI_USE_INTERRUPTS + xcpt_t handler; /* Interrupt handler */ +#endif + uint32_t base; /* QSPI controller register base address */ + uint32_t membase; /* QSPI virtual memory region base address */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + uint8_t mode; /* Mode 0,1,2,3 */ + uint8_t nbits; /* Width of word in bits (8 to 16) */ + uint8_t intf; /* QSPI controller number (0, 1) */ +#ifdef QSPI_USE_INTERRUPTS + uint8_t irq; /* Interrupt number */ +#endif + bool initialized; /* TRUE: Controller has been initialized */ + mutex_t lock; /* Assures mutually exclusive access to QSPI */ + +#ifdef CONFIG_SAMA5_QSPI_DMA + bool candma; /* DMA is supported */ + uint8_t rxintf; /* RX hardware interface number */ + uint8_t txintf; /* TX hardware interface number */ + sem_t dmawait; /* Used to wait for DMA completion */ + struct wdog_s dmadog; /* Watchdog that handles DMA timeouts */ + int result; /* DMA result */ + DMA_HANDLE dmach; /* QSPI DMA handle */ +#endif + + /* Debug stuff */ + +#ifdef CONFIG_SAMA5_QSPI_DMADEBUG + struct sam_dmaregs_s dmaregs[DMA_NSAMPLES]; +#endif + +#ifdef CONFIG_SAMA5_QSPI_REGDEBUG + bool wrlast; /* Last was a write */ + uint32_t addresslast; /* Last address */ + uint32_t valuelast; /* Last value */ + int ntimes; /* Number of times */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +#ifdef CONFIG_SAMA5_QSPI_REGDEBUG +static bool qspi_checkreg(struct sam_qspidev_s *priv, bool wr, + uint32_t value, uint32_t address); +#else +# define qspi_checkreg(priv,wr,value,address) (false) +#endif + +static inline uint32_t qspi_getreg(struct sam_qspidev_s *priv, + unsigned int offset); +static inline void qspi_putreg(struct sam_qspidev_s *priv, uint32_t value, + unsigned int offset); + +#ifdef CONFIG_DEBUG_SPI_INFO +static void qspi_dumpregs(struct sam_qspidev_s *priv, const char *msg); +#else +# define qspi_dumpregs(priv,msg) +#endif + +/* DMA support */ + +#ifdef CONFIG_SAMA5_QSPI_DMA +#ifdef CONFIG_SAMA5_QSPI_DMADEBUG +# define qspi_dma_sample(s,i) sam_dmasample((s)->dmach, &(s)->dmaregs[i]) +static void qspi_dma_sampleinit(struct sam_qspidev_s *priv); +static void qspi_dma_sampledone(struct sam_qspidev_s *priv); + +#else +# define qspi_dma_sample(s,i) +# define qspi_dma_sampleinit(s) +# define qspi_dma_sampledone(s) + +#endif + +static void qspi_dma_callback(DMA_HANDLE handle, void *arg, int result); +static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv, + unsigned int offset); +#endif + +static int qspi_memory_enable(struct sam_qspidev_s *priv, + struct qspi_meminfo_s *meminfo); +#ifdef CONFIG_SAMA5_QSPI_DMA +static int qspi_memory_dma(struct sam_qspidev_s *priv, + struct qspi_meminfo_s *meminfo); +#endif + +static int qspi_memory_nodma(struct sam_qspidev_s *priv, + struct qspi_meminfo_s *meminfo); +static void qspi_memcpy(uint8_t *dest, const uint8_t *src, + size_t buflen); + +/* Interrupts */ + +#ifdef QSPI_USE_INTERRUPTS +static int qspi_interrupt(struct sam_qspidev_s *priv); +#ifdef CONFIG_SAMA5_QSPI +static int qspi0_interrupt(int irq, void *context, void *arg); +#endif +#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 sam_qspidev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI0 +/* 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 sam_qspidev_s g_qspi0dev = +{ + .qspi = + { + .ops = &g_qspi0ops, + }, + .base = SAM_QSPI0_VBASE, + .membase = SAM_QSPI0_VSECTION, +#ifdef QSPI_USE_INTERRUPTS + .handler = qspi0_interrupt, +#endif + .intf = 0, +#ifdef QSPI_USE_INTERRUPTS + .irq = SAM_IRQ_QSPI0, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_SAMA5_QSPI_DMA + .candma = SAMA5_QSPI0_DMA, + .rxintf = XDMAC0_CH_QSPI0_RX, + .txintf = XDMAC0_CH_QSPI0_TX, + .dmawait = SEM_INITIALIZER(0), +#endif +}; +#endif /* CONFIG_SAMA5_QSPI0 */ + +#ifdef CONFIG_SAMA5_QSPI1 +/* QSPI1 driver operations */ + +static const struct qspi_ops_s g_qspi1ops = + { + .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 QSPI1 controller */ + +static struct sam_qspidev_s g_qspi1dev = + { + .qspi = + { + .ops = &g_qspi1ops, + }, + .base = SAM_QSPI1_VBASE, + .membase = SAM_QSPI1_VSECTION, +#ifdef QSPI_USE_INTERRUPTS + .handler = qspi1_interrupt, +#endif + .intf = 0, +#ifdef QSPI_USE_INTERRUPTS + .irq = SAM_IRQ_QSPI1, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_SAMA5_QSPI_DMA + .candma = SAMA5_QSPI1_DMA, + .rxintf = XDMAC0_CH_QSPI1_RX, + .txintf = XDMAC0_CH_QSPI1_TX, + .dmawait = SEM_INITIALIZER(0), +#endif + }; +#endif /* CONFIG_SAMA5_QSPI1 */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: qspi_checkreg + * + * Description: + * Check if the current register access is a duplicate of the preceding. + * + * Input Parameters: + * value - The value to be written + * address - The address of the register to write to + * + * Returned Value: + * true: This is the first register access of this type. + * flase: This is the same as the preceding register access. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_REGDEBUG +static bool qspi_checkreg(struct sam_qspidev_s *priv, bool wr, + uint32_t value, uint32_t address) +{ + if (wr == priv->wrlast && /* Same kind of access? */ + value == priv->valuelast && /* Same value? */ + address == priv->addresslast) /* Same address? */ + { + /* Yes, then just keep a count of the number of times we did this. */ + + priv->ntimes++; + return false; + } + else + { + /* Did we do the previous operation more than once? */ + + if (priv->ntimes > 0) + { + /* Yes... show how many times we did it */ + + spiinfo("...[Repeats %d times]...\n", priv->ntimes); + } + + /* Save information about the new access */ + + priv->wrlast = wr; + priv->valuelast = value; + priv->addresslast = address; + priv->ntimes = 0; + } + + /* Return true if this is the first time that we have done this operation */ + + return true; +} +#endif + +/**************************************************************************** + * Name: qspi_getreg + * + * Description: + * Read an QSPI register + * + ****************************************************************************/ + +static inline uint32_t qspi_getreg(struct sam_qspidev_s *priv, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + uint32_t value = getreg32(address); + +#ifdef CONFIG_SAMA5_QSPI_REGDEBUG + if (qspi_checkreg(priv, false, value, address)) + { + spiinfo("%08x->%08x\n", address, value); + } +#endif + + return value; +} + +/**************************************************************************** + * Name: qspi_putreg + * + * Description: + * Write a value to an QSPI register + * + ****************************************************************************/ + +static inline void qspi_putreg(struct sam_qspidev_s *priv, uint32_t value, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + +#ifdef CONFIG_SAMA5_QSPI_REGDEBUG + if (qspi_checkreg(priv, true, value, address)) + { + spiinfo("%08x<-%08x\n", address, value); + } +#endif + + putreg32(value, address); +} + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_SPI_INFO +static void qspi_dumpregs(struct sam_qspidev_s *priv, const char *msg) +{ + spiinfo("%s:\n", msg); + spiinfo(" MR:%08x SR:%08x IMR:%08x SCR:%08x\n", + getreg32(priv->base + SAM_QSPI_MR_OFFSET), + getreg32(priv->base + SAM_QSPI_SR_OFFSET), + getreg32(priv->base + SAM_QSPI_IMR_OFFSET), + getreg32(priv->base + SAM_QSPI_SCR_OFFSET)); + spiinfo(" IAR:%08x ICR:%08x IFR:%08x SMR:%08x\n", + getreg32(priv->base + SAM_QSPI_IAR_OFFSET), + getreg32(priv->base + SAM_QSPI_ICR_OFFSET), + getreg32(priv->base + SAM_QSPI_IFR_OFFSET), + getreg32(priv->base + SAM_QSPI_SMR_OFFSET)); + spiinfo(" WPCR:%08x WPSR:%08x\n", + getreg32(priv->base + SAM_QSPI_WPCR_OFFSET), + getreg32(priv->base + SAM_QSPI_WPSR_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: qspi_dma_sampleinit + * + * Description: + * Initialize sampling of DMA registers (if CONFIG_SAMA5_QSPI_DMADEBUG) + * + * Input Parameters: + * priv - QSPI driver instance + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_DMADEBUG +static void qspi_dma_sampleinit(struct sam_qspidev_s *priv) +{ + /* Put contents of register samples into a known state */ + + memset(priv->dmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + + /* Then get the initial samples */ + + sam_dmasample(priv->dmach, &priv->dmaregs[DMA_INITIAL]); +} +#endif + +/**************************************************************************** + * Name: qspi_dma_sampledone + * + * Description: + * Dump sampled DMA registers + * + * Input Parameters: + * priv - QSPI driver instance + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_DMADEBUG +static void qspi_dma_sampledone(struct sam_qspidev_s *priv) +{ + /* Sample the final registers */ + + sam_dmasample(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER]); + + /* Then dump the sampled DMA registers */ + + /* Initial register values */ + + sam_dmadump(priv->dmach, &priv->dmaregs[DMA_INITIAL], + "Initial Registers"); + + /* Register values after DMA setup */ + + sam_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_SETUP], + "After DMA Setup"); + + /* Register values after DMA start */ + + sam_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_START], + "After DMA Start"); + + /* Register values at the time of the TX and RX DMA callbacks + * -OR- DMA timeout. + * + * If the DMA timed out, then there will not be any RX DMA + * callback samples. There is probably no TX DMA callback + * samples either, but we don't know for sure. + */ + + if (priv->result == -ETIMEDOUT) + { + sam_dmadump(priv->dmach, &priv->dmaregs[DMA_TIMEOUT], + "At DMA timeout"); + } + else + { + sam_dmadump(priv->dmach, &priv->dmaregs[DMA_CALLBACK], + "At DMA callback"); + } + + sam_dmadump(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER], + "At End-of-Transfer"); +} +#endif + +/**************************************************************************** + * Name: qspi_dma_timeout + * + * Description: + * The watchdog timeout setup when a has expired without completion of a + * DMA. + * + * Input Parameters: + * arg - The argument + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_DMA +static void qspi_dma_timeout(wdparm_t arg) +{ + struct sam_qspidev_s *priv = (struct sam_qspidev_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Sample DMA registers at the time of the timeout */ + + qspi_dma_sample(priv, DMA_CALLBACK); + + /* Report timeout result, perhaps overwriting any failure reports from + * the TX callback. + */ + + priv->result = -ETIMEDOUT; + + /* Then wake up the waiting thread */ + + nxsem_post(&priv->dmawait); +} +#endif + +/**************************************************************************** + * Name: qspi_dma_callback + * + * Description: + * This callback function is invoked at the completion of the QSPI RX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select structure + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_DMA +static void qspi_dma_callback(DMA_HANDLE handle, void *arg, int result) +{ + struct sam_qspidev_s *priv = (struct sam_qspidev_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Cancel the watchdog timeout */ + + wd_cancel(&priv->dmadog); + + /* Sample DMA registers at the time of the callback */ + + qspi_dma_sample(priv, DMA_CALLBACK); + + /* Report the result of the transfer only if the TX callback has not + * already reported an error. + */ + + if (priv->result == -EBUSY) + { + /* Save the result of the transfer if no error was previously + * reported + */ + + priv->result = result; + } + + /* Then wake up the waiting thread */ + + nxsem_post(&priv->dmawait); +} +#endif + +/**************************************************************************** + * Name: qspi_regaddr + * + * Description: + * Return the address of an QSPI register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_DMA +static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv, + unsigned int offset) +{ + return priv->base + offset; +} +#endif + +/**************************************************************************** + * Name: qspi_memory_enable + * + * Description: + * Enable the QSPI memory transfer + * + * Input Parameters: + * priv - 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_enable(struct sam_qspidev_s *priv, + struct qspi_meminfo_s *meminfo) +{ + uint32_t regval; + + /* Write the Instruction code register: + * + * QSPI_ICR_INST(cmd) 8-bit command + * QSPI_ICR_OPT(0) No option + */ + + regval = QSPI_ICR_INST(meminfo->cmd) | QSPI_ICR_OPT(0); + qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); + + /* Is memory data scrambled? */ + + if (QSPIMEM_ISSCRAMBLE(meminfo->flags)) + { + /* Yes.. set the scramble key */ + + qspi_putreg(priv, meminfo->key, SAM_QSPI_SKR_OFFSET); + + /* Enable the scrambler and enable/disable the random value in the + * key. + */ + + regval = QSPI_SMR_SCREN; + if (!QSPIMEM_ISRANDOM(meminfo->flags)) + { + /* Disable random value in key */ + + regval |= QSPI_SMR_RVDIS; + } + + qspi_putreg(priv, 0, SAM_QSPI_SMR_OFFSET); + } + else + { + /* Disable the scrambler */ + + qspi_putreg(priv, 0, SAM_QSPI_SKR_OFFSET); + qspi_putreg(priv, 0, SAM_QSPI_SMR_OFFSET); + } + + /* Write Instruction Frame Register: + * + * QSPI_IFR_WIDTH_? Instruction=single bit/Data depends on + * meminfo->flags + * QSPI_IFR_INSTEN=1 Instruction Enable + * QSPI_IFR_ADDREN=1 Address Enable + * QSPI_IFR_OPTEN=0 Option Disable + * QSPI_IFR_DATAEN=1 Data Enable + * QSPI_IFR_OPTL_* Not used (zero) + * QSPI_IFR_ADDRL=0/1 Depends on meminfo->addrlen; + * QSPI_IFR_TFRTYP_RD/WRMEM Depends on meminfo->flags + * QSPI_IFR_CRM=0 Not continuous read + * QSPI_IFR_NBDUM Depends on meminfo->dummies + */ + + regval = QSPI_IFR_INSTEN | QSPI_IFR_ADDREN | QSPI_IFR_DATAEN | + QSPI_IFR_NBDUM(meminfo->dummies); + + if (QSPIMEM_ISWRITE(meminfo->flags)) + { + regval |= QSPI_IFR_TFRTYP_WRMEM; + } + else + { + regval |= QSPI_IFR_TFRTYP_RDMEM; + } + + if (QSPIMEM_ISQUADIO(meminfo->flags)) + { + regval |= QSPI_IFR_WIDTH_QUADIO; + } + else if (QSPIMEM_ISDUALIO(meminfo->flags)) + { + regval |= QSPI_IFR_WIDTH_DUALIO; + } + else + { + regval |= QSPI_IFR_WIDTH_SINGLE; + } + + if (meminfo->addrlen == 3) + { + regval |= QSPI_IFR_ADDRL_24BIT; + } + else if (meminfo->addrlen == 4) + { + regval |= QSPI_IFR_ADDRL_32BIT; + } + else + { + return -EINVAL; + } + + /* Write the instruction frame value */ + + qspi_putreg(priv, regval, SAM_QSPI_IFR_OFFSET); + qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); + return OK; +} + +/**************************************************************************** + * Name: qspi_memory_dma + * + * Description: + * Perform one QSPI memory transfer using DMA + * + * Input Parameters: + * priv - 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 + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_QSPI_DMA +static int qspi_memory_dma(struct sam_qspidev_s *priv, + struct qspi_meminfo_s *meminfo) +{ + uintptr_t qspimem = priv->membase + meminfo->addr; + uint32_t dmaflags; + int ret; + + /* Initialize register sampling */ + + qspi_dma_sampleinit(priv); + + /* Determine DMA flags and setup the DMA */ + + dmaflags = DMACH_FLAG_FIFOCFG_LARGEST | DMACH_FLAG_PERIPHAHB_AHB_IF1 | + DMACH_FLAG_PERIPHISMEMORY | DMACH_FLAG_PERIPHINCREMENT | + DMACH_FLAG_PERIPHCHUNKSIZE_1 | DMACH_FLAG_MEMPID_MAX | + DMACH_FLAG_MEMAHB_AHB_IF1 | DMACH_FLAG_MEMINCREMENT | + DMACH_FLAG_MEMCHUNKSIZE_1 | DMACH_FLAG_MEMBURST_16; + + if (QSPIMEM_ISWRITE(meminfo->flags)) + { + /* Configure TX DMA */ + + dmaflags |= ((uint32_t)priv->txintf << DMACH_FLAG_PERIPHPID_SHIFT) | + DMACH_FLAG_PERIPHWIDTH_8BITS | DMACH_FLAG_MEMWIDTH_8BITS; + sam_dmaconfig(priv->dmach, dmaflags); + + /* Setup the TX DMA (peripheral-to-memory) */ + + ret = sam_dmatxsetup(priv->dmach, qspimem, (uint32_t)meminfo->buffer, + meminfo->buflen); + } + else + { + /* Configure RX DMA */ + + dmaflags |= ((uint32_t)priv->rxintf << DMACH_FLAG_PERIPHPID_SHIFT) | + DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_MEMWIDTH_32BITS; + sam_dmaconfig(priv->dmach, dmaflags); + + /* Setup the RX DMA (memory-to-peripheral) */ + + ret = sam_dmarxsetup(priv->dmach, qspimem, (uint32_t)meminfo->buffer, + meminfo->buflen); + } + + if (ret < 0) + { + spierr("ERROR: DMA setup failed: %d\n", ret); + return ret; + } + + qspi_dma_sample(priv, DMA_AFTER_SETUP); + + /* Enable the memory transfer */ + + qspi_memory_enable(priv, meminfo); + + /* Start the DMA */ + + priv->result = -EBUSY; + ret = sam_dmastart(priv->dmach, qspi_dma_callback, priv); + if (ret < 0) + { + spierr("ERROR: sam_dmastart failed: %d\n", ret); + return ret; + } + + qspi_dma_sample(priv, DMA_AFTER_START); + + /* Wait for DMA completion. This is done in a loop because there may be + * false alarm semaphore counts that cause sam_wait() not fail to wait + * or to wake-up prematurely (for example due to the receipt of a signal). + * We know that the DMA has completed when the result is anything other + * that -EBUSY. + */ + + do + { + /* Start (or re-start) the watchdog timeout */ + + ret = wd_start(&priv->dmadog, DMA_TIMEOUT_TICKS, + qspi_dma_timeout, (wdparm_t)priv); + if (ret < 0) + { + spierr("ERROR: wd_start failed: %d\n", ret); + } + + /* Wait for the DMA complete */ + + ret = nxsem_wait_uninterruptible(&priv->dmawait); + + /* Cancel the watchdog timeout */ + + wd_cancel(&priv->dmadog); + + /* Check if we were awakened by an error of some kind. */ + + if (ret < 0) + { + DEBUGPANIC(); + return ret; + } + + /* Not that we might be awakened before the wait is over due to + * residual counts on the semaphore. So, to handle, that case, + * we loop until something changes the DMA result to any value other + * than -EBUSY. + */ + } + while (priv->result == -EBUSY); + + /* Wait until the transmission registers are empty. */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0); + qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); + MEMORY_SYNC(); + + /* Dump the sampled DMA registers */ + + qspi_dma_sampledone(priv); + + /* Make sure that the DMA is stopped (it will be stopped automatically + * on normal transfers, but not necessarily when the transfer terminates + * on an error condition). + */ + + sam_dmastop(priv->dmach); + + /* Complain if the DMA fails */ + + if (priv->result) + { + spierr("ERROR: DMA failed with result: %d\n", priv->result); + } + + return priv->result; +} +#endif + +/**************************************************************************** + * Name: qspi_memory_nodma + * + * Description: + * Perform one QSPI memory transfer without using DMA + * + * Input Parameters: + * priv - 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_nodma(struct sam_qspidev_s *priv, + struct qspi_meminfo_s *meminfo) +{ + uintptr_t qspimem = priv->membase + meminfo->addr; + + /* Enable the memory transfer */ + + qspi_memory_enable(priv, meminfo); + + /* Transfer data to/from QSPI memory */ + + if (QSPIMEM_ISWRITE(meminfo->flags)) + { + qspi_memcpy((uint8_t *)qspimem, (const uint8_t *)meminfo->buffer, + meminfo->buflen); + } + else + { + qspi_memcpy((uint8_t *)meminfo->buffer, (const uint8_t *)qspimem, + meminfo->buflen); + } + + MEMORY_SYNC(); + + /* Indicate the end of the transfer as soon as the transmission + * registers are empty. + */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0); + qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); + + /* Wait for the end of the transfer + * + * REVISIT: If DMA is not used then large transfers could come through + * this path. In that case, there would be a benefit to waiting for an + * interrupt to signal the end of the transfer. + */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); + return OK; +} + +/**************************************************************************** + * Name: qspi_memcpy + * + * Description: + * 32-bit version of memcpy. + * + * Input Parameters: + * dest - Destination address of the copy + * src - Source address of the copy + * buflen - The number of 32-bit words to copy. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void qspi_memcpy(uint8_t *dest, const uint8_t *src, size_t buflen) +{ + /* The size of the SPI transfer is equal to the bus access width. + * 8-bit transfers should result in 8-bit SPI accesses. + */ + + for (; buflen > 0; buflen--) + { + *dest++ = *src++; + } +} + +/**************************************************************************** + * 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 sam_qspidev_s *priv = (struct sam_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 sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t actual; + uint32_t scbr; +#if CONFIG_SAMA5_QSPI_DLYBS > 0 + uint32_t dlybs; +#endif +#if CONFIG_SAMA5_QSPI_DLYBCT > 0 + uint32_t dlybct; +#endif + uint32_t regval; + + spiinfo("frequency=%"PRIu32"\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; + } + + /* Configure QSPI to a frequency as close as possible to the requested + * frequency. + * + * QSCK frequency = QSPI_CLK / SCBR, or SCBR = QSPI_CLK / frequency + * + * Where SCBR can have the range 1 to 256 and the SCR register field holds + * SCBR - 1. NOTE that a "ceiling" type of calculation is performed. + * 'frequency' is treated as a not-to-exceed value. + */ + + scbr = (frequency + SAM_QSPI_CLOCK - 1) / frequency; + + /* Make sure that the divider is within range */ + + if (scbr < 1) + { + scbr = 1; + } + else if (scbr > 256) + { + scbr = 256; + } + + /* Save the new SCBR value (minus one) */ + + regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); + regval &= ~(QSPI_SCR_SCBR_MASK | QSPI_SCR_DLYBS_MASK); + regval |= (scbr - 1) << QSPI_SCR_SCBR_SHIFT; + + /* DLYBS: Delay Before QSCK. This field defines the delay from NPCS valid + * to the first valid QSCK transition. When DLYBS equals zero, the NPCS + * valid to QSCK transition is 1/2 the QSCK clock period. Otherwise, the + * following equations determine the delay: + * + * Delay Before QSCK = DLYBS / QSPI_CLK + * + * For a 100 nsec delay (assumes QSPI_CLK is an even multiple of MHz): + * + * DLYBS == 100 * QSPI_CLK / 1000000000 + * == (100 * (QSPI_CLK / 1000000)) / 1000 + */ + +#if CONFIG_SAMA5_QSPI_DLYBS > 0 + dlybs = (CONFIG_SAMA5_QSPI_DLYBS * (SAM_QSPI_CLOCK / 1000000)) / 1000; + regval |= dlybs << QSPI_SCR_DLYBS_SHIFT; +#endif + + qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); + + /* DLYBCT: Delay Between Consecutive Transfers. This field defines the + * delay between two consecutive transfers with the same peripheral without + * removing the chip select. The delay is always inserted after each + * transfer and before removing the chip select if needed. + * + * Delay Between Consecutive Transfers = (32 x DLYBCT) / QSPI_CLK + * + * For a 500 nsec delay (assumes QSPI_CLK is an even multiple of MHz): + * + * DLYBCT = 500 * QSPI_CLK / 1000000000 / 32 + * = (500 * (QSPI_CLK / 1000000) / 1000 / 32 + */ + + regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET); + regval &= ~QSPI_MR_DLYBCT_MASK; + +#if CONFIG_SAMA5_QSPI_DLYBCT > 0 + dlybct = ((CONFIG_SAMA5_QSPI_DLYBCT * (SAM_QSPI_CLOCK / 1000000)) + / 1000 / 32); + regval |= dlybct << QSPI_MR_DLYBCT_SHIFT; +#endif + + qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); + + /* Calculate the new actual frequency */ + + actual = SAM_QSPI_CLOCK / scbr; + spiinfo("SCBR=%"PRIu32" actual=%"PRIu32"\n", scbr, actual); + + /* Save the frequency setting */ + + priv->frequency = frequency; + priv->actual = actual; + + spiinfo("Frequency %"PRIu32"->%"PRIu32"\n", frequency, actual); + return actual; +} + +/**************************************************************************** + * Name: qspi_setmode + * + * Description: + * Set the QSPI mode. Optional. See enum qspi_mode_e for mode definitions + * + * 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 sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t regval; + + 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 + */ + + regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); + regval &= ~(QSPI_SCR_CPOL | QSPI_SCR_CPHA); + + switch (mode) + { + case QSPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + break; + + case QSPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + regval |= QSPI_SCR_CPHA; + break; + + case QSPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + regval |= QSPI_SCR_CPOL; + break; + + case QSPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + regval |= (QSPI_SCR_CPOL | QSPI_SCR_CPHA); + break; + + default: + DEBUGASSERT(FALSE); + return; + } + + qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); + spiinfo("SCR=%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. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void qspi_setbits(struct qspi_dev_s *dev, int nbits) +{ + struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t regval; + + spiinfo("nbits=%d\n", nbits); + DEBUGASSERT(priv != NULL); + DEBUGASSERT(nbits >= SAM_QSPI_MINBITS && nbits <= SAM_QSPI_MAXBITS); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + /* Yes... Set number of bits appropriately */ + + regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET); + regval &= ~QSPI_MR_NBBITS_MASK; + regval |= QSPI_MR_NBBITS(nbits); + qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); + + spiinfo("MR=%08"PRIx32"\n", regval); + + /* Save the selection so that subsequent re-configurations will be + * faster. + */ + + priv->nbits = nbits; + } +} + +/**************************************************************************** + * 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 sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t regval; + uint32_t ifr; + + DEBUGASSERT(priv != NULL && cmdinfo != NULL); + +#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); + + /* Write the instruction address register */ + + ifr = 0; + if (QSPICMD_ISADDRESS(cmdinfo->flags)) + { + DEBUGASSERT(cmdinfo->addrlen == 3 || cmdinfo->addrlen == 4); + + /* Set the address in the IAR. This is required only if the + * instruction frame includes an address, but no data. When data is + * preset, the address of the instruction is determined by the address + * of QSPI memory accesses, and not by the content of the IAR. + */ + + qspi_putreg(priv, cmdinfo->addr, SAM_QSPI_IAR_OFFSET); + + /* Set/clear the address enable bit and the address size in the IFR */ + + ifr |= QSPI_IFR_ADDREN; + + if (cmdinfo->addrlen == 3) + { + ifr |= QSPI_IFR_ADDRL_24BIT; + } + else if (cmdinfo->addrlen == 4) + { + ifr |= QSPI_IFR_ADDRL_32BIT; + } + else + { + return -EINVAL; + } + } + + /* Write the Instruction code register: + * + * QSPI_ICR_INST(cmd) 8-bit command + * QSPI_ICR_OPT(0) No option + */ + + regval = QSPI_ICR_INST(cmdinfo->cmd) | QSPI_ICR_OPT(0); + qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); + + /* Does data accompany the command? */ + + if (QSPICMD_ISDATA(cmdinfo->flags)) + { + DEBUGASSERT(cmdinfo->buffer != NULL && cmdinfo->buflen > 0); + + /* Write Instruction Frame Register: + * + * QSPI_IFR_WIDTH_SINGLE Instruction=single bit/Data single bit + * QSPI_IFR_INSTEN=1 Instruction Enable + * QSPI_IFR_ADDREN=? (See logic above) + * QSPI_IFR_OPTEN=0 Option Disable + * QSPI_IFR_DATAEN=1 Data Enable + * QSPI_IFR_OPTL_* Not used (zero) + * QSPI_IFR_ADDRL=0 Not used (zero) + * QSPI_IFR_TFRTYP_WRITE Write transfer into serial memory, OR + * QSPI_IFR_TFRTYP_READ Read transfer from serial memory + * QSPI_IFR_CRM=0 Not continuous read + * QSPI_IFR_NBDUM(0) No dummy cycles + */ + + ifr |= QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_DATAEN | + QSPI_IFR_NBDUM(0); + + if (QSPICMD_ISIDUAL(cmdinfo->flags)) + { + ifr |= QSPI_IFR_WIDTH_DUALIO; + } + + if (QSPICMD_ISIQUAD(cmdinfo->flags)) + { + ifr |= QSPI_IFR_WIDTH_QUADIO; + } + + /* Read or write operation? */ + + if (QSPICMD_ISWRITE(cmdinfo->flags)) + { + /* Set write data operation + * + * Write the IFR to the hardware. If the instruction frame + * includes data, writing to the IFR does not trigger the + * instruction frame transfer. Rather, the instruction frame + * is triggered by the first access to QSPI memory. + */ + + ifr |= QSPI_IFR_TFRTYP_WRITE; + qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET); + + /* Read QSPI_IFR (dummy read) to synchronize APB and AHB + * accesses. + */ + + qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); + + /* Copy the data to write to QSPI_RAM */ + + qspi_memcpy((uint8_t *) priv->membase, + (const uint8_t *)cmdinfo->buffer, cmdinfo->buflen); + } + else + { + /* Set read data operation + * + * Write the IFR to the hardware. If the instruction frame + * includes data, writing to the IFR does not trigger the + * instruction frame transfer. Rather, the instruction frame + * is triggered by the first access to QSPI memory. + */ + + ifr |= QSPI_IFR_TFRTYP_READ; + qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET); + + /* Read QSPI_IFR (dummy read) to synchronize APB and AHB + * accesses. + */ + + qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); + + /* Copy the data from QSPI memory into the user buffer */ + + qspi_memcpy((uint8_t *)cmdinfo->buffer, + (const uint8_t *) priv->membase, cmdinfo->buflen); + } + + MEMORY_SYNC(); + + /* Indicate the end of the transfer as soon as the transmission + * registers are empty. + */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) + == 0); + + qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); + + /* Fall through to INSTRE wait */ + } + else + { + /* Write Instruction Frame Register: + * + * QSPI_IFR_WIDTH_SINGLE Instruction=single bit/Data single bit + * QSPI_IFR_INSTEN=1 Instruction Enable + * QSPI_IFR_ADDREN=? (See logic above) + * QSPI_IFR_OPTEN=0 Option Disable + * QSPI_IFR_DATAEN=0 Data Disable + * QSPI_IFR_OPTL_* Not used (zero) + * QSPI_IFR_ADDRL=0 Not used (zero) + * QSPI_IFR_TFRTYP_READ Shouldn't matter + * QSPI_IFR_CRM=0 Not continuous read + * QSPI_IFR_NBDUM(0) No dummy cycles + */ + + ifr |= QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_TFRTYP_READ | + QSPI_IFR_NBDUM(0); + qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET); + + MEMORY_SYNC(); + + /* If the instruction frame does not include data, writing to the IFR + * triggers sending of the instruction frame. Fall through to INSTRE + * wait. + */ + } + + /* When the command has been sent, Instruction End Status (INTRE) will be + * set in the QSPI status register. + */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); + + 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 sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + + DEBUGASSERT(priv != NULL && meminfo != NULL); + + spiinfo("Transfer:\n"); + spiinfo(" flags: %02x\n", meminfo->flags); + spiinfo(" cmd: %04x\n", meminfo->cmd); + spiinfo(" address/length: %08lx/%d\n", + (unsigned long)meminfo->addr, meminfo->addrlen); + spiinfo(" %s Data:\n", + QSPIMEM_ISWRITE(meminfo->flags) ? "Write" : "Read"); + spiinfo(" buffer/length: %p/%"PRIu32"\n", + meminfo->buffer, meminfo->buflen); + +#ifdef CONFIG_SAMA5_QSPI_DMA + /* Can we perform DMA? Should we perform DMA? */ + + if (priv->candma && + meminfo->buflen > CONFIG_SAMA5_QSPI_DMATHRESHOLD && + IS_ALIGNED((uintptr_t)meminfo->buffer) && + IS_ALIGNED(meminfo->buflen)) + { + return qspi_memory_dma(priv, meminfo); + } + else +#endif + { + return qspi_memory_nodma(priv, meminfo); + } +} + +/**************************************************************************** + * 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 internal 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. + */ + + return kmm_malloc(ALIGN_UP(buflen)); +} + +/**************************************************************************** + * 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); + } +} + +/**************************************************************************** + * 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 sam_qspidev_s *priv) +{ + uint32_t regval; + + /* Disable the QSPI */ + + qspi_putreg(priv, QSPI_CR_QSPIDIS, SAM_QSPI_CR_OFFSET); + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_QSPIENS) != 0); + + /* Reset the QSPI (twice) */ + + qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET); + qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET); + + /* Configure the QSPI + * + * QSPI_MR_SMM - Serial Memory Mode + * QSPI_MR_CSMODE_LASTXFER - CS de-asserted when LASTXFER transferred + */ + + regval = QSPI_MR_SMM; + qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); + + regval |= QSPI_MR_CSMODE_LASTXFER; + qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); + + /* Set up the initial QSPI clock mode: + * + * Mode 0: CPOL=0; CPHA=0 + */ + + regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); + regval &= ~(QSPI_SCR_CPOL | QSPI_SCR_CPHA); + qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); + + regval |= QSPI_SCR_SCBR(1); + qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); + + /* 8-bit mode */ + + regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET); + regval &= ~QSPI_MR_NBBITS_MASK; + regval |= QSPI_MR_NBBITS_8BIT; + qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); + + priv->nbits = 8; + + /* Enable QSPI */ + + qspi_putreg(priv, QSPI_CR_QSPIEN, SAM_QSPI_CR_OFFSET); + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_QSPIENS) == 0); + + /* Flush any pending transfers */ + + qspi_getreg(priv, SAM_QSPI_SR_OFFSET); + qspi_getreg(priv, SAM_QSPI_RDR_OFFSET); + + qspi_dumpregs(priv, "After initialization"); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_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 *sam_qspi_initialize(int intf) +{ + struct sam_qspidev_s *priv; + int ret; + + /* The supported SAM parts have only a single QSPI port */ + + spiinfo("intf: %d\n", intf); + DEBUGASSERT(intf >= 0 && intf < SAM_NQSPI); + + /* Select the QSPI interface */ + +#ifdef CONFIG_SAMA5_QSPI0 + if (intf == 0) + { + /* If this function is called multiple times, the following operations + * will be performed multiple times. + */ + + /* Select QSPI0 */ + + priv = &g_qspi0dev; + + /* Enable clocking to the QSPI0 peripheral */ + + sam_qspi0_enableclk(); + + /* Configure multiplexed pins as connected on the board. */ + + sam_configpio(PIO_QSPI0_CS); + sam_configpio(PIO_QSPI0_IO0); + sam_configpio(PIO_QSPI0_IO1); + sam_configpio(PIO_QSPI0_IO2); + sam_configpio(PIO_QSPI0_IO3); + sam_configpio(PIO_QSPI0_SCK); + } + else +#endif +#ifdef CONFIG_SAMA5_QSPI1 + if (intf == 1) + { + /* If this function is called multiple times, the following operations + * will be performed multiple times. + */ + + /* Select QSPI1 */ + + priv = &g_qspi1dev; + + /* Enable clocking to the QSPI1 peripheral */ + + sam_qspi1_enableclk(); + + /* Configure multiplexed pins as connected on the board. */ + + sam_configpio(PIO_QSPI1_CS); + sam_configpio(PIO_QSPI1_IO0); + sam_configpio(PIO_QSPI1_IO1); + sam_configpio(PIO_QSPI1_IO2); + sam_configpio(PIO_QSPI1_IO3); + sam_configpio(PIO_QSPI1_SCK); + } + else +#endif + { + spierr("ERROR: QSPI%d not supported\n", intf); + return NULL; + } + + /* Has the QSPI hardware been initialized? */ + + if (!priv->initialized) + { + /* No perform one time initialization */ + +#ifdef CONFIG_SAMA5_QSPI_DMA + /* Pre-allocate DMA channels. */ + + if (priv->candma) + { + priv->dmach = sam_dmachannel(0, 0); + if (!priv->dmach) + { + spierr("ERROR: Failed to allocate the DMA channel\n"); + priv->candma = false; + } + } +#endif + +#ifdef QSPI_USE_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); + goto errout_with_dmach; + } +#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"); + goto errout_with_irq; + } + + /* Enable interrupts at the NVIC */ + + priv->initialized = true; +#ifdef QSPI_USE_INTERRUPTS + up_enable_irq(priv->irq); +#endif + } + + return &priv->qspi; + +errout_with_irq: +#ifdef QSPI_USE_INTERRUPTS + irq_detach(priv->irq); + +errout_with_dmach: +#endif +#ifdef CONFIG_SAMA5_QSPI_DMA + if (priv->dmach) + { + sam_dmafree(priv->dmach); + priv->dmach = NULL; + } +#endif + + return NULL; +} +#endif /* CONFIG_SAMA5_QSPI */ diff --git a/arch/arm/src/sama5/sam_qspi.h b/arch/arm/src/sama5/sam_qspi.h new file mode 100644 index 0000000000..a6d3bcfc0f --- /dev/null +++ b/arch/arm/src/sama5/sam_qspi.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_qspi.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_SAM_QSPI_H +#define __ARCH_ARM_SRC_SAMA5_SAM_QSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "chip.h" +#include "sam_config.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_qspi_initialize + * + * Description: + * Initialize the selected QSPI port in master mode + * + * Input Parameters: + * intf - Interface number(must be zero) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct qspi_dev_s; +struct qspi_dev_s *sam_qspi_initialize(int intf); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_SAMA5_SAM_QSPI_H */