From a3c67df91dbae5addc5213397b2849e9d343947e Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 31 Oct 2018 12:50:05 -0600 Subject: [PATCH] arch/arm/src/imxrt: Add full support for the LPSPI in poll mode; includes a minor fix for LPI2C. --- arch/arm/src/imxrt/Kconfig | 102 +- arch/arm/src/imxrt/Make.defs | 4 + arch/arm/src/imxrt/chip/imxrt105x_pinmux.h | 111 +- arch/arm/src/imxrt/chip/imxrt_lpspi.h | 3 +- arch/arm/src/imxrt/imxrt_clockconfig.c | 16 + arch/arm/src/imxrt/imxrt_lpi2c.c | 10 +- arch/arm/src/imxrt/imxrt_lpi2c.h | 6 +- arch/arm/src/imxrt/imxrt_lpspi.c | 1685 ++++++++++++++++++++ arch/arm/src/imxrt/imxrt_lpspi.h | 187 +++ configs/imxrt1050-evk/include/board.h | 13 + configs/imxrt1050-evk/src/Makefile | 8 + configs/imxrt1050-evk/src/imxrt1050-evk.h | 35 +- configs/imxrt1050-evk/src/imxrt_bringup.c | 31 +- configs/imxrt1050-evk/src/imxrt_mmcsd.c | 130 ++ configs/imxrt1050-evk/src/imxrt_spi.c | 217 +++ 15 files changed, 2445 insertions(+), 113 deletions(-) create mode 100644 arch/arm/src/imxrt/imxrt_lpspi.c create mode 100644 arch/arm/src/imxrt/imxrt_lpspi.h create mode 100644 configs/imxrt1050-evk/src/imxrt_mmcsd.c create mode 100644 configs/imxrt1050-evk/src/imxrt_spi.c diff --git a/arch/arm/src/imxrt/Kconfig b/arch/arm/src/imxrt/Kconfig index 7361c104bd..f422cf6350 100644 --- a/arch/arm/src/imxrt/Kconfig +++ b/arch/arm/src/imxrt/Kconfig @@ -66,6 +66,10 @@ config IMXRT_LPI2C bool default n +config IMXRT_LPSPI + bool + default n + menu "i.MX RT Peripheral Selection" config IMXRT_EDMA @@ -153,17 +157,17 @@ menuconfig IMXRT_LPI2C1 if IMXRT_LPI2C1 - config LPI2C1_BUSYIDLE - int "Bus idle timeout period in clock cycles" - default 0 +config LPI2C1_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 - config LPI2C1_FILTSCL - int "I2C master digital glitch filters for SCL input in clock cycles" - default 0 +config LPI2C1_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 - config LPI2C1_FILTSDA - int "I2C master digital glitch filters for SDA input in clock cycles" - default 0 +config LPI2C1_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 endif # IMXRT_LPI2C1 @@ -174,17 +178,17 @@ menuconfig IMXRT_LPI2C2 if IMXRT_LPI2C2 - config LPI2C2_BUSYIDLE - int "Bus idle timeout period in clock cycles" - default 0 +config LPI2C2_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 - config LPI2C2_FILTSCL - int "I2C master digital glitch filters for SCL input in clock cycles" - default 0 +config LPI2C2_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 - config LPI2C2_FILTSDA - int "I2C master digital glitch filters for SDA input in clock cycles" - default 0 +config LPI2C2_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 endif # IMXRT_LPI2C2 @@ -195,42 +199,66 @@ menuconfig IMXRT_LPI2C3 if IMXRT_LPI2C3 - config LPI2C3_BUSYIDLE - int "Bus idle timeout period in clock cycles" - default 0 +config LPI2C3_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 - config LPI2C3_FILTSCL - int "I2C master digital glitch filters for SCL input in clock cycles" - default 0 +config LPI2C3_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 - config LPI2C3_FILTSDA - int "I2C master digital glitch filters for SDA input in clock cycles" - default 0 +config LPI2C3_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 endif # IMXRT_LPI2C3 -config IMXRT_LPI2C4 +menuconfig IMXRT_LPI2C4 bool "LPI2C4" default n select IMXRT_LPI2C if IMXRT_LPI2C4 - config LPI2C4_BUSYIDLE - int "Bus idle timeout period in clock cycles" - default 0 +config LPI2C4_BUSYIDLE + int "Bus idle timeout period in clock cycles" + default 0 - config LPI2C4_FILTSCL - int "I2C master digital glitch filters for SCL input in clock cycles" - default 0 +config LPI2C4_FILTSCL + int "I2C master digital glitch filters for SCL input in clock cycles" + default 0 - config LPI2C4_FILTSDA - int "I2C master digital glitch filters for SDA input in clock cycles" - default 0 +config LPI2C4_FILTSDA + int "I2C master digital glitch filters for SDA input in clock cycles" + default 0 endif # IMXRT_LPI2C4 endmenu # LPI2C Peripherals +menu "LPSPI Peripherals" + +menuconfig IMXRT_LPSPI1 + bool "LPSPI1" + default n + select IMXRT_LPSPI + +config IMXRT_LPSPI2 + bool "LPSPI2" + default n + select IMXRT_LPSPI + +config IMXRT_LPSPI3 + bool "LPSPI3" + default n + select IMXRT_LPSPI + +config IMXRT_LPSPI4 + bool "LPSPI4" + default n + select IMXRT_LPSPI + +endmenu # LPSPI Peripherals + config IMXRT_SEMC bool "Smart External Memory Controller (SEMC)" default n diff --git a/arch/arm/src/imxrt/Make.defs b/arch/arm/src/imxrt/Make.defs index 31294e5956..afbdb860a7 100644 --- a/arch/arm/src/imxrt/Make.defs +++ b/arch/arm/src/imxrt/Make.defs @@ -138,3 +138,7 @@ endif ifeq ($(CONFIG_IMXRT_LPI2C),y) CHIP_CSRCS += imxrt_lpi2c.c endif + +ifeq ($(CONFIG_IMXRT_LPSPI),y) +CHIP_CSRCS += imxrt_lpspi.c +endif diff --git a/arch/arm/src/imxrt/chip/imxrt105x_pinmux.h b/arch/arm/src/imxrt/chip/imxrt105x_pinmux.h index f1331d1583..4f3b17ab9f 100644 --- a/arch/arm/src/imxrt/chip/imxrt105x_pinmux.h +++ b/arch/arm/src/imxrt/chip/imxrt105x_pinmux.h @@ -624,71 +624,74 @@ #define GPIO_LPI2C3_SCL_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_07_INDEX)) #define GPIO_LPI2C3_SCL_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_22_INDEX) | \ - GPIO_SION_ENABLE | IOMUX_OPENDRAIN | \ - IOMUX_SPEED_MEDIUM | IOMUX_DRIVE_33OHM) + GPIO_SION_ENABLE | IOMUX_OPENDRAIN | IOMUX_SPEED_MEDIUM | IOMUX_DRIVE_33OHM) #define GPIO_LPI2C3_SCL_3 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_00_INDEX)) #define GPIO_LPI2C3_SDA_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_06_INDEX)) #define GPIO_LPI2C3_SDA_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_21_INDEX) | \ - GPIO_SION_ENABLE | IOMUX_OPENDRAIN | \ - IOMUX_SPEED_MEDIUM | IOMUX_DRIVE_33OHM) + GPIO_SION_ENABLE | IOMUX_OPENDRAIN | IOMUX_SPEED_MEDIUM | IOMUX_DRIVE_33OHM) #define GPIO_LPI2C3_SDA_3 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_01_INDEX)) #define GPIO_LPI2C4_SCL_1 (GPIO_PERIPH | GPIO_ALT0 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_12_INDEX)) -#define GPIO_LPI2C4_SCL_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_12_INDEX)) +#define GPIO_LPI2C4_SCL_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_12_INDEX) | \ + GPIO_SION_ENABLE | IOMUX_OPENDRAIN | IOMUX_SPEED_MEDIUM | IOMUX_DRIVE_33OHM) #define GPIO_LPI2C4_SDA_1 (GPIO_PERIPH | GPIO_ALT0 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_13_INDEX)) -#define GPIO_LPI2C4_SDA_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_11_INDEX)) +#define GPIO_LPI2C4_SDA_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_11_INDEX) | \ + GPIO_SION_ENABLE | IOMUX_OPENDRAIN | IOMUX_SPEED_MEDIUM | IOMUX_DRIVE_33OHM) /* Low Power Serial Peripheral Interface (LPSPI) */ -#define GPIO_LPSPI1_PCS0_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_30_INDEX)) -#define GPIO_LPSPI1_PCS0_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_01_INDEX)) -#define GPIO_LPSPI1_PCS1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_31_INDEX)) -#define GPIO_LPSPI1_PCS2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_40_INDEX)) -#define GPIO_LPSPI1_PCS3 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_41_INDEX)) -#define GPIO_LPSPI1_SCK_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_27_INDEX)) -#define GPIO_LPSPI1_SCK_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_00_INDEX)) -#define GPIO_LPSPI1_SDI_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_29_INDEX)) -#define GPIO_LPSPI1_SDI_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_03_INDEX)) -#define GPIO_LPSPI1_SDO_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_28_INDEX)) -#define GPIO_LPSPI1_SDO_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_02_INDEX)) +#define IOMUX_LPSPI (IOMUX_PULL_UP_100K | IOMUX_CMOS_OUTPUT | IOMUX_DRIVE_40OHM | \ + IOMUX_SLEW_FAST | IOMUX_SPEED_MEDIUM) -#define GPIO_LPSPI2_PCS0_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_01_INDEX)) -#define GPIO_LPSPI2_PCS0_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_06_INDEX)) -#define GPIO_LPSPI2_PCS1 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_14_INDEX)) -#define GPIO_LPSPI2_PCS2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_10_INDEX)) -#define GPIO_LPSPI2_PCS3 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_11_INDEX)) -#define GPIO_LPSPI2_SCK_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_00_INDEX)) -#define GPIO_LPSPI2_SCK_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_07_INDEX)) -#define GPIO_LPSPI2_SDI_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_03_INDEX)) -#define GPIO_LPSPI2_SDI_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_09_INDEX)) -#define GPIO_LPSPI2_SDO_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_02_INDEX)) -#define GPIO_LPSPI2_SDO_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_08_INDEX)) +#define GPIO_LPSPI1_PCS0_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_30_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_PCS0_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_01_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_PCS1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_31_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_PCS2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_40_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_PCS3 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_41_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_SCK_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_27_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_SCK_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_00_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_SDI_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_29_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_SDI_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_03_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_SDO_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_28_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI1_SDO_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B0_02_INDEX) | IOMUX_LPSPI) -#define GPIO_LPSPI3_PCS0_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_12_INDEX)) -#define GPIO_LPSPI3_PCS0_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_03_INDEX)) -#define GPIO_LPSPI3_PCS1 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_04_INDEX)) -#define GPIO_LPSPI3_PCS2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_05_INDEX)) -#define GPIO_LPSPI3_PCS3 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_06_INDEX)) -#define GPIO_LPSPI3_SCK_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_15_INDEX)) -#define GPIO_LPSPI3_SCK_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_00_INDEX)) -#define GPIO_LPSPI3_SDI_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_13_INDEX)) -#define GPIO_LPSPI3_SDI_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_02_INDEX)) -#define GPIO_LPSPI3_SDO_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_14_INDEX)) -#define GPIO_LPSPI3_SDO_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_01_INDEX)) +#define GPIO_LPSPI2_PCS0_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_01_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_PCS0_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_06_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_PCS1 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_14_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_PCS2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_10_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_PCS3 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_11_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_SCK_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_00_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_SCK_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_07_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_SDI_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_03_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_SDI_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_09_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_SDO_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_02_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI2_SDO_2 (GPIO_PERIPH | GPIO_ALT4 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_SD_B1_08_INDEX) | IOMUX_LPSPI) -#define GPIO_LPSPI4_PCS0_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_04_INDEX)) -#define GPIO_LPSPI4_PCS0_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_00_INDEX)) -#define GPIO_LPSPI4_PCS1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_03_INDEX)) -#define GPIO_LPSPI4_PCS2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_02_INDEX)) -#define GPIO_LPSPI4_PCS3 (GPIO_PERIPH | GPIO_ALT6 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_11_INDEX)) -#define GPIO_LPSPI4_SCK_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_07_INDEX)) -#define GPIO_LPSPI4_SCK_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_03_INDEX)) -#define GPIO_LPSPI4_SDI_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_05_INDEX)) -#define GPIO_LPSPI4_SDI_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_01_INDEX)) -#define GPIO_LPSPI4_SDO_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_06_INDEX)) -#define GPIO_LPSPI4_SDO_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_02_INDEX)) +#define GPIO_LPSPI3_PCS0_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_12_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_PCS0_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_03_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_PCS1 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_04_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_PCS2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_05_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_PCS3 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_06_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_SCK_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_15_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_SCK_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_00_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_SDI_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_13_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_SDI_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_02_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_SDO_1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_14_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI3_SDO_2 (GPIO_PERIPH | GPIO_ALT7 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_01_INDEX) | IOMUX_LPSPI) -/* Low Power Universal Asynchronous Receiver/Transmitter (LPUART)*/ +#define GPIO_LPSPI4_PCS0_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_04_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_PCS0_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_00_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_PCS1 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_03_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_PCS2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_02_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_PCS3 (GPIO_PERIPH | GPIO_ALT6 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_11_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_SCK_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_07_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_SCK_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_03_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_SDI_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_05_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_SDI_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_01_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_SDO_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_06_INDEX) | IOMUX_LPSPI) +#define GPIO_LPSPI4_SDO_2 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_02_INDEX) | IOMUX_LPSPI) + +/* Low Power Universal Asynchronous Receiver/Transmitter (LPUART) */ #define IOMUX_UART (IOMUX_PULL_UP_100K | IOMUX_CMOS_OUTPUT | IOMUX_DRIVE_40OHM | \ IOMUX_SLEW_FAST | IOMUX_SPEED_MEDIUM | IOMUX_SCHMITT_TRIGGER) @@ -727,9 +730,9 @@ #define GPIO_LPUART5_CTS (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_28_INDEX)) #define GPIO_LPUART5_RTS (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_27_INDEX)) -#define GPIO_LPUART5_RX_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_13_INDEX)) +#define GPIO_LPUART5_RX_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_13_INDEX) | IOMUX_UART) #define GPIO_LPUART5_RX_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_24_INDEX)) -#define GPIO_LPUART5_TX_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_12_INDEX)) +#define GPIO_LPUART5_TX_1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B1_12_INDEX) | IOMUX_UART) #define GPIO_LPUART5_TX_2 (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_23_INDEX)) #define GPIO_LPUART6_CTS (GPIO_PERIPH | GPIO_ALT2 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_30_INDEX)) @@ -773,7 +776,7 @@ #define GPIO_PIT_TRIGGER00 (GPIO_PERIPH | GPIO_ALT6 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_04_INDEX)) -/* Quad Timer (QTimer)*/ +/* Quad Timer (QTimer) */ #define GPIO_QTIMER1_TIMER0 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_00_INDEX)) #define GPIO_QTIMER1_TIMER1 (GPIO_PERIPH | GPIO_ALT1 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_B0_01_INDEX)) diff --git a/arch/arm/src/imxrt/chip/imxrt_lpspi.h b/arch/arm/src/imxrt/chip/imxrt_lpspi.h index 1a77e0a01e..699e4eddf4 100644 --- a/arch/arm/src/imxrt/chip/imxrt_lpspi.h +++ b/arch/arm/src/imxrt/chip/imxrt_lpspi.h @@ -187,7 +187,7 @@ #define LPSPI_SR_REF (1 << 12) /* Bit 12: Receive Error Flag */ #define LPSPI_SR_DMF (1 << 13) /* Bit 13: Data Match Flag */ /* Bits 14-23: Reserved */ - #define LPSPI_SR_REF (1 << 24) /* Bit 24: Module Busy Flag*/ +#define LPSPI_SR_MBF (1 << 24) /* Bit 24: Module Busy Flag */ /* Bits 25-31: Reserved */ /* Interrupt Enable Register */ @@ -310,6 +310,7 @@ /* Bits 24-31: Reserved */ /* Transmit Command Register */ + #define LPSPI_TCR_FRAMESZ_SHIFT (0) /* Bits 0-11: Frame Size */ #define LPSPI_TCR_FRAMESZ_MASK (0xfff << LPSPI_TCR_FRAMESZ_SHIFT) # define LPSPI_TCR_FRAMESZ(n) ((uint32_t)(n) << LPSPI_TCR_FRAMESZ_SHIFT) diff --git a/arch/arm/src/imxrt/imxrt_clockconfig.c b/arch/arm/src/imxrt/imxrt_clockconfig.c index 3ce3f438b0..04b9d1e6c8 100644 --- a/arch/arm/src/imxrt/imxrt_clockconfig.c +++ b/arch/arm/src/imxrt/imxrt_clockconfig.c @@ -212,5 +212,21 @@ void imxrt_clockconfig(void) } #endif +#ifdef CONFIG_IMXRT_LPSPI + /* Set LPSPI close source to PLL3 PFD0 */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= CCM_CBCMR_LPSPI_CLK_SEL_MASK; + reg |= CCM_CBCMR_LPSPI_CLK_SEL_PLL3_PFD0; + putreg32(reg, IMXRT_CCM_CBCMR); + + /* Set LPSPI divider to 5 */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= CCM_CBCMR_LPSPI_PODF_MASK; + reg |= CCM_CBCMR_LPSPI_PODF(7); + putreg32(reg, IMXRT_CCM_CBCMR); +#endif + #endif } diff --git a/arch/arm/src/imxrt/imxrt_lpi2c.c b/arch/arm/src/imxrt/imxrt_lpi2c.c index 7d7a6db8ea..f4a726a720 100644 --- a/arch/arm/src/imxrt/imxrt_lpi2c.c +++ b/arch/arm/src/imxrt/imxrt_lpi2c.c @@ -409,8 +409,8 @@ static const struct imxrt_lpi2c_config_s imxrt_lpi2c4_config = .busy_idle = CONFIG_LPI2C4_BUSYIDLE, .filtscl = CONFIG_LPI2C4_FILTSCL, .filtsda = CONFIG_LPI2C4_FILTSDA, - .scl_pin = GPIO_LPI2C3_SCL, - .sda_pin = GPIO_LPI2C3_SDA, + .scl_pin = GPIO_LPI2C4_SCL, + .sda_pin = GPIO_LPI2C4_SDA, #ifndef CONFIG_I2C_SLAVE .mode = LPI2C_MASTER, #else @@ -1211,7 +1211,7 @@ static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) /* Continue with either sending or reading data */ - /* Check if there is more bytes to send */ + /* Check if there is more butes to send */ if (((priv->flags & I2C_M_READ) == 0) && (status & LPI2C_MSR_TDF) != 0) { @@ -1239,8 +1239,8 @@ static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) { imxrt_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); - /* No interrupts or context switches should occur in the following - * sequence. Otherwise, additional bytes may be sent by the device. + /* No interrupts or contex switches should occur in the following + * seuence. Otherwise, additional bytes may be sent by the device. */ #ifdef CONFIG_I2C_POLLED diff --git a/arch/arm/src/imxrt/imxrt_lpi2c.h b/arch/arm/src/imxrt/imxrt_lpi2c.h index 7fb0b1f705..af11de8d34 100644 --- a/arch/arm/src/imxrt/imxrt_lpi2c.h +++ b/arch/arm/src/imxrt/imxrt_lpi2c.h @@ -46,10 +46,6 @@ #include "chip.h" #include "chip/imxrt_lpi2c.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -67,7 +63,7 @@ * Port number (for hardware that has multiple I2C interfaces) * * Returned Value: - * Valid I2C device structure reference on succcess; a NULL on failure + * Valid I2C device structure reference on success; a NULL on failure * ****************************************************************************/ diff --git a/arch/arm/src/imxrt/imxrt_lpspi.c b/arch/arm/src/imxrt/imxrt_lpspi.c new file mode 100644 index 0000000000..a83be6d338 --- /dev/null +++ b/arch/arm/src/imxrt/imxrt_lpspi.c @@ -0,0 +1,1685 @@ +/************************************************************************************ + * arm/arm/src/imxrt/imxrt_lpspi.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Authors: Teodora Kireva + * Ivan Ucherdzhiev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * The external functions, imxrt_lpspi1/2/3/4select and imxrt_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 imxrt_lpspi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including imxrt_lpspibus_initialize()) + * are provided by common IMXRT logic. To use this common SPI logic on your + * board: + * + * 1. Provide logic in imxrt_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide imxrt_lpspi1/2/3/4select() and imxrt_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 imxrt_lpspibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by imxrt_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). + * + ****************************************************c*******************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "cache.h" +#include "chip.h" + +#include "imxrt_lpspi.h" +#include "imxrt_gpio.h" +#include "chip/imxrt_pinmux.h" +#include "chip/imxrt_lpspi.h" +#include "chip/imxrt_ccm.h" +#include "chip/imxrt_periphclks.h" + +#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \ + defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Configuration ********************************************************************/ + +/* SPI interrupts */ + +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS +# error "Interrupt driven SPI not yet supported" +#endif + +#if defined(CONFIG_IMXRT_LPSPI_DMA) +# error "DMA mode is not yet supported" +#endif + +/* Can't have both interrupt driven SPI and SPI DMA */ + +#if defined(CONFIG_IMXRT_LPSPI_INTERRUPTS) && defined(CONFIG_IMXRT_LPSPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for SPI" +#endif + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +struct imxrt_lpspidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ + uint32_t spiclock; /* Clocking for the SPI module */ +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif + sem_t exclsem; /* 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 */ +}; + +enum imxrt_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 imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset); +static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t value); +static inline uint16_t imxrt_lpspi_readword(FAR struct imxrt_lpspidev_s *priv); +static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, + uint16_t byte); +static inline bool imxrt_lpspi_9to16bitmode(FAR struct imxrt_lpspidev_s *priv); +static inline void imxrt_lpspi_master_set_delays(FAR struct imxrt_lpspidev_s + *priv, uint32_t delay_ns, + enum imxrt_delay_e type); +static inline void imxrt_lpspi_master_set_delay_scaler(FAR struct + imxrt_lpspidev_s *priv, + uint32_t scaler, + enum imxrt_delay_e type); + +/* SPI methods */ + +static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock); +static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency); +static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, + enum spi_mode_e mode); +static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, + imxrt_lpspi_hwfeatures_t features); +#endif +static uint16_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint16_t wd); +static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, FAR void *rxbuffer, + size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords); +static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, + size_t nwords); +#endif + +/* Initialization */ + +static void imxrt_lpspi_bus_initialize(FAR struct imxrt_lpspidev_s *priv); + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +#ifdef CONFIG_IMXRT_LPSPI1 +static const struct spi_ops_s g_spi1ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi1select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi1status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi1cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi1register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi1dev = +{ + .spidev = + { + &g_spi1ops + }, + .spibase = IMXRT_LPSPI1_BASE, + .spiclock = CCM_CCGR_LPSPI1, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI1, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI1_RX, + .txch = DMAMAP_LPSPI1_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +static const struct spi_ops_s g_spi2ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi2select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi2status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi2cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi2register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi2dev = +{ + .spidev = + { + &g_spi2ops + }, + .spibase = IMXRT_LPSPI2_BASE, + .spiclock = CCM_CCGR_LPSPI2, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI2, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI2_RX, + .txch = DMAMAP_LPSPI2_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +static const struct spi_ops_s g_spi3ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi3select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi3status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi3cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi3register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi3dev = +{ + .spidev = + { + &g_spi3ops + }, + .spibase = IMXRT_LPSPI3_BASE, + .spiclock = CCM_CCGR_LPSPI3, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI3, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +static const struct spi_ops_s g_spi4ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi4select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi4status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi4cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi4register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi4dev = +{ + .spidev = + { + &g_spi4ops + }, + .spibase = IMXRT_LPSPI4_BASE, + .spiclock = CCM_CCGR_LPSPI4, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI4, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI1_RX, + .txch = DMAMAP_LPSPI1_TX, +#endif +}; +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: imxrt_lpspi_getreg8 + * + * 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 8-bit register + * + ************************************************************************************/ + +static inline uint8_t imxrt_lpspi_getreg8(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset) +{ + return getreg8(priv->spibase + offset); +} + +/************************************************************************************ + * Name: imxrt_lpspi_putreg8 + * + * Description: + * Write a 8-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 8-bit value to be written + * + ************************************************************************************/ + +static inline void imxrt_lpspi_putreg8(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint8_t value) +{ + putreg8(value, priv->spibase + offset); +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset) +{ + return getreg32(priv->spibase + offset); +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t value) +{ + putreg32(value, priv->spibase + offset); +} + +/************************************************************************************ + * Name: imxrt_lpspi_readword + * + * Description: + * Read one word from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * word as read + * + ************************************************************************************/ + +static inline uint16_t imxrt_lpspi_readword(FAR struct imxrt_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return (uint16_t) imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RDR_OFFSET); +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, + uint16_t word) +{ + /* Wait until the transmit buffer is empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & LPSPI_SR_TDF) == 0); + + /* Then send the word */ + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_TDR_OFFSET, word); +} + +/************************************************************************************ + * Name: imxrt_lpspi_readbyte + * + * Description: + * Read one byte from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Byte as read + * + ************************************************************************************/ + +static inline uint8_t imxrt_lpspi_readbyte(FAR struct imxrt_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return imxrt_lpspi_getreg8(priv, IMXRT_LPSPI_RDR_OFFSET); +} + +/************************************************************************************ + * Name: imxrt_lpspi_writebyte + * + * Description: + * Write one 8-bit frame to the SPI FIFO + * + * Input Parameters: + * priv - Device-specific state data + * byte - Byte to send + * + * Returned Value: + * None + * + ************************************************************************************/ + +static inline void imxrt_lpspi_writebyte(FAR struct imxrt_lpspidev_s *priv, + uint8_t byte) +{ + /* Wait until the transmit buffer is empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & LPSPI_SR_TDF) == 0); + + /* Then send the byte */ + + imxrt_lpspi_putreg8(priv, IMXRT_LPSPI_TDR_OFFSET, byte); +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_9to16bitmode(FAR struct imxrt_lpspidev_s *priv) +{ + bool ret; + + if (((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & + LPSPI_TCR_FRAMESZ_MASK) + 1) < 9) + { + ret = false; + } + else + { + ret = true; + } + + return ret; +} + +/************************************************************************************ + * Name: imxrt_lpspi_modifyreg + * + * 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 imxrt_lpspi_modifyreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t clrbits, + uint32_t setbits) +{ + modifyreg32(priv->spibase + offset, clrbits, setbits); +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_master_set_delay_scaler(FAR struct imxrt_lpspidev_s *priv, + uint32_t scaler, + enum imxrt_delay_e type) +{ + switch (type) + { + case LPSPI_PCS_TO_SCK: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_PCSSCK_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_PCSSCK(scaler)); + break; + + case LPSPI_LAST_SCK_TO_PCS: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKPCS_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_SCKPCS(scaler)); + break; + + case LPSPI_BETWEEN_TRANSFER: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, LPSPI_CCR_DBT_MASK, + 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_DBT(scaler)); + break; + } +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_master_set_delays(FAR struct imxrt_lpspidev_s *priv, + uint32_t delay_ns, + enum imxrt_delay_e type) +{ + uint32_t pll3_div; + uint32_t pll_freq; + uint32_t src_freq; + uint64_t real_delay; + uint64_t best_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; + + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; + + /* Assumption this formula will work only if the LPSPI Clock Source is PLL3 + * PFD0 so check if LPSPI clock source is set to 1 (PLL3 PFD0) in CCM_CBCMR + * register bits 4-5 + */ + + src_freq = pll_freq / + ((getreg32(IMXRT_CCM_ANALOG_PFD_480) & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); + src_freq *= 18; + src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> + CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; + + clock_div_prescaler = src_freq / + (1 << ((imxrt_lpspi_getreg32(priv, IMXRT_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; + + /* Calculate the maximum delay */ + + best_delay = 1000000000U; + + /* based on DBT+2, or 255 + 2 */ + + best_delay *= 257; + best_delay /= 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; + + /* Calculate the maximum delay */ + + best_delay = 1000000000U; + + /* Based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */ + + best_delay *= 256; + best_delay /= 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) + { + imxrt_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; + best_delay = real_delay; + } + } + } + + imxrt_lpspi_master_set_delay_scaler(priv, best_scaler, type); + } +} + +/************************************************************************************ + * Name: imxrt_lpspi_lock + * + * Description: + * On SPI busses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the busses 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 buss 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 imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + int ret; + + if (lock) + { + /* Take the semaphore (perhaps waiting) */ + + do + { + ret = nxsem_wait(&priv->exclsem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); + } + else + { + (void)nxsem_post(&priv->exclsem); + ret = OK; + } + + return ret; +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + + uint32_t men; + uint32_t regval; + uint32_t pll_freq; + uint32_t pll3_div; + 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 = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, LPSPI_CR_MEN, 0); + } + + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; + + /* Assumption this formula will work only if the LPSPI Clock Source is + * PLL3 PFD0 * so check if LPSPI clock source is set to 1 (PLL3 PFD0) in + * CCM_CBCMR register bits 4-5 + */ + + src_freq = pll_freq / + ((getreg32(IMXRT_CCM_ANALOG_PFD_480) & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); + src_freq *= 18; + src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> + CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; + + 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 */ + + regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CCR_OFFSET); + regval &= ~LPSPI_CCR_SCKDIV_MASK; + regval |= LPSPI_CCR_SCKDIV(best_scaler); + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CCR_OFFSET, regval); + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + LPSPI_TCR_PRESCALE_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, 0, + LPSPI_TCR_PRESCALE(best_prescaler)); + + priv->frequency = frequency; + priv->actual = best_frequency; + + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_PCS_TO_SCK); + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_LAST_SCK_TO_PCS); + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_BETWEEN_TRANSFER); + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); + } + } + + return priv->actual; +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + uint32_t men; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, LPSPI_CR_MEN, 0); + } + + 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; + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, clrbits, setbits); + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET) & + LPSPI_RSR_RXEMPTY) != LPSPI_RSR_RXEMPTY) + { + /* Flush SPI read FIFO */ + + imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET); + } + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); + } + } +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t regval; + uint32_t men; + int savbits = nbits; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + + if (nbits < 2 || nbits > 4096) + { + return; + } + + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, LPSPI_CR_MEN, 0); + } + + regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET); + regval &= ~LPSPI_TCR_FRAMESZ_MASK; + regval |= LPSPI_TCR_FRAMESZ(nbits - 1); + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_TCR_OFFSET, regval); + + /* Save the selection so the subsequence re-configurations will be faster */ + + priv->nbits = savbits; /* nbits has been clobbered... save the signed + * value. */ + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); + } + } +} + +/**************************************************************************** + * Name: imxrt_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 imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, + imxrt_lpspi_hwfeatures_t features) +{ +#ifdef CONFIG_SPI_BITORDER + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + int savbits = nbits; + + 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; + } + + imxrt_lpspi_modigyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, clrbits, setbits); + + /* Other H/W features are not supported */ + + return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS; +#else + return -ENOSYS; +#endif +} +#endif + +/************************************************************************************ + * Name: imxrt_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 uint16_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint16_t wd) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t regval; + uint16_t ret; + + DEBUGASSERT(priv && priv->spibase); + + imxrt_lpspi_writeword(priv, (uint32_t) wd); + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & LPSPI_SR_RDF) != + LPSPI_SR_RDF); + + ret = imxrt_lpspi_readword(priv); + + /* Check and clear any error flags (Reading from the SR clears the error + * flags). + */ + + regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET); + + spiinfo("Sent: %04x Return: %04x Status: %02x\n", wd, ret, regval); + + UNUSED(regval); + return ret; +} + +/************************************************************************************ + * Name: imxrt_lpspi_exchange (no DMA). aka imxrt_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 exchaned 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_IMXRT_LPSPI_DMA) || defined(CONFIG_IMXRT_DMACAPABLE) +#if !defined(CONFIG_IMXRT_LPSPI_DMA) +static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, FAR void *rxbuffer, + size_t nwords) +#else +static void imxrt_lpspi_exchange_nodma(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#endif +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + DEBUGASSERT(priv && priv->spibase); + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* 8- or 16-bit mode? */ + + if (imxrt_lpspi_9to16bitmode(priv)) + { + /* 16-bit mode */ + + const uint16_t *src = (const uint16_t *)txbuffer; + uint16_t *dest = (uint16_t *) 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 = imxrt_lpspi_send(dev, word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } + else + { + /* 8-bit mode */ + + const uint8_t *src = (const uint8_t *)txbuffer; + uint8_t *dest = (uint8_t *) 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) imxrt_lpspi_send(dev, (uint16_t) word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } +} +#endif /* !CONFIG_IMXRT_LPSPI_DMA || CONFIG_IMXRT_DMACAPABLE */ + +/**************************************************************************** + * Name: imxrt_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 imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); + return imxrt_lpspi_exchange(dev, txbuffer, NULL, nwords); +} +#endif + +/************************************************************************************ + * Name: imxrt_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 recieve 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 imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, + size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); + return imxrt_lpspi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/************************************************************************************ + * Name: imxrt_lpspi_clock_enable + * + * Description: + * Ungate LPSPI clock + * + ************************************************************************************/ + +void imxrt_lpspi_clock_enable(uint32_t base) +{ + if (base == IMXRT_LPSPI1_BASE) + { + imxrt_clockall_lpspi1(); + } + else if (base == IMXRT_LPSPI2_BASE) + { + imxrt_clockall_lpspi2(); + } + else if (base == IMXRT_LPSPI3_BASE) + { + imxrt_clockall_lpspi3(); + } + else if (base == IMXRT_LPSPI4_BASE) + { + imxrt_clockall_lpspi4(); + } +} + +/************************************************************************************ + * Name: imxrt_lpspi_clock_disable + * + * Description: + * Gate LPSPI clock + * + ************************************************************************************/ + +void imxrt_lpspi_clock_disable(uint32_t base) +{ + if (base == IMXRT_LPSPI1_BASE) + { + imxrt_clockoff_lpspi1(); + } + else if (base == IMXRT_LPSPI2_BASE) + { + imxrt_clockoff_lpspi2(); + } + else if (base == IMXRT_LPSPI3_BASE) + { + imxrt_clockoff_lpspi3(); + } + else if (base == IMXRT_LPSPI4_BASE) + { + imxrt_clockoff_lpspi4(); + } +} + +/************************************************************************************ + * Name: imxrt_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 imxrt_lpspi_bus_initialize(struct imxrt_lpspidev_s *priv) +{ + uint32_t reg = 0; + + /* Enable power and reset the peripheral */ + + imxrt_lpspi_clock_enable(priv->spibase); + + /* Reset to known status */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_RST); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_RTF | LPSPI_CR_RRF); + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0x00); + + /* Set LPSPI to master */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_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 = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET); + reg &= ~(LPSPI_CFGR1_OUTCFG | LPSPI_CFGR1_PINCFG_MASK | LPSPI_CFGR1_NOSTALL); + reg |= LPSPI_CFGR1_OUTCFG_RETAIN | LPSPI_CFGR1_PINCFG_SIN_SOUT; + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, reg); + + /* Set frequency and delay times */ + + imxrt_lpspi_setfrequency((FAR struct spi_dev_s *)priv, 400000); + + /* Set default watermarks */ + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_FCR_OFFSET, + LPSPI_FCR_TXWATER(0) | LPSPI_FCR_RXWATER(0)); + + /* Set Transmit Command Register */ + + imxrt_lpspi_setbits((FAR struct spi_dev_s *)priv, 8); + + imxrt_lpspi_setmode((FAR struct spi_dev_s *)priv, SPIDEV_MODE0); + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + nxsem_init(&priv->exclsem, 0, 1); + + /* Enable LPSPI */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: imxrt_lpspibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has mutiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ************************************************************************************/ + +FAR struct spi_dev_s *imxrt_lpspibus_initialize(int bus) +{ + FAR struct imxrt_lpspidev_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + +#ifdef CONFIG_IMXRT_LPSPI1 + if (bus == 1) + { + /* Select SPI1 */ + + priv = &g_lpspi1dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN) == 0) + { + /* Configure SPI1 pins: SCK, MISO, and MOSI */ + + (void)imxrt_config_gpio(GPIO_LPSPI1_SCK); + (void)imxrt_config_gpio(GPIO_LPSPI1_MISO); + (void)imxrt_config_gpio(GPIO_LPSPI1_MOSI); + + putreg32(0x1, IMXRT_INPUT_LPSPI1_SCK); + putreg32(0x1, IMXRT_INPUT_LPSPI1_SDI); + putreg32(0x1, IMXRT_INPUT_LPSPI1_SDO); + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI2 + if (bus == 2) + { + /* Select SPI2 */ + + priv = &g_lpspi2dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN) == 0) + { + /* Configure SPI2 pins: SCK, MISO, and MOSI */ + + (void)imxrt_config_gpio(GPIO_LPSPI2_SCK); + (void)imxrt_config_gpio(GPIO_LPSPI2_MISO); + (void)imxrt_config_gpio(GPIO_LPSPI2_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI3 + if (bus == 3) + { + /* Select SPI3 */ + + priv = &g_lpspi3dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN) == 0) + { + /* Configure SPI3 pins: SCK, MISO, and MOSI */ + + (void)imxrt_config_gpio(GPIO_LPSPI3_SCK); + (void)imxrt_config_gpio(GPIO_LPSPI3_MISO); + (void)imxrt_config_gpio(GPIO_LPSPI3_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI4 + if (bus == 4) + { + /* Select SPI4 */ + + priv = &g_lpspi4dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN) == 0) + { + /* Configure SPI4 pins: SCK, MISO, and MOSI */ + + (void)imxrt_config_gpio(GPIO_LPSPI4_SCK); + (void)imxrt_config_gpio(GPIO_LPSPI4_MISO); + (void)imxrt_config_gpio(GPIO_LPSPI4_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif + { + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + return NULL; + } + + leave_critical_section(flags); + + return (FAR struct spi_dev_s *)priv; +} + +#endif /* CONFIG_IMXRT_LPSPI1 */ diff --git a/arch/arm/src/imxrt/imxrt_lpspi.h b/arch/arm/src/imxrt/imxrt_lpspi.h new file mode 100644 index 0000000000..01368f90c8 --- /dev/null +++ b/arch/arm/src/imxrt/imxrt_lpspi.h @@ -0,0 +1,187 @@ +/************************************************************************************ + * arch/arm/src/imxrt/imxrt_lpspi.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Authors: Ivan Ucherdzhiev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_IMXRT_IMXRT_LPSPI_H +#define __ARCH_ARM_SRC_IMXRT_IMXRT_LPSPI_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include + +#include + +#include "chip.h" +#include "chip/imxrt_lpspi.h" + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#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: imxrt_lpspibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * bus number (for hardware that has mutiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on succcess; a NULL on failure + * + ************************************************************************************/ + +FAR struct spi_dev_s *imxrt_lpspibus_initialize(int bus); + +/************************************************************************************ + * Name: imxrt_lpspi1/2/...select and imxrt_lpspi1/2/...status + * + * Description: + * The external functions, imxrt_lpspi1/2/...select, imxrt_lpspi1/2/...status, and + * imxrt_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 imxrt_lpspibus_initialize()) are provided by common IMXRT logic. To use this + * common SPI logic on your board: + * + * 1. Provide logic in imxrt_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide imxrt_lpspi1/2/...select() and imxrt_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 imxrt_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 imxrt_lpspibus_initialize() in your low level application + * initialization logic + * 5. The handle returned by imxrt_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). + * + ************************************************************************************/ + +#ifdef CONFIG_IMXRT_LPSPI1 +void imxrt_lpspi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t imxrt_lpspi1status(FAR struct spi_dev_s *dev, uint32_t devid); +int imxrt_lpspi1cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +void imxrt_lpspi2select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t imxrt_lpspi2status(FAR struct spi_dev_s *dev, uint32_t devid); +int imxrt_lpspi2cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +void imxrt_lpspi3select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t imxrt_lpspi3status(FAR struct spi_dev_s *dev, uint32_t devid); +int imxrt_lpspi3cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +void imxrt_lpspi4select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t imxrt_lpspi4status(FAR struct spi_dev_s *dev, uint32_t devid); +int imxrt_lpspi4cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif + +/************************************************************************************ + * Name: imxrt_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 +#ifdef CONFIG_IMXRT_LPSPI1 +int imxrt_lpspi1register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +int imxrt_lpspi2register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +int imxrt_lpspi3register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +int imxrt_lpspi4register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_IMXRT_IMXRT_LPSPI_H */ + diff --git a/configs/imxrt1050-evk/include/board.h b/configs/imxrt1050-evk/include/board.h index 369d73cfac..420fcff559 100644 --- a/configs/imxrt1050-evk/include/board.h +++ b/configs/imxrt1050-evk/include/board.h @@ -194,6 +194,19 @@ #define GPIO_LPI2C3_SDA GPIO_LPI2C3_SDA_2 /* GPIO_AD_B1_01 */ #define GPIO_LPI2C3_SCL GPIO_LPI2C3_SCL_2 /* GPIO_AD_B1_00 */ +/* LPSPI + * + * Arduino Connector + * + * J24 D09 GPIO_AD_B0_02 LPSPI3_SDI + * J24 D14 GPIO_AD_B0_01 LPSPI3_SDO + * J24 D15 GPIO_AD_B0_00 LPSPI3_SCK + */ + +#define GPIO_LPSPI3_SCK GPIO_LPSPI3_SCK_2 /* GPIO_AD_B0_00 */ +#define GPIO_LPSPI3_MISO GPIO_LPSPI3_SDI_2 /* GPIO_AD_B0_02 */ +#define GPIO_LPSPI3_MOSI GPIO_LPSPI3_SDO_2 /* GPIO_AD_B0_01 */ + /************************************************************************************ * Public Types ************************************************************************************/ diff --git a/configs/imxrt1050-evk/src/Makefile b/configs/imxrt1050-evk/src/Makefile index bccd353bc4..ec30672089 100644 --- a/configs/imxrt1050-evk/src/Makefile +++ b/configs/imxrt1050-evk/src/Makefile @@ -62,4 +62,12 @@ ifeq ($(CONFIG_IMXRT_ENET),y) CSRCS += imxrt_ethernet.c endif +ifeq ($(CONFIG_IMXRT_LPSPI),y) +CSRCS += imxrt_spi.c +endif + +ifeq ($(CONFIG_MMCSD),y) +CSRCS += imxrt_mmcsd.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/imxrt1050-evk/src/imxrt1050-evk.h b/configs/imxrt1050-evk/src/imxrt1050-evk.h index f673e049d9..4819a872ef 100644 --- a/configs/imxrt1050-evk/src/imxrt1050-evk.h +++ b/configs/imxrt1050-evk/src/imxrt1050-evk.h @@ -88,8 +88,8 @@ * The IMXRT board has one external user button * * 1. SW8 (IRQ88) GPIO5-00 - * */ + #define IOMUX_SW8 (IOMUX_SLEW_FAST | IOMUX_DRIVE_50OHM | \ IOMUX_SPEED_MEDIUM | IOMUX_PULL_UP_100K | \ _IOMUX_PULL_ENABLE) @@ -121,6 +121,28 @@ #define GPIO_ENET_RST (GPIO_OUTPUT | GPIO_OUTPUT_ZERO | \ GPIO_PORT1 | GPIO_PIN9 | IOMUX_ENET_RST) +/* LPSPI1 CS: GPIO_SD_B0_01 */ + +#define IOMUX_LPSPI1_CS (IOMUX_SLEW_FAST | IOMUX_DRIVE_50OHM | \ + IOMUX_SPEED_MEDIUM | IOMUX_PULL_UP_100K | \ + _IOMUX_PULL_ENABLE) +#define GPIO_LPSPI1_CS (GPIO_OUTPUT | GPIO_OUTPUT_ONE | \ + GPIO_PORT3 | GPIO_PIN13 | IOMUX_LPSPI1_CS) + +#define IOMUX_MMCSD_EN (IOMUX_SLEW_FAST | IOMUX_DRIVE_50OHM | \ + IOMUX_SPEED_MEDIUM | IOMUX_PULL_UP_100K | \ + _IOMUX_PULL_ENABLE) +#define GPIO_MMCSD_EN (GPIO_OUTPUT | GPIO_OUTPUT_ZERO | \ + GPIO_PORT3 | GPIO_PIN2 | IOMUX_MMCSD_EN) + +/* LPSPI3 CS: GPIO_AD_B0_03 */ + +#define IOMUX_LPSPI3_CS (IOMUX_SLEW_FAST | IOMUX_DRIVE_50OHM | \ + IOMUX_SPEED_MEDIUM | IOMUX_PULL_UP_100K | \ + _IOMUX_PULL_ENABLE) +#define GPIO_LPSPI3_CS (GPIO_OUTPUT | GPIO_OUTPUT_ONE | \ + GPIO_PORT1 | GPIO_PIN3 | IOMUX_LPSPI3_CS) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -135,6 +157,17 @@ * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: imxrt_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the imxrt1050-evk + * board. + * + ****************************************************************************/ + +void weak_function imxrt_spidev_initialize(void); + /**************************************************************************** * Name: imxrt_bringup * diff --git a/configs/imxrt1050-evk/src/imxrt_bringup.c b/configs/imxrt1050-evk/src/imxrt_bringup.c index 1217a93a89..5ddc59fbf7 100644 --- a/configs/imxrt1050-evk/src/imxrt_bringup.c +++ b/configs/imxrt1050-evk/src/imxrt_bringup.c @@ -46,18 +46,29 @@ #include #include #include +#include #include "imxrt1050-evk.h" +#include /* Must always be included last */ + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* Checking needed by MMC/SDCard */ + +#ifdef CONFIG_NSH_MMCSDMINOR +# define MMCSD_MINOR CONFIG_NSH_MMCSDMINOR +#else +# define MMCSD_MINOR 0 +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ -#if defined(CONFIG_I2C_DRIVER) +#if defined(CONFIG_I2C_DRIVER) && defined(CONFIG_IMXRT_LPI2C) static void imxrt_i2c_register(int bus) { FAR struct i2c_master_s *i2c; @@ -114,16 +125,16 @@ int imxrt_bringup(void) #if defined(CONFIG_I2C_DRIVER) && defined(CONFIG_IMXRT_LPI2C1) imxrt_i2c_register(1); #endif -#if defined(CONFIG_I2C_DRIVER) && defined(CONFIG_IMXRT_LPI2C2) - imxrt_i2c_register(2); -#endif -#if defined(CONFIG_I2C_DRIVER) && defined(CONFIG_IMXRT_LPI2C3) - imxrt_i2c_register(3); -#endif -#if defined(CONFIG_I2C_DRIVER) && defined(CONFIG_IMXRT_LPI2C4) - imxrt_i2c_register(4); -#endif +#ifdef CONFIG_MMCSD + imxrt_spidev_initialize(); + + ret = imxrt_mmcsd_initialize(MMCSD_MINOR); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize SD slot %d: %d\n", ret); + } +#endif UNUSED(ret); return OK; diff --git a/configs/imxrt1050-evk/src/imxrt_mmcsd.c b/configs/imxrt1050-evk/src/imxrt_mmcsd.c new file mode 100644 index 0000000000..5363f62ef7 --- /dev/null +++ b/configs/imxrt1050-evk/src/imxrt_mmcsd.c @@ -0,0 +1,130 @@ +/***************************************************************************** + * configs/imxrt/src/imxrt_mmcsd.c + * + * Copyright (C) 2018 Greg Nutt. All rights reserved. + * Author: ivan Ucherdzhiev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/***************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "up_arch.h" +#include "chip.h" +#include "imxrt_lpspi.h" + +/***************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_IMXRT_LPSPI1 +# error "SD driver requires CONFIG_IMXRT_LPSPI1 to be enabled" +#endif + +#ifdef CONFIG_DISABLE_MOUNTPOINT +# error "SD driver requires CONFIG_DISABLE_MOUNTPOINT to be disabled" +#endif + +/***************************************************************************** + * Private Definitions + ****************************************************************************/ + +static const int SD_SPI_PORT = CONFIG_NSH_MMCSDSPIPORTNO; /* SD is connected to SPI1 port */ +static const int SD_SLOT_NO = 0; /* There is only one SD slot */ + +/***************************************************************************** + * Private Functions + ****************************************************************************/ + +/* NOTE: We are using a SDCard adapter/module without Card Detect pin! + * Then we don't need to Card Detect callback here. + */ + +/***************************************************************************** + * Public Functions + ****************************************************************************/ + +/***************************************************************************** + * Name: imxrt_spi1register + * + * Description: + * Registers media change callback + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LPSPI1 +int imxrt_lpspi1register(struct spi_dev_s *dev, spi_mediachange_t callback, + void *arg) +{ + spiinfo("INFO: Registering spi1 device\n"); + return OK; +} +#endif + +/***************************************************************************** + * Name: imxrt_mmcsd_initialize + * + * Description: + * Initialize SPI-based SD card and card detect thread. + ****************************************************************************/ + +int imxrt_mmcsd_initialize(int minor) +{ + struct spi_dev_s *spi; + int rv; + + mcinfo("INFO: Initializing mmcsd card\n"); + + spi = imxrt_lpspibus_initialize(SD_SPI_PORT); + if (spi == NULL) + { + mcerr("ERROR: Failed to initialize SPI port %d\n", SD_SPI_PORT); + return -ENODEV; + } + + rv = mmcsd_spislotinitialize(minor, SD_SLOT_NO, spi); + if (rv < 0) + { + mcerr("ERROR: Failed to bind SPI port %d to SD slot %d\n", + SD_SPI_PORT, SD_SLOT_NO); + return rv; + } + + spiinfo("INFO: mmcsd card has been initialized successfully\n"); + return OK; +} diff --git a/configs/imxrt1050-evk/src/imxrt_spi.c b/configs/imxrt1050-evk/src/imxrt_spi.c new file mode 100644 index 0000000000..19203a2c3d --- /dev/null +++ b/configs/imxrt1050-evk/src/imxrt_spi.c @@ -0,0 +1,217 @@ +/************************************************************************************ + * configs/imxrt1050-evk/src/imxrt_spi.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Ivan Ucherdzhiev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "up_arch.h" + +#include "imxrt_config.h" +#include "imxrt_lpspi.h" +#include "imxrt_gpio.h" +#include "imxrt1050-evk.h" + +#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \ + defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4) + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: imxrt_spidev_initialize + * + * Description: + * Called to configure SPI chip select GPIO pins for the imxrt1050-evk board. + * + ************************************************************************************/ + +void weak_function imxrt_spidev_initialize(void) +{ +#ifdef CONFIG_IMXRT_LPSPI1 + (void)imxrt_config_gpio(GPIO_LPSPI1_CS); /* LPSPI1 chip select */ + (void)imxrt_config_gpio(GPIO_MMCSD_EN); +#endif +#ifdef CONFIG_IMXRT_LPSPI3 + (void)imxrt_config_gpio(GPIO_LPSPI3_CS); /* LPSPI3 chip select */ +#endif +} + +/**************************************************************************** + * Name: imxrt_lpspi1/2/3select and imxrt_lpspi1/2/3status + * + * Description: + * The external functions, imxrt_lpspi1/2/3select and imxrt_lpspi1/2/3status must be + * provided by board-specific logic. They are implementations of the select + * and status methods of the SPI interface defined by struct spi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including imxrt_lpspibus_initialize()) + * are provided by common STM32 logic. To use this common SPI logic on your + * board: + * + * 1. Provide logic in imxrt_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide imxrt_lpspi1/2/3select() and imxrt_lpspi1/2/3status() 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 imxrt_lpspibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by imxrt_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). + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LPSPI1 +void imxrt_lpspi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + + imxrt_gpio_write(GPIO_LPSPI1_CS, !selected); +} + +uint8_t imxrt_lpspi1status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +void imxrt_lpspi2select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + + imxrt_gpio_write(GPIO_LPSPI2_CS, !selected); +} + +uint8_t imxrt_lpspi2status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +void imxrt_lpspi3select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + + imxrt_gpio_write(GPIO_LPSPI3_CS, !selected); +} + +uint8_t imxrt_lpspi3status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +void imxrt_lpspi4select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + + imxrt_gpio_write(GPIO_LPSPI4_CS, !selected); +} + +uint8_t imxrt_lpspi4status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi1cmddata + * + * Description: + * Set or clear the SH1101A A0 or SD1306 D/C n bit to select data (true) + * or command (false). This function must be provided by platform-specific + * logic. This is an implementation of the cmddata method of the SPI + * interface defined by struct spi_ops_s (see include/nuttx/spi/spi.h). + * + * Input Parameters: + * + * spi - SPI device that controls the bus the device that requires the CMD/ + * DATA selection. + * devid - If there are multiple devices on the bus, this selects which one + * to select cmd or data. NOTE: This design restricts, for example, + * one one SPI display per SPI bus. + * cmd - true: select command; false: select data + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CMDDATA +#ifdef CONFIG_IMXRT_LPSPI1 +int imxrt_lpspi1cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +int imxrt_lpspi2cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +int imxrt_lpspi3cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +int imxrt_lpspi4cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif +#endif /* CONFIG_SPI_CMDDATA */ + +#endif /* CONFIG_IMXRT_LPSPI1 || CONFIG_IMXRT_LPSPI2 */