From e6bfaa14ee353bdcc90682ceeb2d2ee456dd9b4a Mon Sep 17 00:00:00 2001 From: TimJTi <56726697+TimJTi@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:54:35 +0000 Subject: [PATCH] SAMA5D2 add Flexcom SPI Flexcom working, DMA not looked at for SAMA5D2 Update sam_flexcom_spi.c Update sam_flexcom_spi.h Update sam_config.h Update hardware/sam_flexcom_spi.h pkarashchenko reviews SAMA5 serial and flexcom serial corrections --- arch/arm/src/sama5/Make.defs | 4 + arch/arm/src/sama5/hardware/sam_flexcom_spi.h | 253 ++ arch/arm/src/sama5/sam_config.h | 8 + arch/arm/src/sama5/sam_flexcom_spi.c | 2127 +++++++++++++++++ arch/arm/src/sama5/sam_flexcom_spi.h | 277 +++ 5 files changed, 2669 insertions(+) create mode 100644 arch/arm/src/sama5/sam_flexcom_spi.c create mode 100755 arch/arm/src/sama5/sam_flexcom_spi.h diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index 24ac6fbba3..91a5e256a8 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -80,6 +80,10 @@ ifeq ($(CONFIG_SAMA5_FLEXCOM_USART),y) CHIP_CSRCS += sam_flexcom_serial.c endif +ifeq ($(CONFIG_SAMA5_FLEXCOM_SPI), y) +CHIP_CSRCS += sam_flexcom_spi.c +endif + ifeq ($(CONFIG_SAMA5_SPI0),y) CHIP_CSRCS += sam_spi.c else diff --git a/arch/arm/src/sama5/hardware/sam_flexcom_spi.h b/arch/arm/src/sama5/hardware/sam_flexcom_spi.h index 154392ba5a..4dfd1f0f59 100644 --- a/arch/arm/src/sama5/hardware/sam_flexcom_spi.h +++ b/arch/arm/src/sama5/hardware/sam_flexcom_spi.h @@ -34,10 +34,263 @@ * Pre-processor Definitions ****************************************************************************/ +#define SAM_FLEXCOM_SPI_NCS 2 /* Two chip selects */ + /* Flexcom SPI Register Offsets *********************************************/ +/* SPI register offsets *****************************************************/ + +#define SAM_FLEXCOM_SPI_CR_OFFSET 0x0400 /* Control Register */ +#define SAM_FLEXCOM_SPI_MR_OFFSET 0x0404 /* Mode Register */ +#define SAM_FLEXCOM_SPI_RDR_OFFSET 0x0408 /* Receive Data Register */ +#define SAM_FLEXCOM_SPI_TDR_OFFSET 0x040c /* Transmit Data Register */ +#define SAM_FLEXCOM_SPI_SR_OFFSET 0x0410 /* Status Register */ +#define SAM_FLEXCOM_SPI_IER_OFFSET 0x0414 /* Interrupt Enable Register */ +#define SAM_FLEXCOM_SPI_IDR_OFFSET 0x0418 /* Interrupt Disable Register */ +#define SAM_FLEXCOM_SPI_IMR_OFFSET 0x041c /* Interrupt Mask Register */ + /* 0x420-0x42c: Reserved */ +#define SAM_FLEXCOM_SPI_CSR0_OFFSET 0x0430 /* Chip Select Register 0 */ +#define SAM_FLEXCOM_SPI_CSR1_OFFSET 0x0434 /* Chip Select Register 1 */ + /* 0x438-0x43C: Reserved */ +#define SAM_FLEXCOM_SPI_FMR_OFFSET 0x0440 /* FIFO Mode register */ +#define SAM_FLEXCOM_SPI_FLR_OFFSET 0x0444 /* FIFO Level Register */ +#define SAM_FLEXCOM_SPI_CMPR_OFFSET 0x0448 /* Comparison register */ + /* 0x044C-0x04E0: Reserved */ +#define SAM_FLEXCOM_SPI_WPMR_OFFSET 0x04e4 /* Write Protection Mode Register */ +#define SAM_FLEXCOM_SPI_WPSR_OFFSET 0x04e8 /* Write Protection Status Register */ + /* 0x04ec-0x5FC: Reserved */ + /* Flexcom SPI Register Addresses *******************************************/ +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI +#define SAM_FLEXCOM_SPI0_CR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_CR_OFFSET) +#define SAM_FLEXCOM_SPI0_MR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_MR_OFFSET) +#define SAM_FLEXCOM_SPI0_RDR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_RDR_OFFSET) +#define SAM_FLEXCOM_SPI0_TDR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_TDR_OFFSET) +#define SAM_FLEXCOM_SPI0_SR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_SR_OFFSET) +#define SAM_FLEXCOM_SPI0_IER (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_IER_OFFSET) +#define SAM_FLEXCOM_SPI0_IDR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_IDR_OFFSET) +#define SAM_FLEXCOM_SPI0_IMR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_IMR_OFFSET) +#define SAM_FLEXCOM_SPI0_CSR0 (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_CSR0_OFFSET) +#define SAM_FLEXCOM_SPI0_CSR1 (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_CSR1_OFFSET) +#define SAM_FLEXCOM_SPI0_FMR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_FMR_OFFSET) +#define SAM_FLEXCOM_SPI0_FLR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_FLR_OFFSET) +#define SAM_FLEXCOM_SPI0_CMPR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_CMPR_OFFSET) +#define SAM_FLEXCOM_SPI0_WPMR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_WPMR_OFFSET) +#define SAM_FLEXCOM_SPI0_WPSR (SAM_FLEXCOM0_VBASE+SAM_FLEXCOM_SPI_WPSR_OFFSET) +#endif + +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI +#define SAM_FLEXCOM_SPI1_CR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_CR_OFFSET) +#define SAM_FLEXCOM_SPI1_MR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_MR_OFFSET) +#define SAM_FLEXCOM_SPI1_RDR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_RDR_OFFSET) +#define SAM_FLEXCOM_SPI1_TDR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_TDR_OFFSET) +#define SAM_FLEXCOM_SPI1_SR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_SR_OFFSET) +#define SAM_FLEXCOM_SPI1_IER (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_IER_OFFSET) +#define SAM_FLEXCOM_SPI1_IDR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_IDR_OFFSET) +#define SAM_FLEXCOM_SPI1_IMR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_IMR_OFFSET) +#define SAM_FLEXCOM_SPI1_CSR0 (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_CSR0_OFFSET) +#define SAM_FLEXCOM_SPI1_CSR1 (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_CSR1_OFFSET) +#define SAM_FLEXCOM_SPI1_FMR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_FMR_OFFSET) +#define SAM_FLEXCOM_SPI1_FLR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_FLR_OFFSET) +#define SAM_FLEXCOM_SPI1_CMPR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_CMPR_OFFSET) +#define SAM_FLEXCOM_SPI1_WPMR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_WPMR_OFFSET) +#define SAM_FLEXCOM_SPI1_WPSR (SAM_FLEXCOM1_VBASE+SAM_FLEXCOM_SPI_WPSR_OFFSET) +#endif + +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI +#define SAM_FLEXCOM_SPI2_CR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_CR_OFFSET) +#define SAM_FLEXCOM_SPI2_MR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_MR_OFFSET) +#define SAM_FLEXCOM_SPI2_RDR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_RDR_OFFSET) +#define SAM_FLEXCOM_SPI2_TDR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_TDR_OFFSET) +#define SAM_FLEXCOM_SPI2_SR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_SR_OFFSET) +#define SAM_FLEXCOM_SPI2_IER (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_IER_OFFSET) +#define SAM_FLEXCOM_SPI2_IDR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_IDR_OFFSET) +#define SAM_FLEXCOM_SPI2_IMR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_IMR_OFFSET) +#define SAM_FLEXCOM_SPI2_CSR0 (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_CSR0_OFFSET) +#define SAM_FLEXCOM_SPI2_CSR1 (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_CSR1_OFFSET) +#define SAM_FLEXCOM_SPI2_FMR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_FMR_OFFSET) +#define SAM_FLEXCOM_SPI2_FLR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_FLR_OFFSET) +#define SAM_FLEXCOM_SPI2_CMPR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_CMPR_OFFSET) +#define SAM_FLEXCOM_SPI2_WPMR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_WPMR_OFFSET) +#define SAM_FLEXCOM_SPI2_WPSR (SAM_FLEXCOM2_VBASE+SAM_FLEXCOM_SPI_WPSR_OFFSET) +#endif + +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI +#define SAM_FLEXCOM_SPI3_CR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_CR_OFFSET) +#define SAM_FLEXCOM_SPI3_MR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_MR_OFFSET) +#define SAM_FLEXCOM_SPI3_RDR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_RDR_OFFSET) +#define SAM_FLEXCOM_SPI3_TDR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_TDR_OFFSET) +#define SAM_FLEXCOM_SPI3_SR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_SR_OFFSET) +#define SAM_FLEXCOM_SPI3_IER (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_IER_OFFSET) +#define SAM_FLEXCOM_SPI3_IDR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_IDR_OFFSET) +#define SAM_FLEXCOM_SPI3_IMR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_IMR_OFFSET) +#define SAM_FLEXCOM_SPI3_CSR0 (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_CSR0_OFFSET) +#define SAM_FLEXCOM_SPI3_CSR1 (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_CSR1_OFFSET) +#define SAM_FLEXCOM_SPI3_FMR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_FMR_OFFSET) +#define SAM_FLEXCOM_SPI3_FLR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_FLR_OFFSET) +#define SAM_FLEXCOM_SPI3_CMPR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_CMPR_OFFSET) +#define SAM_FLEXCOM_SPI3_WPMR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_WPMR_OFFSET) +#define SAM_FLEXCOM_SPI3_WPSR (SAM_FLEXCOM3_VBASE+SAM_FLEXCOM_SPI_WPSR_OFFSET) +#endif + +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI +#define SAM_FLEXCOM_SPI4_CR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_CR_OFFSET) +#define SAM_FLEXCOM_SPI4_MR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_MR_OFFSET) +#define SAM_FLEXCOM_SPI4_RDR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_RDR_OFFSET) +#define SAM_FLEXCOM_SPI4_TDR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_TDR_OFFSET) +#define SAM_FLEXCOM_SPI4_SR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_SR_OFFSET) +#define SAM_FLEXCOM_SPI4_IER (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_IER_OFFSET) +#define SAM_FLEXCOM_SPI4_IDR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_IDR_OFFSET) +#define SAM_FLEXCOM_SPI4_IMR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_IMR_OFFSET) +#define SAM_FLEXCOM_SPI4_CSR0 (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_CSR0_OFFSET) +#define SAM_FLEXCOM_SPI4_CSR1 (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_CSR1_OFFSET) +#define SAM_FLEXCOM_SPI4_FMR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_FMR_OFFSET) +#define SAM_FLEXCOM_SPI4_FLR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_FLR_OFFSET) +#define SAM_FLEXCOM_SPI4_CMPR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_CMPR_OFFSET) +#define SAM_FLEXCOM_SPI4_WPMR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_WPMR_OFFSET) +#define SAM_FLEXCOM_SPI4_WPSR (SAM_FLEXCOM4_VBASE+SAM_FLEXCOM_SPI_WPSR_OFFSET) +#endif /* Flexcom SPI Register Bit Field Definitions *******************************/ +/* SPI Control Register */ + +#define FLEXCOM_SPI_CR_SPIEN (1 << 0) /* Bit 0: SPI Enable */ +#define FLEXCOM_SPI_CR_SPIDIS (1 << 1) /* Bit 1: SPI Disable */ +#define FLEXCOM_SPI_CR_SWRST (1 << 7) /* Bit 7: SPI Software Reset */ +#define FLEXCOM_SPI_CR_REQCLR (1 << 12) /* Bit 12: SPI Request to Clear Comparison Trigger */ +#define FLEXCOM_SPI_CR_TXFCLR (1 << 16) /* Bit 16: SPI Transmit FIFO clear */ +#define FLEXCOM_SPI_CR_RXFCLR (1 << 17) /* Bit 17: SPI Receive FIFO clear */ +#define FLEXCOM_SPI_CR_LASTXFER (1 << 24) /* Bit 24: Last Transfer */ +#define FLEXCOM_SPI_CR_FIFOEN (1 << 30) /* Bit 30: Enable TX and RX FIFOs */ +#define FLEXCOM_SPI_CR_FIFODIS (1 << 31) /* Bit 31: Disable TX and RX FIFOs */ + +/* SPI Mode Register */ + +#define FLEXCOM_SPI_MR_MSTR (1 << 0) /* Bit 0: Master/Slave Mode */ +#define FLEXCOM_SPI_MR_PS (1 << 1) /* Bit 1: Peripheral Select */ +#define FLEXCOM_SPI_MR_PCSDEC (1 << 2) /* Bit 2: Chip Select Decode */ +#define FLEXCOM_SPI_MR_BSCRCCLK (1 << 3) /* Bit 3: Bit Rate Source Clock */ +#define FLEXCOM_SPI_MR_MODFDIS (1 << 4) /* Bit 4: Mode Fault Detection */ +#define FLEXCOM_SPI_MR_WDRBT (1 << 5) /* Bit 5: Wait Data Read Before Transfer */ +#define FLEXCOM_SPI_MR_LLB (1 << 7) /* Bit 7: Local Loopback Enable */ +#define FLEXCOM_SPI_MR_CMPMODE (1 << 12) /* Bit 12: Comparison Mode */ +#define FLEXCOM_SPI_MR_PCS_SHIFT (16) /* Bits 16-17: Peripheral Chip Select */ +#define FLEXCOM_SPI_MR_PCS_MASK (3 << FLEXCOM_SPI_MR_PCS_SHIFT) + /* Only 2 chip selects */ +#define FLEXCOM_SPI_MR_PCS0 (0 << FLEXCOM_SPI_MR_PCS_SHIFT) + /* NPCS[1:0] = 10 */ +#define FLEXCOM_SPI_MR_PCS1 (1 << FLEXCOM_SPI_MR_PCS_SHIFT) + /* NPCS[1:0] = 01 */ + +#define FLEXCOM_SPI_MR_DLYBCS_SHIFT (24) /* Bits 24-31: Delay Between Chip Selects */ +#define FLEXCOM_SPI_MR_DLYBCS_MASK (0xff << FLEXCOM_SPI_MR_DLYBCS_SHIFT) + +/* SPI Receive Data Register */ + +#define FLEXCOM_SPI_RDR_RD_SHIFT (0) /* Bits 0-15: Receive Data */ +#define FLEXCOM_SPI_RDR_RD_MASK (0xffff << FLEXCOM_SPI_RDR_RD_SHIFT) +#define FLEXCOM_SPI_RDR_PCS_SHIFT (16) /* Bits 16-19: Peripheral Chip Select */ +#define FLEXCOM_SPI_RDR_PCS_MASK (3 << FLEXCOM_SPI_RDR_PCS_SHIFT) +# define FLEXCOM_SPI_RDR_PCS0 (0 << FLEXCOM_SPI_RDR_PCS_SHIFT) /* NPCS[1:0] = 10 */ +# define FLEXCOM_SPI_RDR_PCS1 (1 << FLEXCOM_SPI_RDR_PCS_SHIFT) /* NPCS[1:0] = 01 */ + +/* SPI Transmit Data Register */ + +#define FLEXCOM_SPI_TDR_TD_SHIFT (0) /* Bits 0-15: Transmit Data */ +#define FLEXCOM_SPI_TDR_TD_MASK (0xffff << FLEXCOM_SPI_TDR_TD_SHIFT) +#define FLEXCOM_SPI_TDR_PCS_SHIFT (16) /* Bits 16-19: Peripheral Chip Select */ +#define FLEXCOM_SPI_TDR_PCS_MASK (3 << FLEXCOM_SPI_TDR_PCS_SHIFT) +#define FLEXCOM_SPI_TDR_PCS0 (0 << FLEXCOM_SPI_TDR_PCS_SHIFT) /* NPCS[1:0] = 1110 (w/PCSDEC=1) */ +#define FLEXCOM_SPI_TDR_PCS1 (1 << FLEXCOM_SPI_TDR_PCS_SHIFT) /* NPCS[1:0] = 1101 (w/PCSDEC=1) */ + +#define FLEXCOM_SPI_TDR_LASTXFER (1 << 24) /* Bit 24: Last Transfer */ + +/* SPI Status Register, SPI Interrupt Enable Register, SPI Interrupt + * Disable Register, and SPI Interrupt Mask Register (common bit fields) + */ + +#define FLEXCOM_SPI_INT_RDRF (1 << 0) /* Bit 0: Receive Data Register Full Interrupt */ +#define FLEXCOM_SPI_INT_TDRE (1 << 1) /* Bit 1: Transmit Data Register Empty Interrupt */ +#define FLEXCOM_SPI_INT_MODF (1 << 2) /* Bit 2: Mode Fault Error Interrupt */ +#define FLEXCOM_SPI_INT_OVRES (1 << 3) /* Bit 3: Overrun Error Interrupt */ +#define FLEXCOM_SPI_INT_NSSR (1 << 8) /* Bit 8: NSS Rising Interrupt */ +#define FLEXCOM_SPI_INT_TXEMPTY (1 << 9) /* Bit 9: Transmission Registers Empty Interrupt */ +#define FLEXCOM_SPI_INT_UNDES (1 << 10) /* Bit 10: Underrun Error Status Interrupt (slave) */ +#define FLEXCOM_SPI_INT_CMP (1 << 11) /* Bit 11: Comparison Status */ +#define FLEXCOM_SPI_SR_SPIENS (1 << 16) /* Bit 16: SPI Enable Status (SR only) */ +#define FLEXCOM_SPI_INT_TXFEF (1 << 24) /* Bit 24: TX FIFO empty flag */ +#define FLEXCOM_SPI_INT_TXFFF (1 << 25) /* Bit 25: TX FIFO full flag */ +#define FLEXCOM_SPI_INT_TXFTHF (1 << 26) /* Bit 25: TX FIFO FIFO Threshold flag */ +#define FLEXCOM_SPI_INT_RXFEF (1 << 27) /* Bit 27: RX FIFO empty flag */ +#define FLEXCOM_SPI_INT_RXFFF (1 << 28) /* Bit 28: RX FIFO full flag */ +#define FLEXCOM_SPI_INT_RXFTHF (1 << 29) /* Bit 29: RX FIFO FIFO Threshold flag */ +#define FLEXCOM_SPI_INT_TXFPTEF (1 << 30) /* Bit 30: TX FIFO pointer error flag */ +#define FLEXCOM_SPI_INT_RXFPTEF (1 << 31) /* Bit 31: RX FIFO pointer error flag */ + +/* SPI Chip Select Registers 0-3 */ + +#define FLEXCOM_SPI_CSR_CPOL (1 << 0) /* Bit 0: Clock Polarity */ +#define FLEXCOM_SPI_CSR_NCPHA (1 << 1) /* Bit 1: Clock Phase */ +#define FLEXCOM_SPI_CSR_CSNAAT (1 << 2) /* Bit 2: Chip Select Not Active After Transfer */ +#define FLEXCOM_SPI_CSR_CSAAT (1 << 3) /* Bit 3: Chip Select Active After Transfer */ +#define FLEXCOM_SPI_CSR_BITS_SHIFT (4) /* Bits 4-7: Bits Per Transfer */ +#define FLEXCOM_SPI_CSR_BITS_MASK (15 << FLEXCOM_SPI_CSR_BITS_SHIFT) +# define FLEXCOM_SPI_CSR_BITS(n) (((n)-8) << FLEXCOM_SPI_CSR_BITS_SHIFT) /* n, n=8-16 */ + +# define FLEXCOM_SPI_CSR_BITS8 (0 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 8 */ +# define FLEXCOM_SPI_CSR_BITS9 (1 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 9 */ +# define FLEXCOM_SPI_CSR_BITS10 (2 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 10 */ +# define FLEXCOM_SPI_CSR_BITS11 (3 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 11 */ +# define FLEXCOM_SPI_CSR_BITS12 (4 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 12 */ +# define FLEXCOM_SPI_CSR_BITS13 (5 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 13 */ +# define FLEXCOM_SPI_CSR_BITS14 (6 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 14 */ +# define FLEXCOM_SPI_CSR_BITS15 (7 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 15 */ +# define FLEXCOM_SPI_CSR_BITS16 (8 << FLEXCOM_SPI_CSR_BITS_SHIFT) /* 16 */ + +#define FLEXCOM_SPI_CSR_SCBR_SHIFT (8) /* Bits 8-15: Serial Clock Baud Rate */ +#define FLEXCOM_SPI_CSR_SCBR_MASK (0xff << FLEXCOM_SPI_CSR_SCBR_SHIFT) +# define FLEXCOM_SPI_CSR_SCBR(n) ((uint32_t)(n) << FLEXCOM_SPI_CSR_SCBR_SHIFT) +#define FLEXCOM_SPI_CSR_DLYBS_SHIFT (16) /* Bits 16-23: Delay Before SPCK */ +#define FLEXCOM_SPI_CSR_DLYBS_MASK (0xff << FLEXCOM_SPI_CSR_DLYBS_SHIFT) +# define FLEXCOM_SPI_CSR_DLYBS(n) ((uint32_t)(n) << FLEXCOM_SPI_CSR_DLYBS_SHIFT) +#define FLEXCOM_SPI_CSR_DLYBCT_SHIFT (24) /* Bits 24-31: Delay Between Consecutive Transfers */ +#define FLEXCOM_SPI_CSR_DLYBCT_MASK (0xff << FLEXCOM_SPI_CSR_DLYBCT_SHIFT) +# define FLEXCOM_SPI_CSR_DLYBCT(n) ((uint32_t)(n) << FLEXCOM_SPI_CSR_DLYBCT_SHIFT) + +/* SPI FIFO Mode Register */ +#define FLEXCOM_SPI_FMR_TXRDYM_SHIFT (1 << 0) /* Bits 0-1: TX Data Register empty mode */ +#define FLEXCOM_SPI_FMR_TXRDYM_MASK (3 << FLEXCOM_SPI_FMR_TXRDYM_SHIFT) +#define FLEXCOM_SPI_FMR_RXRDYM_SHIFT (1 << 4) /* Bits 0-1: RX Data Register full mode */ +#define FLEXCOM_SPI_FMR_RXRDYM_MASK (3 << FLEXCOM_SPI_FMR_RXRDYM_SHIFT) +#define FLEXCOM_SPI_FMR_TXFTHRES_SHIFT (1 << 16) /* Bits 16-21: Transmit FIFO threshold */ +#define FLEXCOM_SPI_FMR_TXFTHRES_MASK (0x3f << FLEXCOM_SPI_FMR_TXFTHRES_SHIFT) +#define FLEXCOM_SPI_FMR_RXFTHRES_SHIFT (1 << 24) /* Bits 24-29: Transmit FIFO threshold */ +#define FLEXCOM_SPI_FMR_RXFTHRES_MASK (0x3f << FLEXCOM_SPI_FMR_RXFTHRES_SHIFT) + +/* SPI FIFO Level Register */ +#define FLEXCOM_SPI_FLR_TXFL_SHIFT (1 << 0) /* Bits 0-5: Transmit FIFO Level */ +#define FLEXCOM_SPI_FLR_TXFL_MASK (0x3f << FLEXCOM_SPI_FLR_TXFL_SHIFT) +#define FLEXCOM_SPI_FLR_RXFL_SHIFT (16 << 0) /* Bits 16-21: Transmit FIFO Level */ +#define FLEXCOM_SPI_FLR_RXFL_MASK (0x3f << FLEXCOM_SPI_FLR_RXFL_SHIFT) + +/* SPI Comparison Register */ +#define FLEXCOM_SPI_CMPR_VAL1_SHIFT (1 << 0) /* Bits 0-15: First Comparison Value for Received Character */ +#define FLEXCOM_SPI_CMPR_VAL1_MASK (0xffff << FLEXCOM_SPI_CMPR_VAL1_SHIFT) +#define FLEXCOM_SPI_CMPR_VAL2_SHIFT (1 << 0) /* Bits 16-32: Second Comparison Value for Received Character */ +#define FLEXCOM_SPI_CMPR_VAL2_MASK (0xffff << FLEXCOM_SPI_CMPR_VAL2_SHIFT) + +/* SPI Write Protection Control Register */ + +#define FLEXCOM_SPI_WPCR_WPEN (1 << 0) /* Bit 0: SPI Write Protection Enable */ +#define FLEXCOM_SPI_WPCR_WPKEY_SHIFT (8) /* Bits 8-31: SPI Write Protection Key Password */ +#define FLEXCOM_SPI_WPCR_WPKEY_MASK (0x00ffffff << FLEXCOM_SPI_WPCR_WPKEY_SHIFT) +# define FLEXCOM_SPI_WPCR_WPKEY (0x00535049 << FLEXCOM_SPI_WPCR_WPKEY_SHIFT) + +/* SPI Write Protection Status Register */ + +#define FLEXCOM_SPI_WPSR_WPVS (1 << 0) /* Bit 0: SPI Write Protection Violation Status */ +#define FLEXCOM_SPI_WPSR_WPVSRC_SHIFT (8) /* Bits 8-15: SPI Write Protection Violation Source */ +#define FLEXCOM_SPI_WPSR_WPVSRC_MASK (0xff << FLEXCOM_SPI_WPSR_WPVSRC_SHIFT) + #endif /* __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_FLEXCOM_SPI_H */ diff --git a/arch/arm/src/sama5/sam_config.h b/arch/arm/src/sama5/sam_config.h index 9efe7aaa34..7f05d419de 100644 --- a/arch/arm/src/sama5/sam_config.h +++ b/arch/arm/src/sama5/sam_config.h @@ -379,4 +379,12 @@ # undef SAMA5_HAVE_FLEXCOM_CONSOLE #endif +#if defined(CONFIG_SAMA5_FLEXCOM0_SPI) || \ + defined(CONFIG_SAMA5_FLEXCOM1_SPI) || \ + defined(CONFIG_SAMA5_FLEXCOM2_SPI) || \ + defined(CONFIG_SAMA5_FLEXCOM3_SPI) || \ + defined(CONFIG_SAMA5_FLEXCOM4_SPI) +# define SAMA5_HAVE_FLEXCOM_SPI +#endif + #endif /* __ARCH_ARM_SRC_SAMA5_SAM_CONFIG_H */ diff --git a/arch/arm/src/sama5/sam_flexcom_spi.c b/arch/arm/src/sama5/sam_flexcom_spi.c new file mode 100644 index 0000000000..49f5f9786f --- /dev/null +++ b/arch/arm/src/sama5/sam_flexcom_spi.c @@ -0,0 +1,2127 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_flexcom_spi.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 + +#include "arm_internal.h" + +#include "chip.h" +#include "sam_pio.h" +#include "sam_dmac.h" +#include "sam_memories.h" +#include "sam_periphclks.h" +#include "sam_flexcom_spi.h" +#include "hardware/sam_pmc.h" +#include "hardware/sam_flexcom_spi.h" +#include "hardware/sam_flexcom.h" +#include "sam_config.h" +#include "hardware/sam_pinmap.h" + +#if defined(SAMA5_HAVE_FLEXCOM_SPI) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* When SPI 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_SPI_DMATHRESHOLD. + */ + +# ifndef CONFIG_SAMA5_SPI_DMATHRESHOLD +# define CONFIG_SAMA5_SPI_DMATHRESHOLD 4 +# endif + +# ifndef CONFIG_DEBUG_SPI_INFO +# undef CONFIG_SAMA5_SPI_REGDEBUG +# endif + +# ifdef CONFIG_SAMA5_SPI_DMA + +# if defined(CONFIG_SAMA5_FLEXCOM0_SPI) && defined(CONFIG_SAMA5_XDMAC0) +# define SAMA5_FLEXCOM0_SPI_DMA true +# else +# define SAMA5_FLEXCOM0_SPI_DMA false +# endif + +# if defined(CONFIG_SAMA5_FLEXCOM1_SPI) && defined(CONFIG_SAMA5_XDMAC0) +# define SAMA5_FLEXCOM1_SPI_DMA true +# else +# define SAMA5_FLEXCOM1_SPI_DMA false +# endif + +# if defined(CONFIG_SAMA5_FLEXCOM1_SPI) && defined(CONFIG_SAMA5_XDMAC0) +# define SAMA5_FLEXCOM2_SPI_DMA true +# else +# define SAMA5_FLEXCOM2_SPI_DMA false +# endif + +# if defined(CONFIG_SAMA5_FLEXCOM3_SPI) && defined(CONFIG_SAMA5_XDMAC0) +# define SAMA5_FLEXCOM3_SPI_DMA true +# else +# define SAMA5_FLEXCOM3_SPI_DMA false +# endif + +# if defined(CONFIG_SAMA5_FLEXCOM4_SPI) && defined(CONFIG_SAMA5_XDMAC0) +# define SAMA5_FLEXCOM4_SPI_DMA true +# else +# define SAMA5_FLEXCOM4_SPI_DMA false +# endif + +#endif + +#ifndef CONFIG_SAMA5_SPI_DMA +# undef CONFIG_SAMA5_SPI_DMADEBUG +#endif + +/* Clocking *****************************************************************/ + +/* Select MCU-specific settings + * + * SPI is driven by the main clock. + */ + +#define SAM_FLEXCOM_SPI_CLOCK BOARD_MCK_FREQUENCY + +/* 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) + +/* Debug ********************************************************************/ + +/* Check if SPI debug is enabled */ + +#ifndef CONFIG_DEBUG_DMA +# undef CONFIG_SAMA5_SPI_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 one SPI chip select */ + +struct sam_flex_spics_s +{ + struct spi_dev_s flex_spidev; /* Externally visible part of the + * SPI interface */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + uint8_t nbits; /* Width of word in bits (8 to 16) */ + uint8_t mode; /* Mode 0,1,2,3 */ +#ifdef SAMA5_HAVE_FLEXCOM_SPI + uint8_t flex_spino; /* SPI controller number (0/1/2/3/4) */ +#endif + uint8_t cs; /* Chip select number */ + +#ifdef CONFIG_SAMA5_SPI_DMA + bool candma; /* DMA is supported */ + sem_t dmawait; /* Used to wait for DMA completion */ + struct wdog_s dmadog; /* Watchdog that handles DMA timeouts */ + int result; /* DMA result */ + DMA_HANDLE rxdma; /* SPI RX DMA handle */ + DMA_HANDLE txdma; /* SPI TX DMA handle */ +#endif + + /* Debug stuff */ + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG + struct sam_dmaregs_s rxdmaregs[DMA_NSAMPLES]; + struct sam_dmaregs_s txdmaregs[DMA_NSAMPLES]; +#endif +}; + +/* Type of board-specific SPI status function */ + +typedef void (*select_t)(uint32_t devid, bool selected); + +/* Chip select register offsetrs */ + +/* The overall state of one SPI controller */ + +struct sam_flex_spidev_s +{ + uint32_t base; /* SPI controller register base address */ + mutex_t spilock; /* Assures mutually exclusive access to SPI */ + select_t select; /* SPI select callout */ + bool initialized; /* TRUE: Controller has been initialized */ +#ifdef CONFIG_SAMA5_SPI_DMA + uint8_t pid; /* Peripheral ID */ +#endif + + /* Debug stuff */ + +#ifdef CONFIG_SAMA5_SPI_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_SPI_REGDEBUG +static bool flex_spi_checkreg(struct sam_flex_spidev_s *flex_spi, + bool wr, uint32_t value, uint32_t address); +#else +# define flex_spi_checkreg(flex_spi,wr,value,address) (false) +#endif + +static inline uint32_t flex_spi_getreg(struct sam_flex_spidev_s *flex_spi, + unsigned int offset); +static inline void flex_spi_putreg(struct sam_flex_spidev_s *spi, + uint32_t value, unsigned int offset); +static inline struct sam_flex_spidev_s + *flex_spi_dev(struct sam_flex_spics_s *flex_spics); + +#ifdef CONFIG_DEBUG_SPI_INFO +static void flex_spi_dumpregs(struct sam_flex_spidev_s *flex_spi, + const char *msg); +#else +# define flex_spi_dumpregs(flex_spi,msg) +#endif + +static inline void flex_spi_flush(struct sam_flex_spidev_s *flex_spi); +static inline uint32_t flex_spi_cs2pcs(struct sam_flex_spics_s *flex_spics); + +/* DMA support */ + +#ifdef CONFIG_SAMA5_SPI_DMA + +# ifdef CONFIG_SAMA5_SPI_DMADEBUG +# define spi_rxdma_sample(s,i) sam_dmasample((s)->rxdma, &(s)->rxdmaregs[i]) +# define spi_txdma_sample(s,i) sam_dmasample((s)->txdma, &(s)->txdmaregs[i]) +static void flex_spi_dma_sampleinit(struct sam_flex_spics_s *flex_spics); +static void flex_spi_dma_sampledone(struct sam_flex_spics_s *flex_spics); + +# else +# define flex_spi_rxdma_sample(s,i) +# define flex_spi_txdma_sample(s,i) +# define flex_spi_dma_sampleinit(s) +# define flex_spi_dma_sampledone(s) + +# endif + +static void flex_spi_rxcallback(DMA_HANDLE handle, void *arg, int result); +static void flex_spi_txcallback(DMA_HANDLE handle, void *arg, int result); +static inline uintptr_t flex_spi_physregaddr( + struct sam_flex_spics_s *flex_spics, + unsigned int offset); +#endif + +/* SPI methods */ + +static int flex_spi_lock(struct spi_dev_s *dev, bool lock); +static void flex_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected); +static uint32_t flex_spi_setfrequency(struct spi_dev_s *dev, uint32_t freq); +static void flex_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); +static void flex_spi_setbits(struct spi_dev_s *dev, int nbits); +static uint32_t flex_spi_send(struct spi_dev_s *dev, uint32_t wd); + +#ifdef CONFIG_SAMA5_SPI_DMA +static void flex_spi_exchange_nodma(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, + size_t nwords); +#endif +static void flex_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void flex_spi_sndblock(struct spi_dev_s *dev, + const void *buffer, size_t nwords); +static void flex_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This array maps chip select numbers (0-1) to CSR register offsets */ + +static const uint16_t g_csroffset[2] = +{ + SAM_FLEXCOM_SPI_CSR0_OFFSET, SAM_FLEXCOM_SPI_CSR1_OFFSET +}; + +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI +/* FLEXCOM0 SPI driver operations */ + +static const struct spi_ops_s g_flexcom0_spiops = +{ + .lock = flex_spi_lock, + .select = flex_spi_select, + .setfrequency = flex_spi_setfrequency, + .setmode = flex_spi_setmode, + .setbits = flex_spi_setbits, +# ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +# endif + .status = sam_flexcom0_spistatus, +# ifdef CONFIG_SPI_CMDDATA + .cmddata = sam_flexcom0_spicmddata, +# endif + .send = flex_spi_send, +# ifdef CONFIG_SPI_EXCHANGE + .exchange = flex_spi_exchange, +# else + .sndblock = flex_spi_sndblock, + .recvblock = flex_spi_recvblock, +# endif + .registercallback = 0, /* Not implemented */ +}; + +/* This is the overall state of the FLEXCOM0 SPI controller */ + +static struct sam_flex_spidev_s g_flexcom0dev = +{ + .base = SAM_FLEXCOM0_VBASE, + .spilock = NXMUTEX_INITIALIZER, + .select = sam_flexcom0_spiselect, +# ifdef SAMA5_FLEXCOM0_SPI_DMA + .pid = SAM_PID_FLEXCOM0, +# endif +}; +#endif /* CONFIG_SAMA5_FLEXCOM0_SPI */ + +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI +/* FLEXCOM1 SPI driver operations */ + +static const struct spi_ops_s g_flexcom1_spiops = +{ + .lock = flex_spi_lock, + .select = flex_spi_select, + .setfrequency = flex_spi_setfrequency, + .setmode = flex_spi_setmode, + .setbits = flex_spi_setbits, +# ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +# endif + .status = sam_flexcom1_spistatus, +# ifdef CONFIG_SPI_CMDDATA + .cmddata = sam_flexcom1_spicmddata, +# endif + .send = flex_spi_send, +# ifdef CONFIG_SPI_EXCHANGE + .exchange = flex_spi_exchange, +# else + .sndblock = flex_spi_sndblock, + .recvblock = flex_spi_recvblock, +# endif + .registercallback = 0, /* Not implemented */ +}; + +/* This is the overall state of the FLEXCOM1 SPI controller */ + +static struct sam_flex_spidev_s g_flexcom1dev = +{ + .base = SAM_FLEXCOM1_VBASE, + .spilock = NXMUTEX_INITIALIZER, + .select = sam_flexcom1_spiselect, +# ifdef SAMA5_FLEXCOM1_SPI_DMA + .pid = SAM_PID_FLEXCOM1, +# endif +}; +#endif /* CONFIG_SAMA5_FLEXCOM1_SPI */ + +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI +/* FLEXCOM2 SPI driver operations */ + +static const struct spi_ops_s g_flexcom2_spiops = +{ + .lock = flex_spi_lock, + .select = flex_spi_select, + .setfrequency = flex_spi_setfrequency, + .setmode = flex_spi_setmode, + .setbits = flex_spi_setbits, +# ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +# endif + .status = sam_flexcom2_spistatus, +# ifdef CONFIG_SPI_CMDDATA + .cmddata = sam_flexcom2_spicmddata, +# endif + .send = flex_spi_send, +# ifdef CONFIG_SPI_EXCHANGE + .exchange = flex_spi_exchange, +# else + .sndblock = flex_spi_sndblock, + .recvblock = flex_spi_recvblock, +# endif + .registercallback = 0, /* Not implemented */ +}; + +/* This is the overall state of the FLEXCOM2 SPI controller */ + +static struct sam_flex_spidev_s g_flexcom2dev = +{ + .base = SAM_FLEXCOM2_VBASE, + .spilock = NXMUTEX_INITIALIZER, + .select = sam_flexcom2_spiselect, +# ifdef SAMA5_FLEXCOM2_SPI_DMA + .pid = SAM_PID_FLEXCOM2, +# endif +}; +#endif /* CONFIG_SAMA5_FLEXCOM2_SPI */ + +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI +/* FLEXCOM3 SPI driver operations */ + +static const struct spi_ops_s g_flexcom3_spiops = +{ + .lock = flex_spi_lock, + .select = flex_spi_select, + .setfrequency = flex_spi_setfrequency, + .setmode = flex_spi_setmode, + .setbits = flex_spi_setbits, +# ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +# endif + .status = sam_flexcom3_spistatus, +# ifdef CONFIG_SPI_CMDDATA + .cmddata = sam_flexcom3_spicmddata, +# endif + .send = flex_spi_send, +# ifdef CONFIG_SPI_EXCHANGE + .exchange = flex_spi_exchange, +# else + .sndblock = flex_spi_sndblock, + .recvblock = flex_spi_recvblock, +# endif + .registercallback = 0, /* Not implemented */ +}; + +/* This is the overall state of the FLEXCOM3 SPI controller */ + +static struct sam_flex_spidev_s g_flexcom3dev = +{ + .base = SAM_FLEXCOM3_VBASE, + .spilock = NXMUTEX_INITIALIZER, + .select = sam_flexcom3_spiselect, +# ifdef SAMA5_FLEXCOM3_SPI_DMA + .pid = SAM_PID_FLEXCOM3, +# endif +}; +#endif /* CONFIG_SAMA5_FLEXCOM3_SPI */ + +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI +/* FLEXCOM4 SPI driver operations */ + +static const struct spi_ops_s g_flexcom4_spiops = +{ + .lock = flex_spi_lock, + .select = flex_spi_select, + .setfrequency = flex_spi_setfrequency, + .setmode = flex_spi_setmode, + .setbits = flex_spi_setbits, +# ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +# endif + .status = sam_flexcom4_spistatus, +# ifdef CONFIG_SPI_CMDDATA + .cmddata = sam_flexcom4_spicmddata, +# endif + .send = flex_spi_send, +# ifdef CONFIG_SPI_EXCHANGE + .exchange = flex_spi_exchange, +# else + .sndblock = flex_spi_sndblock, + .recvblock = flex_spi_recvblock, +# endif + .registercallback = 0, /* Not implemented */ +}; + +/* This is the overall state of the FLEXCOM4 SPI controller */ + +static struct sam_flex_spidev_s g_flexcom4dev = +{ + .base = SAM_FLEXCOM4_VBASE, + .spilock = NXMUTEX_INITIALIZER, + .select = sam_flexcom4_spiselect, +# ifdef SAMA5_FLEXCOM4_SPI_DMA + .pid = SAM_PID_FLEXCOM4, +# endif +}; +#endif /* CONFIG_SAMA5_FLEXCOM4_SPI */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: flex_spi_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_SPI_REGDEBUG +static bool flex_spi_checkreg(struct sam_flex_spidev_s *flex_spi, bool wr, + uint32_t value, uint32_t address) +{ + if (wr == flex_spi->wrlast && /* Same kind of access? */ + value == flex_spi->valuelast && /* Same value? */ + address == flex_spi->addresslast) /* Same address? */ + { + /* Yes, then just keep a count of the number of times we did this. */ + + flex_spi->ntimes++; + return false; + } + else + { + /* Did we do the previous operation more than once? */ + + if (flex_spi->ntimes > 0) + { + /* Yes... show how many times we did it */ + + spiinfo("...[Repeats %d times]...\n", flex_spi->ntimes); + } + + /* Save information about the new access */ + + flex_spi->wrlast = wr; + flex_spi->valuelast = value; + flex_spi->addresslast = address; + flex_spi->ntimes = 0; + } + + /* Return true if this is the first time that we have done this operation */ + + return true; +} +#endif /* CONFIG_SAMA5_SPI_REGDEBUG */ + +/**************************************************************************** + * Name: flex_spi_getreg + * + * Description: + * Read an SPI register + * + ****************************************************************************/ + +static inline uint32_t flex_spi_getreg(struct sam_flex_spidev_s *flex_spi, + unsigned int offset) +{ + uint32_t address = flex_spi->base + offset; + uint32_t value = getreg32(address); + +#ifdef CONFIG_SAMA5_SPI_REGDEBUG + if (flex_spi_checkreg(flex_spi, false, value, address)) + { + spiinfo("%08" PRIx32 "->%08" PRIx32 "\n", address, value); + } +#endif + + return value; +} + +/**************************************************************************** + * Name: flex_spi_putreg + * + * Description: + * Write a value to an SPI register + * + ****************************************************************************/ + +static inline void flex_spi_putreg(struct sam_flex_spidev_s *flex_spi, + uint32_t value, unsigned int offset) +{ + uint32_t address = flex_spi->base + offset; + +#ifdef CONFIG_SAMA5_SPI_REGDEBUG + if (flex_spi_checkreg(flex_spi, true, value, address)) + { + spiinfo("%08" PRIx32 "<-%08" PRIx32 "\n", address, value); + } +#endif + + putreg32(value, address); +} + +/**************************************************************************** + * Name: flex_spi_dumpregs + * + * Description: + * Dump the contents of all SPI registers + * + * Input Parameters: + * spi - The SPI controller to dump + * msg - Message to print before the register data + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_SPI_INFO +static void flex_spi_dumpregs(struct sam_flex_spidev_s *flex_spi, + const char *msg) +{ + spiinfo("%s:\n", msg); + spiinfo(" MR:%08x SR:%08x IMR:%08x\n", + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_MR_OFFSET), + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_SR_OFFSET), + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_IMR_OFFSET)); + spiinfo(" CSR0:%08x CSR1:%08x \n", + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_CSR0_OFFSET), + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_CSR1_OFFSET)); + spiinfo(" WPCR:%08x WPSR:%08x\n", + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_WPCR_OFFSET), + getreg32(flex_spi->base + SAM_FLEXCOM_SPI_WPSR_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: flex_spi_dev + * + * Description: + * Given a chip select instance, return a pointer to the parent SPI + * controller instance. + * + ****************************************************************************/ + +static inline struct sam_flex_spidev_s *flex_spi_dev(struct sam_flex_spics_s + *flex_spics) +{ + switch (flex_spics->flex_spino) + { +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI + case 0: + return &g_flexcom0dev; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI + case 1: + return &g_flexcom1dev; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI + case 2: + return &g_flexcom2dev; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI + case 3: + return &g_flexcom3dev; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI + case 4: + return &g_flexcom4dev; + break; +#endif + default: + + /* shouldn't get here */ + + DEBUGASSERT(false); + return NULL; + break; + } +} + +/**************************************************************************** + * Name: flex_spi_flush + * + * Description: + * Make sure that there are now dangling SPI transfer in progress + * + * Input Parameters: + * spi - SPI controller state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void flex_spi_flush(struct sam_flex_spidev_s *flex_spi) +{ + /* Make sure the no TX activity is in progress... waiting if necessary */ + + while ((flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET) & + FLEXCOM_SPI_INT_TXEMPTY) == 0); + + /* Then make sure that there is no pending RX data .. reading as + * discarding as necessary. + */ + + while ((flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET) & + FLEXCOM_SPI_INT_RDRF) != 0) + { + flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_RDR_OFFSET); + } +} + +/**************************************************************************** + * Name: flex_spi_cs2pcs + * + * Description: + * Map the chip select number to the bit-set PCS field used in the SPI + * registers. A chip select number is used for indexing and identifying + * chip selects. However, the chip select information is represented by + * a bit set in the SPI registers. This function maps those chip select + * numbers to the correct bit set: + * + * CS Returned Spec Effective + * No. PCS Value NPCS + * ---- ------ ------ -------- + * 0 00 x0 10 + * 1 01 01 01 + * + * Input Parameters: + * flex_spics - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline uint32_t flex_spi_cs2pcs(struct sam_flex_spics_s *flex_spics) +{ + return ((uint32_t)1 << flex_spics->cs) - 1; +} + +/**************************************************************************** + * Name: flex_spi_dma_sampleinit + * + * Description: + * Initialize sampling of DMA registers (if CONFIG_SAMA5_SPI_DMADEBUG) + * + * Input Parameters: + * flex_spics - Chip select doing the DMA + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG +static void flex_spi_dma_sampleinit(struct sam_flex_spics_s *flex_spics) +{ + /* Put contents of register samples into a known state */ + + memset(flex_spics->rxdmaregs, 0xff, + DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + memset(flex_spics->txdmaregs, 0xff, + DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + + /* Then get the initial samples */ + + sam_dmasample(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_INITIAL]); + sam_dmasample(flex_spics->txdma, &flex_spics->txdmaregs[DMA_INITIAL]); +} +#endif + +/**************************************************************************** + * Name: flex_spi_dma_sampledone + * + * Description: + * Dump sampled DMA registers + * + * Input Parameters: + * flex_spics - Chip select doing the DMA + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG +static void flex_spi_dma_sampledone(struct sam_flex_spics_s *flex_spics) +{ + /* Sample the final registers */ + + sam_dmasample(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_END_TRANSFER]); + sam_dmasample(flex_spics->txdma, &flex_spics->txdmaregs[DMA_END_TRANSFER]); + + /* Then dump the sampled DMA registers */ + + /* Initial register values */ + + sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_INITIAL], + "TX: Initial Registers"); + sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_INITIAL], + "RX: Initial Registers"); + + /* Register values after DMA setup */ + + sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_AFTER_SETUP], + "TX: After DMA Setup"); + sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_AFTER_SETUP], + "RX: After DMA Setup"); + + /* Register values after DMA start */ + + sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_AFTER_START], + "TX: After DMA Start"); + sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_AFTER_START], + "RX: After DMA Start"); + + /* Register values at the time of the TX and RX DMA callbacks + * -OR- DMA timeout. + * + * If the DMA timedout, 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. + */ + + sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_CALLBACK], + "TX: At DMA callback"); + + /* Register values at the end of the DMA */ + + if (flex_spics->result == -ETIMEDOUT) + { + sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_TIMEOUT], + "RX: At DMA timeout"); + } + else + { + sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_CALLBACK], + "RX: At DMA callback"); + } + + sam_dmadump(flex_spics->rxdma, &flex_spics->rxdmaregs[DMA_END_TRANSFER], + "RX: At End-of-Transfer"); + sam_dmadump(flex_spics->txdma, &flex_spics->txdmaregs[DMA_END_TRANSFER], + "TX: At End-of-Transfer"); +} +#endif /* CONFIG_SAMA5_SPI_DMADEBUG */ + +/**************************************************************************** + * Name: flex_spi_dmatimeout + * + * 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_SPI_DMA +static void flex_spi_dmatimeout(wdparm_t arg) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)arg; + DEBUGASSERT(flex_spics != NULL); + + /* Sample DMA registers at the time of the timeout */ + + flex_spi_rxdma_sample(flex_spics, DMA_CALLBACK); + + /* Report timeout result, perhaps overwriting any failure reports from + * the TX callback. + */ + + flex_spics->result = -ETIMEDOUT; + + /* Then wake up the waiting thread */ + + nxsem_post(&flex_spics->dmawait); +} +#endif + +/**************************************************************************** + * Name: flex_spi_rxcallback + * + * Description: + * This callback function is invoked at the completion of the SPI RX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static void flex_spi_rxcallback(DMA_HANDLE handle, void *arg, int result) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)arg; + DEBUGASSERT(flex_spics != NULL); + + /* Cancel the watchdog timeout */ + + wd_cancel(&flex_spics->dmadog); + + /* Sample DMA registers at the time of the callback */ + + flex_spi_rxdma_sample(flex_spics, DMA_CALLBACK); + + /* Report the result of the transfer only if the TX callback has not + * already reported an error. + */ + + if (flex_spics->result == -EBUSY) + { + /* Save the result of the transfer if no error was previously + * reported + */ + + flex_spics->result = result; + } + + /* Then wake up the waiting thread */ + + nxsem_post(&flex_spics->dmawait); +} +#endif + +/**************************************************************************** + * Name: flex_spi_txcallback + * + * Description: + * This callback function is invoked at the completion of the SPI TX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static void flex_spi_txcallback(DMA_HANDLE handle, void *arg, int result) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_spics_s *)arg; + DEBUGASSERT(flex_spics != NULL); + + flex_spi_txdma_sample(flex_spics, DMA_CALLBACK); + + /* Do nothing on the TX callback unless an error is reported. This + * callback is not really important because the SPI exchange is not + * complete until the RX callback is received. + */ + + if (result != OK && flex_spics->result == -EBUSY) + { + /* Save the result of the transfer if an error is reported */ + + flex_spics->result = result; + } +} +#endif + +/**************************************************************************** + * Name: flex_spi_physregaddr + * + * Description: + * Return the physical address of an SPI register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static inline uintptr_t flex_spi_physregaddr(struct sam_flex_spics_s + *flex_spics, unsigned int offset) +{ + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + return sam_physregaddr(flex_spi->base + offset); +} +#endif + +/**************************************************************************** + * Name: flex_spi_lock + * + * Description: + * On SPI buses where there are multiple devices, it will be necessary to + * lock SPI 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 SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI bus is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int flex_spi_lock(struct spi_dev_s *flex_dev, bool lock) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)flex_dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + int ret; + + spiinfo("lock=%d\n", lock); + if (lock) + { + ret = nxmutex_lock(&flex_spi->spilock); + } + else + { + ret = nxmutex_unlock(&flex_spi->spilock); + } + + return ret; +} + +/**************************************************************************** + * Name: flex_spi_select + * + * Description: + * This function does not actually set the chip select line. Rather, it + * simply maps the device ID into a chip select number and retains that + * chip select number for later use. + * + * Input Parameters: + * dev - Device-specific state data + * devid - the flexcom device to select + * selected - bool to indicate whether to select or deselect + * + ****************************************************************************/ + +static void flex_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + uint32_t regval; + + /* Are we selecting or de-selecting the device? */ + + spiinfo("selected=%d\n", selected); + if (selected) + { + spiinfo("cs=%d\n", flex_spics->cs); + + /* Before writing the TDR, the PCS field in the SPI_MR register must be + * set in order to select a slave. + */ + + regval = flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_MR_OFFSET); + regval &= ~FLEXCOM_SPI_MR_PCS_MASK; + regval |= (flex_spi_cs2pcs(flex_spics) << FLEXCOM_SPI_MR_PCS_SHIFT); + flex_spi_putreg(flex_spi, regval, SAM_FLEXCOM_SPI_MR_OFFSET); + } + + /* Perform any board-specific chip select operations. PIO chip select + * pins may be programmed by the board specific logic in one of two + * different ways. First, the pins may be programmed as SPI peripherals. + * In that case, the pins are completely controlled by the SPI driver. + * The sam_spi[0|1]select methods still needs to be provided, but they + * may be only stubs. + * + * An alternative way to program the PIO chip select pins is as normal + * PIO outputs. In that case, the automatic control of the CS pins is + * bypassed and this function must provide control of the chip select. + * NOTE: In this case, the PIO output pin does *not* have to be the + * same as the NPCS pin normal associated with the chip select number. + */ + + flex_spi->select(devid, selected); +} + +/**************************************************************************** + * Name: flex_spi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * freq - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static uint32_t flex_spi_setfrequency(struct spi_dev_s *dev, uint32_t freq) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + uint32_t actual; + uint32_t scbr; + uint32_t dlybs; + uint32_t dlybct; + uint32_t regval; + unsigned int offset; + + spiinfo("cs=%d frequency=%" PRId32 "\n", flex_spics->cs, freq); + + /* Check if the requested frequency is the same as the frequency + * selection + */ + + if (flex_spics->frequency == freq) + { + /* We are already at this frequency. Return the actual. */ + + return flex_spics->actual; + } + + /* Configure SPI to a frequency as close as possible to the requested + * frequency. + * + * SPCK frequency = SPI_CLK / SCBR, or SCBR = SPI_CLK / frequency + */ + + scbr = SAM_FLEXCOM_SPI_CLOCK / freq; + + if (scbr < 8) + { + scbr = 8; + } + else if (scbr > 254) + { + scbr = 254; + } + + scbr = (scbr + 1) & ~1; + + /* Save the new scbr value */ + + offset = g_csroffset[flex_spics->cs]; + regval = flex_spi_getreg(flex_spi, offset); + regval &= ~(FLEXCOM_SPI_CSR_SCBR_MASK | FLEXCOM_SPI_CSR_DLYBS_MASK | + FLEXCOM_SPI_CSR_DLYBCT_MASK); + regval |= scbr << FLEXCOM_SPI_CSR_SCBR_SHIFT; + + /* DLYBS: Delay Before SPCK. This field defines the delay from NPCS valid + * to the first valid SPCK transition. When DLYBS equals zero, the NPCS + * valid to SPCK transition is 1/2 the SPCK clock period. Otherwise, the + * following equations determine the delay: + * + * Delay Before SPCK = DLYBS / SPI_CLK + * + * For a 2uS delay + * + * DLYBS = SPI_CLK * 0.000002 = SPI_CLK / 500000 + */ + + dlybs = SAM_FLEXCOM_SPI_CLOCK / 500000; + regval |= dlybs << FLEXCOM_SPI_CSR_DLYBS_SHIFT; + + /* 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) / SPI_CLK + * + * For a 5uS delay: + * + * DLYBCT = SPI_CLK * 0.000005 / 32 = SPI_CLK / 200000 / 32 + */ + + dlybct = SAM_FLEXCOM_SPI_CLOCK / 200000 / 32; + regval |= dlybct << FLEXCOM_SPI_CSR_DLYBCT_SHIFT; + flex_spi_putreg(flex_spi, regval, offset); + + /* Calculate the new actual frequency */ + + actual = SAM_FLEXCOM_SPI_CLOCK / scbr; + spiinfo("csr[offset=%02x]=%08" PRIx32 " actual=%" PRId32 "\n", + offset, regval, actual); + + /* Save the frequency setting */ + + flex_spics->frequency = freq; + flex_spics->actual = actual; + + spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", freq, actual); + return actual; +} + +/**************************************************************************** + * Name: flex_spi_setmode + * + * Description: + * Set the SPI mode. Optional. See enum spi_mode_e for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void flex_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + uint32_t regval; + unsigned int offset; + + spiinfo("cs=%d mode=%d\n", flex_spics->cs, mode); + + /* Has the mode changed? */ + + if (mode != flex_spics->mode) + { + /* Yes... Set the mode appropriately: + * + * SPI CPOL NCPHA + * MODE + * 0 0 1 + * 1 0 0 + * 2 1 1 + * 3 1 0 + */ + + offset = g_csroffset[flex_spics->cs]; + regval = flex_spi_getreg(flex_spi, offset); + regval &= ~(FLEXCOM_SPI_CSR_CPOL | FLEXCOM_SPI_CSR_NCPHA); + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; NCPHA=1 */ + regval |= FLEXCOM_SPI_CSR_NCPHA; + break; + + case SPIDEV_MODE1: /* CPOL=0; NCPHA=0 */ + break; + + case SPIDEV_MODE2: /* CPOL=1; NCPHA=1 */ + regval |= (FLEXCOM_SPI_CSR_CPOL | FLEXCOM_SPI_CSR_NCPHA); + break; + + case SPIDEV_MODE3: /* CPOL=1; NCPHA=0 */ + regval |= FLEXCOM_SPI_CSR_CPOL; + break; + + default: + DEBUGASSERT(false); + return; + } + + flex_spi_putreg(flex_spi, regval, offset); + spiinfo("csr[offset=%02x]=%08" PRIx32 "\n", offset, regval); + + /* Save the mode so that subsequent re-configurations will be faster */ + + flex_spics->mode = mode; + } +} + +/**************************************************************************** + * Name: flex_spi_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 flex_spi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + uint32_t regval; + unsigned int offset; + + spiinfo("cs=%d nbits=%d\n", flex_spics->cs, nbits); + DEBUGASSERT(nbits > 7 && nbits < 17); + + /* NOTE: The logic in spi_send and in spi_exchange only handles 8-bit + * data at the present time. So the following extra assertion is a + * reminder that we have to fix that someday. + */ + + DEBUGASSERT(nbits == 8); /* Temporary -- FIX ME */ + + /* Has the number of bits changed? */ + + if (nbits != flex_spics->nbits) + { + /* Yes... Set number of bits appropriately */ + + offset = g_csroffset[flex_spics->cs]; + regval = flex_spi_getreg(flex_spi, offset); + regval &= ~FLEXCOM_SPI_CSR_BITS_MASK; + regval |= FLEXCOM_SPI_CSR_BITS(nbits); + flex_spi_putreg(flex_spi, regval, offset); + + spiinfo("csr[offset=%02x]=%08" PRIx32 "\n", offset, regval); + + /* Save the selection so that subsequent re-configurations will be + * faster. + */ + + flex_spics->nbits = nbits; + } +} + +/**************************************************************************** + * Name: flex_spi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ****************************************************************************/ + +static uint32_t flex_spi_send(struct spi_dev_s *dev, uint32_t wd) +{ + uint8_t txbyte; + uint8_t rxbyte; + + /* spi_exchange can do this. Note: right now, this only deals with 8-bit + * words. If the SPI interface were configured for words of other sizes, + * this would fail. + */ + + txbyte = (uint8_t)wd; + rxbyte = (uint8_t)0; + flex_spi_exchange(dev, &txbyte, &rxbyte, 1); + + spiinfo("Sent %02x received %02x\n", txbyte, rxbyte); + return (uint32_t)rxbyte; +} + +/**************************************************************************** + * Name: flex_spi_exchange (and flex_spi_exchange_nodma) + * + * Description: + * Exchange a block of data from SPI. There are two versions of this + * function: (1) One that is enabled only when CONFIG_SAMA5_SPI_DMA=y + * that performs DMA SPI transfers, but only when a larger block of + * data is being transferred. And (2) another version that does polled + * SPI transfers. When CONFIG_SAMA5_SPI_DMA=n the latter is the only + * version available; when CONFIG_SAMA5_SPI_DMA=y, this version is only + * used for short SPI transfers and gets renamed as spi_exchange_nodma). + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static void spi_exchange_nodma(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +#else +static void flex_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +#endif +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + uint8_t *rxptr = (uint8_t *)rxbuffer; + uint8_t *txptr = (uint8_t *)txbuffer; + uint32_t pcs; + uint32_t data; + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* Set up PCS bits */ + + pcs = flex_spi_cs2pcs(flex_spics) << FLEXCOM_SPI_TDR_PCS_SHIFT; + + /* Make sure that any previous transfer is flushed from the hardware */ + + flex_spi_flush(flex_spi); + + /* Loop, sending each word in the user-provided data buffer. + * + * Note 1: Right now, this only deals with 8-bit words. If the SPI + * interface were configured for words of other sizes, this + * would fail. + * Note 2: Good SPI performance would require that we implement DMA + * transfers! + * Note 3: This loop might be made more efficient. Would logic + * like the following improve the throughput? Or would it + * just add the risk of overruns? + * + * Get word 1; + * Send word 1; Now word 1 is "in flight" + * nwords--; + * for (; nwords > 0; nwords--) + * { + * Get word N. + * Wait for TDRE meaning that word N-1 has moved to the shift + * register. + * Disable interrupts to keep the following atomic + * Send word N. Now both work N-1 and N are "in flight" + * Wait for RDRF meaning that word N-1 is available + * Read word N-1. + * Re-enable interrupts. + * Save word N-1. + * } + * Wait for RDRF meaning that the final word is available + * Read the final word. + * Save the final word. + */ + + for (; nwords > 0; nwords--) + { + /* Get the data to send (0xff if there is no data source) */ + + if (txptr) + { + data = (uint32_t)*txptr++; + } + else + { + data = 0xffff; + } + + /* Set the PCS field in the value written to the TDR */ + + data |= pcs; + + /* Do we need to set the LASTXFER bit in the TDR value too? */ + +#ifdef CONFIG_SPI_VARSELECT + if (nwords == 1) + { + data |= FLEXCOM_SPI_TDR_LASTXFER; + } +#endif + + /* Wait for any previous data written to the TDR to be transferred + * to the serializer. + */ + + while ((flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET) & + FLEXCOM_SPI_INT_TDRE) == 0); + + /* Write the data to transmitted to the Transmit Data Register (TDR) */ + + flex_spi_putreg(flex_spi, data, SAM_FLEXCOM_SPI_TDR_OFFSET); + + /* Wait for the read data to be available in the RDR. + * TODO: Data transfer rates would be improved using the RX FIFO + * (and also DMA) + */ + + while ((flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET) & + FLEXCOM_SPI_INT_RDRF) == 0); + + /* Read the received data from the SPI Data Register.. + * TODO: The following only works if nbits <= 8. + */ + + data = flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_RDR_OFFSET); + if (rxptr) + { + *rxptr++ = (uint8_t)data; + } + } +} + +#ifdef CONFIG_SAMA5_SPI_DMA +static void flex_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + struct sam_flex_spics_s *flex_spics = (struct sam_flex_spics_s *)dev; + struct sam_flex_spidev_s *flex_spi = flex_spi_dev(flex_spics); + uint32_t rxflags; + uint32_t txflags; + uint32_t txdummy; + uint32_t rxdummy; + uint32_t paddr; + uint32_t maddr; + int ret; + + /* If we cannot do DMA -OR- if this is a small SPI transfer, then let + * spi_exchange_nodma() do the work. + */ + + if (!flex_spics->candma || nwords <= CONFIG_SAMA5_SPI_DMATHRESHOLD) + { + flex_spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords); + return; + } + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + flex_spics = (struct flex_sam_spics_s *)dev; + flex_spi = flex_spi_dev(flex_spics); + DEBUGASSERT(flex_spics && flex_spi); + + /* Make sure that any previous transfer is flushed from the hardware */ + + flex_spi_flush(flex_spi); + + /* Sample initial DMA registers */ + + flex_spi_dma_sampleinit(flex_spics); + + /* Configure the DMA channels. There are four different cases: + * + * 1) A true exchange with the memory address incrementing on both + * RX and TX channels, + * 2) A read operation with the memory address incrementing only on + * the receive channel, + * 3) A write operation where the memory address increments only on + * the receive channel, and + * 4) A corner case where there the memory address does not increment + * on either channel. This case might be used in certain cases + * where you want to assure that certain number of clocks are + * provided on the SPI bus. + */ + + rxflags = DMACH_FLAG_FIFOCFG_LARGEST | + DMACH_FLAG_PERIPHPID(flex_spi->pid) | + DMACH_FLAG_PERIPHH2SEL | + DMACH_FLAG_PERIPHISPERIPH | + DMACH_FLAG_PERIPHAHB_AHB_IF2 | + DMACH_FLAG_PERIPHWIDTH_8BITS | + DMACH_FLAG_PERIPHCHUNKSIZE_1 | + DMACH_FLAG_MEMPID_MAX | + DMACH_FLAG_MEMAHB_AHB_IF0 | + DMACH_FLAG_MEMWIDTH_8BITS | + DMACH_FLAG_MEMCHUNKSIZE_1 | + DMACH_FLAG_MEMBURST_4; + + if (!rxbuffer) + { + /* No sink data buffer. Point to our dummy buffer and leave + * the rxflags so that no address increment is performed. + */ + + rxbuffer = (void *)&rxdummy; + } + else + { + /* A receive buffer is available. Use normal TX memory incrementing. */ + + rxflags |= DMACH_FLAG_MEMINCREMENT; + } + + txflags = DMACH_FLAG_FIFOCFG_LARGEST | + DMACH_FLAG_PERIPHPID(flex_spi->pid) | + DMACH_FLAG_PERIPHH2SEL | + DMACH_FLAG_PERIPHISPERIPH | + DMACH_FLAG_PERIPHAHB_AHB_IF2 | + DMACH_FLAG_PERIPHWIDTH_8BITS | + DMACH_FLAG_PERIPHCHUNKSIZE_1 | + DMACH_FLAG_MEMPID_MAX | + DMACH_FLAG_MEMAHB_AHB_IF0 | + DMACH_FLAG_MEMWIDTH_8BITS | + DMACH_FLAG_MEMCHUNKSIZE_1 | + DMACH_FLAG_MEMBURST_4; + + if (!txbuffer) + { + /* No source data buffer. Point to our dummy buffer and configure + * the txflags so that no address increment is performed. + */ + + txdummy = 0xffffffff; + txbuffer = (const void *)&txdummy; + } + else + { + /* Source data is available. Use normal TX memory incrementing. */ + + txflags |= DMACH_FLAG_MEMINCREMENT; + } + + /* Then configure the DMA channels to make it so */ + + sam_dmaconfig(flex_spics->rxdma, rxflags); + sam_dmaconfig(flex_spics->txdma, txflags); + + /* Configure the exchange transfers */ + + paddr = flex_spi_physregaddr(flex_spics, SAM_FLEXCOM_SPI_RDR_OFFSET); + maddr = sam_physramaddr((uintptr_t)rxbuffer); + + ret = sam_dmarxsetup(flex_spics->rxdma, paddr, maddr, nwords); + if (ret < 0) + { + dmaerr("ERROR: sam_dmarxsetup failed: %d\n", ret); + return; + } + + flex_spi_rxdma_sample(flex_spics, DMA_AFTER_SETUP); + + paddr = flex_spi_physregaddr(flex_spics, SAM_FLEXCOM_SPI_TDR_OFFSET); + maddr = sam_physramaddr((uintptr_t)txbuffer); + + ret = sam_dmatxsetup(flex_spics->txdma, paddr, maddr, nwords); + if (ret < 0) + { + dmaerr("ERROR: sam_dmatxsetup failed: %d\n", ret); + return; + } + + flex_spi_txdma_sample(flex_spics, DMA_AFTER_SETUP); + + /* Start the DMA transfer */ + + flex_spics->result = -EBUSY; + ret = sam_dmastart(flex_spics->rxdma, flex_spi_rxcallback, + (void *)flex_spics); + if (ret < 0) + { + dmaerr("ERROR: RX sam_dmastart failed: %d\n", ret); + return; + } + + flex_spi_rxdma_sample(flex_spics, DMA_AFTER_START); + + ret = sam_dmastart(flex_spics->txdma, flex_spi_txcallback, + (void *)flex_spics); + if (ret < 0) + { + dmaerr("ERROR: RX sam_dmastart failed: %d\n", ret); + sam_dmastop(flex_spics->rxdma); + return; + } + + flex_spi_txdma_sample(spics, DMA_AFTER_START); + + /* Wait for DMA completion. This is done in a loop because there my 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(&flex_spics->dmadog, DMA_TIMEOUT_TICKS, + flex_spi_dmatimeout, (wdparm_t)flex_spics); + if (ret < 0) + { + spierr("ERROR: wd_start failed: %d\n", ret); + } + + /* Wait for the DMA complete */ + + ret = nxsem_wait_uninterruptible(&flex_spics->dmawait); + + /* Cancel the watchdog timeout */ + + wd_cancel(&flex_spics->dmadog); + + /* Check if we were awakened by an error of some kind. */ + + if (ret < 0) + { + DEBUGPANIC(); + return; + } + + /* Not that we might be awkened 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 (flex_spics->result == -EBUSY); + + /* Dump the sampled DMA registers */ + + flex_spi_dma_sampledone(flex_spics); + + /* 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(flex_spics->rxdma); + sam_dmastop(flex_spics->txdma); + + /* All we can do is complain if the DMA fails */ + + if (flex_spics->result) + { + spierr("ERROR: DMA failed with result: %d\n", spics->result); + } +} +#endif /* CONFIG_SAMA5_SPI_DMA */ + +/**************************************************************************** + * Name: flex_spi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * buffer - A pointer to the buffer of data to be sent + * nwords - the length of data to send from the buffer in number of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void flex_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords) +{ + /* spi_exchange can do this. */ + + flex_spi_exchange(dev, buffer, NULL, nwords); +} +#endif + +/**************************************************************************** + * Name: flex_spi_recvblock + * + * Description: + * Revice a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * buffer - A pointer to the buffer in which to receive data + * nwords - the length of data that can be received in the buffer in number + * of words. The wordsize is determined by the number of + * bits-per-word selected for the SPI interface. If nbits <= 8, + * the data is packed into uint8_t's; if nbits >8, the data is + * packed into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void flex_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords) +{ + /* spi_exchange can do this. */ + + flex_spi_exchange(dev, NULL, buffer, nwords); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_flex_spibus_initialize + * + * Description: + * Initialize the selected flexcom SPI port + * + * Input Parameters: + * port - the 5 flexcom ports only have 2 physial CS lines + * - so there are 10 "logical" ports. + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *sam_flex_spibus_initialize(int port) +{ + struct sam_flex_spidev_s *flex_spi; + struct sam_flex_spics_s *flex_spics; + unsigned int flex_csno = (port & __FLEXCOM_SPI_CS_MASK) >> + __FLEXCOM_SPI_CS_SHIFT; + unsigned int flex_spino = (port & __FLEXCOM_SPI_SPI_MASK) >> + __FLEXCOM_SPI_SPI_SHIFT; + irqstate_t flags; + uint32_t regval; + unsigned int offset; + + spiinfo("port: %d flex_csno: %d spino: %d\n", port, flex_csno, flex_spino); + DEBUGASSERT(flex_csno >= 0 && flex_csno < SAM_FLEXCOM_SPI_NCS); + +#if !defined(CONFIG_SAMA5_FLEXCOM0_SPI) + DEBUGASSERT(flex_spino != 0); +#endif +#if !defined(CONFIG_SAMA5_FLEXCOM1_SPI) + DEBUGASSERT(flex_spino != 1); +#endif +#if !defined(CONFIG_SAMA5_FLEXCOM2_SPI) + DEBUGASSERT(flex_spino != 2); +#endif +#if !defined(CONFIG_SAMA5_FLEXCOM3_SPI) + DEBUGASSERT(flex_spino != 3); +#endif +#if !defined(CONFIG_SAMA5_FLEXCOM4_SPI) + DEBUGASSERT(flex_spino != 4); +#endif + + /* Allocate a new state structure for this chip select. NOTE that there + * is no protection if the same chip select is used in two different + * chip select structures. + */ + + flex_spics = (struct sam_flex_spics_s *)kmm_zalloc( + sizeof(struct sam_flex_spics_s)); + if (!flex_spics) + { + spierr("ERROR: Failed to allocate a flexcom chip select structure\n"); + return NULL; + } + + /* Set up the initial state for this chip select structure. Other fields + * were zeroed by kmm_zalloc(). + */ + +#ifdef CONFIG_SAMA5_SPI_DMA + switch (flex_spino) + { +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI + case 0: + flex_spics->candma = SAMA5_FLEXCOM0_SPI_DMA; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI + case 1: + flex_spics->candma = SAMA5_FLEXCOM1_SPI_DMA; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI + case 2: + flex_spics->candma = SAMA5_FLEXCOM2_SPI_DMA; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI + case 3: + flex_spics->candma = SAMA5_FLEXCOM3_SPI_DMA; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI + case 4: + flex_spics->candma = SAMA5_FLEXCOM4_SPI_DMA; + break; +#endif + default: + DEBUGASSERT(false); + break; + } + + flex_spics->candma = flex_spino ? SAMA5_SPI1_DMA : SAMA5_SPI0_DMA; + + /* Pre-allocate DMA channels. These allocations exploit that fact that + * SPI0 is managed by DMAC0 and SPI1 is managed by DMAC1. Hence, + * the SPI number (spino) is the same as the DMAC number. + */ + + if (flex_spics->candma) + { + flex_spics->rxdma = sam_dmachannel(flex_spino, 0); + if (!flex_spics->rxdma) + { + spierr("ERROR: Failed to allocate the RX DMA channel\n"); + flex_spics->candma = false; + } + } + + if (flex_spics->candma) + { + flex_spics->txdma = sam_dmachannel(flex_spino, 0); + if (!flex_spics->txdma) + { + spierr("ERROR: Failed to allocate the TX DMA channel\n"); + sam_dmafree(flex_spics->rxdma); + flex_spics->rxdma = NULL; + flex_spics->candma = false; + } + } +#endif /* CONFIG_SAMA5_SPI_DMA */ + + /* Select the SPI operations */ + + /* have already used DEBUGASSERT to weed out invalid SPI port assignments */ + + switch (flex_spino) + { +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI + case 0: + putreg32(FLEX_MR_OPMODE_SPI, SAM_FLEX0_MR); + flex_spics->flex_spidev.ops = &g_flexcom0_spiops; + flex_spics->cs = flex_csno; + flex_spics->flex_spino = flex_spino; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI + case 1: + putreg32(FLEX_MR_OPMODE_SPI, SAM_FLEX1_MR); + flex_spics->flex_spidev.ops = &g_flexcom1_spiops; + flex_spics->cs = flex_csno; + flex_spics->flex_spino = flex_spino; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI + case 2: + putreg32(FLEX_MR_OPMODE_SPI, SAM_FLEX2_MR); + flex_spics->flex_spidev.ops = &g_flexcom2_spiops; + flex_spics->cs = flex_csno; + flex_spics->flex_spino = flex_spino; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI + case 3: + putreg32(FLEX_MR_OPMODE_SPI, SAM_FLEX3_MR); + flex_spics->flex_spidev.ops = &g_flexcom3_spiops; + flex_spics->cs = flex_csno; + flex_spics->flex_spino = flex_spino; + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI + case 4: + putreg32(FLEX_MR_OPMODE_SPI, SAM_FLEX4_MR); + flex_spics->flex_spidev.ops = &g_flexcom4_spiops; + flex_spics->cs = flex_csno; + flex_spics->flex_spino = flex_spino; + break; +#endif + default: + + /* shouldn't get here */ + + DEBUGASSERT(false); + break; + } + + /* Get the SPI device structure associated with the chip select */ + + flex_spi = flex_spi_dev(flex_spics); + + /* Has the SPI hardware been initialized? */ + + if (!flex_spi->initialized) + { + flags = enter_critical_section(); + + /* Enable clocking to the flexcom SPI block(s) */ + + switch (flex_spics->flex_spino) + { +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI + case 0: + sam_flexcom0_enableclk(); + + /* Configure any multiplexed pins as connected on the board, + * by board-specific logic here. + */ + + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI + case 1: + sam_flexcom1_enableclk(); + + /* Configure any multiplexed pins as connected on the board, + * by board-specific logic here. + */ + + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI + case 2: + sam_flexcom2_enableclk(); + + /* Configure any multiplexed pins as connected on the board, + * by board-specific logic here. + */ + + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI + case 3: + sam_flexcom3_enableclk(); + + /* Configure any multiplexed pins as connected on the board, + * by board-specific logic here. + */ + + break; +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI + case 4: + sam_flexcom4_enableclk(); + + /* Configure any multiplexed pins as connected on the board, + * by board-specific logic here. + */ + + break; +#endif + default: + + /* shouldn't get here */ + + DEBUGASSERT(false); + break; + } + + /* Disable SPI clocking */ + + flex_spi_putreg(flex_spi, FLEXCOM_SPI_CR_SPIDIS, + SAM_FLEXCOM_SPI_CR_OFFSET); + + /* Execute a software reset of the SPI (twice) */ + + flex_spi_putreg(flex_spi, FLEXCOM_SPI_CR_SWRST, + SAM_FLEXCOM_SPI_CR_OFFSET); + flex_spi_putreg(flex_spi, FLEXCOM_SPI_CR_SWRST, + SAM_FLEXCOM_SPI_CR_OFFSET); + + leave_critical_section(flags); + + /* Configure the SPI mode register */ + + flex_spi_putreg(flex_spi, FLEXCOM_SPI_MR_MSTR | FLEXCOM_SPI_MR_MODFDIS, + SAM_FLEXCOM_SPI_MR_OFFSET); + + /* And enable the SPI */ + + flex_spi_putreg(flex_spi, FLEXCOM_SPI_CR_SPIEN, + SAM_FLEXCOM_SPI_CR_OFFSET); + up_mdelay(20); + + /* Flush any pending transfers */ + + flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_SR_OFFSET); + flex_spi_getreg(flex_spi, SAM_FLEXCOM_SPI_RDR_OFFSET); + + /* Initialize the SPI semaphore that enforces mutually exclusive + * access to the SPI registers. + */ + + flex_spi->initialized = true; + +#ifdef CONFIG_SAMA5_SPI_DMA + /* Initialize the SPI semaphore that is used to wake up the waiting + * thread when the DMA transfer completes. This semaphore is used for + * signaling and, hence, should not have priority inheritance enabled. + */ + + nxsem_init(&flex_spics->dmawait, 0, 0); + nxsem_set_protocol(&flex_spics->dmawait, SEM_PRIO_NONE); +#endif + + flex_spi_dumpregs(flex_spi, "After initialization"); + } + + /* Set to mode=0 and nbits=8 and impossible frequency. The SPI will only + * be reconfigured if there is a change. + */ + + offset = g_csroffset[flex_csno]; + regval = flex_spi_getreg(flex_spi, offset); + regval &= ~(FLEXCOM_SPI_CSR_CPOL | FLEXCOM_SPI_CSR_NCPHA | + FLEXCOM_SPI_CSR_BITS_MASK); + regval |= (FLEXCOM_SPI_CSR_NCPHA | FLEXCOM_SPI_CSR_BITS(8)); + flex_spi_putreg(flex_spi, regval, offset); + + flex_spics->nbits = 8; + spiinfo("csr[offset=%02x]=%08" PRIx32 "\n", offset, regval); + + return &flex_spics->flex_spidev; +} + +#endif /* SAMA5_HAVE_FLEXCOM_SPI */ diff --git a/arch/arm/src/sama5/sam_flexcom_spi.h b/arch/arm/src/sama5/sam_flexcom_spi.h new file mode 100755 index 0000000000..3f2fd03c19 --- /dev/null +++ b/arch/arm/src/sama5/sam_flexcom_spi.h @@ -0,0 +1,277 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_flexcom_spi.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_FLEXCOM_SPI_H +#define __ARCH_ARM_SRC_SAMA5_SAM_FLEXCOM_SPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The Flexcom SPI "port" number used as an input to sam_flexcom_spibus_ + * initialize encodes information about the Flexcom SPI controller (0 thru 4) + * and the Flexcom SPI chip select (0 or 1) + */ + +#define __FLEXCOM_SPI_CS_SHIFT (0) /* Bit 0: Flexcom SPI chip select number */ +#define __FLEXCOM_SPI_CS_MASK (1 << __FLEXCOM_SPI_CS_SHIFT) +# define __FLEXCOM_SPI_CS0 (0 << __FLEXCOM_SPI_CS_SHIFT) +# define __FLEXCOM_SPI_CS1 (1 << __FLEXCOM_SPI_CS_SHIFT) +#define __FLEXCOM_SPI_SPI_SHIFT (1) /* Bit 1-3: Flexcom number */ +#define __FLEXCOM_SPI_SPI_MASK (7 << __FLEXCOM_SPI_SPI_SHIFT) +# define __FLEXCOM0_SPI (0 << __FLEXCOM_SPI_SPI_SHIFT) /* FLEXCOM0 SPI */ +# define __FLEXCOM1_SPI (1 << __FLEXCOM_SPI_SPI_SHIFT) /* FLEXCOM1 SPI */ +# define __FLEXCOM2_SPI (2 << __FLEXCOM_SPI_SPI_SHIFT) /* FLEXCOM2 SPI */ +# define __FLEXCOM3_SPI (3 << __FLEXCOM_SPI_SPI_SHIFT) /* FLEXCOM3 SPI */ +# define __FLEXCOM4_SPI (4 << __FLEXCOM_SPI_SPI_SHIFT) /* FLEXCOM4 SPI */ + +#define FLEXCOM0_SPI_CS0 (__FLEXCOM0_SPI | __FLEXCOM_SPI_CS0) +#define FLEXCOM0_SPI_CS1 (__FLEXCOM0_SPI | __FLEXCOM_SPI_CS1) + +#define FLEXCOM1_SPI_CS0 (__FLEXCOM1_SPI | __FLEXCOM_SPI_CS0) +#define FLEXCOM1_SPI_CS1 (__FLEXCOM1_SPI | __FLEXCOM_SPI_CS1) + +#define FLEXCOM2_SPI_CS0 (__FLEXCOM2_SPI | __FLEXCOM_SPI_CS0) +#define FLEXCOM2_SPI_CS1 (__FLEXCOM3_SPI | __FLEXCOM_SPI_CS1) + +#define FLEXCOM3_SPI_CS0 (__FLEXCOM3_SPI | __FLEXCOM_SPI_CS0) +#define FLEXCOM3_SPI_CS1 (__FLEXCOM3_SPI | __FLEXCOM_SPI_CS1) + +#define FLEXCOM4_SPI_CS0 (__FLEXCOM4_SPI | __FLEXCOM_SPI_CS0) +#define FLEXCOM4_SPI_CS1 (__FLEXCOM4_SPI | __FLEXCOM_SPI_CS1) + +/**************************************************************************** + * 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_flex_spibus_initialize + * + * Description: + * Initialize the selected SPI port + * + * Input Parameters: + * cs - Chip select number (identifying the "logical" SPI port) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *sam_flex_spibus_initialize(int port); + +/**************************************************************************** + * Name: sam_flexcom_spi[0|1|2|3|4]select, sam_flexcom_spi[0|1|2|3|4]status, + * and sam_flexcom_spi[0|1|2|3|4]cmddata + * + * Description: + * These external functions must be provided by board-specific logic. They + * include: + * + * o sam_flexcom_spi[0|1|2|3|4]select is a functions to manage the + * board-specific chip selects + * o sam_flexcom_spi[0|1|2|3|4]status and sam_flexcom_spi[0|1|2|3|4]cmddata + * implementations of the status and cmddata methods of the SPI interface + * defined by struct spi_ops_ (see include/nuttx/spi/spi.h). + * All other methods including sam_flex_spibus_initialize()) are provided + * by common SAM2/3/4 logic. + * + * To use this common SPI logic on your board: + * + * 1. Provide logic in sam_boardinitialize() to configure FLEXCOM SPI + * chip select pins. + * 2. Provide sam_flexcom_spi[0|1|2|3|4]select() and + * sam_flexcom_spi[0|1|2|3|4]status() functions in your board-specific + * logic. These functions will perform chip selection and status + * operations using PIOs in the way your board is configured. + * 2. If CONFIG_FLEXCOMx_SPI_CMDDATA is defined in the NuttX configuration, + * provide sam_flexcom_spi[0|1|2|3|4]cmddata() functions in your + * board-specific logic. This function will perform cmd/data selection + * operations using PIOs in the way your board is configured. + * 3. Add a call to sam_flex_spibus_initialize() in your low level + * application initialization logic + * 4. The handle returned by sam_flex_spibus_initialize() may then be used + * to bind the FLEXCOM SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_flexcom_spi[0|1|2|3|4]select + * + * Description: + * PIO chip select pins may be programmed by the board specific logic in + * one of two different ways. First, the pins may be programmed as SPI + * peripherals. In that case, the pins are completely controlled by the + * SPI driver. This method still needs to be provided, but it may be only + * a stub. + * + * An alternative way to program the PIO chip select pins is as a normal + * PIO output. In that case, the automatic control of the CS pins is + * bypassed and this function must provide control of the chip select. + * NOTE: In this case, the PIO output pin does *not* have to be the + * same as the NPCS pin normal associated with the chip select number. + * + * Input Parameters: + * dev - SPI device info + * devid - Identifies the (logical) device + * selected - TRUE:Select the device, FALSE:De-select the device + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI +void sam_flexcom0_spiselect(uint32_t devid, bool selected); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI +void sam_flexcom1_spiselect(uint32_t devid, bool selected); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI +void sam_flexcom2_spiselect(uint32_t devid, bool selected); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI +void sam_flexcom3_spiselect(uint32_t devid, bool selected); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI +void sam_flexcom4_spiselect(uint32_t devid, bool selected); +#endif + +/**************************************************************************** + * Name: sam_flexcom_spi[0|1|2|3|4]status + * + * Description: + * Return status information associated with the SPI device. + * + * Input Parameters: + * dev - SPI device info + * devid - Identifies the (logical) device + * + * Returned Value: + * Bit-encoded SPI status (see include/nuttx/spi/spi.h. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI +uint8_t sam_flexcom0_spistatus(struct spi_dev_s *dev, uint32_t devid); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI +uint8_t sam_flexcom1_spistatus(struct spi_dev_s *dev, uint32_t devid); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI +uint8_t sam_flexcom2_spistatus(struct spi_dev_s *dev, uint32_t devid); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI +uint8_t sam_flexcom3_spistatus(struct spi_dev_s *dev, uint32_t devid); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI +uint8_t sam_flexcom4_spistatus(struct spi_dev_s *dev, uint32_t devid); +#endif + +/**************************************************************************** + * Name: sam_spi[0|1|2|3|4]cmddata + * + * Description: + * Some SPI devices require an additional control to determine if the SPI + * data being sent is a command or is data. If CONFIG_SPI_CMDDATA then + * this function will be called to different be command and data transfers. + * + * This is often needed, for example, by LCD drivers. Some LCD hardware + * may be configured to use 9-bit data transfers with the 9th bit + * indicating command or data. That same hardware may be configurable, + * instead, to use 8-bit data but to require an additional, board- + * specific PIO control to distinguish command and data. This function + * would be needed in that latter case. + * + * Input Parameters: + * dev - SPI device info + * devid - Identifies the (logical) device + * + * Returned Value: + * Zero on success; a negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_FLEXCOM_SPI_CMDDATA +#ifdef CONFIG_SAMA5_FLEXCOM0_SPI +int sam_flexcom0_spicmddata(struct spi_dev_s *dev, uint32_t devid, + bool cmd); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM1_SPI +int sam_flexcom1_spicmddata(struct spi_dev_s *dev, uint32_t devid, + bool cmd); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM2_SPI +int sam_flexcom2_spicmddata(struct spi_dev_s *dev, uint32_t devid, + bool cmd); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM3_SPI +int sam_flexcom3_spicmddata(struct spi_dev_s *dev, uint32_t devid, + bool cmd); +#endif +#ifdef CONFIG_SAMA5_FLEXCOM4_SPI +int sam_flexcom4_spicmddata(struct spi_dev_s *dev, uint32_t devid, + bool cmd); +#endif +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_SAMA5_SAM_FLEXCOM_SPI_H */