diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 7673dc6356..aa27d2fb3f 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -301,6 +301,10 @@ config IMX9_LPI2C bool "LPI2C support" default n +config IMX9_LPSPI + bool "LPSPI support" + default n + config IMX9_PLL bool "PLL setup support (WIP)" default n @@ -496,6 +500,51 @@ config IMX9_LPI2C8_FILTSDA endif # IMX9_LPI2C8 endmenu # LPI2C Peripherals + +menu "LPSPI Peripherals" + +menuconfig IMX9_LPSPI1 + bool "LPSPI1" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI2 + bool "LPSPI2" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI3 + bool "LPSPI3" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI4 + bool "LPSPI4" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI5 + bool "LPSPI5" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI6 + bool "LPSPI6" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI7 + bool "LPSPI7" + default n + select IMX9_LPSPI + +menuconfig IMX9_LPSPI8 + bool "LPSPI8" + default n + select IMX9_LPSPI + +endmenu # LPSPI Peripherals + menu "LPI2C Configuration" depends on IMX9_LPI2C @@ -550,4 +599,81 @@ config IMX9_LPI2C_TIMEOTICKS endmenu # LPI2C Configuration +menu "LPSPI Configuration" + depends on IMX9_LPSPI + +config IMX9_LPSPI_DMA + bool "LPSPI DMA" + depends on IMX9_EDMA + default n + ---help--- + Use DMA to improve LPSPI transfer performance. + +config IMX9_LPSPI_DMATHRESHOLD + int "LPSPI DMA threshold" + default 4 + depends on IMX9_LPSPI_DMA + ---help--- + 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. + +config IMX9_LPSPI1_DMA + bool "LPSPI1 DMA" + default n + depends on IMX9_LPSPI1 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve LPSPI1 transfer performance. + +config IMX9_LPSPI2_DMA + bool "LPSPI2 DMA" + default n + depends on IMX9_LPSPI2 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve LPSPI2 transfer performance. + +config IMX9_LPSPI3_DMA + bool "LPSPI3 DMA" + default n + depends on IMX9_LPSPI3 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve LPSPI3 transfer performance. + +config IMX9_LPSPI4_DMA + bool "LPSPI4 DMA" + default n + depends on IMX9_LPSPI4 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve SPI4 transfer performance. + +config IMX9_LPSPI5_DMA + bool "LPSPI5 DMA" + default n + depends on IMX9_LPSPI5 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve SPI5 transfer performance. + +config IMX9_LPSPI6_DMA + bool "LPSPI6 DMA" + default n + depends on IMX9_LPSPI6 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve SPI6 transfer performance. + +config IMX9_LPSPI7_DMA + bool "LPSPI7 DMA" + default n + depends on IMX9_LPSPI7 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve SPI7 transfer performance. + +config IMX9_LPSPI8_DMA + bool "LPSPI8 DMA" + default n + depends on IMX9_LPSPI8 && IMX9_LPSPI_DMA + ---help--- + Use DMA to improve SPI8 transfer performance. + +endmenu # LPSPI Configuration + endif # ARCH_CHIP_IMX9 diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index 5e11a38b59..c420afd939 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -47,3 +47,7 @@ endif ifeq ($(CONFIG_IMX9_LPI2C),y) CHIP_CSRCS += imx9_lpi2c.c endif + +ifeq ($(CONFIG_IMX9_LPSPI), y) + CHIP_CSRCS += imx9_lpspi.c +endif diff --git a/arch/arm64/src/imx9/hardware/imx9_lpspi.h b/arch/arm64/src/imx9/hardware/imx9_lpspi.h new file mode 100644 index 0000000000..06f7b02dc5 --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_lpspi.h @@ -0,0 +1,351 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_lpspi.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_ARM64_SRC_IMX9_HARDWARE_IMX9_LPSPI_H_ +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_LPSPI_H_ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define IMX9_LPSPI_VERID_OFFSET (0x0000) /* Version ID Register (VERID) */ +#define IMX9_LPSPI_PARAM_OFFSET (0x0004) /* Parameter Register (PARAM) */ +#define IMX9_LPSPI_CR_OFFSET (0x0010) /* Control Register (CR) */ +#define IMX9_LPSPI_SR_OFFSET (0x0014) /* Status Register (SR) */ +#define IMX9_LPSPI_IER_OFFSET (0x0018) /* Interrupt Enable Register (IER) */ +#define IMX9_LPSPI_DER_OFFSET (0x001c) /* DMA Enable Register (DER) */ +#define IMX9_LPSPI_CFGR0_OFFSET (0x0020) /* Configuration Register 0 (CFGR0) */ +#define IMX9_LPSPI_CFGR1_OFFSET (0x0024) /* Configuration Register 1 (CFGR1) */ +#define IMX9_LPSPI_DMR0_OFFSET (0x0030) /* Data Match Register 0 (DMR0) */ +#define IMX9_LPSPI_DMR1_OFFSET (0x0034) /* Data Match Register 1 (DMR1) */ +#define IMX9_LPSPI_CCR_OFFSET (0x0040) /* Clock Configuration Register (CCR) */ +#define IMX9_LPSPI_CCR1_OFFSET (0x0044) /* Clock Configuration Register 1 (CCR1) */ +#define IMX9_LPSPI_FCR_OFFSET (0x0058) /* FIFO Control Register (FCR) */ +#define IMX9_LPSPI_FSR_OFFSET (0x005c) /* FIFO Status Register (FSR) */ +#define IMX9_LPSPI_TCR_OFFSET (0x0060) /* Transmit Command Register (TCR) */ +#define IMX9_LPSPI_TDR_OFFSET (0x0064) /* Transmit Data Register (TDR) */ +#define IMX9_LPSPI_RSR_OFFSET (0x0070) /* Receive Status Register (RSR) */ +#define IMX9_LPSPI_RDR_OFFSET (0x0074) /* Receive Data Register (RDR) */ +#define IMX9_LPSPI_RDROR_OFFSET (0x0078) /* Receive Data Read Only Register (RDROR) */ +#define IMX9_LPSPI_TCBR_OFFSET (0x03fc) /* Transmit Command Burst Register (TCBR) */ + +#define IMX9_LPSPI_TDBR_OFFSET(n) (0x0400 + ((n) << 2)) /* Transmit Data Burst Register n=0..127 (TDBRn) */ +#define IMX9_LPSPI_RDBR_OFFSET(n) (0x0600 + ((n) << 2)) /* Receive Data Burst Register n=0..127 (RDBRn) */ + +/* Register addresses *******************************************************/ + +#define IMX9_LPSPI0_VERID(n) ((n) + IMX9_LPSPI_VERID_OFFSET) +#define IMX9_LPSPI0_PARAM(n) ((n) + IMX9_LPSPI_PARAM_OFFSET) +#define IMX9_LPSPI0_CR(n) ((n) + IMX9_LPSPI_CR_OFFSET) +#define IMX9_LPSPI0_SR(n) ((n) + IMX9_LPSPI_SR_OFFSET) +#define IMX9_LPSPI0_IER(n) ((n) + IMX9_LPSPI_IER_OFFSET) +#define IMX9_LPSPI0_DER(n) ((n) + IMX9_LPSPI_DER_OFFSET) +#define IMX9_LPSPI0_CFGR0(n) ((n) + IMX9_LPSPI_CFGR0_OFFSET) +#define IMX9_LPSPI0_CFGR1(n) ((n) + IMX9_LPSPI_CFGR1_OFFSET) +#define IMX9_LPSPI0_DMR0(n) ((n) + IMX9_LPSPI_DMR0_OFFSET) +#define IMX9_LPSPI0_DMR1(n) ((n) + IMX9_LPSPI_DMR1_OFFSET) +#define IMX9_LPSPI0_CCR(n) ((n) + IMX9_LPSPI_CCR_OFFSET) +#define IMX9_LPSPI0_CCR1(n) ((n) + IMX9_LPSPI_CCR1_OFFSET) +#define IMX9_LPSPI0_FCR(n) ((n) + IMX9_LPSPI_FCR_OFFSET) +#define IMX9_LPSPI0_FSR(n) ((n) + IMX9_LPSPI_FSR_OFFSET) +#define IMX9_LPSPI0_TCR(n) ((n) + IMX9_LPSPI_TCR_OFFSET) +#define IMX9_LPSPI0_TDR(n) ((n) + IMX9_LPSPI_TDR_OFFSET) +#define IMX9_LPSPI0_RSR(n) ((n) + IMX9_LPSPI_RSR_OFFSET) +#define IMX9_LPSPI0_RDR(n) ((n) + IMX9_LPSPI_RDR_OFFSET) +#define IMX9_LPSPI0_RDROR(n) ((n) + IMX9_LPSPI_RDROR_OFFSET) +#define IMX9_LPSPI0_TCBR(n) ((n) + IMX9_LPSPI_TCBR_OFFSET) +#define IMX9_LPSPI0_TDBR(n,v) ((n) + IMX9_LPSPI_TDBR_OFFSET(v)) +#define IMX9_LPSPI0_RDBR(n,v) ((n) + IMX9_LPSPI_RDBR_OFFSET(v)) + +/* Register bit definitions *************************************************/ + +/* Version ID Register (VERID) */ + +#define LPSPI_VERID_FEATURE_SHIFT (0) /* Bits 0-15: Module Identification Number (FEATURE) */ +#define LPSPI_VERID_FEATURE_MASK (0xffff << LPSPI_VERID_FEATURE_SHIFT) +#define LPSPI_VERID_MINOR_SHIFT (16) /* Bits 16-23: Minor Version Number (MINOR) */ +#define LPSPI_VERID_MINOR_MASK (0xff << LPSPI_VERID_MINOR_SHIFT) +#define LPSPI_VERID_MAJOR_SHIFT (24) /* Bits 24-31: Major Version Number (MAJOR) */ +#define LPSPI_VERID_MAJOR_MASK (0xff << LPSPI_VERID_MAJOR_SHIFT) + +/* Parameter Register (PARAM) */ + +#define LPSPI_PARAM_TXFIFO_SHIFT (0) /* Bits 0-7: Transmit FIFO Size (TXFIFO) */ +#define LPSPI_PARAM_TXFIFO_MASK (0xff << LPSPI_PARAM_TXFIFO_SHIFT) +#define LPSPI_PARAM_RXFIFO_SHIFT (8) /* Bits 8-15: Receive FIFO Size (RXFIFO) */ +#define LPSPI_PARAM_RXFIFO_MASK (0xff << LPSPI_PARAM_RXFIFO_SHIFT) +#define LPSPI_PARAM_PCSNUM_SHIFT (16) /* Bits 16-23: PCS Number (PCSNUM) */ +#define LPSPI_PARAM_PCSNUM_MASK (0xff << LPSPI_PARAM_PCSNUM_SHIFT) + /* Bits 24-31: Reserved */ + +/* Control Register (CR) */ + +#define LPSPI_CR_MEN (1 << 0) /* Bit 0: Module Enable (MEN) */ +#define LPSPI_CR_RST (1 << 1) /* Bit 1: Software Reset (RST) */ + /* Bit 2: Reserved */ +#define LPSPI_CR_DBGEN (1 << 3) /* Bit 3: Debug Enable (DBGEN) */ + /* Bits 4-7: Reserved */ +#define LPSPI_CR_RTF (1 << 8) /* Bit 8: Reset Transmit FIFO (RTF) */ +#define LPSPI_CR_RRF (1 << 9) /* Bit 9: Reset Receive FIFO (RRF) */ + /* Bits 10-31: Reserved */ + +/* Status Register (SR) */ + +#define LPSPI_SR_TDF (1 << 0) /* Bit 0: Transmit Data Flag (TDF) */ +#define LPSPI_SR_RDF (1 << 1) /* Bit 1: Receive Data Flag (RDF) */ + /* Bits 2-7: Reserved */ +#define LPSPI_SR_WCF (1 << 8) /* Bit 8: Word Complete Flag (WCF) */ +#define LPSPI_SR_FCF (1 << 9) /* Bit 9: Frame Complete Flag (FCF) */ +#define LPSPI_SR_TCF (1 << 10) /* Bit 10: Transfer Complete Flag (TCF) */ +#define LPSPI_SR_TEF (1 << 11) /* Bit 11: Transmit Error Flag (TEF) */ +#define LPSPI_SR_REF (1 << 12) /* Bit 12: Receive Error Flag (REF) */ +#define LPSPI_SR_DMF (1 << 13) /* Bit 13: Data Match Flag (DMF) */ + /* Bits 14-23: Reserved */ +#define LPSPI_SR_MBF (1 << 24) /* Bit 24: Module Busy Flag (MBF) */ + /* Bits 25-31: Reserved */ + +/* Interrupt Enable Register (IER) */ + +#define LPSPI_IER_TDIE (1 << 0) /* Bit 0: Transmit Data Interrupt Enable (TDIE) */ +#define LPSPI_IER_RDIE (1 << 1) /* Bit 1: Receive Data Interrupt Enable (RDIE) */ + /* Bits 2-7: Reserved */ +#define LPSPI_IER_WCIE (1 << 8) /* Bit 8: Word Complete Interrupt Enable (WCIE) */ +#define LPSPI_IER_FCIE (1 << 9) /* Bit 9: Frame Complete Interrupt Enable (FCIE) */ +#define LPSPI_IER_TCIE (1 << 10) /* Bit 10: Transfer Complete Interrupt Enable (TCIE) */ +#define LPSPI_IER_TEIE (1 << 11) /* Bit 11: Transmit Error Interrupt Enable (TEIE) */ +#define LPSPI_IER_REIE (1 << 12) /* Bit 12: Receive Error Interrupt Enable (REIE) */ +#define LPSPI_IER_DMIE (1 << 13) /* Bit 13: Data Match Interrupt Enable (DMIE) */ + /* Bits 14-31: Reserved */ + +/* DMA Enable Register (DER) */ + +#define LPSPI_DER_TDDE (1 << 0) /* Bit 0: Transmit Data DMA Enable (TDDE) */ +#define LPSPI_DER_RDDE (1 << 1) /* Bit 1: Receive Data DMA Enable (RDDE) */ + /* Bits 2-31: Reserved */ + +/* Configuration Register 0 (CFGR0) */ + +#define LPSPI_CFGR0_HREN (1 << 0) /* Bit 0: Host Request Enable (HREN) */ +#define LPSPI_CFGR0_HRPOL (1 << 1) /* Bit 1: Host Request Polarity (HRPOL) */ +# define LPSPI_CFGR0_HRPOL_HIGH (0 << 1) /* HREQ pin or input trigger is active high */ +# define LPSPI_CFGR0_HRPOL_LOW (1 << 1) /* HREQ pin or input trigger is active low */ +#define LPSPI_CFGR0_HRSEL (1 << 2) /* Bit 2: Host Request Select (HRSEL) */ +# define LPSPI_CFGR0_HRSEL_HREQ (0 << 2) /* Host request input is the LPSPI_HREQ pin */ +# define LPSPI_CFGR0_HRSEL_INTR (1 << 2) /* Host request input is the input trigger */ +#define LPSPI_CFGR0_HRDIR (1 << 3) /* Bit 3: Host Request Direction (HRDIR) */ +# define LPSPI_CFGR0_HRDIR_INPUT (0 << 3) /* HREQ pin is configured as input */ +# define LPSPI_CFGR0_HRDIR_OUTPUT (1 << 3) /* HREQ pin is configured as output */ + /* Bits 4-7: Reserved */ +#define LPSPI_CFGR0_CIRFIFO (1 << 8) /* Bit 8: Circular FIFO Enable (CIRCFIFO) */ +#define LPSPI_CFGR0_RDMO (1 << 9) /* Bit 9: Receive Data Match Only (RDMO) */ +# define LPSPI_CFGR0_RDMO_FIFO (0 << 9) /* Received data is stored in the receive FIFO as in normal operations */ +# define LPSPI_CFGR0_RDMO_DMF (1 << 9) /* Received data is discarded unless the Data Match Flag (DMF) is set */ + /* Bits 10-31: Reserved */ + +/* Configuration Register 1 (CFGR1) */ + +#define LPSPI_CFGR1_MASTER (1 << 0) /* Bit 0: Master Mode (MASTER) */ +#define LPSPI_CFGR1_SAMPLE (1 << 1) /* Bit 1: Sample Point (SAMPLE) */ +# define LPSPI_CFGR1_SAMPLE_SCK (0 << 1) /* Input data is sampled on SCK edge */ +# define LPSPI_CFGR1_SAMPLE_DELAY (1 << 1) /* Input data is sampled on delayed SCK edge */ +#define LPSPI_CFGR1_AUTOPCS (1 << 2) /* Bit 2: Automatic PCS (AUTOPCS) */ +#define LPSPI_CFGR1_NOSTALL (1 << 3) /* Bit 3: No Stall (NOSTALL) */ +#define LPSPI_CFGR1_PARTIAL (1 << 4) /* Bit 4: Partial Enable (PARTIAL) */ + /* Bits 5-7: Reserved */ +#define LPSPI_CFGR1_PCSPOL_SHIFT (8) /* Bits 8-15: Peripheral Chip Select Polarity (PCSPOL) */ +#define LPSPI_CFGR1_PCSPOL_MASK (0xff << LPSPI_CFGR1_PCSPOL_SHIFT) +# define LPSPI_CFGR1_PCSPOL_LOW(n) (0 << (LPSPI_CFGR1_PCSPOL_SHIFT + (n))) /* The Peripheral Chip Select PCS[n] pin is active low */ +# define LPSPI_CFGR1_PCSPOL_HIGH(n) (1 << (LPSPI_CFGR1_PCSPOL_SHIFT + (n))) /* The Peripheral Chip Select PCS[n] pin is active high */ + +#define LPSPI_CFGR1_MATCFG_SHIFT (16) /* Bits 16-18: Match Configuration (MATCFG) */ +#define LPSPI_CFGR1_MATCFG_MASK (0x07 << LPSPI_CFGR1_MATCFG_SHIFT) +#define LPSPI_CFGR1_MATCFG_DIS (0x00 << LPSPI_CFGR1_MATCFG_SHIFT) /* Match is disabled */ + + /* Bits 19-23: Reserved */ +#define LPSPI_CFGR1_PINCFG_SHIFT (24) /* Bits 24-25: Pin Configuration (PINCFG) */ +#define LPSPI_CFGR1_PINCFG_MASK (0x03 << LPSPI_CFGR1_PINCFG_SHIFT) +# define LPSPI_CFGR1_PINCFG_SIN_SOUT (0x00 << LPSPI_CFGR1_PINCFG_SHIFT) /* SIN is used for input data and SOUT is used for output data */ +# define LPSPI_CFGR1_PINCFG_SIN_SIN (0x01 << LPSPI_CFGR1_PINCFG_SHIFT) /* SIN is used for both input and output data */ +# define LPSPI_CFGR1_PINCFG_SOUT_SOUT (0x02 << LPSPI_CFGR1_PINCFG_SHIFT) /* SOUT is used for both input and output data */ +# define LPSPI_CFGR1_PINCFG_SOUT_SIN (0x03 << LPSPI_CFGR1_PINCFG_SHIFT) /* SOUT is used for input data and SIN is used for output data */ +# define LPSPI_CFGR1_PINCFG(n) ((n) << LPSPI_CFGR1_PINCFG_SHIFT) + +#define LPSPI_CFGR1_OUTCFG (1 << 26) /* Bit 26: Output Config (OUTCFG) */ +# define LPSPI_CFGR1_OUTCFG_RETAIN (0 << 26) /* Output data retains last value when chip select is negated */ +# define LPSPI_CFGR1_OUTCFG_TRISTATE (1 << 26) /* Output data is tristated when chip select is negated */ +#define LPSPI_CFGR1_PCSCFG_SHIFT (27) /* Bits 27-28: Peripheral Chip Select Configuration (PCSCFG) */ +#define LPSPI_CFGR1_PCSCFG_MASK (0x03 << LPSPI_CFGR1_PCSCFG_SHIFT) +# define LPSPI_CFGR1_PCSCFG_PCS (0x00 << LPSPI_CFGR1_PCSCFG_SHIFT) /* PCS[2:7] are configured for chip select function */ +# define LPSPI_CFGR1_PCSCFG_4BIT (0x01 << LPSPI_CFGR1_PCSCFG_SHIFT) /* PCS[2:3] are configured for half-duplex 4-bit transfers */ +# define LPSPI_CFGR1_PCSCFG_8BIT (0x03 << LPSPI_CFGR1_PCSCFG_SHIFT) /* PCS[2:7] are configured for half-duplex 4-bit and 8-bit transfers */ + + /* Bits 29-31: Reserved */ + +/* Data Match Register 0 (DMR0) */ + +#define LPSPI_DMR0_MATCH0_SHIFT (0) /* Bits 0-31: Match 0 Value (MATCH0) */ +#define LPSPI_DMR0_MATCH0_MASK (0xffffffff << LPSPI_DMR0_MATCH0_SHIFT) + +/* Data Match Register 0 (DMR1) */ + +#define LPSPI_DMR1_MATCH1_SHIFT (0) /* Bits 0-31: Match 1 Value (MATCH1) */ +#define LPSPI_DMR1_MATCH1_MASK (0xffffffff << LPSPI_DMR1_MATCH1_SHIFT) + +/* Clock Configuration Register (CCR) */ + +#define LPSPI_CCR_SCKDIV_SHIFT (0) /* Bits 0-7: SCK Divider (SCKDIV) */ +#define LPSPI_CCR_SCKDIV_MASK (0xff << LPSPI_CCR_SCKDIV_SHIFT) +# define LPSPI_CCR_SCKDIV(n) (((uint32_t)(n) << LPSPI_CCR_SCKDIV_SHIFT) & LPSPI_CCR_SCKDIV_MASK) +#define LPSPI_CCR_DBT_SHIFT (8) /* Bits 8-15: Delay Between Transfers (DBT) */ +#define LPSPI_CCR_DBT_MASK (0xff << LPSPI_CCR_DBT_SHIFT) +# define LPSPI_CCR_DBT(n) (((uint32_t)(n) << LPSPI_CCR_DBT_SHIFT) & LPSPI_CCR_DBT_MASK) +#define LPSPI_CCR_PCSSCK_SHIFT (16) /* Bits 16-23: PCS-to-SCK Delay (PCSSCK) */ +#define LPSPI_CCR_PCSSCK_MASK (0xff << LPSPI_CCR_PCSSCK_SHIFT) +# define LPSPI_CCR_PCSSCK(n) (((uint32_t)(n) << LPSPI_CCR_PCSSCK_SHIFT) & LPSPI_CCR_PCSSCK_MASK) +#define LPSPI_CCR_SCKPCS_SHIFT (24) /* Bits 24-31: SCK-to-PCS Delay (SCKPCS) */ +#define LPSPI_CCR_SCKPCS_MASK (0xff << LPSPI_CCR_SCKPCS_SHIFT) +# define LPSPI_CCR_SCKPCS(n) (((uint32_t)(n) << LPSPI_CCR_SCKPCS_SHIFT) & LPSPI_CCR_SCKPCS_MASK) + +/* Clock Configuration Register 1 (CCR1) */ + +#define LPSPI_CCR1_SCKSET_SHIFT (0) /* Bits 0-7: SCK Setup (SCKSET) */ +#define LPSPI_CCR1_SCKSET_MASK (0xff << LPSPI_CCR1_SCKSET_SHIFT) +#define LPSPI_CCR1_SCKHLD_SHIFT (8) /* Bits 8-15: SCK Hold (SCKHLD) */ +#define LPSPI_CCR1_SCKHLD_MASK (0xff << LPSPI_CCR1_SCKHLD_SHIFT) +#define LPSPI_CCR1_PCSPCS_SHIFT (16) /* Bits 16-23: PCS to PCS Delay (PCSPCS) */ +#define LPSPI_CCR1_PCSPCS_MASK (0xff << LPSPI_CCR1_PCSPCS_SHIFT) +#define LPSPI_CCR1_SCKSCK_SHIFT (24) /* Bits 24-31: SCK Inter-Frame Delay (SCKSCK) */ +#define LPSPI_CCR1_SCKSCK_MASK (0xff << LPSPI_CCR1_SCKSCK_SHIFT) + +/* FIFO Control Register (FCR) */ + +#define LPSPI_FCR_TXWATER_SHIFT (0) /* Bits 0-1: Transmit FIFO Watermark (TXWATER) */ +#define LPSPI_FCR_TXWATER_MASK (0x03 << LPSPI_FCR_TXWATER_SHIFT) +# define LPSPI_FCR_TXWATER(n) ((uint32_t)(n) << LPSPI_FCR_TXWATER_SHIFT) + /* Bits 2-15: Reserved */ +#define LPSPI_FCR_RXWATER_SHIFT (16) /* Bits 16-17: Receive FIFO Watermark (RXWATER) */ +#define LPSPI_FCR_RXWATER_MASK (0x03 << LPSPI_FCR_RXWATER_SHIFT) +# define LPSPI_FCR_RXWATER(n) ((uint32_t)(n) << LPSPI_FCR_RXWATER_SHIFT) + /* Bits 18-31: Reserved */ + +/* FIFO Status Register (FSR) */ + +#define LPSPI_FSR_TXCOUNT_SHIFT (0) /* Bits 0-2: Transmit FIFO Count (TXCOUNT) */ +#define LPSPI_FSR_TXCOUNT_MASK (0x07 << LPSPI_FSR_TXCOUNT_SHIFT) + /* Bits 3-15: Reserved */ +#define LPSPI_FSR_RXCOUNT_SHIFT (16) /* Bits 16-18: Receive FIFO Count (RXCOUNT) */ +#define LPSPI_FSR_RXCOUNT_MASK (0x07 << LPSPI_FSR_RXCOUNT_SHIFT) + /* Bits 19-31: Reserved */ + +/* Transmit Command Register (TCR) */ + +#define LPSPI_TCR_FRAMESZ_SHIFT (0) /* Bits 0-11: Frame Size (FRAMESZ) */ +#define LPSPI_TCR_FRAMESZ_MASK (0x0fff << LPSPI_TCR_FRAMESZ_SHIFT) +# define LPSPI_TCR_FRAMESZ(n) ((uint32_t)(n) << LPSPI_TCR_FRAMESZ_SHIFT) + /* Bits 12-15: Reserved */ +#define LPSPI_TCR_WIDTH_SHIFT (16) /* Bits 16-17: Transfer Width (WIDTH) */ +#define LPSPI_TCR_WIDTH_MASK (0x03 << LPSPI_TCR_WIDTH_SHIFT) +# define LPSPI_TCR_WIDTH_1BIT (0x00 << LPSPI_TCR_WIDTH_SHIFT) /* 1 bit transfer */ +# define LPSPI_TCR_WIDTH_2BIT (0x01 << LPSPI_TCR_WIDTH_SHIFT) /* 2 bit transfer */ +# define LPSPI_TCR_WIDTH_4BIT (0x02 << LPSPI_TCR_WIDTH_SHIFT) /* 4 bit transfer */ +# define LPSPI_TCR_WIDTH_8BIT (0x03 << LPSPI_TCR_WIDTH_SHIFT) /* 8 bit transfer */ + +#define LPSPI_TCR_TXMSK (1 << 18) /* Bit 18: Transmit Data Mask (TXMSK) */ +#define LPSPI_TCR_RXMSK (1 << 19) /* Bit 19: Receive Data Mask (RXMSK) */ +#define LPSPI_TCR_CONTC (1 << 20) /* Bit 20: Continuing Command (CONTC) */ +#define LPSPI_TCR_CONT (1 << 21) /* Bit 21: Continuous Transfer (CONT) */ +#define LPSPI_TCR_BYSW (1 << 22) /* Bit 22: Byte Swap (BYSW) */ +#define LPSPI_TCR_LSBF (1 << 23) /* Bit 23: LSB First (LSBF) */ +# define LPSPI_TCR_MSBF (0 << 23) /* MSB First */ +#define LPSPI_TCR_PCS_SHIFT (24) /* Bits 24-26: Peripheral Chip Select (PCS) */ +#define LPSPI_TCR_PCS_MASK (0x07 << LPSPI_TCR_PCS_SHIFT) +# define LPSPI_TCR_PCS_0 (0x00 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[0] */ +# define LPSPI_TCR_PCS_1 (0x01 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[1] */ +# define LPSPI_TCR_PCS_2 (0x02 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[2] */ +# define LPSPI_TCR_PCS_3 (0x03 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[3] */ +# define LPSPI_TCR_PCS_4 (0x04 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[4] */ +# define LPSPI_TCR_PCS_5 (0x05 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[5] */ +# define LPSPI_TCR_PCS_6 (0x06 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[6] */ +# define LPSPI_TCR_PCS_7 (0x07 << LPSPI_TCR_PCS_SHIFT) /* Transfer using PCS[7] */ + +#define LPSPI_TCR_PRESCALE_SHIFT (27) /* Bits 27-29: Prescaler Value (PRESCALE) */ +#define LPSPI_TCR_PRESCALE_MASK (0x07 << LPSPI_TCR_PRESCALE_SHIFT) +# define LPSPI_TCR_PRESCALE_DIV1 (0x00 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 1 */ +# define LPSPI_TCR_PRESCALE_DIV2 (0x01 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 2 */ +# define LPSPI_TCR_PRESCALE_DIV4 (0x02 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 4 */ +# define LPSPI_TCR_PRESCALE_DIV8 (0x03 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 8 */ +# define LPSPI_TCR_PRESCALE_DIV16 (0x04 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 16 */ +# define LPSPI_TCR_PRESCALE_DIV32 (0x05 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 32 */ +# define LPSPI_TCR_PRESCALE_DIV64 (0x06 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 64 */ +# define LPSPI_TCR_PRESCALE_DIV128 (0x07 << LPSPI_TCR_PRESCALE_SHIFT) /* Divide by 128 */ +# define LPSPI_TCR_PRESCALE(n) ((n) << LPSPI_TCR_PRESCALE_SHIFT) + +#define LPSPI_TCR_CPHA (1 << 30) /* Bit 30: Clock Phase (CPHA) */ +# define LPSPI_TCR_CPHA_CAPTURED (0 << 30) /* Data is captured on the leading edge of SCK and changed on the following edge of SCK */ +# define LPSPI_TCR_CPHA_CHANGED (1 << 30) /* Data is changed on the leading edge of SCK and captured on the following edge of SCK */ +#define LPSPI_TCR_CPOL (1 << 31) /* Bit 31: Clock Polarity (CPOL) */ +# define LPSPI_TCR_CPOL_LOW (0 << 31) /* The inactive state value of SCK is low */ +# define LPSPI_TCR_CPOL_HIGH (1 << 31) /* The inactive state value of SCK is high */ + +/* Transmit Data Register (TDR) */ + +#define LPSPI_TDR_DATA_SHIFT (0) /* Bits 0-31: Transmit Data (DATA) */ +#define LPSPI_TDR_DATA_MASK (0xffffffff << LPSPI_TDR_DATA_SHIFT) + +/* Receive Status Register (RSR) */ + +#define LPSPI_RSR_SOF (1 << 0) /* Bit 0: Start Of Frame (SOF) */ +#define LPSPI_RSR_RXEMPTY (1 << 1) /* Bit 1: RX FIFO Empty (RXEMPTY) */ + /* Bits 2-31: Reserved */ + +/* Receive Data Register (RDR) */ + +#define LPSPI_RDR_DATA_SHIFT (0) /* Bits 0-31: Receive Data (DATA) */ +#define LPSPI_RDR_DATA_MASK (0xffffffff << LPSPI_RDR_DATA_SHIFT) + +/* Receive Data Read Only Register (RDROR) */ + +#define LPSPI_RDROR_DATA_SHIFT (0) /* Bits 0-31: Receive Data (DATA) */ +#define LPSPI_RDROR_DATA_MASK (0xffffffff << LPSPI_RDROR_DATA_SHIFT) + +/* Transmit Command Burst Register (TCBR) */ + +#define LPSPI_TCBR_DATA_SHIFT (0) /* Bits 0-31: Command Data (DATA) */ +#define LPSPI_TCBR_DATA_MASK (0xffffffff << LPSPI_TCBR_DATA_SHIFT) + +/* Transmit Data Burst Register (TDBR) */ + +#define LPSPI_TDBR_DATA_SHIFT (0) /* Bits 0-31: Data (DATA) */ +#define LPSPI_TDBR_DATA_MASK (0xffffffff << LPSPI_TDBR_DATA_SHIFT) + +/* Receive Data Burst Register (RDBR) */ + +#define LPSPI_RDBR_DATA_SHIFT (0) /* Bits 0-31: Data (DATA) */ +#define LPSPI_RDBR_DATA_MASK (0xffffffff << LPSPI_RDBR_DATA_SHIFT) + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_LPSPI_H_ */ diff --git a/arch/arm64/src/imx9/imx9_lpspi.c b/arch/arm64/src/imx9/imx9_lpspi.c new file mode 100644 index 0000000000..48d8ccf582 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_lpspi.c @@ -0,0 +1,2060 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_lpspi.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. + * + ****************************************************************************/ + +/**************************************************************************** + * The external functions, imx9_lpspi1/2/3/4select and + * imx9_lpspi1/2/3/4status must be provided by board-specific logic. + * They are implementations of the select and status methods of the SPI + * interface defined by struct imx9_lpspi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including + * imx9_lpspibus_initialize()) are provided by common IMX9 logic. + * To use this common SPI logic on your board: + * + * 1. Provide logic in imx9_boardinitialize() to configure SPI chip + * select pins. + * 2. Provide imx9_lpspi1/2/3/4select() and imx9_lpspi1/2/3/4status() + * functions in your board-specific logic. These functions will + * perform chip selection and status operations using GPIOs in the way + * your board is configured. + * 3. Add a calls to imx9_lpspibus_initialize() in your low level + * application initialization logic + * 4. The handle returned by imx9_lpspibus_initialize() may then be + * used to bind the SPI driver to higher level logic (e.g., calling + * mmcsd_lpspislotinitialize(), for example, will bind the SPI + * driver to the SPI MMC/SD driver). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "arm64_internal.h" +#include "imx9_ccm.h" +#include "imx9_clockconfig.h" +#include "imx9_gpio.h" +#include "imx9_iomuxc.h" +#include "imx9_lpspi.h" + +#include "hardware/imx9_ccm.h" +#include "hardware/imx9_lpspi.h" +#include "hardware/imx9_pinmux.h" + +#ifdef CONFIG_IMX9_LPSPI_DMA +# include "chip.h" +# include "imx9_edma.h" +# include "hardware/imx9_dmamux.h" +#endif + +#ifdef CONFIG_IMX9_LPSPI + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* SPI interrupts */ + +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS +# error "Interrupt driven SPI not yet supported" +#endif + +/* Can't have both interrupt driven SPI and SPI DMA */ + +#if defined(CONFIG_IMX9_LPSPI_INTERRUPTS) && defined(CONFIG_IMX9_LPSPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for SPI" +#endif + +#define SPI_SR_CLEAR (LPSPI_SR_WCF | LPSPI_SR_FCF | LPSPI_SR_TCF | \ + LPSPI_SR_TEF | LPSPI_SR_REF | LPSPI_SR_DMF) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imx9_lpspidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ + uint8_t clk_root; /* SPIn clock root */ + uint8_t clk_gate; /* SPIn clock gate */ +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif + mutex_t lock; /* Held while chip is selected for mutual exclusion */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + int8_t nbits; /* Width of word in bits */ + uint8_t mode; /* Mode 0,1,2,3 */ +#ifdef CONFIG_IMX9_LPSPI_DMA + volatile uint32_t rxresult; /* Result of the RX DMA */ + volatile uint32_t txresult; /* Result of the TX DMA */ + const uint16_t rxch; /* The RX DMA channel number */ + const uint16_t txch; /* The TX DMA channel number */ + DMACH_HANDLE rxdma; /* DMA channel handle for RX transfers */ + DMACH_HANDLE txdma; /* DMA channel handle for TX transfers */ + sem_t rxsem; /* Wait for RX DMA to complete */ + sem_t txsem; /* Wait for TX DMA to complete */ +#endif +}; + +enum imx9_delay_e +{ + LPSPI_PCS_TO_SCK = 1, /* PCS-to-SCK delay. */ + LPSPI_LAST_SCK_TO_PCS, /* Last SCK edge to PCS delay. */ + LPSPI_BETWEEN_TRANSFER /* Delay between transfers. */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +static inline uint32_t +imx9_lpspi_getreg32(struct imx9_lpspidev_s *priv, uint8_t offset); +static inline void imx9_lpspi_putreg32(struct imx9_lpspidev_s *priv, + uint8_t offset, uint32_t value); +static inline uint32_t imx9_lpspi_readword( + struct imx9_lpspidev_s *priv); +static inline void imx9_lpspi_writeword(struct imx9_lpspidev_s *priv, + uint16_t byte); +static inline bool imx9_lpspi_9to16bitmode( + struct imx9_lpspidev_s *priv); +static inline void imx9_lpspi_master_set_delays(struct imx9_lpspidev_s + *priv, uint32_t delay_ns, + enum imx9_delay_e type); +static inline void imx9_lpspi_master_set_delay_scaler( + struct imx9_lpspidev_s *priv, + uint32_t scaler, + enum imx9_delay_e type); + +/* DMA support */ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static int spi_dmarxwait(struct imx9_lpspidev_s *priv); +static int spi_dmatxwait(struct imx9_lpspidev_s *priv); +static inline void spi_dmarxwakeup(struct imx9_lpspidev_s *priv); +static inline void spi_dmatxwakeup(struct imx9_lpspidev_s *priv); +static void spi_dmarxcallback(DMACH_HANDLE handle, void *arg, + bool done, int result); +static void spi_dmatxcallback(DMACH_HANDLE handle, void *arg, + bool done, int result); +static inline void spi_dmarxstart(struct imx9_lpspidev_s *priv); +static inline void spi_dmatxstart(struct imx9_lpspidev_s *priv); +#endif + +/* SPI methods */ + +static int imx9_lpspi_lock(struct spi_dev_s *dev, bool lock); +static uint32_t imx9_lpspi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency); +static void imx9_lpspi_setmode(struct spi_dev_s *dev, + enum spi_mode_e mode); +static void imx9_lpspi_setbits(struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int imx9_lpspi_hwfeatures(struct spi_dev_s *dev, + imx9_lpspi_hwfeatures_t features); +#endif +static uint32_t imx9_lpspi_send(struct spi_dev_s *dev, uint32_t wd); +static void imx9_lpspi_exchange(struct spi_dev_s *dev, + const void *txbuffer, + void *rxbuffer, + size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void imx9_lpspi_sndblock(struct spi_dev_s *dev, + const void *txbuffer, size_t nwords); +static void imx9_lpspi_recvblock(struct spi_dev_s *dev, + void *rxbuffer, + size_t nwords); +#endif + +/* Initialization */ + +static void imx9_lpspi_bus_initialize(struct imx9_lpspidev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct spi_ops_s g_spiops = +{ + .lock = imx9_lpspi_lock, + .select = imx9_lpspi_select, + .setfrequency = imx9_lpspi_setfrequency, + .setmode = imx9_lpspi_setmode, + .setbits = imx9_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imx9_lpspi_hwfeatures, +#endif + .status = imx9_lpspi_status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imx9_lpspi_cmddata, +#endif + .send = imx9_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imx9_lpspi_exchange, +#else + .sndblock = imx9_lpspi_sndblock, + .recvblock = imx9_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imx9_lpspi_register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +#ifdef CONFIG_IMX9_LPSPI1 +static struct imx9_lpspidev_s g_lpspi1dev = +{ + .spidev = + { + .ops = &g_spiops, + }, + .spibase = IMX9_LPSPI1_BASE, + .clk_root = CCM_CR_LPSPI1, + .clk_gate = CCM_LPCG_LPSPI1, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI1, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI1_DMA + .rxch = DMA_REQUEST_MUXLPSPI1RX, + .txch = DMA_REQUEST_MUXLPSPI1TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI2 +static struct imx9_lpspidev_s g_lpspi2dev = +{ + .spidev = + { + .ops = &g_spi2ops, + }, + .spibase = IMX9_LPSPI2_BASE, + .clk_root = CCM_CR_LPSPI2, + .clk_gate = CCM_LPCG_LPSPI2, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI2, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI2_DMA + .rxch = DMA_REQUEST_MUXLPSPI2RX, + .txch = DMA_REQUEST_MUXLPSPI2TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI3 +static struct imx9_lpspidev_s g_lpspi3dev = +{ + .spidev = + { + .ops = &g_spi3ops, + }, + .spibase = IMX9_LPSPI3_BASE, + .clk_root = CCM_CR_LPSPI3, + .clk_gate = CCM_LPCG_LPSPI3, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI3, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI3_DMA + .rxch = DMA_REQUEST_MUXLPSPI3RX, + .txch = DMA_REQUEST_MUXLPSPI3TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI4 +static struct imx9_lpspidev_s g_lpspi4dev = +{ + .spidev = + { + .ops = &g_spiops, + }, + .spibase = IMX9_LPSPI4_BASE, + .clk_root = CCM_CR_LPSPI4, + .clk_gate = CCM_LPCG_LPSPI4, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI4, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI4_DMA + .rxch = DMA_REQUEST_MUXLPSPI4RX, + .txch = DMA_REQUEST_MUXLPSPI4TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI5 +static struct imx9_lpspidev_s g_lpspi5dev = +{ + .spidev = + { + &g_spiops + }, + .spibase = IMX9_LPSPI5_BASE, + .clk_root = CCM_CR_LPSPI5, + .clk_gate = CCM_LPCG_LPSPI5, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI5, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI5_DMA + .rxch = DMA_REQUEST_MUXLPSPI5RX, + .txch = DMA_REQUEST_MUXLPSPI5TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI6 +static struct imx9_lpspidev_s g_lpspi6dev = +{ + .spidev = + { + &g_spiops + }, + .spibase = IMX9_LPSPI6_BASE, + .clk_root = CCM_CR_LPSPI6, + .clk_gate = CCM_LPCG_LPSPI6, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI6, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI6_DMA + .rxch = DMA_REQUEST_MUXLPSPI6RX, + .txch = DMA_REQUEST_MUXLPSPI6TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI7 +static struct imx9_lpspidev_s g_lpspi7dev = +{ + .spidev = + { + &g_spiops + }, + .spibase = IMX9_LPSPI7_BASE, + .clk_root = CCM_CR_LPSPI7, + .clk_gate = CCM_LPCG_LPSPI7, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI7, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI7_DMA + .rxch = DMA_REQUEST_MUXLPSPI7RX, + .txch = DMA_REQUEST_MUXLPSPI7TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +#ifdef CONFIG_IMX9_LPSPI8 +static struct imx9_lpspidev_s g_lpspi8dev = +{ + .spidev = + { + &g_spiops + }, + .spibase = IMX9_LPSPI8_BASE, + .clk_root = CCM_CR_LPSPI8, + .clk_gate = CCM_LPCG_LPSPI8, +#ifdef CONFIG_IMX9_LPSPI_INTERRUPTS + .spiirq = IMX9_IRQ_LPSPI8, +#endif + .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_IMX9_LPSPI6_DMA + .rxch = DMA_REQUEST_MUXLPSPI8RX, + .txch = DMA_REQUEST_MUXLPSPI8TX, + .rxsem = SEM_INITIALIZER(0), + .txsem = SEM_INITIALIZER(0), +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_lpspi_getreg + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline uint32_t +imx9_lpspi_getreg32(struct imx9_lpspidev_s *priv, uint8_t offset) +{ + return getreg32(priv->spibase + offset); +} + +/**************************************************************************** + * Name: imx9_lpspi_putreg + * + * Description: + * Write a 16-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 32-bit value to be written + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline void imx9_lpspi_putreg32(struct imx9_lpspidev_s *priv, + uint8_t offset, uint32_t value) +{ + putreg32(value, priv->spibase + offset); +} + +/**************************************************************************** + * Name: imx9_lpspi_readword + * + * Description: + * Read one word from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * word as read + * + ****************************************************************************/ + +static inline uint32_t +imx9_lpspi_readword(struct imx9_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_SR_OFFSET) + & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return imx9_lpspi_getreg32(priv, IMX9_LPSPI_RDR_OFFSET); +} + +/**************************************************************************** + * Name: imx9_lpspi_writeword + * + * Description: + * Write one word to SPI + * + * Input Parameters: + * priv - Device-specific state data + * word - word to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imx9_lpspi_writeword(struct imx9_lpspidev_s *priv, + uint16_t word) +{ + /* Wait until the transmit buffer is empty */ + + while ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_SR_OFFSET) + & LPSPI_SR_TDF) == 0); + + /* Then send the word */ + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_TDR_OFFSET, word); +} + +/**************************************************************************** + * Name: imx9_lpspi_9to16bitmode + * + * Description: + * Check if the SPI is operating in more then 8 bit mode + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * true: >8 bit mode-bit mode, false: <= 8-bit mode + * + ****************************************************************************/ + +static inline bool +imx9_lpspi_9to16bitmode(struct imx9_lpspidev_s *priv) +{ + bool ret; + + if (((imx9_lpspi_getreg32(priv, IMX9_LPSPI_TCR_OFFSET) & + LPSPI_TCR_FRAMESZ_MASK) + 1) < 9) + { + ret = false; + } + else + { + ret = true; + } + + return ret; +} + +/**************************************************************************** + * Name: imx9_lpspi_modifyreg32 + * + * Description: + * Clear and set bits in register + * + * Input Parameters: + * priv - Device-specific state data + * offset - Register offset + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imx9_lpspi_modifyreg32(struct imx9_lpspidev_s *priv, + uint8_t offset, uint32_t clrbits, + uint32_t setbits) +{ + modifyreg32(priv->spibase + offset, clrbits, setbits); +} + +/**************************************************************************** + * Name: imx9_lpspi_modifyreg32 + * + * Description: + * Clear and set bits TCR register. Need a safe wrapper as TCR expects + * LPSPI is _enabled_ when writing. + * + * Input Parameters: + * priv - Device-specific state data + * offset - Register offset + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imx9_lpspi_modifytcr(struct imx9_lpspidev_s *priv, + uint32_t clrbits, uint32_t setbits) +{ + uint32_t men; + + /* Enable LPSPI if it was disabled previously */ + + men = imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (!men) + { + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + + /* Update the register */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_TCR_OFFSET, clrbits, setbits); + + /* Disable LPSPI if it was disabled */ + + if (!men) + { + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } +} + +/**************************************************************************** + * Name: imx9_lpspi_master_set_delays + * + * Description: + * SET LPSPI Delay times + * + * Input Parameters: + * priv - Device-specific state data + * scaler - scaler value + * type - delay time type + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imx9_lpspi_master_set_delay_scaler( + struct imx9_lpspidev_s *priv, + uint32_t scaler, + enum imx9_delay_e type) +{ + uint32_t ccr1; + uint32_t dbt; + uint32_t sckdiv; + + /* Read from SCKDIV and DTB will always return 0. In order to preserve the + * old values we must calculate them here locally from CCR1 values. + */ + + ccr1 = imx9_lpspi_getreg32(priv, IMX9_LPSPI_CCR1_OFFSET); + dbt = (ccr1 & LPSPI_CCR1_SCKSCK_MASK) >> LPSPI_CCR1_SCKSCK_SHIFT; + sckdiv = (ccr1 & LPSPI_CCR1_SCKHLD_MASK) >> LPSPI_CCR1_SCKHLD_SHIFT; + sckdiv += (ccr1 & LPSPI_CCR1_SCKSET_MASK) >> LPSPI_CCR1_SCKSET_SHIFT; + + switch (type) + { + case LPSPI_PCS_TO_SCK: + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, + LPSPI_CCR_PCSSCK_MASK, 0); + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_DBT(dbt) | + LPSPI_CCR_PCSSCK(scaler) | + LPSPI_CCR_SCKDIV(sckdiv)); + break; + + case LPSPI_LAST_SCK_TO_PCS: + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKPCS_MASK, 0); + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_DBT(dbt) | + LPSPI_CCR_PCSSCK(scaler) | + LPSPI_CCR_SCKDIV(sckdiv)); + break; + + case LPSPI_BETWEEN_TRANSFER: + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, + LPSPI_CCR_DBT_MASK, 0); + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_DBT(dbt) | + LPSPI_CCR_PCSSCK(scaler) | + LPSPI_CCR_SCKDIV(sckdiv)); + break; + } +} + +/**************************************************************************** + * Name: imx9_lpspi_master_set_delays + * + * Description: + * SET LPSPI Delay times + * + * Input Parameters: + * priv - Device-specific state data + * delay_ns - delay time in nano seconds + * type - delay time type + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imx9_lpspi_master_set_delays( + struct imx9_lpspidev_s *priv, + uint32_t delay_ns, + enum imx9_delay_e type) +{ + uint32_t src_freq; + uint64_t real_delay; + uint32_t scaler; + uint32_t best_scaler; + uint32_t diff; + uint32_t min_diff; + uint64_t initial_delay_ns; + uint32_t clock_div_prescaler; + uint32_t additional_scaler; + + imx9_get_rootclock(priv->clk_root, &src_freq); + + clock_div_prescaler = src_freq / + (1 << ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_TCR_OFFSET) & + LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT)); + + min_diff = 0xffffffff; + + /* Initialize scaler to max value to generate the max delay */ + + best_scaler = 0xff; + + if (type == LPSPI_BETWEEN_TRANSFER) + { + /* First calculate the initial, default delay, note min delay is 2 + * clock cycles. Due to large size of * calculated values (uint64_t), + * we need to break up the calculation into several steps to ensure + * accurate calculated results + */ + + initial_delay_ns = 1000000000U; + initial_delay_ns *= 2; + initial_delay_ns /= clock_div_prescaler; + + additional_scaler = 1U; + } + else + { + /* First calculate the initial, default delay, min delay is 1 clock + * cycle. Due to large size of calculated values (uint64_t), we need to + * break up the calculation into several steps to ensure accurate + * calculated * results. + */ + + initial_delay_ns = 1000000000U; + initial_delay_ns /= clock_div_prescaler; + + additional_scaler = 0; + } + + /* If the initial, default delay is already greater than the desired delay, + * then * set the delay to their initial value (0) and return the delay. In + * other words, * there is no way to decrease the delay value further. + */ + + if (initial_delay_ns >= delay_ns) + { + imx9_lpspi_master_set_delay_scaler(priv, 0, type); + } + else + { + /* If min_diff = 0, the exit for loop */ + + for (scaler = 0; (scaler < 256) && min_diff; scaler++) + { + /* Calculate the real delay value as we cycle through the scaler + * values. Due to large size of calculated values (uint64_t), + * we need to break up the calculation into several steps to + * ensure accurate calculated results + */ + + real_delay = 1000000000U; + real_delay *= (scaler + 1 + additional_scaler); + real_delay /= clock_div_prescaler; + + /* calculate the delay difference based on the conditional + * statement that states that the calculated delay must not be + * less then the desired delay + */ + + if (real_delay >= delay_ns) + { + diff = real_delay - delay_ns; + if (min_diff > diff) + { + /* A better match found */ + + min_diff = diff; + best_scaler = scaler; + } + } + } + + imx9_lpspi_master_set_delay_scaler(priv, best_scaler, type); + } +} + +/**************************************************************************** + * Name: imx9_lpspi_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 imx9_lpspi_lock(struct spi_dev_s *dev, bool lock) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + int ret; + + if (lock) + { + ret = nxmutex_lock(&priv->lock); + } + else + { + ret = nxmutex_unlock(&priv->lock); + } + + return ret; +} + +/**************************************************************************** + * Name: imx9_lpspi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static uint32_t imx9_lpspi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + + uint32_t men; + uint32_t src_freq = 0; + uint32_t prescaler; + uint32_t best_prescaler; + uint32_t scaler; + uint32_t best_scaler; + uint32_t real_frequency; + uint32_t best_frequency; + uint32_t diff; + uint32_t min_diff; + + /* Has the LPSPI bus frequency changed? */ + + if (frequency != priv->frequency) + { + /* Disable LPSPI if it is enabled */ + + men = imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + imx9_get_rootclock(priv->clk_root, &src_freq); + + min_diff = 0xffffffff; + best_prescaler = 7; + best_scaler = 255; + best_frequency = 0; + + for (prescaler = 0; (prescaler < 8) && min_diff; prescaler++) + { + for (scaler = 0; (scaler < 256) && min_diff; scaler++) + { + real_frequency = src_freq / ((1 << prescaler) * (scaler + 2)); + + /* Calculate the frequency difference based on conditional + * statement that states that the calculated frequency must not + * exceed desired frequency. + */ + + if (frequency >= real_frequency) + { + diff = frequency - real_frequency; + if (min_diff > diff) + { + /* A better match found */ + + min_diff = diff; + best_prescaler = prescaler; + best_scaler = scaler; + best_frequency = real_frequency; + } + } + } + } + + /* Write the best values in the CCR register */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKDIV_MASK, + LPSPI_CCR_SCKDIV(best_scaler)); + + /* Update TCR */ + + imx9_lpspi_modifytcr(priv, LPSPI_TCR_PRESCALE_MASK, + LPSPI_TCR_PRESCALE(best_prescaler)); + + priv->frequency = frequency; + priv->actual = best_frequency; + imx9_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_PCS_TO_SCK); + imx9_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_LAST_SCK_TO_PCS); + imx9_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_BETWEEN_TRANSFER); + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } + + return priv->actual; +} + +/**************************************************************************** + * Name: imx9_lpspi_setmode + * + * Description: + * Set the SPI mode. see enum spi_mode_e mode for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static void imx9_lpspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Disable LPSPI if it is enabled */ + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + setbits = 0; + clrbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + setbits = LPSPI_TCR_CPHA; + clrbits = LPSPI_TCR_CPOL; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + setbits = LPSPI_TCR_CPOL; + clrbits = LPSPI_TCR_CPHA; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + setbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; + clrbits = 0; + break; + + default: + return; + } + + /* Update TCR register */ + + imx9_lpspi_modifytcr(priv, clrbits, setbits); + + while ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_RSR_OFFSET) & + LPSPI_RSR_RXEMPTY) != LPSPI_RSR_RXEMPTY) + { + /* Flush SPI read FIFO */ + + imx9_lpspi_getreg32(priv, IMX9_LPSPI_RSR_OFFSET); + } + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + } +} + +/**************************************************************************** + * Name: imx9_lpspi_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imx9_lpspi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + if (nbits < 2 || nbits > 4096) + { + return; + } + + /* Update TCR */ + + imx9_lpspi_modifytcr(priv, LPSPI_TCR_FRAMESZ_MASK, + LPSPI_TCR_FRAMESZ(nbits - 1)); + + /* Save the selection so the subsequent re-configurations + * will be faster. + */ + + priv->nbits = nbits; + } +} + +/**************************************************************************** + * Name: imx9_lpspi_hwfeatures + * + * Description: + * Set hardware-specific feature flags. + * + * Input Parameters: + * dev - Device-specific state data + * features - H/W feature flags + * + * Returned Value: + * Zero (OK) if the selected H/W features are enabled; A negated errno + * value if any H/W feature is not supportable. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_HWFEATURES +static int imx9_lpspi_hwfeatures(struct spi_dev_s *dev, + imx9_lpspi_hwfeatures_t features) +{ +#ifdef CONFIG_SPI_BITORDER + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + + spiinfo("features=%08x\n", features); + + /* Transfer data LSB first? */ + + if ((features & HWFEAT_LSBFIRST) != 0) + { + setbits = LPSPI_TCR_LSBF; + clrbits = 0; + } + else + { + setbits = 0; + clrbits = LPSPI_TCR_LSBF; + } + + imx9_lpspi_modifytcr(priv, clrbits, setbits); + + /* Other H/W features are not supported */ + + return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS; +#else + return -ENOSYS; +#endif +} +#endif + +/**************************************************************************** + * Name: imx9_lpspi_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 imx9_lpspi_send(struct spi_dev_s *dev, uint32_t wd) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + uint32_t regval; + uint32_t ret; + + DEBUGASSERT(priv && priv->spibase); + + imx9_lpspi_writeword(priv, wd); + + while ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_SR_OFFSET) & + LPSPI_SR_RDF) != LPSPI_SR_RDF); + + ret = imx9_lpspi_readword(priv); + + /* Check and clear any error flags (Reading from the SR clears the error + * flags). + */ + + regval = imx9_lpspi_getreg32(priv, IMX9_LPSPI_SR_OFFSET); + + spiinfo( + "Sent: %04" PRIx32 " Return: %04" PRIx32 " Status: %02" PRIx32 "\n", + wd, ret, regval); + + UNUSED(regval); + return ret; +} + +/**************************************************************************** + * Name: imx9_lpspi_exchange (no DMA). aka imx9_lpspi_exchange_nodma + * + * Description: + * Exchange a block of data on SPI without using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data 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 + * + ****************************************************************************/ + +#if !defined(CONFIG_IMX9_LPSPI_DMA) +static void imx9_lpspi_exchange(struct spi_dev_s *dev, + const void *txbuffer, + void *rxbuffer, + size_t nwords) +#else +static void imx9_lpspi_exchange_nodma(struct spi_dev_s *dev, + const void *txbuffer, + void *rxbuffer, size_t nwords) +#endif +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + DEBUGASSERT(priv && priv->spibase); + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%lu\n", txbuffer, rxbuffer, + nwords); + + /* 8- or 16-bit mode? */ + + if (imx9_lpspi_9to16bitmode(priv)) + { + /* 16-bit mode */ + + const uint16_t *src = txbuffer; + uint16_t *dest = rxbuffer; + uint16_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xffff; + } + + /* Exchange one word */ + + word = (uint16_t) imx9_lpspi_send(dev, (uint32_t) word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } + else + { + /* 8-bit mode */ + + const uint8_t *src = txbuffer; + uint8_t *dest = rxbuffer; + uint8_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xff; + } + + /* Exchange one word */ + + word = (uint8_t)imx9_lpspi_send(dev, word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } +} + +/**************************************************************************** + * Name: spi_exchange (with DMA capability) + * + * Description: + * Exchange a block of data on SPI using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data 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_IMX9_LPSPI_DMA +static void imx9_lpspi_exchange(struct spi_dev_s *dev, + const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + int ret; + size_t adjust; + ssize_t nbytes; + static uint8_t rxdummy[4] aligned_data(4); + static const uint16_t txdummy = 0xffff; + uint32_t regval; + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv && priv->spibase); + spiinfo("txbuffer=%p rxbuffer=%p nwords=%lu\n", txbuffer, rxbuffer, + nwords); + + /* Convert the number of word to a number of bytes */ + + nbytes = (priv->nbits > 8) ? nwords << 2 : nwords; + + /* Invalid DMA channels fall back to non-DMA method. */ + + if (priv->rxdma == NULL || priv->txdma == NULL +#ifdef CONFIG_IMX9_LPSPI_DMATHRESHOLD + /* If this is a small SPI transfer, then let + * imx9_lpspi_exchange_nodma() do the work. + */ + + || nbytes <= CONFIG_IMX9_LPSPI_DMATHRESHOLD +#endif + ) + { + imx9_lpspi_exchange_nodma(dev, txbuffer, rxbuffer, nwords); + return; + } + + /* Disable SPI when we are configuring it */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, LPSPI_CR_MEN, 0); + + /* ERR050456 workaround: Reset FIFOs using CR[RST] bit */ + + regval = imx9_lpspi_getreg32(priv, IMX9_LPSPI_CFGR1_OFFSET); + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, + LPSPI_CR_RTF | LPSPI_CR_RRF, + LPSPI_CR_RTF | LPSPI_CR_RRF); + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_CFGR1_OFFSET, regval); + + /* Clear all status bits */ + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_SR_OFFSET, SPI_SR_CLEAR); + + /* disable DMA */ + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_DER_OFFSET, 0); + + if (txbuffer) + { + up_clean_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + nbytes); + } + + if (rxbuffer) + { + up_invalidate_dcache((uintptr_t)rxbuffer, + (uintptr_t)rxbuffer + nbytes); + } + + /* Set up the DMA */ + + adjust = (priv->nbits > 8) ? 2 : 1; + + struct imx9_edma_xfrconfig_s config; + + config.saddr = priv->spibase + IMX9_LPSPI_RDR_OFFSET; + config.daddr = (uintptr_t) (rxbuffer ? rxbuffer : rxdummy); + config.soff = 0; + config.doff = rxbuffer ? adjust : 0; + config.iter = nbytes; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT; + config.dsize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT; + config.nbytes = adjust; +#ifdef CONFIG_IMX9_EDMA_ELINK + config.linkch = NULL; +#endif + imx9_dmach_xfrsetup(priv->rxdma, &config); + + config.saddr = (uintptr_t) (txbuffer ? txbuffer : &txdummy); + config.daddr = priv->spibase + IMX9_LPSPI_TDR_OFFSET; + config.soff = txbuffer ? adjust : 0; + config.doff = 0; + config.iter = nbytes; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT; + config.dsize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT; + config.nbytes = adjust; +#ifdef CONFIG_IMX9_EDMA_ELINK + config.linkch = NULL; +#endif + imx9_dmach_xfrsetup(priv->txdma, &config); + + /* Start the DMAs */ + + spi_dmarxstart(priv); + spi_dmatxstart(priv); + + /* Enable SPI again */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); + + /* Invoke SPI DMA */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_DER_OFFSET, + 0, LPSPI_DER_TDDE | LPSPI_DER_RDDE); + + /* Then wait for each to complete */ + + ret = spi_dmarxwait(priv); + + if (ret < 0) + { + ret = spi_dmatxwait(priv); + } + + /* Reset any status */ + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_SR_OFFSET, SPI_SR_CLEAR); + + /* Disable DMA */ + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_DER_OFFSET, 0); + + if (rxbuffer) + { + up_invalidate_dcache((uintptr_t)rxbuffer, + (uintptr_t)rxbuffer + nbytes); + } +} + +#endif /* CONFIG_IMX9_SPI_DMA */ + +/**************************************************************************** + * Name: imx9_lpspi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - 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 imx9_lpspi_sndblock(struct spi_dev_s *dev, + const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%lu\n", txbuffer, nwords); + return imx9_lpspi_exchange(dev, txbuffer, NULL, nwords); +} +#endif + +/**************************************************************************** + * Name: imx9_lpspi_recvblock + * + * Description: + * Receive a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * rxbuffer - 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 imx9_lpspi_recvblock(struct spi_dev_s *dev, + void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%lu\n", rxbuffer, nwords); + return imx9_lpspi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: imx9_lpspi_clock_enable + * + * Description: + * Ungate LPSPI clock + * + ****************************************************************************/ + +static void imx9_lpspi_clock_enable(struct imx9_lpspidev_s *priv) +{ + imx9_ccm_gate_on(priv->clk_gate, true); +} + +/**************************************************************************** + * Name: imx9_lpspi_bus_initialize + * + * Description: + * Initialize the selected SPI bus in its default state + * (Master, 8-bit, mode 0, etc.) + * + * Input Parameters: + * priv - private SPI device structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imx9_lpspi_bus_initialize(struct imx9_lpspidev_s *priv) +{ + uint32_t reg = 0; + + /* Enable power and reset the peripheral */ + + imx9_lpspi_clock_enable(priv); + + /* Reset to known status */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, 0, LPSPI_CR_RST); + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, 0, + LPSPI_CR_RTF | LPSPI_CR_RRF); + imx9_lpspi_putreg32(priv, IMX9_LPSPI_CR_OFFSET, 0x00); + + /* Set LPSPI to master */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CFGR1_OFFSET, 0, + LPSPI_CFGR1_MASTER); + + /* Set specific PCS to active high or low + * TODO: Not needed for now + */ + + /* Set Configuration Register 1 related setting. */ + + reg = imx9_lpspi_getreg32(priv, IMX9_LPSPI_CFGR1_OFFSET); + reg &= ~(LPSPI_CFGR1_OUTCFG | LPSPI_CFGR1_PINCFG_MASK | + LPSPI_CFGR1_NOSTALL); + reg |= LPSPI_CFGR1_OUTCFG_RETAIN | LPSPI_CFGR1_PINCFG_SIN_SOUT; + imx9_lpspi_putreg32(priv, IMX9_LPSPI_CFGR1_OFFSET, reg); + + /* Set frequency and delay times */ + + imx9_lpspi_setfrequency((struct spi_dev_s *)priv, 400000); + + /* Set default watermarks */ + + imx9_lpspi_putreg32(priv, IMX9_LPSPI_FCR_OFFSET, + LPSPI_FCR_TXWATER(0) | LPSPI_FCR_RXWATER(0)); + + /* Set Transmit Command Register */ + + imx9_lpspi_setbits((struct spi_dev_s *)priv, 8); + + imx9_lpspi_setmode((struct spi_dev_s *)priv, SPIDEV_MODE0); + + /* Enable LPSPI */ + + imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); +} + +/**************************************************************************** + * Name: spi_dmarxwait + * + * Description: + * Wait for DMA to complete. + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static int spi_dmarxwait(struct imx9_lpspidev_s *priv) +{ + int ret; + + /* Take the semaphore (perhaps waiting). If the result is zero, then the + * DMA must not really have completed. + */ + + do + { + ret = nxsem_wait_uninterruptible(&priv->rxsem); + + /* The only expected error is ECANCELED which would occur if the + * calling thread were canceled. + */ + + DEBUGASSERT(ret == OK || ret == -ECANCELED); + } + while (priv->rxresult == 0 && ret == OK); + + return ret; +} +#endif + +/**************************************************************************** + * Name: spi_dmatxwait + * + * Description: + * Wait for DMA to complete. + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static int spi_dmatxwait(struct imx9_lpspidev_s *priv) +{ + int ret; + + /* Take the semaphore (perhaps waiting). If the result is zero, then the + * DMA must not really have completed. + */ + + do + { + ret = nxsem_wait_uninterruptible(&priv->txsem); + + /* The only expected error is ECANCELED which would occur if the + * calling thread were canceled. + */ + + DEBUGASSERT(ret == OK || ret == -ECANCELED); + } + while (priv->txresult == 0 && ret == OK); + + return ret; +} +#endif + +/**************************************************************************** + * Name: spi_dmarxwakeup + * + * Description: + * Signal that DMA is complete + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static inline void spi_dmarxwakeup(struct imx9_lpspidev_s *priv) +{ + nxsem_post(&priv->rxsem); +} +#endif + +/**************************************************************************** + * Name: spi_dmatxwakeup + * + * Description: + * Signal that DMA is complete + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static inline void spi_dmatxwakeup(struct imx9_lpspidev_s *priv) +{ + nxsem_post(&priv->txsem); +} +#endif + +/**************************************************************************** + * Name: spi_dmarxcallback + * + * Description: + * Called when the RX DMA completes + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static void spi_dmarxcallback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)arg; + + priv->rxresult = result | 0x80000000; /* assure non-zero */ + spi_dmarxwakeup(priv); +} +#endif + +/**************************************************************************** + * Name: spi_dmatxcallback + * + * Description: + * Called when the RX DMA completes + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static void spi_dmatxcallback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)arg; + + /* Wake-up the SPI driver */ + + priv->txresult = result | 0x80000000; /* assure non-zero */ + spi_dmatxwakeup(priv); +} +#endif + +/**************************************************************************** + * Name: spi_dmarxstart + * + * Description: + * Start RX DMA + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static inline void spi_dmarxstart(struct imx9_lpspidev_s *priv) +{ + priv->rxresult = 0; + imx9_dmach_start(priv->rxdma, spi_dmarxcallback, priv); +} +#endif + +/**************************************************************************** + * Name: spi_dmatxstart + * + * Description: + * Start TX DMA + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI_DMA +static inline void spi_dmatxstart(struct imx9_lpspidev_s *priv) +{ + priv->txresult = 0; + imx9_dmach_start(priv->txdma, spi_dmatxcallback, priv); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_lpspibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *imx9_lpspibus_initialize(int bus) +{ + struct imx9_lpspidev_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + +#ifdef CONFIG_IMX9_LPSPI1 + if (bus == 1) + { + /* Select SPI1 */ + + priv = &g_lpspi1dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI1 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI1_SCK); + imx9_iomux_configure(MUX_LPSPI1_MISO); + imx9_iomux_configure(MUX_LPSPI1_MOSI); +#if defined(MUX_LPSPI1_CS) && defined(GPIO_LPSPI1_CS) + imx9_iomux_configure(MUX_LPSPI1_CS); + imx9_config_gpio(GPIO_LPSPI1_CS); +#endif +#if defined(GPIO_LPSPI1_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI1_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI2 + if (bus == 2) + { + /* Select SPI2 */ + + priv = &g_lpspi2dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI2 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI2_SCK); + imx9_iomux_configure(MUX_LPSPI2_MISO); + imx9_iomux_configure(MUX_LPSPI2_MOSI); +#if defined(MUX_LPSPI2_CS) && defined(GPIO_LPSPI2_CS) + imx9_iomux_configure(MUX_LPSPI2_CS); + imx9_config_gpio(GPIO_LPSPI2_CS); +#endif +#if defined(GPIO_LPSPI2_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI2_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI3 + if (bus == 3) + { + /* Select SPI3 */ + + priv = &g_lpspi3dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI3 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI3_SCK); + imx9_iomux_configure(MUX_LPSPI3_MISO); + imx9_iomux_configure(MUX_LPSPI3_MOSI); +#if defined(MUX_LPSPI3_CS) && defined(GPIO_LPSPI3_CS) + imx9_iomux_configure(MUX_LPSPI3_CS); + imx9_config_gpio(GPIO_LPSPI3_CS); +#endif +#if defined(GPIO_LPSPI3_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI3_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI4 + if (bus == 4) + { + /* Select SPI4 */ + + priv = &g_lpspi4dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI4 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI4_SCK); + imx9_iomux_configure(MUX_LPSPI4_MISO); + imx9_iomux_configure(MUX_LPSPI4_MOSI); +#if defined(MUX_LPSPI4_CS) && defined(GPIO_LPSPI4_CS) + imx9_iomux_configure(MUX_LPSPI4_CS); + imx9_config_gpio(GPIO_LPSPI4_CS); +#endif +#if defined(GPIO_LPSPI4_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI4_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI5 + if (bus == 5) + { + /* Select SPI5 */ + + priv = &g_lpspi5dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI5 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI5_SCK); + imx9_iomux_configure(MUX_LPSPI5_MISO); + imx9_iomux_configure(MUX_LPSPI5_MOSI); +#if defined(MUX_LPSPI5_CS) && defined(GPIO_LPSPI5_CS) + imx9_iomux_configure(MUX_LPSPI5_CS); + imx9_config_gpio(GPIO_LPSPI5_CS); +#endif +#if defined(GPIO_LPSPI5_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI5_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI6 + if (bus == 6) + { + /* Select SPI6 */ + + priv = &g_lpspi6dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI6 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI6_SCK); + imx9_iomux_configure(MUX_LPSPI6_MISO); + imx9_iomux_configure(MUX_LPSPI6_MOSI); +#if defined(MUX_LPSPI6_CS) && defined(GPIO_LPSPI6_CS) + imx9_iomux_configure(MUX_LPSPI6_CS); + imx9_config_gpio(GPIO_LPSPI6_CS); +#endif +#if defined(GPIO_LPSPI6_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI6_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI7 + if (bus == 7) + { + /* Select SPI7 */ + + priv = &g_lpspi7dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI7 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI7_SCK); + imx9_iomux_configure(MUX_LPSPI7_MISO); + imx9_iomux_configure(MUX_LPSPI7_MOSI); +#if defined(MUX_LPSPI7_CS) && defined(GPIO_LPSPI7_CS) + imx9_iomux_configure(MUX_LPSPI7_CS); + imx9_config_gpio(GPIO_LPSPI7_CS); +#endif +#if defined(GPIO_LPSPI7_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI7_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMX9_LPSPI8 + if (bus == 8) + { + /* Select SPI8 */ + + priv = &g_lpspi8dev; + + /* Only configure if the bus is not already configured */ + + if ((imx9_lpspi_getreg32(priv, IMX9_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI6 pins: SCK, MISO, and MOSI */ + + imx9_iomux_configure(MUX_LPSPI8_SCK); + imx9_iomux_configure(MUX_LPSPI8_MISO); + imx9_iomux_configure(MUX_LPSPI8_MOSI); +#if defined(MUX_LPSPI8_CS) && defined(GPIO_LPSPI8_CS) + imx9_iomux_configure(MUX_LPSPI8_CS); + imx9_config_gpio(GPIO_LPSPI8_CS); +#endif +#if defined(GPIO_LPSPI8_DC) && defined(CONFIG_SPI_CMDDATA) + imx9_iomux_configure(GPIO_LPSPI8_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imx9_lpspi_bus_initialize(priv); + } + } + else +#endif + { + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + } + +#ifdef CONFIG_IMX9_LPSPI_DMA + if (priv->rxch && priv->txch) + { + if (priv->txdma == NULL && priv->rxdma == NULL) + { + priv->txdma = imx9_dmach_alloc(priv->txch, 0); + priv->rxdma = imx9_dmach_alloc(priv->rxch, 0); + DEBUGASSERT(priv->rxdma && priv->txdma); + } + } + else + { + priv->rxdma = NULL; + priv->txdma = NULL; + } +#endif + + leave_critical_section(flags); + + return (struct spi_dev_s *)priv; +} + +#endif /* CONFIG_IMX9_LPSPI */ diff --git a/arch/arm64/src/imx9/imx9_lpspi.h b/arch/arm64/src/imx9/imx9_lpspi.h new file mode 100644 index 0000000000..0090732069 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_lpspi.h @@ -0,0 +1,139 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_lpspi.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_ARM64_SRC_IMX9_IMX9_LPSPI_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_LPSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include "chip.h" +#include "hardware/imx9_lpspi.h" + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +struct spi_dev_s; /* Forward reference */ + +/**************************************************************************** + * Name: imx9_lpspibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * bus number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *imx9_lpspibus_initialize(int bus); + +/**************************************************************************** + * Name: imx9_lpspi1/2/...select and imx9_lpspi1/2/...status + * + * Description: + * The external functions, imx9_lpspi1/2/...select, + * imx9_lpspi1/2/...status, and imx9_lpspi1/2/...cmddata must be + * provided by board-specific logic. These are implementations of the + * select, status, and cmddata methods of the SPI interface defined by + * struct spi_ops_s (see include/nuttx/spi/spi.h). All other methods + * (including imx9_lpspibus_initialize()) are provided by common IMX9 + * logic. To use this common SPI logic on your board: + * + * 1. Provide logic in imx9_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide imx9_lpspi1/2/...select() and imx9_lpspi1/2/...status() + * functions in your board-specific logic. These functions will perform + * chip selection and status operations using GPIOs in the way your + * board is configured. + * 3. If CONFIG_SPI_CMDDATA is defined in your NuttX configuration file, + * then provide imx9_lpspi1/2/...cmddata() functions in your + * board-specific logic. These functions will perform cmd/data selection + * operations using GPIOs in the way your board is configured. + * 4. Add a calls to imx9_lpspibus_initialize() in your low level + * application initialization logic + * 5. The handle returned by imx9_lpspibus_initialize() may then be used + * to bind the SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ****************************************************************************/ + +void imx9_lpspi_select(struct spi_dev_s *dev, + uint32_t devid, bool selected); +uint8_t imx9_lpspi_status(struct spi_dev_s *dev, uint32_t devid); +int imx9_lpspi_cmddata(struct spi_dev_s *dev, + uint32_t devid, bool cmd); + +/**************************************************************************** + * Name: imx9_lpspi1/2/...register + * + * Description: + * If the board supports a card detect callback to inform the SPI-based + * MMC/SD driver when an SD card is inserted or removed, then + * CONFIG_SPI_CALLBACK should be defined and the following function(s) + * must be implemented. These functions implements the registercallback + * method of the SPI interface (see include/nuttx/spi/spi.h for details) + * + * Input Parameters: + * dev - Device-specific state data + * callback - The function to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CALLBACK +int imx9_lpspi_register(struct spi_dev_s *dev, + spi_mediachange_t callback, + void *arg); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_LPSPI_H */ diff --git a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig index b4ae81e0a0..206f086215 100644 --- a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig +++ b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig @@ -36,6 +36,7 @@ CONFIG_IMX9_GPIO_IRQ=y CONFIG_IMX9_LPI2C1=y CONFIG_IMX9_LPI2C_DYNTIMEO=y CONFIG_IMX9_LPI2C_DYNTIMEO_STARTSTOP=10 +CONFIG_IMX9_LPSPI6=y CONFIG_IMX9_LPUART1=y CONFIG_IMX9_TPM3_PWM=y CONFIG_IMX9_TPM3_PWM_CHMUX=0x00000003 @@ -60,6 +61,7 @@ CONFIG_SCHED_HPWORK=y CONFIG_SCHED_HPWORKPRIORITY=192 CONFIG_SCHED_LPWORK=y CONFIG_SCHED_LPWORKPRIORITY=50 +CONFIG_SPI=y CONFIG_SPINLOCK=y CONFIG_STACK_COLORATION=y CONFIG_START_MONTH=3 @@ -68,6 +70,7 @@ CONFIG_SYMTAB_ORDEREDBYNAME=y CONFIG_SYSTEM_CDCACM=y CONFIG_SYSTEM_I2CTOOL=y CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_SPITOOL=y CONFIG_SYSTEM_SYSTEM=y CONFIG_SYSTEM_TIME64=y CONFIG_TESTING_GETPRIME=y diff --git a/boards/arm64/imx9/imx93-evk/include/board.h b/boards/arm64/imx9/imx93-evk/include/board.h index 37d46536ec..d1a74a7e5a 100644 --- a/boards/arm64/imx9/imx93-evk/include/board.h +++ b/boards/arm64/imx9/imx93-evk/include/board.h @@ -31,7 +31,11 @@ * Pre-processor Definitions ****************************************************************************/ +/* Default PAD configurations */ + #define IOMUX_LPI2C_DEFAULT (IOMUXC_PAD_OD_ENABLE | IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_DSE_X6) +#define IOMUX_LPSPI_DEFAULT (IOMUXC_PAD_PU_ON | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6) +#define IOMUX_GPIO_DEFAULT (IOMUXC_PAD_FSEL_SLOW | IOMUXC_PAD_DSE_X6) /* UART pin muxings */ @@ -68,6 +72,22 @@ #define GPIO_LPI2C1_SCL_RESET (GPIO_PORT1 | GPIO_PIN0 | GPIO_OUTPUT | GPIO_OUTPUT_ONE) #define GPIO_LPI2C1_SDA_RESET (GPIO_PORT1 | GPIO_PIN1 | GPIO_OUTPUT | GPIO_OUTPUT_ONE) +/* LPSPIs */ + +#define MUX_LPSPI3_SCK IOMUX_CFG(IOMUXC_PAD_GPIO_IO11_LPSPI3_SCK, IOMUX_LPSPI_DEFAULT, IOMUXC_MUX_SION_ON) +#define MUX_LPSPI3_MOSI IOMUX_CFG(IOMUXC_PAD_GPIO_IO10_LPSPI3_SOUT, IOMUX_LPSPI_DEFAULT, IOMUXC_MUX_SION_ON) +#define MUX_LPSPI3_MISO IOMUX_CFG(IOMUXC_PAD_GPIO_IO09_LPSPI3_SIN, IOMUX_LPSPI_DEFAULT, IOMUXC_MUX_SION_ON) +#define MUX_LPSPI6_SCK IOMUX_CFG(IOMUXC_PAD_GPIO_IO03_LPSPI6_SCK, IOMUX_LPSPI_DEFAULT, IOMUXC_MUX_SION_ON) +#define MUX_LPSPI6_MOSI IOMUX_CFG(IOMUXC_PAD_GPIO_IO02_LPSPI6_SOUT, IOMUX_LPSPI_DEFAULT, IOMUXC_MUX_SION_ON) +#define MUX_LPSPI6_MISO IOMUX_CFG(IOMUXC_PAD_GPIO_IO01_LPSPI6_SIN, IOMUX_LPSPI_DEFAULT, IOMUXC_MUX_SION_ON) + +/* SPI CS */ + +#define MUX_LPSPI3_CS IOMUX_CFG(IOMUXC_PAD_GPIO_IO08_GPIO2_IO08, IOMUX_GPIO_DEFAULT, IOMUXC_MUX_SION_ON) +#define GPIO_LPSPI3_CS (GPIO_PORT2 | GPIO_PIN8 | GPIO_OUTPUT | GPIO_OUTPUT_ONE) +#define MUX_LPSPI6_CS IOMUX_CFG(IOMUXC_PAD_GPIO_IO00_GPIO2_IO00, IOMUX_GPIO_DEFAULT, IOMUXC_MUX_SION_ON) +#define GPIO_LPSPI6_CS (GPIO_PORT2 | GPIO_PIN0 | GPIO_OUTPUT | GPIO_OUTPUT_ONE) + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/boards/arm64/imx9/imx93-evk/src/Makefile b/boards/arm64/imx9/imx93-evk/src/Makefile index 564a2db926..7849be43dc 100644 --- a/boards/arm64/imx9/imx93-evk/src/Makefile +++ b/boards/arm64/imx9/imx93-evk/src/Makefile @@ -34,4 +34,8 @@ ifeq ($(CONFIG_IMX9_LPI2C),y) CSRCS += imx9_i2c.c endif +ifeq ($(CONFIG_IMX9_LPSPI),y) +CSRCS += imx9_spi.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm64/imx9/imx93-evk/src/imx93-evk.h b/boards/arm64/imx9/imx93-evk/src/imx93-evk.h index 1da0b3b88e..be6bceb339 100644 --- a/boards/arm64/imx9/imx93-evk/src/imx93-evk.h +++ b/boards/arm64/imx9/imx93-evk/src/imx93-evk.h @@ -79,5 +79,17 @@ int imx9_pwm_setup(void); int imx9_i2c_initialize(void); #endif +/**************************************************************************** + * Name: imx9_spi_setup + * + * Description: + * Initialize SPI devices and driver + * + ****************************************************************************/ + +#if defined(CONFIG_SPI_DRIVER) +int imx9_spi_initialize(void); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM64_IMX9_IMX93_EVK_SRC_IMX93_EVK_H */ diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c b/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c index 6048509b67..acd7b309a0 100644 --- a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c +++ b/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c @@ -77,6 +77,16 @@ int imx9_bringup(void) } #endif +#if defined(CONFIG_SPI_DRIVER) + /* Configure SPI peripheral interfaces */ + + ret = imx9_spi_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize SPI driver: %d\n", ret); + } +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_spi.c b/boards/arm64/imx9/imx93-evk/src/imx9_spi.c new file mode 100644 index 0000000000..826cc73dbb --- /dev/null +++ b/boards/arm64/imx9/imx93-evk/src/imx9_spi.c @@ -0,0 +1,162 @@ +/**************************************************************************** + * boards/arm64/imx9/imx93-evk/src/imx9_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 "imx9_gpio.h" +#include "imx9_lpspi.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI6 +static struct spi_dev_s *g_spi6; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_IMX9_LPSPI + +/**************************************************************************** + * Name: imx9_lpspix_select + * + * Description: + * Enable/disable the SPI chip select. The implementation of this method + * must include handshaking: If a device is selected, it must hold off + * all other attempts to select the device until the device is deselected. + * Required. + * + * Input Parameters: + * dev - Device-specific state data + * devid - Identifies the device to select + * selected - true: slave selected, false: slave de-selected + * + * Returned Value: + * None + * + ****************************************************************************/ + +void imx9_lpspi_select(struct spi_dev_s *dev, uint32_t devid, bool selected) +{ +#ifdef CONFIG_IMX9_LPSPI6 + if (dev == g_spi6) + { + imx9_gpio_write(GPIO_LPSPI6_CS, !selected); + } +#endif +} + +/**************************************************************************** + * Name: imx9_lpspix_status + * + * Description: + * Get SPI/MMC status. Optional. + * + * Input Parameters: + * dev - Device-specific state data + * devid - Identifies the device to report status on + * + * Returned Value: + * Returns a bitset of status values (see SPI_STATUS_* defines) + * + ****************************************************************************/ + +uint8_t imx9_lpspi_status(struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} + +/**************************************************************************** + * Name: imx9_lpspixcmddata + * + * Description: + * Some devices require an additional out-of-band bit to specify if the + * next word sent to the device is a command or data. This is typical, for + * example, in "9-bit" displays where the 9th bit is the CMD/DATA bit. + * This function provides selection of command or data. + * + * This "latches" the CMD/DATA state. It does not have to be called before + * every word is transferred; only when the CMD/DATA state changes. This + * method is required if CONFIG_SPI_CMDDATA is selected in the NuttX + * configuration + * + * Input Parameters: + * dev - Device-specific state data + * cmd - TRUE: The following word is a command; FALSE: the following words + * are data. + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +int imx9_lpspi_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif + +/**************************************************************************** + * Name: board_spi_initialize + * + * Description: + * Initialize and register SPI driver for the defined SPI ports. + * + ****************************************************************************/ + +int imx9_spi_initialize(void) +{ + int ret = OK; + +#if defined(CONFIG_IMX9_LPSPI) + /* Initialize SPI device */ + + g_spi6 = imx9_lpspibus_initialize(6); + if (g_spi6 == NULL) + { + spierr("Failed to initialize SPI6\n"); + return -ENODEV; + } +#endif /* CONFIG_MPFS_SPI0 */ + +#ifdef CONFIG_SPI_DRIVER + ret = spi_register(g_spi6, 0); + if (ret < 0) + { + spierr("Failed to register /dev/spi0: %d\n", ret); + } +#endif /* CONFIG_SPI_DRIVER */ + + return OK; +}