diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig index b8dbbaa895c..e6425fda5a1 100644 --- a/arch/arm/src/stm32h5/Kconfig +++ b/arch/arm/src/stm32h5/Kconfig @@ -366,6 +366,30 @@ config STM32H5_LPUART1 select ARCH_HAVE_SERIAL_TERMIOS select STM32H5_USART +config STM32H5_I2C + bool + default n + +config STM32H5_I2C1 + bool "I2C1" + default n + select STM32H5_I2C + +config STM32H5_I2C2 + bool "I2C2" + default n + select STM32H5_I2C + +config STM32H5_I2C3 + bool "I2C3" + default n + select STM32H5_I2C + +config STM32H5_I2C4 + bool "I2C4" + default n + select STM32H5_I2C + endmenu @@ -1061,4 +1085,212 @@ config STM32H5_NO_PHY endmenu # Ethernet MAC Configuration + +menu "I2C Configuration" + depends on STM32H5_I2C + +menu "Clock Selection" + +choice + depends on STM32H5_I2C1 + prompt "I2C1 Input Clock Selection" + default STM32H5_I2C1_CLK_PCLK1 +config STM32H5_I2C1_CLK_CSI + bool "CSI" +config STM32H5_I2C1_CLK_HSI + bool "HSI" +config STM32H5_I2C1_CLK_PCLK1 + bool "PCLK1" +config STM32H5_I2C1_CLK_PLL3R + bool "PLL3R" +endchoice +choice + depends on STM32H5_I2C2 + prompt "I2C2 Input Clock Selection" + default STM32H5_I2C2_CLK_PCLK1 +config STM32H5_I2C2_CLK_CSI + bool "CSI" +config STM32H5_I2C2_CLK_HSI + bool "HSI" +config STM32H5_I2C2_CLK_PCLK1 + bool "PCLK1" +config STM32H5_I2C2_CLK_PLL3R + bool "PLL3R" +endchoice +choice + depends on STM32H5_I2C3 + prompt "I2C3 Input Clock Selection" + default STM32H5_I2C3_CLK_PCLK3 +config STM32H5_I2C3_CLK_CSI + bool "CSI" +config STM32H5_I2C3_CLK_HSI + bool "HSI" +config STM32H5_I2C3_CLK_PCLK3 + bool "PCLK3" +config STM32H5_I2C3_CLK_PLL3R + bool "PLL3R" +endchoice +choice + depends on STM32H5_I2C4 + prompt "I2C4 Input Clock Selection" + default STM32H5_I2C4_CLK_PCLK3 +config STM32H5_I2C4_CLK_CSI + bool "CSI" +config STM32H5_I2C4_CLK_HSI + bool "HSI" +config STM32H5_I2C4_CLK_PCLK3 + bool "PCLK3" +config STM32H5_I2C4_CLK_PLL3R + bool "PLL3R" +endchoice + +endmenu + +menu "Rise/Fall Override" +config STM32H5_I2C1_RF_OVERRIDE + bool "I2C1" + default n + depends on STM32H5_I2C1 +config STM32H5_I2C2_RF_OVERRIDE + bool "I2C2" + default n + depends on STM32H5_I2C2 +config STM32H5_I2C3_RF_OVERRIDE + bool "I2C3" + default n + depends on STM32H5_I2C3 +config STM32H5_I2C4_RF_OVERRIDE + bool "I2C4" + default n + depends on STM32H5_I2C4 + +menu "Rise/Fall Values" +config STM32H5_I2C1_RISE + int "I2C1 Rise Time (ns)" + range 0 1000 + default 20 + depends on STM32H5_I2C1_RF_OVERRIDE +config STM32H5_I2C1_FALL + int "I2C1 Fall Time (ns)" + range 0 300 + default 20 + depends on STM32H5_I2C1_RF_OVERRIDE +config STM32H5_I2C2_RISE + int "I2C2 Rise Time (ns)" + range 0 1000 + default 20 + depends on STM32H5_I2C2_RF_OVERRIDE +config STM32H5_I2C2_FALL + int "I2C2 Fall Time (ns)" + range 0 300 + default 20 + depends on STM32H5_I2C2_RF_OVERRIDE +config STM32H5_I2C3_RISE + int "I2C3 Rise Time (ns)" + range 0 1000 + default 20 + depends on STM32H5_I2C3_RF_OVERRIDE +config STM32H5_I2C3_FALL + int "I2C3 Fall Time (ns)" + range 0 300 + default 20 + depends on STM32H5_I2C3_RF_OVERRIDE +config STM32H5_I2C4_RISE + int "I2C4 Rise Time (ns)" + range 0 1000 + default 20 + depends on STM32H5_I2C4_RF_OVERRIDE +config STM32H5_I2C4_FALL + int "I2C4 Fall Time (ns)" + range 0 300 + default 20 + depends on STM32H5_I2C4_RF_OVERRIDE +endmenu +endmenu + +menu "Filtering" + +menu "Digital Filters" +config STM32H5_I2C1_DNF + int "I2C1 Digital Noise Filter" + range 0 15 + default 0 + depends on STM32H5_I2C1 +config STM32H5_I2C2_DNF + int "I2C2 Digital Noise Filter" + range 0 15 + default 0 + depends on STM32H5_I2C2 +config STM32H5_I2C3_DNF + int "I2C3 Digital Noise Filter" + range 0 15 + default 0 + depends on STM32H5_I2C3 +config STM32H5_I2C4_DNF + int "I2C4 Digital Noise Filter" + range 0 15 + default 0 + depends on STM32H5_I2C4 +endmenu + +menu "Analog Filters" +config STM32H5_I2C1_ANFOFF + int "Turn off I2C1 Analog Filter (0=on, 1=off)" + default 1 + range 0 1 + depends on STM32H5_I2C1 + +config STM32H5_I2C2_ANFOFF + int "Turn off I2C2 Analog Filter (0=on, 1=off)" + default 1 + range 0 1 + depends on STM32H5_I2C2 + +config STM32H5_I2C3_ANFOFF + int "Turn off I2C3 Analog Filter (0=on, 1=off)" + default 1 + range 0 1 + depends on STM32H5_I2C3 + +config STM32H5_I2C4_ANFOFF + int "Turn off I2C4 Analog Filter (0=on, 1=off)" + default 1 + range 0 1 + depends on STM32H5_I2C4 +endmenu + +endmenu + +config STM32H5_I2C_DYNTIMEO + bool "Use dynamic timeouts" + default n + depends on STM32H5_I2C + +config STM32H5_I2C_DYNTIMEO_USECPERBYTE + int "Timeout Microseconds per Byte" + default 500 + depends on STM32H5_I2C_DYNTIMEO + +config STM32H5_I2C_DYNTIMEO_STARTSTOP + int "Timeout for Start/Stop (Milliseconds)" + default 1000 + depends on STM32H5_I2C_DYNTIMEO + +config STM32H5_I2CTIMEOSEC + int "Timeout seconds" + default 0 + depends on STM32H5_I2C + +config STM32H5_I2CTIMEOMS + int "Timeout Milliseconds" + default 500 + depends on STM32H5_I2C && !STM32H5_I2C_DYNTIMEO + +config STM32H5_I2CTIMEOTICKS + int "Timeout for Done and Stop (ticks)" + default 500 + depends on STM32H5_I2C && !STM32H5_I2C_DYNTIMEO + +endmenu # "I2C Configuration" + endif # ARCH_CHIP_STM32H5 diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs index 56cce6201a3..1c3e72053fd 100644 --- a/arch/arm/src/stm32h5/Make.defs +++ b/arch/arm/src/stm32h5/Make.defs @@ -44,6 +44,10 @@ ifeq ($(CONFIG_TIMER),y) CHIP_CSRCS += stm32h5_tim_lowerhalf.c endif +ifeq ($(CONFIG_STM32H5_I2C),y) +CHIP_CSRCS += stm32_i2c.c +endif + # Required chip type specific files ifeq ($(CONFIG_STM32H5_STM32H5XXXX),y) diff --git a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h index ee3ea1b1c17..c857bb18e0d 100644 --- a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h +++ b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h @@ -100,6 +100,42 @@ #define GPIO_MCO_0 (GPIO_ALT|GPIO_AF0|GPIO_PORTA|GPIO_PIN8) +/* I2C */ + +#define GPIO_I2C1_SDA_1 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN7) +#define GPIO_I2C1_SDA_2 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN9) +#define GPIO_I2C1_SCL_1 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN6) +#define GPIO_I2C1_SCL_2 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN8) +#define GPIO_I2C1_SMBA_1 (GPIO_ALT|GPIO_AF4|GPIO_PORTB|GPIO_PIN5) + +#define GPIO_I2C2_SDA_1 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN11) +#define GPIO_I2C2_SDA_2 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN12) +#define GPIO_I2C2_SDA_3 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN3) +#define GPIO_I2C2_SDA_4 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTH|GPIO_PIN5) +#define GPIO_I2C2_SCL_1 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN10) +#define GPIO_I2C2_SCL_2 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTF|GPIO_PIN1) +#define GPIO_I2C2_SCL_3 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTH|GPIO_PIN4) +#define GPIO_I2C2_SMBA_1 (GPIO_ALT|GPIO_AF4|GPIO_PORTH|GPIO_PIN6) + +#define GPIO_I2C3_SDA_1 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTC|GPIO_PIN9) +#define GPIO_I2C3_SDA_2 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTH|GPIO_PIN8) +#define GPIO_I2C3_SCL_1 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTA|GPIO_PIN8) +#define GPIO_I2C3_SCL_2 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTH|GPIO_PIN7) +#define GPIO_I2C3_SMBA_1 (GPIO_ALT|GPIO_AF4|GPIO_PORTA|GPIO_PIN9) +#define GPIO_I2C3_SMBA_2 (GPIO_ALT|GPIO_AF4|GPIO_PORTH|GPIO_PIN9) + +#define GPIO_I2C4_SDA_1 (GPIO_ALT|GPIO_AF6|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN7) +#define GPIO_I2C4_SDA_2 (GPIO_ALT|GPIO_AF6|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN9) +#define GPIO_I2C4_SDA_3 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTD|GPIO_PIN13) +#define GPIO_I2C4_SDA_4 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTF|GPIO_PIN15) +#define GPIO_I2C4_SCL_1 (GPIO_ALT|GPIO_AF6|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN6) +#define GPIO_I2C4_SCL_2 (GPIO_ALT|GPIO_AF6|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN8) +#define GPIO_I2C4_SCL_3 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTD|GPIO_PIN12) +#define GPIO_I2C4_SCL_4 (GPIO_ALT|GPIO_AF4|GPIO_OPENDRAIN|GPIO_PORTF|GPIO_PIN5) +#define GPIO_I2C4_SMBA_1 (GPIO_ALT|GPIO_AF6|GPIO_PORTB|GPIO_PIN5) +#define GPIO_I2C4_SMBA_2 (GPIO_ALT|GPIO_AF4|GPIO_PORTD|GPIO_PIN11) +#define GPIO_I2C4_SMBA_3 (GPIO_ALT|GPIO_AF4|GPIO_PORTF|GPIO_PIN13) + /* JTAG */ #define GPIO_JTCK_SWCLK_0 (GPIO_ALT|GPIO_AF0|GPIO_PORTA|GPIO_PIN14) diff --git a/arch/arm/src/stm32h5/hardware/stm32h5xxx_i2c.h b/arch/arm/src/stm32h5/hardware/stm32h5xxx_i2c.h index 5335dd8327a..7e2a9c8df50 100644 --- a/arch/arm/src/stm32h5/hardware/stm32h5xxx_i2c.h +++ b/arch/arm/src/stm32h5/hardware/stm32h5xxx_i2c.h @@ -27,74 +27,74 @@ /* Register Offsets *********************************************************/ -#define STM32_I2CCR1_OFFSET 0x0000 /* Control register 1 (32-bit) */ -#define STM32_I2CCR2_OFFSET 0x0004 /* Control register 2 (32-bit) */ -#define STM32_I2COAR1_OFFSET 0x0008 /* Own address register 1 (16-bit) */ -#define STM32_I2COAR2_OFFSET 0x000c /* Own address register 2 (16-bit) */ -#define STM32_I2CTIMINGR_OFFSET 0x0010 /* Timing register */ -#define STM32_I2CTIMEOUTR_OFFSET 0x0014 /* Timeout register */ -#define STM32_I2CISR_OFFSET 0x0018 /* Interrupt and Status register */ -#define STM32_I2CICR_OFFSET 0x001c /* Interrupt clear register */ -#define STM32_I2CPECR_OFFSET 0x0020 /* Packet error checking register */ -#define STM32_I2CRXDR_OFFSET 0x0024 /* Receive data register */ -#define STM32_I2CTXDR_OFFSET 0x0028 /* Transmit data register */ +#define STM32_I2C_CR1_OFFSET 0x0000 /* Control register 1 (32-bit) */ +#define STM32_I2C_CR2_OFFSET 0x0004 /* Control register 2 (32-bit) */ +#define STM32_I2C_OAR1_OFFSET 0x0008 /* Own address register 1 (16-bit) */ +#define STM32_I2C_OAR2_OFFSET 0x000c /* Own address register 2 (16-bit) */ +#define STM32_I2C_TIMINGR_OFFSET 0x0010 /* Timing register */ +#define STM32_I2C_TIMEOUTR_OFFSET 0x0014 /* Timeout register */ +#define STM32_I2C_ISR_OFFSET 0x0018 /* Interrupt and Status register */ +#define STM32_I2C_ICR_OFFSET 0x001c /* Interrupt clear register */ +#define STM32_I2C_PECR_OFFSET 0x0020 /* Packet error checking register */ +#define STM32_I2C_RXDR_OFFSET 0x0024 /* Receive data register */ +#define STM32_I2C_TXDR_OFFSET 0x0028 /* Transmit data register */ /* Register Addresses *******************************************************/ #if STM32H5_NI2C > 0 -# define STM32_I2C1_CR1 (STM32_I2C1_BASE+STM32_I2CCR1_OFFSET) -# define STM32_I2C1_CR2 (STM32_I2C1_BASE+STM32_I2CCR2_OFFSET) -# define STM32_I2C1_OAR1 (STM32_I2C1_BASE+STM32_I2COAR1_OFFSET) -# define STM32_I2C1_OAR2 (STM32_I2C1_BASE+STM32_I2COAR2_OFFSET) -# define STM32_I2C1_TIMINGR (STM32_I2C1_BASE+STM32_I2CTIMINGR_OFFSET) -# define STM32_I2C1_TIMEOUTR (STM32_I2C1_BASE+STM32_I2CTIMEOUTR_OFFSET) -# define STM32_I2C1_ISR (STM32_I2C1_BASE+STM32_I2CISR_OFFSET) -# define STM32_I2C1_ICR (STM32_I2C1_BASE+STM32_I2CICR_OFFSET) -# define STM32_I2C1_PECR (STM32_I2C1_BASE+STM32_I2CPECR_OFFSET) -# define STM32_I2C1_RXDR (STM32_I2C1_BASE+STM32_I2CRXDR_OFFSET) -# define STM32_I2C1_TXDR (STM32_I2C1_BASE+STM32_I2CTXDR_OFFSET) +# define STM32_I2C1_CR1 (STM32_I2C1_BASE+STM32_I2C_CR1_OFFSET) +# define STM32_I2C1_CR2 (STM32_I2C1_BASE+STM32_I2C_CR2_OFFSET) +# define STM32_I2C1_OAR1 (STM32_I2C1_BASE+STM32_I2C_OAR1_OFFSET) +# define STM32_I2C1_OAR2 (STM32_I2C1_BASE+STM32_I2C_OAR2_OFFSET) +# define STM32_I2C1_TIMINGR (STM32_I2C1_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C1_TIMEOUTR (STM32_I2C1_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C1_ISR (STM32_I2C1_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C1_ICR (STM32_I2C1_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C1_PECR (STM32_I2C1_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C1_RXDR (STM32_I2C1_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C1_TXDR (STM32_I2C1_BASE+STM32_I2C_TXDR_OFFSET) #endif #if STM32H5_NI2C > 1 -# define STM32_I2C2_CR1 (STM32_I2C2_BASE+STM32_I2CCR1_OFFSET) -# define STM32_I2C2_CR2 (STM32_I2C2_BASE+STM32_I2CCR2_OFFSET) -# define STM32_I2C2_OAR1 (STM32_I2C2_BASE+STM32_I2COAR1_OFFSET) -# define STM32_I2C2_OAR2 (STM32_I2C2_BASE+STM32_I2COAR2_OFFSET) -# define STM32_I2C2_TIMINGR (STM32_I2C2_BASE+STM32_I2CTIMINGR_OFFSET) -# define STM32_I2C2_TIMEOUTR (STM32_I2C2_BASE+STM32_I2CTIMEOUTR_OFFSET) -# define STM32_I2C2_ISR (STM32_I2C2_BASE+STM32_I2CISR_OFFSET) -# define STM32_I2C2_ICR (STM32_I2C2_BASE+STM32_I2CICR_OFFSET) -# define STM32_I2C2_PECR (STM32_I2C2_BASE+STM32_I2CPECR_OFFSET) -# define STM32_I2C2_RXDR (STM32_I2C2_BASE+STM32_I2CRXDR_OFFSET) -# define STM32_I2C2_TXDR (STM32_I2C2_BASE+STM32_I2CTXDR_OFFSET) +# define STM32_I2C2_CR1 (STM32_I2C2_BASE+STM32_I2C_CR1_OFFSET) +# define STM32_I2C2_CR2 (STM32_I2C2_BASE+STM32_I2C_CR2_OFFSET) +# define STM32_I2C2_OAR1 (STM32_I2C2_BASE+STM32_I2C_OAR1_OFFSET) +# define STM32_I2C2_OAR2 (STM32_I2C2_BASE+STM32_I2C_OAR2_OFFSET) +# define STM32_I2C2_TIMINGR (STM32_I2C2_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C2_TIMEOUTR (STM32_I2C2_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C2_ISR (STM32_I2C2_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C2_ICR (STM32_I2C2_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C2_PECR (STM32_I2C2_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C2_RXDR (STM32_I2C2_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C2_TXDR (STM32_I2C2_BASE+STM32_I2C_TXDR_OFFSET) #endif #if STM32H5_NI2C > 2 -# define STM32_I2C3_CR1 (STM32_I2C3_BASE+STM32_I2CCR1_OFFSET) -# define STM32_I2C3_CR2 (STM32_I2C3_BASE+STM32_I2CCR2_OFFSET) -# define STM32_I2C3_OAR1 (STM32_I2C3_BASE+STM32_I2COAR1_OFFSET) -# define STM32_I2C3_OAR2 (STM32_I2C3_BASE+STM32_I2COAR2_OFFSET) -# define STM32_I2C3_TIMINGR (STM32_I2C3_BASE+STM32_I2CTIMINGR_OFFSET) -# define STM32_I2C3_TIMEOUTR (STM32_I2C3_BASE+STM32_I2CTIMEOUTR_OFFSET) -# define STM32_I2C3_ISR (STM32_I2C3_BASE+STM32_I2CISR_OFFSET) -# define STM32_I2C3_ICR (STM32_I2C3_BASE+STM32_I2CICR_OFFSET) -# define STM32_I2C3_PECR (STM32_I2C3_BASE+STM32_I2CPECR_OFFSET) -# define STM32_I2C3_RXDR (STM32_I2C3_BASE+STM32_I2CRXDR_OFFSET) -# define STM32_I2C3_TXDR (STM32_I2C3_BASE+STM32_I2CTXDR_OFFSET) +# define STM32_I2C3_CR1 (STM32_I2C3_BASE+STM32_I2C_CR1_OFFSET) +# define STM32_I2C3_CR2 (STM32_I2C3_BASE+STM32_I2C_CR2_OFFSET) +# define STM32_I2C3_OAR1 (STM32_I2C3_BASE+STM32_I2C_OAR1_OFFSET) +# define STM32_I2C3_OAR2 (STM32_I2C3_BASE+STM32_I2C_OAR2_OFFSET) +# define STM32_I2C3_TIMINGR (STM32_I2C3_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C3_TIMEOUTR (STM32_I2C3_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C3_ISR (STM32_I2C3_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C3_ICR (STM32_I2C3_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C3_PECR (STM32_I2C3_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C3_RXDR (STM32_I2C3_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C3_TXDR (STM32_I2C3_BASE+STM32_I2C_TXDR_OFFSET) #endif #if STM32H5_NI2C > 3 -# define STM32_I2C4_CR1 (STM32_I2C4_BASE+STM32_I2CCR1_OFFSET) -# define STM32_I2C4_CR2 (STM32_I2C4_BASE+STM32_I2CCR2_OFFSET) -# define STM32_I2C4_OAR1 (STM32_I2C4_BASE+STM32_I2COAR1_OFFSET) -# define STM32_I2C4_OAR2 (STM32_I2C4_BASE+STM32_I2COAR2_OFFSET) -# define STM32_I2C4_TIMINGR (STM32_I2C4_BASE+STM32_I2CTIMINGR_OFFSET) -# define STM32_I2C4_TIMEOUTR (STM32_I2C4_BASE+STM32_I2CTIMEOUTR_OFFSET) -# define STM32_I2C4_ISR (STM32_I2C4_BASE+STM32_I2CISR_OFFSET) -# define STM32_I2C4_ICR (STM32_I2C4_BASE+STM32_I2CICR_OFFSET) -# define STM32_I2C4_PECR (STM32_I2C4_BASE+STM32_I2CPECR_OFFSET) -# define STM32_I2C4_RXDR (STM32_I2C4_BASE+STM32_I2CRXDR_OFFSET) -# define STM32_I2C4_TXDR (STM32_I2C4_BASE+STM32_I2CTXDR_OFFSET) +# define STM32_I2C4_CR1 (STM32_I2C4_BASE+STM32_I2C_CR1_OFFSET) +# define STM32_I2C4_CR2 (STM32_I2C4_BASE+STM32_I2C_CR2_OFFSET) +# define STM32_I2C4_OAR1 (STM32_I2C4_BASE+STM32_I2C_OAR1_OFFSET) +# define STM32_I2C4_OAR2 (STM32_I2C4_BASE+STM32_I2C_OAR2_OFFSET) +# define STM32_I2C4_TIMINGR (STM32_I2C4_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C4_TIMEOUTR (STM32_I2C4_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C4_ISR (STM32_I2C4_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C4_ICR (STM32_I2C4_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C4_PECR (STM32_I2C4_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C4_RXDR (STM32_I2C4_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C4_TXDR (STM32_I2C4_BASE+STM32_I2C_TXDR_OFFSET) #endif /* Register Bitfield Definitions ********************************************/ @@ -119,12 +119,12 @@ #define I2C_CR1_RXDMAEN (1 << 15) /* Bit 15: DMA reception requests enable */ #define I2C_CR1_SBC (1 << 16) /* Bit 16: Slave byte control */ #define I2C_CR1_NOSTRETCH (1 << 17) /* Bit 17: Clock stretching disable */ -#define I2C_CR1_WUPEN (1 << 18) /* Bit 18: Wakeup from STOP enable */ #define I2C_CR1_GCEN (1 << 19) /* Bit 19: General call enable */ #define I2C_CR1_SMBHEN (1 << 20) /* Bit 20: SMBus Host address enable */ #define I2C_CR1_SMBDEN (1 << 21) /* Bit 21: SMBus Device Default address enable */ #define I2C_CR1_ALERTEN (1 << 22) /* Bit 22: SMBus alert enable */ #define I2C_CR1_PECEN (1 << 23) /* Bit 23: PEC enable */ +#define I2C_CR1_FMP (1 << 24) /* Bit 24: FMP enable */ /* Control register 2 */ @@ -204,6 +204,18 @@ # define I2C_TIMEOUTR_B(n) ((n) << I2C_TIMEOUTR_B_SHIFT) #define I2C_TIMEOUTR_TEXTEN (1 << 31) /* Bits 31: Extended clock timeout enable */ +/* Fields unique to the Interrupt and Status register */ + +#define I2C_ISR_TXE (1 << 0) /* Bit 0: Transmit data register empty (transmitters) */ +#define I2C_ISR_TXIS (1 << 1) /* Bit 1: Transmit interrupt status (transmitters) */ +#define I2C_ISR_RXNE (1 << 2) /* Bit 2: Receive data register not empty (receivers) */ +#define I2C_ISR_TC (1 << 6) /* Bit 6: Transfer Complete (master) */ +#define I2C_ISR_TCR (1 << 7) /* Bit 7: Transfer Complete Reload */ +#define I2C_ISR_BUSY (1 << 15) /* Bit 15: Bus busy */ +#define I2C_ISR_DIR (1 << 16) /* Bit 16: Transfer direction (slave) */ +#define I2C_ISR_ADDCODE_SHIFT (17) /* Bits 17-23: Address match code (slave) */ +#define I2C_ISR_ADDCODE_MASK (0x7f << I2C_ISR_ADDCODE_SHIFT) + /* Interrupt and Status register and interrupt clear register */ /* Common interrupt bits */ @@ -218,18 +230,6 @@ #define I2C_INT_TIMEOUT (1 << 12) /* Bit 12: Timeout or tLOW detection flag */ #define I2C_INT_ALERT (1 << 13) /* Bit 13: SMBus alert */ -/* Fields unique to the Interrupt and Status register */ - -#define I2C_ISR_TXE (1 << 0) /* Bit 0: Transmit data register empty (transmitters) */ -#define I2C_ISR_TXIS (1 << 1) /* Bit 1: Transmit interrupt status (transmitters) */ -#define I2C_ISR_RXNE (1 << 2) /* Bit 2: Receive data register not empty (receivers) */ -#define I2C_ISR_TC (1 << 6) /* Bit 6: Transfer Complete (master) */ -#define I2C_ISR_TCR (1 << 7) /* Bit 7: Transfer Complete Reload */ -#define I2C_ISR_BUSY (1 << 15) /* Bit 15: Bus busy */ -#define I2C_ISR_DIR (1 << 16) /* Bit 16: Transfer direction (slave) */ -#define I2C_ISR_ADDCODE_SHIFT (17) /* Bits 17-23: Address match code (slave) */ -#define I2C_ISR_ADDCODE_MASK (0x7f << I2C_ISR_ADDCODE_SHIFT) - #define I2C_ISR_ERRORMASK (I2C_INT_BERR | I2C_INT_ARLO | I2C_INT_OVR | I2C_INT_PECERR | I2C_INT_TIMEOUT) #define I2C_ICR_CLEARMASK (I2C_INT_ADDR | I2C_INT_NACK | I2C_INT_STOP | I2C_INT_BERR | I2C_INT_ARLO \ diff --git a/arch/arm/src/stm32h5/stm32.h b/arch/arm/src/stm32h5/stm32.h index 28d97cac6f8..d1c0b94c9fb 100644 --- a/arch/arm/src/stm32h5/stm32.h +++ b/arch/arm/src/stm32h5/stm32.h @@ -42,4 +42,5 @@ #include "stm32_rcc.h" #include "stm32_uart.h" #include "stm32_lowputc.h" +#include "stm32_i2c.h" #endif /* __ARCH_ARM_SRC_STM32H5_STM32_H */ diff --git a/arch/arm/src/stm32h5/stm32_i2c.c b/arch/arm/src/stm32h5/stm32_i2c.c new file mode 100644 index 00000000000..b0eabc8d507 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_i2c.c @@ -0,0 +1,3130 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_i2c.c + * STM32 I2C Hardware Layer - Device Driver + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * With extensions and modifications for the F1, F2, and F4 by: + * + * Copyright (C) 2016-2017 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * John Wharington + * David Sidrane + * Bob Feretich + * Modified for STM32H7 by Mateusz Szafoni + * + * Major rewrite of ISR and supporting methods, including support + * for NACK and RELOAD by: + * + * Copyright (c) 2016 Doug Vetter. All rights reserved. + * Author: Doug Vetter + * + * Major rewrite of setclock to dynamically determine TIMINGR. + * Written by: + * + * Copyright (c) 2024 Kyle Wilson. All rights reserved. + * Author: Kyle Wilson + * + * 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. + * + ****************************************************************************/ + +/* -------------------------------------------------------------------------- + * + * STM32 H5 I2C Driver + * + * Supports: + * - Master operation: + * Standard-mode (up to 100 kHz) + * Fast-mode (up to 400 kHz) + * Fast-mode Plus (up to 1 MHz) + * fI2CCLK clock source selection is based on + * STM32_RCC_CCIPR4_I2CxSRC + * + * - Multiple instances (shared bus) + * - Interrupt based operation + * - RELOAD support + * + * Unsupported, possible future work: + * - More effective error reporting to higher layers + * - Slave operation + * - Support of fI2CCLK frequencies other than 8MHz, 16MHz, 32MHz, 64MHz + * - Support for other input clocks besides HSI + * - Polled operation (code present but untested) + * - SMBus support + * - Multi-master support + * - IPMI + * + * Test Environment: + * + * - STM32H563ZI on ST Nucleo-144 Board + * + * Operational Status: + * + * All supported features have been tested and found to be operational. + * + * Although the RELOAD capability has been tested as it was required to + * implement the I2C_M_NOSTART flag on F3 hardware, the associated + * logic to support the transfer messages with more than 255 byte + * payloads has not been tested as the author lacked access to a real + * device supporting these types of transfers. + * + * Performance Benchmarks: TBD + * + * Time to transfer two messages, each a byte in length, in addition to + * the START condition, in interrupt mode: + * + * DEBUG enabled (development): TBDms + * Excessive delay here is caused by printing to the console and + * is of no concern. + * + * DEBUG disabled (production): TBSus + * Between Messages: TBDus + * Between Bytes: TBDus + * + * Implementation: + * + * - Device: structure as defined by the nuttx/i2c/i2c.h + * + * - Instance: + * represents each individual access to the I2C driver, obtained by the + * i2c_init(); it extends the Device structure from the nuttx/i2c/i2c.h; + * Instance points to OPS, to common I2C Hardware private data and + * contains its own private data including frequency, address and mode + * of operation. + * + * - Private: Private data of an I2C Hardware + * + * High Level Functional Description + * + * This driver works with I2C "messages" (struct i2c_msg_s), which carry a + * buffer intended to transfer data to, or store data read from, the I2C bus. + * + * As the hardware can only transmit or receive one byte at a time the basic + * job of the driver (and the ISR specifically) is to process each message in + * the order they are stored in the message list, one byte at a time. When + * no messages are left the ISR exits and returns the result to the caller. + * + * The order of the list of I2C messages provided to the driver is important + * and dependent upon the hardware in use. A typical I2C transaction between + * the F3 as an I2C Master and some other IC as a I2C Slave requires two + * messages that communicate the: + * + * 1) Subaddress (register offset on the slave device) + * 2) Data sent to or read from the device + * + * These messages will typically be one byte in length but may be up to 2^31 + * bytes in length. Incidentally, the maximum length is limited only because + * i2c_msg_s.length is a signed int for some odd reason. + * + * Interrupt mode relies on the following interrupt events: + * + * TXIS - Transmit interrupt + * (data transmitted to bus and acknowledged) + * NACKF - Not Acknowledge Received + * (data transmitted to bus and NOT acknowledged) + * RXNE - Receive interrupt + * (data received from bus) + * TC - Transfer Complete + * (All bytes in message transferred) + * TCR - Transfer Complete (Reload) + * (Current batch of bytes in message transferred) + * + * The driver currently supports Single Master mode only. Slave mode is not + * supported. Additionally, the driver runs in Software End Mode (AUTOEND + * disabled) so the driver is responsible for telling the hardware what to + * do at the end of a transfer. + * + * -------------------------------------------------------------------------- + * + * Configuration: + * + * To use this driver, enable the following configuration variable: + * + * One of: + * + * CONFIG_STM32H5_STM32H5XXXXX + * + * and one or more interfaces: + * + * CONFIG_STM32H5_I2C1 + * CONFIG_STM32H5_I2C2 + * CONFIG_STM32H5_I2C3 + * CONFIG_STM32H5_I2C4 + * + * To configure the ISR timeout using fixed values + * (CONFIG_STM32H5_I2C_DYNTIMEO=n): + * + * CONFIG_STM32H5_I2CTIMEOSEC (Timeout in seconds) + * CONFIG_STM32H5_I2CTIMEOMS (Timeout in milliseconds) + * CONFIG_STM32H5_I2CTIMEOTICKS (Timeout in ticks) + * + * To configure the ISR timeout using dynamic values + * (CONFIG_STM32H5_I2C_DYNTIMEO=y): + * + * CONFIG_STM32H5_I2C_DYNTIMEO_USECPERBYTE + * (Timeout in microseconds per byte) + * CONFIG_STM32H5_I2C_DYNTIMEO_STARTSTOP + * (Timeout for start/stop in msec) + * + * Debugging output enabled with: + * + * CONFIG_DEBUG_FEATURES and CONFIG_DEBUG_I2C_{ERROR|WARN|INFO} + * + * ISR Debugging output may be enabled with: + * + * CONFIG_DEBUG_FEATURES and CONFIG_DEBUG_I2C_INFO + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "stm32_rcc.h" +#include "stm32_i2c.h" +#include "stm32_gpio.h" +#include "hardware/stm32_pinmap.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_STM32H5_I2C1) || defined(CONFIG_STM32H5_I2C2) || \ + defined(CONFIG_STM32H5_I2C3) || defined(CONFIG_STM32H5_I2C4) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#undef INVALID_CLOCK_SOURCE + +/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. + * Instead, CPU-intensive polling will be used. + */ + +/* Interrupt wait timeout in seconds and milliseconds */ + +#if !defined(CONFIG_STM32H5_I2CTIMEOSEC) && !defined(CONFIG_STM32H5_I2CTIMEOMS) +# define CONFIG_STM32H5_I2CTIMEOSEC 0 +# define CONFIG_STM32H5_I2CTIMEOMS 500 /* Default is 500 milliseconds */ +# warning "Using Default 500 Ms Timeout" +#elif !defined(CONFIG_STM32H5_I2CTIMEOSEC) +# define CONFIG_STM32H5_I2CTIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_STM32H5_I2CTIMEOMS) +# define CONFIG_STM32H5_I2CTIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_STM32H5_I2CTIMEOTICKS +# define CONFIG_STM32H5_I2CTIMEOTICKS \ + (SEC2TICK(CONFIG_STM32H5_I2CTIMEOSEC) + MSEC2TICK(CONFIG_STM32H5_I2CTIMEOMS)) +#endif + +#ifndef CONFIG_STM32H5_I2C_DYNTIMEO_STARTSTOP +# define CONFIG_STM32H5_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_STM32H5_I2CTIMEOTICKS) +#endif + +/* Macros to convert a I2C pin to a GPIO output */ + +#define I2C_OUTPUT (GPIO_OUTPUT | GPIO_FLOAT | GPIO_OPENDRAIN |\ + GPIO_SPEED_50MHz | GPIO_OUTPUT_SET) + +#define MKI2C_OUTPUT(p) (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT) + +#define I2C_CR1_TXRX (I2C_CR1_RXIE | I2C_CR1_TXIE) +#define I2C_CR1_ALLINTS (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ERRIE) + +/* Unused bit in I2c_ISR used to communicate a bad state has occurred in + * the isr processing + */ + +#define I2C_INT_BAD_STATE 0x8000000 + +/* I2C event tracing + * + * To enable tracing statements which show the details of the state machine + * enable the following configuration variable: + * + * CONFIG_I2C_TRACE + * + * Note: This facility uses syslog, which sends output to the console by + * default. No other debug configuration variables are required. + */ + +#ifndef CONFIG_I2C_TRACE +# define stm32_i2c_tracereset(p) +# define stm32_i2c_tracenew(p,s) +# define stm32_i2c_traceevent(p,e,a) +# define stm32_i2c_tracedump(p) +#endif + +#ifndef CONFIG_I2C_NTRACE +# define CONFIG_I2C_NTRACE 32 +#endif + +/* I2C Timing */ + +/* Input delay introduced by Analog Filter in nanoseconds */ + +#define STM32_I2C_TAF_MIN 50 +#define STM32_I2C_TAF_MAX 160 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Interrupt state */ + +enum stm32_intstate_e +{ + INTSTATE_IDLE = 0, /* No I2C activity */ + INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ + INTSTATE_DONE, /* Interrupt activity complete */ +}; + +/* Trace events */ + +enum stm32_trace_e +{ + I2CEVENT_NONE = 0, + I2CEVENT_STATE_ERROR, + I2CEVENT_ISR_SHUTDOWN, + I2CEVENT_ISR_CALL, + I2CEVENT_ISR_EMPTY_CALL, + I2CEVENT_MSG_HANDLING, + I2CEVENT_POLL_NOT_READY, + I2CEVENT_EMPTY_MSG, + I2CEVENT_START, + I2CEVENT_ADDRESS_ACKED, + I2CEVENT_ADDRESS_NACKED, + I2CEVENT_NACK, + I2CEVENT_READ, + I2CEVENT_READ_ERROR, + I2CEVENT_WRITE_TO_DR, + I2CEVENT_WRITE_STOP, + I2CEVENT_WRITE_RESTART, + I2CEVENT_WRITE_NO_RESTART, + I2CEVENT_WRITE_ERROR, + I2CEVENT_WRITE_FLAG_ERROR, + I2CEVENT_TC_RESTART, + I2CEVENT_TC_NO_RESTART +}; + +/* Trace data */ + +struct stm32_trace_s +{ + uint32_t status; /* I2C 32-bit SR2|SR1 status */ + uint32_t count; /* Interrupt count when status change */ + enum stm32_intstate_e event; /* Last event that occurred with this status */ + uint32_t parm; /* Parameter associated with the event */ + clock_t time; /* First of event or first status */ +}; + +/* I2C Device hardware configuration */ + +struct stm32_i2c_config_s +{ + uint32_t base; /* I2C base address */ + uint32_t clk_reg; /* Clock enable register */ + uint32_t clk_bit; /* Clock enable bit */ + uint32_t reset_reg; /* Reset register */ + uint32_t reset_bit; /* Reset bit */ + uint32_t scl_pin; /* GPIO configuration for SCL as SCL */ + uint32_t sda_pin; /* GPIO configuration for SDA as SDA */ +#ifndef CONFIG_I2C_POLLED + uint32_t ev_irq; /* Event IRQ */ + uint32_t er_irq; /* Error IRQ */ +#endif +}; + +/* I2C Device Private Data */ + +struct stm32_i2c_priv_s +{ + /* Port configuration */ + + const struct stm32_i2c_config_s *config; + + int refs; /* Reference count */ + mutex_t lock; /* Mutual exclusion mutex */ +#ifndef CONFIG_I2C_POLLED + sem_t sem_isr; /* Interrupt wait semaphore */ +#endif + volatile uint8_t intstate; /* Interrupt handshake (see enum stm32_intstate_e) */ + + uint8_t msgc; /* Message count */ + struct i2c_msg_s *msgv; /* Message list */ + uint8_t *ptr; /* Current message buffer */ + uint32_t frequency; /* Current I2C frequency */ + int dcnt; /* Current message bytes remaining to transfer */ + uint16_t flags; /* Current message flags */ + bool astart; /* START sent */ + + /* I2C trace support */ + +#ifdef CONFIG_I2C_TRACE + int tndx; /* Trace array index */ + clock_t start_time; /* Time when the trace was started */ + + /* The actual trace data */ + + struct stm32_trace_s trace[CONFIG_I2C_NTRACE]; +#endif + + uint32_t status; /* End of transfer SR2|SR1 status */ + +#ifdef CONFIG_PM + struct pm_callback_s pm_cb; /* PM callbacks */ +#endif +}; + +/* I2C Device, Instance */ + +struct stm32_i2c_inst_s +{ + const struct i2c_ops_s *ops; /* Standard I2C operations */ + struct stm32_i2c_priv_s *priv; /* Common driver private data structure */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint16_t stm32_i2c_getreg(struct stm32_i2c_priv_s *priv, + uint8_t offset); +static inline void stm32_i2c_putreg(struct stm32_i2c_priv_s *priv, + uint8_t offset, uint16_t value); +static inline void stm32_i2c_putreg32(struct stm32_i2c_priv_s *priv, + uint8_t offset, uint32_t value); +static inline void stm32_i2c_modifyreg32(struct stm32_i2c_priv_s *priv, + uint8_t offset, uint32_t clearbits, + uint32_t setbits); +#ifdef CONFIG_STM32H5_I2C_DYNTIMEO +static uint32_t stm32_i2c_toticks(int msgc, struct i2c_msg_s *msgs); +#endif /* CONFIG_STM32H5_I2C_DYNTIMEO */ +static inline int stm32_i2c_sem_waitdone(struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sem_waitstop(struct stm32_i2c_priv_s *priv); +#ifdef CONFIG_I2C_TRACE +static void stm32_i2c_tracereset(struct stm32_i2c_priv_s *priv); +static void stm32_i2c_tracenew(struct stm32_i2c_priv_s *priv, + uint32_t status); +static void stm32_i2c_traceevent(struct stm32_i2c_priv_s *priv, + enum stm32_trace_e event, uint32_t parm); +static void stm32_i2c_tracedump(struct stm32_i2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ +static void stm32_i2c_setclock(struct stm32_i2c_priv_s *priv, + uint32_t frequency); +static inline void stm32_i2c_sendstart(struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sendstop(struct stm32_i2c_priv_s *priv); +static inline +uint32_t stm32_i2c_getstatus(struct stm32_i2c_priv_s *priv); +static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv); +#ifndef CONFIG_I2C_POLLED +static int stm32_i2c_isr(int irq, void *context, void *arg); +#endif +static int stm32_i2c_init(struct stm32_i2c_priv_s *priv); +static int stm32_i2c_deinit(struct stm32_i2c_priv_s *priv); + +static int stm32_i2c_process(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); +static int stm32_i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int stm32_i2c_reset(struct i2c_master_s *dev); +#endif +#ifdef CONFIG_PM +static int stm32_i2c_pm_prepare(struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_I2C1 +static const struct stm32_i2c_config_s stm32_i2c1_config = +{ + .base = STM32_I2C1_BASE, + .clk_reg = STM32_RCC_APB1LENR, + .clk_bit = RCC_APB1LENR_I2C1EN, + .reset_reg = STM32_RCC_APB1LRSTR, + .reset_bit = RCC_APB1LRSTR_I2C1RST, + .scl_pin = GPIO_I2C1_SCL, + .sda_pin = GPIO_I2C1_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C1_EV, + .er_irq = STM32_IRQ_I2C1_ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c1_priv = +{ + .config = &stm32_i2c1_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif +}; +#endif + +#ifdef CONFIG_STM32H5_I2C2 +static const struct stm32_i2c_config_s stm32_i2c2_config = +{ + .base = STM32_I2C2_BASE, + .clk_reg = STM32_RCC_APB1LENR, + .clk_bit = RCC_APB1LENR_I2C2EN, + .reset_reg = STM32_RCC_APB1LRSTR, + .reset_bit = RCC_APB1LRSTR_I2C2RST, + .scl_pin = GPIO_I2C2_SCL, + .sda_pin = GPIO_I2C2_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C2_EV, + .er_irq = STM32_IRQ_I2C2_ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c2_priv = +{ + .config = &stm32_i2c2_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif +}; +#endif + +#ifdef CONFIG_STM32H5_I2C3 +static const struct stm32_i2c_config_s stm32_i2c3_config = +{ + .base = STM32_I2C3_BASE, + .clk_reg = STM32_RCC_APB3ENR, + .clk_bit = RCC_APB3ENR_I2C3EN, + .reset_reg = STM32_RCC_APB3RSTR, + .reset_bit = RCC_APB3RSTR_I2C3RST, + .scl_pin = GPIO_I2C3_SCL, + .sda_pin = GPIO_I2C3_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C3_EV, + .er_irq = STM32_IRQ_I2C3_ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c3_priv = +{ + .config = &stm32_i2c3_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif +}; +#endif + +#ifdef CONFIG_STM32H5_I2C4 +static const struct stm32_i2c_config_s stm32_i2c4_config = +{ + .base = STM32_I2C4_BASE, + .clk_reg = STM32_RCC_APB3ENR, + .clk_bit = RCC_APB3ENR_I2C4EN, + .reset_reg = STM32_RCC_APB3RSTR, + .reset_bit = RCC_APB3RSTR_I2C4RST, + .scl_pin = GPIO_I2C4_SCL, + .sda_pin = GPIO_I2C4_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C4_EV, + .er_irq = STM32_IRQ_I2C4_ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c4_priv = +{ + .config = &stm32_i2c4_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif +}; +#endif + +/* Device Structures, Instantiation */ + +static const struct i2c_ops_s stm32_i2c_ops = +{ + .transfer = stm32_i2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = stm32_i2c_reset, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_i2c_getreg + * + * Description: + * Get a 16-bit register value by offset + * + ****************************************************************************/ + +static inline uint16_t stm32_i2c_getreg(struct stm32_i2c_priv_s *priv, + uint8_t offset) +{ + return getreg16(priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_i2c_getreg32 + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t stm32_i2c_getreg32(struct stm32_i2c_priv_s *priv, + uint8_t offset) +{ + return getreg32(priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_i2c_putreg + * + * Description: + * Put a 16-bit register value by offset + * + ****************************************************************************/ + +static inline void stm32_i2c_putreg(struct stm32_i2c_priv_s *priv, + uint8_t offset, uint16_t value) +{ + putreg16(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_i2c_putreg32 + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void stm32_i2c_putreg32(struct stm32_i2c_priv_s *priv, + uint8_t offset, uint32_t value) +{ + putreg32(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_i2c_modifyreg32 + * + * Description: + * Modify a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void stm32_i2c_modifyreg32(struct stm32_i2c_priv_s *priv, + uint8_t offset, uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->config->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: stm32_i2c_toticks + * + * Description: + * Return a micro-second delay based on the number of bytes left to be + * processed. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_I2C_DYNTIMEO +static uint32_t stm32_i2c_toticks(int msgc, struct i2c_msg_s *msgs) +{ + size_t bytecount = 0; + int i; + + /* Count the number of bytes left to process */ + + for (i = 0; i < msgc; i++) + { + bytecount += msgs[i].length; + } + + /* Then return a number of microseconds based on a user provided scaling + * factor. + */ + + return USEC2TICK(CONFIG_STM32H5_I2C_DYNTIMEO_USECPERBYTE * bytecount); +} +#endif + +/**************************************************************************** + * Name: stm32_i2c_enableinterrupts + * + * Description: + * Enable I2C interrupts + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static inline void stm32_i2c_enableinterrupts(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, + (I2C_CR1_TXRX | I2C_CR1_NACKIE)); +} +#endif + +/**************************************************************************** + * Name: stm32_i2c_sem_waitdone + * + * Description: + * Wait for a transfer to complete + * + * There are two versions of this function. The first is included when using + * interrupts while the second is used if polling (CONFIG_I2C_POLLED=y). + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static inline int stm32_i2c_sem_waitdone(struct stm32_i2c_priv_s *priv) +{ + irqstate_t flags; + int ret; + + flags = enter_critical_section(); + + /* Enable I2C interrupts */ + + /* The TXIE and RXIE interrupts are enabled initially in stm32_i2c_process. + * The remainder of the interrupts, including error-related, are enabled + * here. + */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, + (I2C_CR1_ALLINTS & ~I2C_CR1_TXRX)); + + /* Signal the interrupt handler that we are waiting */ + + priv->intstate = INTSTATE_WAITING; + do + { + /* Wait until either the transfer is complete or the timeout expires */ + +#ifdef CONFIG_STM32H5_I2C_DYNTIMEO + ret = nxsem_tickwait_uninterruptible(&priv->sem_isr, + stm32_i2c_toticks(priv->msgc, priv->msgv)); +#else + ret = nxsem_tickwait_uninterruptible(&priv->sem_isr, + CONFIG_STM32H5_I2CTIMEOTICKS); +#endif + if (ret < 0) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by + * nxsem_tickwait_uninterruptible. + */ + + break; + } + } + + /* Loop until the interrupt level transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE); + + /* Set the interrupt state back to IDLE */ + + priv->intstate = INTSTATE_IDLE; + + /* Disable I2C interrupts */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_ALLINTS, 0); + + leave_critical_section(flags); + return ret; +} +#else +static inline int stm32_i2c_sem_waitdone(struct stm32_i2c_priv_s *priv) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_STM32H5_I2C_DYNTIMEO + timeout = stm32_i2c_toticks(priv->msgc, priv->msgv); +#else + timeout = CONFIG_STM32H5_I2CTIMEOTICKS; +#endif + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_tickwait_uninterruptible() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + start = clock_systime_ticks(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Poll by simply calling the timer interrupt handler until it + * reports that it is done. + */ + + stm32_i2c_isr_process(priv); + } + + /* Loop until the transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE && elapsed < timeout); + + i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: 0x%08" PRIx32 + "\n", priv->intstate, (long)elapsed, (long)timeout, priv->status); + + /* Set the interrupt state back to IDLE */ + + ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; + priv->intstate = INTSTATE_IDLE; + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_i2c_set_7bit_address + * + * Description: + * + ****************************************************************************/ + +static inline void +stm32_i2c_set_7bit_address(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_SADD7_MASK, + ((priv->msgv->addr & 0x7f) << I2C_CR2_SADD7_SHIFT)); +} + +/**************************************************************************** + * Name: stm32_i2c_set_bytes_to_transfer + * + * Description: + * + ****************************************************************************/ + +static inline void +stm32_i2c_set_bytes_to_transfer(struct stm32_i2c_priv_s *priv, + uint8_t n_bytes) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_NBYTES_MASK, + (n_bytes << I2C_CR2_NBYTES_SHIFT)); +} + +/**************************************************************************** + * Name: stm32_i2c_set_write_transfer_dir + * + * Description: + * + ****************************************************************************/ + +static inline void +stm32_i2c_set_write_transfer_dir(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_RD_WRN, 0); +} + +/**************************************************************************** + * Name: stm32_i2c_set_read_transfer_dir + * + * Description: + * + ****************************************************************************/ + +static inline void +stm32_i2c_set_read_transfer_dir(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_RD_WRN); +} + +/**************************************************************************** + * Name: stm32_i2c_enable_reload + * + * Description: + * + ****************************************************************************/ + +static inline void +stm32_i2c_enable_reload(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_RELOAD); +} + +/**************************************************************************** + * Name: stm32_i2c_disable_reload + * + * Description: + * + ****************************************************************************/ + +static inline void +stm32_i2c_disable_reload(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_RELOAD, 0); +} + +/**************************************************************************** + * Name: stm32_i2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline void stm32_i2c_sem_waitstop(struct stm32_i2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t cr; + uint32_t sr; + + /* Select a timeout */ + +#ifdef CONFIG_STM32H5_I2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_STM32H5_I2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_STM32H5_I2CTIMEOTICKS; +#endif + + /* Wait as stop might still be in progress */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for STOP condition */ + + cr = stm32_i2c_getreg32(priv, STM32_I2C_CR2_OFFSET); + if ((cr & I2C_CR2_STOP) == 0) + { + return; + } + + /* Check for timeout error */ + + sr = stm32_i2c_getreg(priv, STM32_I2C_ISR_OFFSET); + if ((sr & I2C_INT_TIMEOUT) != 0) + { + return; + } + } + + /* Loop until the stop is complete or a timeout occurs. */ + + while (elapsed < timeout); + + /* If we get here then a timeout occurred with the STOP condition + * still pending. + */ + + i2cinfo("Timeout with CR: %04" PRIx32 " SR: %04" PRIx32 "\n", cr, sr); +} + +/**************************************************************************** + * Name: stm32_i2c_trace* + * + * Description: + * I2C trace instrumentation + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void stm32_i2c_traceclear(struct stm32_i2c_priv_s *priv) +{ + struct stm32_trace_s *trace = &priv->trace[priv->tndx]; + + trace->status = 0; /* I2C 32-bit status */ + trace->count = 0; /* Interrupt count when status change */ + trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ + trace->parm = 0; /* Parameter associated with the event */ + trace->time = 0; /* Time of first status or event */ +} + +static void stm32_i2c_tracereset(struct stm32_i2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systime_ticks(); + stm32_i2c_traceclear(priv); +} + +static void stm32_i2c_tracenew(struct stm32_i2c_priv_s *priv, + uint32_t status) +{ + struct stm32_trace_s *trace = &priv->trace[priv->tndx]; + + /* Is the current entry uninitialized? Has the status changed? */ + + if (trace->count == 0 || status != trace->status) + { + /* Yes.. Was it the status changed? */ + + if (trace->count != 0) + { + /* Yes.. bump up the trace index + * (unless we are out of trace entries) + */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + trace = &priv->trace[priv->tndx]; + } + + /* Initialize the new trace entry */ + + stm32_i2c_traceclear(priv); + trace->status = status; + trace->count = 1; + trace->time = clock_systime_ticks(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void stm32_i2c_traceevent(struct stm32_i2c_priv_s *priv, + enum stm32_trace_e event, uint32_t parm) +{ + struct stm32_trace_s *trace; + + if (event != I2CEVENT_NONE) + { + trace = &priv->trace[priv->tndx]; + + /* Initialize the new trace entry */ + + trace->event = event; + trace->parm = parm; + + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + stm32_i2c_traceclear(priv); + } +} + +static void stm32_i2c_tracedump(struct stm32_i2c_priv_s *priv) +{ + struct stm32_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %d\n", + (int)(clock_systime_ticks() - priv->start_time)); + + for (i = 0; i < priv->tndx; i++) + { + trace = &priv->trace[i]; + syslog(LOG_DEBUG, + "%2d. STATUS: %08x COUNT: %3d EVENT: %2d PARM: %08x TIME: %d\n", + i + 1, trace->status, trace->count, trace->event, trace->parm, + (int)(trace->time - priv->start_time)); + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: stm32_i2c_setclock + * + * Description: + * + * Sets the I2C bus clock frequency by configuring the I2C_TIMINGR + * register. + * + * This function supports bus clock frequencies of 0-1MHz. However, + * high input i2c clock speeds will result in higher minimum frequencies. + * For example, a 250 MHz clock will not support frequencies below ~33 KHz. + * + * Below are standard modes: + * 1000Khz (Fast Mode+) + * 400Khz (Fast Mode) + * 100Khz (Standard Mode) + * + * Attempts to set a frequency below the calculated minimum frequency + * will quietly provision the minimum frequency. Use care in selecting + * the input clock for the given application. . + * + * Clock Selection: + * + * The I2C peripheral clock can be provided by either PCLK1 (or PCLK3), + * PLL3_R, HSI, or CSI. + * + * PCLK1 >------|\ I2CCLK12 + * PLL3_R >------| |---------> + * HSI >------| | + * CSI >------|/ + * + * PCLK1 is the default. + * + * PCLK3 >------|\ I2CCLK34 + * PLL3_R >------| |---------> + * HSI >------| | + * CSI >------|/ + * + * PCLK3 is the default. + * + * References: + * + * I2C Specification, STM32H52x_3x_6x_7x Reference Manual RM0481. + * + ****************************************************************************/ + +static void stm32_i2c_setclock(struct stm32_i2c_priv_s *priv, + uint32_t frequency) +{ + /* STM32H5 variables */ + + uint8_t presc; + uint32_t t_presc_ns; + uint32_t tsync1; + uint32_t tsync2; + uint8_t scl_delay; + int32_t sda_delay_min; + uint8_t sda_delay; + uint16_t scl_h_period; + uint16_t scl_l_period; + uint8_t fmp = 0; + uint8_t anfoff; + uint8_t dnf; + + /* Clocks */ + + uint32_t i2c_ker_ck; + uint32_t t_i2cclk_ns; + uint32_t t_clk_ns; + int32_t tlow_ns; + int32_t thigh_ns; + uint8_t tlow_ratio_min; + + /* I2C Specifications */ + + uint32_t tsu_dat_min; + const uint8_t thd_dat_min = 0; + uint32_t tr_max; + uint32_t tf_max; + bool rf_override = false; + + /* Calculate timingr only if frequency changes */ + + if (frequency != priv->frequency) + { + /* Set timing specs based on i2c frequency + * Specifications pulled from secion 6.1 in + * UM10204 (NXP I2C Spec). Time in nanoseconds. + */ + + if (frequency <= 100000) + { + tsu_dat_min = 250; + tr_max = 1000; + tf_max = 300; + tlow_ratio_min = 47; + } + else if (frequency <= 400000) + { + tsu_dat_min = 100; + tr_max = 300; + tf_max = 300; + tlow_ratio_min = 52; + } + else if (frequency <= 1000000) + { + tsu_dat_min = 50; + tr_max = 120; + tf_max = 120; + tlow_ratio_min = 50; + fmp = 1; + } + else + { + /* If frequency > 1MHz, set to 1MHz and fast mode plus */ + + frequency = 1000000; + tsu_dat_min = 50; + tr_max = 120; + tf_max = 120; + tlow_ratio_min = 50; + fmp = 1; + } + + /* Added 1% to t_clk_ns to make sure frequency is not too fast */ + + t_clk_ns = (1010000000 + (frequency / 2)) / frequency; + + /* Obtain input clock period in nanoseconds. Each i2c peripheral can + * have separate settings. PCLK1 is the default clock. + */ + + switch (priv->config->base) + { +#ifdef CONFIG_STM32H5_I2C1 + case STM32_I2C1_BASE: +# if defined(CONFIG_STM32H5_I2C1_CLK_HSI) + i2c_ker_ck = STM32_HSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C1_CLK_CSI) + i2c_ker_ck = STM32_CSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C1_CLK_PCLK1) + i2c_ker_ck = STM32_PCLK1_FREQUENCY; +# else + i2c_ker_ck = STM32_PLL3R_FREQUENCY; +# endif + anfoff = CONFIG_STM32H5_I2C1_ANFOFF; + dnf = CONFIG_STM32H5_I2C1_DNF; +# ifdef CONFIG_STM32H5_I2C1_RF_OVERRIDE + tr_max = CONFIG_STM32H5_I2C1_RISE; + tf_max = CONFIG_STM32H5_I2C1_FALL; + rf_override = true; +# endif + break; +#endif +#ifdef CONFIG_STM32H5_I2C2 + case STM32_I2C2_BASE: +# if defined(CONFIG_STM32H5_I2C2_CLK_HSI) + i2c_ker_ck = STM32_HSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C2_CLK_CSI) + i2c_ker_ck = STM32_CSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C2_CLK_PCLK1) + i2c_ker_ck = STM32_PCLK1_FREQUENCY; +# else + i2c_ker_ck = STM32_PLL3R_FREQUENCY; +# endif + anfoff = CONFIG_STM32H5_I2C2_ANFOFF; + dnf = CONFIG_STM32H5_I2C2_DNF; +# ifdef CONFIG_STM32H5_I2C2_RF_OVERRIDE + tr_max = CONFIG_STM32H5_I2C2_RISE; + tf_max = CONFIG_STM32H5_I2C2_FALL; + rf_override = true; +# endif + break; +#endif +#ifdef CONFIG_STM32H5_I2C3 + case STM32_I2C3_BASE: +# if defined(CONFIG_STM32H5_I2C3_CLK_HSI) + i2c_ker_ck = STM32_HSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C3_CLK_CSI) + i2c_ker_ck = STM32_CSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C3_CLK_PCLK3) + i2c_ker_ck = STM32_PCLK3_FREQUENCY; +# else + i2c_ker_ck = STM32_PLL3R_FREQUENCY; +# endif + anfoff = CONFIG_STM32H5_I2C3_ANFOFF; + dnf = CONFIG_STM32H5_I2C3_DNF; +# ifdef CONFIG_STM32H5_I2C3_RF_OVERRIDE + tr_max = CONFIG_STM32H5_I2C3_RISE; + tf_max = CONFIG_STM32H5_I2C3_FALL; + rf_override = true; +# endif + break; +#endif +#ifdef CONFIG_STM32H5_I2C4 + case STM32_I2C4_BASE: +# if defined(CONFIG_STM32H5_I2C4_CLK_HSI) + i2c_ker_ck = STM32_HSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C4_CLK_CSI) + i2c_ker_ck = STM32_CSI_FREQUENCY; +# elif defined(CONFIG_STM32H5_I2C4_CLK_PCLK3) + i2c_ker_ck = STM32_PCLK3_FREQUENCY; +# else + i2c_ker_ck = STM32_PLL3R_FREQUENCY; +# endif + anfoff = CONFIG_STM32H5_I2C4_ANFOFF; + dnf = CONFIG_STM32H5_I2C4_DNF; +# ifdef CONFIG_STM32H5_I2C4_RF_OVERRIDE + tr_max = CONFIG_STM32H5_I2C4_RISE; + tf_max = CONFIG_STM32H5_I2C4_FALL; + rf_override = true; +# endif + break; +#endif + default: + } + + /* Set i2c_ker_ck period ti2cclk in nanoseconds */ + + t_i2cclk_ns = 1000000000 / i2c_ker_ck; + + /* See Section 48.4.9 and Figure 640 in RM0481. + * Use avg 40ns trise/tfall by default + */ + + if (rf_override) + { + tsync1 = tr_max + (!anfoff * STM32_I2C_TAF_MIN) + \ + ((dnf + 2) * t_i2cclk_ns); + tsync2 = tf_max + (!anfoff * STM32_I2C_TAF_MIN) + \ + ((dnf + 2) * t_i2cclk_ns); + } + else + { + tsync1 = 40 + (!anfoff * STM32_I2C_TAF_MIN) + \ + ((dnf + 2) * t_i2cclk_ns); + tsync2 = tsync1; + } + + /* See Figure 640 in RM0481. Add 3% margin to tlow. */ + + tlow_ns = (t_clk_ns * (tlow_ratio_min + 3)) / 100; + thigh_ns = t_clk_ns - tlow_ns; + tlow_ns -= tsync1; + thigh_ns -= tsync2; + + /* Iterate through prescaler values until t_presc is large enough + * to calculate a proper scl_l_period, scl_delay, and sda_delay + */ + + for (presc = 0; presc < 16; ++presc) + { + t_presc_ns = t_i2cclk_ns * (presc + 1); + + scl_l_period = ((tlow_ns + (t_presc_ns / 2)) / t_presc_ns) - 1; + if (scl_l_period > 255) + { + continue; + } + + scl_h_period = ((thigh_ns + (t_presc_ns / 2)) / t_presc_ns) - 1; + + /* See RM0481 Section 48.4.5 */ + + sda_delay_min = ((int32_t) (tf_max + thd_dat_min - \ + (!anfoff * STM32_I2C_TAF_MIN) - \ + ((dnf + 3) * t_i2cclk_ns)) / \ + (int32_t) t_presc_ns); + + if (sda_delay_min < 0) + { + sda_delay_min = 0; + } + + sda_delay = sda_delay_min + 1; + + if (sda_delay > 15) + { + continue; + } + + /* See RM0481 Rev 2 Section 48.4.5 + * SCL and SDA rise times are often identical + * However, still add tr_max/2 to scl_delay to account + * for potential differences in rise times. + */ + + scl_delay = (((tr_max / 2) + tsu_dat_min) / t_presc_ns) - 1; + + if (scl_delay > 15) + { + continue; + } + + break; + } + + /* Note: It is possible to exit this loop with invalid settings when + * using an improper i2c_ker_ck. Choose i2c_ker_ck wisely. + * Addtionally, take care setting the digital and analog filters. + */ + + /* I2C peripheral must be disabled to update clocking configuration. + * This will SW reset the device. + */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0); + + uint32_t timingr = + (presc << I2C_TIMINGR_PRESC_SHIFT) | + (scl_delay << I2C_TIMINGR_SCLDEL_SHIFT) | + (sda_delay << I2C_TIMINGR_SDADEL_SHIFT) | + (scl_h_period << I2C_TIMINGR_SCLH_SHIFT) | + (scl_l_period << I2C_TIMINGR_SCLL_SHIFT); + + stm32_i2c_putreg32(priv, STM32_I2C_TIMINGR_OFFSET, timingr); + priv->frequency = frequency; + + uint32_t cr1_reg_clr = I2C_CR1_DNF_MASK | I2C_CR1_ANFOFF | I2C_CR1_FMP; + + uint32_t cr1_reg_set = (dnf << I2C_CR1_DNF_SHIFT) | \ + (anfoff * I2C_CR1_ANFOFF) | \ + (fmp * I2C_CR1_FMP); + + /* Set DNF, ANFOFF, and FMP */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, + cr1_reg_clr, cr1_reg_set); + } + + /* Enable I2C peripheral */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); +} + +/**************************************************************************** + * Name: stm32_i2c_sendstart + * + * Description: + * Send the START condition / force Master mode + * + * A START condition in I2C consists of a single byte that contains both the + * 7 bit slave address and a read/write bit (0 = WRITE, 1 = READ). If the + * address is recognized by one of the slave devices that slave device will + * ACK the byte so that data transfers can begin. + * + * A RESTART (or repeated START per the I2CSPEC) is simply a START condition + * issued in the middle of a transfer (i.e. after the initial START and + * before a STOP). A RESTART sends a new address byte and R/W bit to the bus. + * A RESTART is optional in most cases but mandatory in the event the + * transfer direction is changed. + * + * Most of the time reading data from an I2C slave requires a WRITE of the + * subaddress followed by a READ (and hence a RESTART in between). Writing + * to an I2C slave typically requires only WRITE operations and hence no + * RESTARTs. + * + * This function is therefore called both at the beginning of a transfer + * (START) and at appropriate times during a transfer (RESTART). + * + ****************************************************************************/ + +static inline void stm32_i2c_sendstart(struct stm32_i2c_priv_s *priv) +{ + bool next_norestart = false; + + /* Set the private "current message" data used in protocol processing. + * + * ptr: A pointer to the start of the current message buffer. This is + * advanced after each byte in the current message is transferred. + * + * dcnt: A running counter of the bytes in the current message waiting to + * be transferred. This is decremented each time a byte is + * transferred. + * The hardware normally accepts a maximum of 255 bytes per transfer + * but can support more via the RELOAD mechanism. If dcnt initially + * exceeds 255, the RELOAD mechanism will be enabled automatically. + * + * flags: Used to characterize handling of the current message. + * + * The default flags value is 0 which specifies: + * + * - A transfer direction of WRITE (R/W bit = 0) + * - RESTARTs between all messages + * + * The following flags can be used to override this behavior as follows: + * + * - I2C_M_READ: Sets the transfer direction to READ (R/W bit = 1) + * - I2C_M_NOSTART: Prevents a RESTART from being issued prior to the + * transfer of the message (where allowed by the protocol). + * + */ + + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; + + if ((priv->flags & I2C_M_NOSTART) == 0) + { + /* Flag the first byte as an address byte */ + + priv->astart = true; + } + + /* Enabling RELOAD allows the transfer of: + * + * - individual messages with a payload exceeding 255 bytes + * - multiple messages back to back without a RESTART in between + * + * so we enable it if either of those conditions exist and disable + * it otherwise. + */ + + /* Check if there are multiple messages and the next is a continuation */ + + if (priv->msgc > 1) + { + next_norestart = (((priv->msgv + 1)->flags & I2C_M_NOSTART) != 0); + } + + if (next_norestart || priv->dcnt > 255) + { + i2cinfo("RELOAD enabled: dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + stm32_i2c_enable_reload(priv); + } + else + { + i2cinfo("RELOAD disable: dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + stm32_i2c_disable_reload(priv); + } + + /* Set the number of bytes to transfer (I2C_CR2->NBYTES) to the number of + * bytes in the current message or 255, whichever is lower so as to not + * exceed the hardware maximum allowed. + */ + + if (priv->dcnt > 255) + { + stm32_i2c_set_bytes_to_transfer(priv, 255); + } + else + { + stm32_i2c_set_bytes_to_transfer(priv, priv->dcnt); + } + + /* Set the (7 bit) address. + * 10 bit addressing is not yet supported. + */ + + stm32_i2c_set_7bit_address(priv); + + /* The flag of the current message is used to determine the direction of + * transfer required for the current message. + */ + + if (priv->flags & I2C_M_READ) + { + stm32_i2c_set_read_transfer_dir(priv); + } + else + { + stm32_i2c_set_write_transfer_dir(priv); + } + + /* Set the I2C_CR2->START bit to 1 to instruct the hardware to send the + * START condition using the address and transfer direction data entered. + */ + + i2cinfo("Sending START: dcnt=%i msgc=%i flags=0x%04x\n", + priv->dcnt, priv->msgc, priv->flags); + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_START); +} + +/**************************************************************************** + * Name: stm32_i2c_sendstop + * + * Description: + * Send the STOP conditions + * + * A STOP condition can be requested by setting the STOP bit in the I2C_CR2 + * register. Setting the STOP bit clears the TC flag and the STOP condition + * is sent on the bus. + * + ****************************************************************************/ + +static inline void stm32_i2c_sendstop(struct stm32_i2c_priv_s *priv) +{ + i2cinfo("Sending STOP\n"); + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_STOP, 0); + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_STOP); +} + +/**************************************************************************** + * Name: stm32_i2c_getstatus + * + * Description: + * Get 32-bit status (SR1 and SR2 combined) + * + ****************************************************************************/ + +static inline uint32_t stm32_i2c_getstatus(struct stm32_i2c_priv_s *priv) +{ + return getreg32(priv->config->base + STM32_I2C_ISR_OFFSET); +} + +/**************************************************************************** + * Name: stm32_i2c_clearinterrupts + * + * Description: + * Clear all interrupts + * + ****************************************************************************/ + +static inline void stm32_i2c_clearinterrupts(struct stm32_i2c_priv_s *priv) +{ + stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); +} + +/**************************************************************************** + * Name: stm32_i2c_isr_process + * + * Description: + * Common interrupt service routine (ISR) that handles I2C protocol logic. + * This is instantiated for each configured I2C interface + * (I2C1, I2C2, I2C3). + * + * This ISR is activated and deactivated by: + * + * stm32_i2c_process + * and + * stm32_i2c_waitdone + * + * Input Parameters: + * priv - The private struct of the I2C driver. + * + ****************************************************************************/ + +static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv) +{ + uint32_t status; + + /* Get state of the I2C controller */ + + status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + + i2cinfo("ENTER: status = 0x%08" PRIx32 "\n", status); + + /* Update private version of the state assuming a good state */ + + priv->status = status & ~I2C_INT_BAD_STATE; + + /* If this is a new transmission set up the trace table accordingly */ + + stm32_i2c_tracenew(priv, status); + stm32_i2c_traceevent(priv, I2CEVENT_ISR_CALL, 0); + + /* ------------------- Start of I2C protocol handling ------------------ */ + + /* I2C protocol logic follows. It's organized in an if else chain such that + * only one mode of operation is executed every time the ISR is called. + * + * If you need to add additional states to support new features be sure + * they continue the chain (i.e. begin with "else if") and are placed + * before the empty call / error states at the end of the chain. + */ + + /* NACK Handling + * + * This branch is only triggered when the NACK (Not Acknowledge Received) + * interrupt occurs. This interrupt will only fire when the + * I2C_CR1->NACKIE bit is 1. + * + * I2C_ISR->NACKF is set by hardware when a NACK is received after a byte + * is transmitted and the slave fails to acknowledge it. This is the + * opposite of, and mutually exclusive to, the I2C_ISR->TXIS event. + * + * In response to the NACK the hardware automatically triggers generation + * of a STOP condition, terminating the transfer. The only valid response + * to this state is to exit the ISR and report the failure. + * + * To differentiate an "address NACK" from a NACK that might occur during + * the transfer of other bytes the "priv->astart" parameter is + * used. This flag is set to TRUE in sendstart() and set to FALSE when + * the first TXIS event is received, which would be after the first byte + * (the address) is transmitted successfully (acknowledged). + */ + + if (status & I2C_INT_NACK) + { + if (priv->astart == true) + { + /* NACK received on first (address) byte: address is invalid */ + + i2cinfo("NACK: Address invalid: dcnt=%i msgc=%i " + "status=0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_NACKED, + priv->msgv->addr); + } + else + { + /* NACK received on regular byte */ + + i2cinfo("NACK: NACK received: dcnt=%i msgc=%i " + "status=0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_NACKED, + priv->msgv->addr); + } + + /* Set flags to terminate message transmission: + * + * set message length to -1 to indicate last byte of message sent + * set message count to 0 to indicate no more messages to send + * + * As we fall through the logic in the ISR the message handling block + * will be triggered by these flags and signal the ISR to terminate. + */ + + priv->dcnt = -1; + priv->msgc = 0; + } + + /* Transmit Interrupt Status (TXIS) Handler + * + * This branch is only triggered when the TXIS interrupt occurs. This + * interrupt will only fire when the I2C_CR1->TXIE bit is 1. + * + * This indicates the transmit data register I2C_TXDR has been emptied + * following the successful transmission of a byte and slave + * acknowledgment. + * In this state the I2C_TXDR register is ready to accept another byte for + * transmission. The TXIS bit will be cleared automatically when the next + * byte is written to I2C_TXDR. + * + * The number of TXIS events during the transfer corresponds to NBYTES. + * + * The TXIS flag is not set when a NACK is received. + * + * When RELOAD is disabled (RELOAD=0) and NBYTES data have been + * transferred: + * + * - In Automatic End Mode (AUTOEND=1), a STOP is automatically sent. + * + * Note: Automatic End Mode is not currently supported. + * + * - In Software End Mode (AUTOEND=0), the TC event occurs and the SCL + * line is stretched low in order to allow software actions (STOP, + * RESTART). + * + * When RELOAD is enabled (RELOAD=1) and NBYTES bytes have been transferred + * a TCR event occurs instead and that handler simply updates NBYTES which + * causes TXIS events to continue. The process repeats until all bytes in + * the message have been transferred. + */ + + else if ((priv->flags & (I2C_M_READ)) == 0 && + (status & (I2C_ISR_TXIS)) != 0) + { + /* TXIS interrupt occurred, address valid, ready to transmit */ + + stm32_i2c_traceevent(priv, I2CEVENT_WRITE, 0); + i2cinfo("TXIS: ENTER dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + + /* The first event after the address byte is sent will be either TXIS + * or NACKF so it's safe to set the astart flag to false on + * the first TXIS event to indicate that it is no longer necessary to + * check for address validity. + */ + + if (priv->astart == true) + { + i2cinfo("TXIS: Address Valid\n"); + stm32_i2c_traceevent(priv, + I2CEVENT_ADDRESS_ACKED, priv->msgv->addr); + priv->astart = false; + } + + /* If one or more bytes in the current message are ready to transmit */ + + if (priv->dcnt > 0) + { + /* Prepare to transmit the current byte */ + + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_TO_DR, priv->dcnt); + i2cinfo("TXIS: Write Data 0x%02x\n", *priv->ptr); + + /* Decrement byte counter */ + + priv->dcnt--; + + /* If we are about to transmit the last byte in the current + * message + */ + + if (priv->dcnt == 0) + { + /* If this is also the last message to send, disable RELOAD so + * TC fires next and issues STOP condition. If we don't do + * this TCR will fire next, and since there are no bytes to + * send we can't write NBYTES to clear TCR so it will fire + * forever. + */ + + if (priv->msgc == 1) + { + stm32_i2c_disable_reload(priv); + } + } + + /* Transmit current byte */ + + stm32_i2c_putreg(priv, STM32_I2C_TXDR_OFFSET, *priv->ptr); + + /* Advance to next byte */ + + priv->ptr++; + } + else + { + /* Unsupported state */ + + i2cerr("ERROR: TXIS Unsupported state detected, dcnt=%i, " + "status 0x%08" PRIx32 "\n", + priv->dcnt, status); + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_ERROR, 0); + + /* Indicate the bad state, so that on termination HW will be + * reset + */ + + priv->status |= I2C_INT_BAD_STATE; + } + + i2cinfo("TXIS: EXIT dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + } + + /* Receive Buffer Not Empty (RXNE) State Handler + * + * This branch is only triggered when the RXNE interrupt occurs. This + * interrupt will only fire when the I2C_CR1->RXIE bit is 1. + * + * This indicates data has been received from the bus and is waiting to + * be read from the I2C_RXDR register. When I2C_RXDR is read this bit + * is automatically cleared and then an ACK or NACK is sent depending on + * whether we have more bytes to receive. + * + * When RELOAD is disabled and bytes remain to be transferred an + * acknowledge is automatically sent on the bus and the RXNE events + * continue until the last byte is received. + * + * When RELOAD is disabled (RELOAD=0) and BYTES have been transferred: + * + * - In Automatic End Mode (AUTOEND=1), a NACK and a STOP are + * automatically sent after the last received byte. + * + * Note: Automatic End Mode is not currently supported. + * + * - In Software End Mode (AUTOEND=0), a NACK is automatically sent + * after the last received byte, the TC event occurs and the SCL + * line is stretched low in order to allow software actions + * (STOP, RESTART). + * + * When RELOAD is enabled (RELOAD=1) and NBYTES bytes have been + * transferred a TCR event occurs and that handler simply updates + * NBYTES which causes RXNE events to continue until all bytes have + * been transferred. + */ + + else if ((priv->flags & (I2C_M_READ)) != 0 && + (status & I2C_ISR_RXNE) != 0) + { + /* When read flag is set and the receive buffer is not empty + * (RXNE is set) then the driver can read from the data register. + */ + + stm32_i2c_traceevent(priv, I2CEVENT_READ, 0); + i2cinfo("RXNE: ENTER dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + + /* If more bytes in the current message */ + + if (priv->dcnt > 0) + { + stm32_i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); + + /* No interrupts or context switches may occur in the following + * sequence. Otherwise, additional bytes may be received. + */ + +#ifdef CONFIG_I2C_POLLED + irqstate_t state = enter_critical_section(); +#endif + /* Receive a byte */ + + *priv->ptr = stm32_i2c_getreg(priv, STM32_I2C_RXDR_OFFSET); + + i2cinfo("RXNE: Read Data 0x%02x\n", *priv->ptr); + + /* Advance buffer to the next byte in the message */ + + priv->ptr++; + + /* Signal byte received */ + + priv->dcnt--; + +#ifdef CONFIG_I2C_POLLED + leave_critical_section(state); +#endif + } + else + { + /* Unsupported state */ + + stm32_i2c_traceevent(priv, I2CEVENT_READ_ERROR, 0); + status = stm32_i2c_getreg(priv, STM32_I2C_ISR_OFFSET); + i2cerr("ERROR: RXNE Unsupported state detected, dcnt=%i, " + "status 0x%08" PRIx32 "\n", + priv->dcnt, status); + + /* Set signals that will terminate ISR and wake waiting thread */ + + priv->status |= I2C_INT_BAD_STATE; + priv->dcnt = -1; + priv->msgc = 0; + } + + i2cinfo("RXNE: EXIT dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + } + + /* Transfer Complete (TC) State Handler + * + * This branch is only triggered when the TC interrupt occurs. This + * interrupt will only fire when: + * + * I2C_CR1->TCIE = 1 (Transfer Complete Interrupts Enabled) + * I2C_CR2->RELOAD = 0 (Reload Mode Disabled) + * I2C_CR2->AUTOEND = 0 (Autoend Mode Disabled, i.e. Software End Mode) + * + * This event indicates that the number of bytes initially defined + * in NBYTES, meaning, the number of bytes in the current message + * (priv->dcnt) has been successfully transmitted or received. + * + * When the TC interrupt occurs we have two choices to clear it and move + * on, regardless of the transfer direction: + * + * - if more messages follow, perform a repeated START if required + * and then fall through to transmit or receive the next message. + * + * - if no messages follow, perform a STOP and set flags needed to + * exit the ISR. + * + * The fact that the hardware must either RESTART or STOP when a TC + * event occurs explains why, when messages must be sent back to back + * (i.e. without a restart by specifying the I2C_M_NOSTART flag), + * RELOAD mode must be enabled and TCR event(s) must be generated + * instead. See the TCR handler for more. + */ + + else if ((status & I2C_ISR_TC) != 0) + { + i2cinfo("TC: ENTER dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + + /* Prior message has been sent successfully. Or there could have + * been an error that set msgc to 0; So test for that case as + * we do not want to decrement msgc less then zero nor move msgv + * past the last message. + */ + + if (priv->msgc > 0) + { + priv->msgc--; + } + + /* Are there additional messages remain to be transmitted / received? */ + + if (priv->msgc > 0) + { + i2cinfo("TC: RESTART: dcnt=%i, msgc=%i\n", + priv->dcnt, priv->msgc); + stm32_i2c_traceevent(priv, I2CEVENT_TC_NO_RESTART, priv->msgc); + + /* Issue a START condition. + * + * Note that the first thing sendstart does is update the + * private structure "current message" data (ptr, dcnt, flags) + * so they all reflect the next message in the list so we + * update msgv before we get there. + */ + + /* Advance to the next message in the list */ + + priv->msgv++; + + stm32_i2c_sendstart(priv); + } + else + { + /* Issue a STOP conditions. + * + * No additional messages to transmit / receive, so the + * transfer is indeed complete. Nothing else to do but + * issue a STOP and exit. + */ + + i2cinfo("TC: STOP: dcnt=%i msgc=%i\n", + priv->dcnt, priv->msgc); + stm32_i2c_traceevent(priv, I2CEVENT_STOP, priv->dcnt); + + stm32_i2c_sendstop(priv); + + /* Set signals that will terminate ISR and wake waiting thread */ + + priv->dcnt = -1; + priv->msgc = 0; + } + + i2cinfo("TC: EXIT dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + } + + /* Transfer Complete (Reload) State Handler + * + * This branch is only triggered when the TCR interrupt occurs. This + * interrupt will only fire when: + * + * I2C_CR1->TCIE = 1 (Transfer Complete Interrupts Enabled) + * I2C_CR2->RELOAD = 1 (Reload Mode Active) + * I2C_CR2->AUTOEND = 0 (Autoend Mode Disabled, i.e. Software End Mode) + * + * This is similar to the TC event except that TCR assumes that additional + * bytes are available to transfer. So despite what its name might imply + * the transfer really isn't complete. + * + * There are two reasons RELOAD would be enabled: + * + * 1) We're trying to send a message with a payload greater than 255 bytes. + * 2) We're trying to send messages back to back, regardless of their + * payload size, to avoid a RESTART (i.e. I2C_M_NOSTART flag is set). + * + * These conditions may be true simultaneously, as would be the case if + * we're sending multiple messages with payloads > 255 bytes. So we only + * advance to the next message if we arrive here and dcnt is 0, meaning, + * we're finished with the last message and ready to move to the next. + * + * This logic supports the transfer of bytes limited only by the size of + * the i2c_msg_s length variable. The SCL line will be stretched low + * until NBYTES is written with a non-zero value, allowing the transfer + * to continue. + * + * TODO: + * RESTARTs are required by the I2CSPEC if the next message transfer + * direction changes. + * Right now the NORESTART flag overrides this behavior. + * May have to introduce logic to issue sendstart, assuming it's legal + * with the hardware in the TCR state. + */ + + else if ((status & I2C_ISR_TCR) != 0) + { + i2cinfo("TCR: ENTER dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + + /* If no more bytes in the current message to transfer */ + + if (priv->dcnt == 0) + { + /* Prior message has been sent successfully */ + + priv->msgc--; + + /* Advance to the next message in the list */ + + priv->msgv++; + + /* Update current message data */ + + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; + + /* if this is the last message, disable reload so the + * TC event fires next time. + */ + + if (priv->msgc == 0) + { + i2cinfo("TCR: DISABLE RELOAD: dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + + stm32_i2c_disable_reload(priv); + } + + /* Update NBYTES with length of current message */ + + i2cinfo("TCR: NEXT MSG dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + + stm32_i2c_set_bytes_to_transfer(priv, priv->dcnt); + } + else + { + /* More bytes in the current (greater than 255 byte payload + * length) message, so set NBYTES according to the bytes + * remaining in the message, up to a maximum each cycle of 255. + */ + + if (priv->dcnt > 255) + { + i2cinfo( + "TCR: ENABLE RELOAD: NBYTES = 255 dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + + /* More than 255 bytes to transfer so the RELOAD bit is + * set in order to generate a TCR event rather than a TC + * event when 255 bytes are successfully transferred. + * This forces us to return here to update NBYTES and + * continue until NBYTES is set to less than 255 bytes, + * at which point RELOAD will be disabled and a TC + * event will (eventually) follow to officially terminate + * the transfer. + */ + + stm32_i2c_enable_reload(priv); + + stm32_i2c_set_bytes_to_transfer(priv, 255); + } + else + { + /* Less than 255 bytes left to transfer, which means we'll + * complete the transfer of all bytes in the current message + * the next time around. + * + * This means we need to disable the RELOAD functionality so + * we receive a TC event next time which will allow us to + * either RESTART and continue sending the contents of the + * next message or send a STOP condition and exit the ISR. + */ + + i2cinfo("TCR: DISABLE RELOAD: NBYTES = dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + + stm32_i2c_set_bytes_to_transfer(priv, priv->dcnt); + + stm32_i2c_disable_reload(priv); + } + + i2cinfo("TCR: EXIT dcnt = %i msgc = %i status 0x%08" PRIx32 "\n", + priv->dcnt, priv->msgc, status); + } + } + + /* Empty call handler + * + * Case to handle an empty call to the ISR where it has nothing to + * do and should exit immediately. + */ + + else if (priv->dcnt == -1 && priv->msgc == 0) + { + status = stm32_i2c_getreg(priv, STM32_I2C_ISR_OFFSET); + i2cwarn("WARNING: EMPTY CALL: Stopping ISR: status 0x%08" PRIx32 "\n", + status); + stm32_i2c_traceevent(priv, I2CEVENT_ISR_EMPTY_CALL, 0); + } + + /* Error handler + * + * We get to this branch only if we can't handle the current state. + * + * This can happen in interrupt based operation on ARLO & BUSY. + * + * This will happen during polled operation when the device is not + * in one of the supported states when polled. + */ + + else + { +#ifdef CONFIG_I2C_POLLED + stm32_i2c_traceevent(priv, I2CEVENT_POLL_DEV_NOT_RDY, 0); +#else + /* Read rest of the state */ + + status = stm32_i2c_getreg(priv, STM32_I2C_ISR_OFFSET); + + i2cerr("ERROR: Invalid state detected, status 0x%08" PRIx32 "\n", + status); + + /* set condition to terminate ISR and wake waiting thread */ + + priv->status |= I2C_INT_BAD_STATE; + priv->dcnt = -1; + priv->msgc = 0; + stm32_i2c_traceevent(priv, I2CEVENT_STATE_ERROR, 0); +#endif + } + + /* ------------------- End of I2C protocol handling ------------------ */ + + /* Message Handling + * + * Transmission of the whole message chain has been completed. We have to + * terminate the ISR and wake up stm32_i2c_process() that is waiting for + * the ISR cycle to handle the sending/receiving of the messages. + */ + + if (priv->dcnt == -1 && priv->msgc == 0) + { + i2cinfo("MSG: Shutting down I2C ISR\n"); + + stm32_i2c_traceevent(priv, I2CEVENT_ISR_SHUTDOWN, 0); + + /* clear pointer to message content to reflect we are done + * with the current transaction. + */ + + priv->msgv = NULL; + +#ifdef CONFIG_I2C_POLLED + priv->intstate = INTSTATE_DONE; +#else + + /* Update private state to capture NACK which is used in combination + * with the astart flag to report the type of NACK received (address + * vs data) to the upper layers once we exit the ISR. + * + * Note: We do this prior to clearing interrupts because the NACKF + * flag will naturally be cleared by that process. + */ + + status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + + /* Clear all interrupts */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, + 0, I2C_ICR_CLEARMASK); + + /* Was a bad state detected in the processing? */ + + if (priv->status & I2C_INT_BAD_STATE) + { + /* SW reset device */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0); + } + + /* Update private status from above sans I2C_INT_BAD_STATE */ + + priv->status = status; + + /* If a thread is waiting then inform it transfer is complete */ + + if (priv->intstate == INTSTATE_WAITING) + { + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#endif + } + + status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + i2cinfo("EXIT: status = 0x%08" PRIx32 "\n", status); + + return OK; +} + +/**************************************************************************** + * Name: stm32_i2c_isr + * + * Description: + * Common I2C interrupt service routine + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static int stm32_i2c_isr(int irq, void *context, void *arg) +{ + struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)arg; + + DEBUGASSERT(priv != NULL); + return stm32_i2c_isr_process(priv); +} +#endif + +/**************************************************************************** + * Name: stm32_i2c_init + * + * Description: + * Setup the I2C hardware, ready for operation with defaults + * + ****************************************************************************/ + +static int stm32_i2c_init(struct stm32_i2c_priv_s *priv) +{ + /* Power-up and configure GPIOs */ + + /* Enable power and reset the peripheral */ + + modifyreg32(priv->config->clk_reg, 0, priv->config->clk_bit); + modifyreg32(priv->config->reset_reg, 0, priv->config->reset_bit); + modifyreg32(priv->config->reset_reg, priv->config->reset_bit, 0); + + /* Configure pins */ + + if (stm32_configgpio(priv->config->scl_pin) < 0) + { + return ERROR; + } + + if (stm32_configgpio(priv->config->sda_pin) < 0) + { + stm32_unconfiggpio(priv->config->scl_pin); + return ERROR; + } + +#ifndef CONFIG_I2C_POLLED + /* Attach error and event interrupts to the ISRs */ + + irq_attach(priv->config->ev_irq, stm32_i2c_isr, priv); + irq_attach(priv->config->er_irq, stm32_i2c_isr, priv); + up_enable_irq(priv->config->ev_irq); + up_enable_irq(priv->config->er_irq); +#endif + + /* - Provide means to set peripheral clock source via RCC_CCIPR4_I2CxSEL */ + + switch (priv->config->base) + { +#ifdef CONFIG_STM32H5_I2C1 + case STM32_I2C1_BASE: +# if defined(CONFIG_STM32H5_I2C1_CLK_HSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C1SEL_MASK, + RCC_CCIPR4_I2C1SEL_HSIKERCK); +# elif defined(CONFIG_STM32H5_I2C1_CLK_CSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C1SEL_MASK, + RCC_CCIPR4_I2C1SEL_CSIKERCK); +# elif defined(CONFIG_STM32H5_I2C1_CLK_PCLK1) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C1SEL_MASK, + RCC_CCIPR4_I2C1SEL_RCCPCLK1); +# else + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C1SEL_MASK, + RCC_CCIPR4_I2C1SEL_PLL3RCK); +# endif + break; +#endif +#ifdef CONFIG_STM32H5_I2C2 + case STM32_I2C2_BASE: +# if defined(CONFIG_STM32H5_I2C2_CLK_HSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C2SEL_MASK, + RCC_CCIPR4_I2C2SEL_HSIKERCK); +# elif defined(CONFIG_STM32H5_I2C2_CLK_CSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C2SEL_MASK, + RCC_CCIPR4_I2C2SEL_CSIKERCK); +# elif defined(CONFIG_STM32H5_I2C2_CLK_PCLK1) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C2SEL_MASK, + RCC_CCIPR4_I2C2SEL_PCLK1); +# else + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C2SEL_MASK, + RCC_CCIPR4_I2C2SEL_PLL3RCK); +# endif + break; +#endif +#ifdef CONFIG_STM32H5_I2C3 + case STM32_I2C3_BASE: +# if defined(CONFIG_STM32H5_I2C3_CLK_HSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C3SEL_MASK, + RCC_CCIPR4_I2C3SEL_HSIKERCK); +# elif defined(CONFIG_STM32H5_I2C3_CLK_CSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C3SEL_MASK, + RCC_CCIPR4_I2C3SEL_CSIKERCK); +# elif defined(CONFIG_STM32H5_I2C3_CLK_PCLK3) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C3SEL_MASK, + RCC_CCIPR4_I2C3SEL_PCLK3); +# else + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C3SEL_MASK, + RCC_CCIPR4_I2C3SEL_PLL3RCK); +# endif + break; +#endif +#ifdef CONFIG_STM32H5_I2C4 + case STM32_I2C4_BASE: +# if defined(CONFIG_STM32H5_I2C4_CLK_HSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C4SEL_MASK, + RCC_CCIPR4_I2C4SEL_HSIKERCK); +# elif defined(CONFIG_STM32H5_I2C4_CLK_CSI) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C4SEL_MASK, + RCC_CCIPR4_I2C4SEL_CSIKERCK); +# elif defined(CONFIG_STM32H5_I2C4_CLK_PCLK3) + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C4SEL_MASK, + RCC_CCIPR4_I2C4SEL_PCLK3); +# else + modifyreg32(STM32_RCC_CCIPR4, + RCC_CCIPR4_I2C4SEL_MASK, + RCC_CCIPR4_I2C4SEL_PLL3RCK); +# endif + break; +#endif + default: + } + + /* Force a frequency update */ + + priv->frequency = 0; + stm32_i2c_setclock(priv, 100000); + + return OK; +} + +/**************************************************************************** + * Name: stm32_i2c_deinit + * + * Description: + * Shutdown the I2C hardware + * + ****************************************************************************/ + +static int stm32_i2c_deinit(struct stm32_i2c_priv_s *priv) +{ + /* Disable I2C */ + + stm32_i2c_putreg32(priv, STM32_I2C_CR1_OFFSET, 0); + + /* Unconfigure GPIO pins */ + + stm32_unconfiggpio(priv->config->scl_pin); + stm32_unconfiggpio(priv->config->sda_pin); + +#ifndef CONFIG_I2C_POLLED + + /* Disable and detach interrupts */ + + up_disable_irq(priv->config->ev_irq); + up_disable_irq(priv->config->er_irq); + irq_detach(priv->config->ev_irq); + irq_detach(priv->config->er_irq); +#endif + + /* Disable clocking */ + + modifyreg32(priv->config->clk_reg, priv->config->clk_bit, 0); + + return OK; +} + +/**************************************************************************** + * Name: stm32_i2c_process + * + * Description: + * Common I2C transfer logic + * + * Initiates a master mode transaction on the I2C bus to transfer the + * provided messages to and from the slave devices. + * + ****************************************************************************/ + +static int stm32_i2c_process(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count) +{ + struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev; + struct stm32_i2c_priv_s *priv = inst->priv; + uint32_t status = 0; + uint32_t cr1; + uint32_t cr2; + int errval = 0; + int waitrc = 0; + + DEBUGASSERT(count > 0); + + /* Wait for any STOP in progress */ + + stm32_i2c_sem_waitstop(priv); + + /* Clear any pending error interrupts */ + + stm32_i2c_clearinterrupts(priv); + + /* Old transfers are done */ + + priv->msgv = msgs; + priv->msgc = count; + + /* Reset I2C trace logic */ + + stm32_i2c_tracereset(priv); + + /* Set I2C clock frequency toggles I2C_CR1_PE performing a SW reset! */ + + stm32_i2c_setclock(priv, msgs->frequency); + + /* Trigger start condition, then the process moves into the ISR. I2C + * interrupts will be enabled within stm32_i2c_waitdone(). + */ + + priv->status = 0; + +#ifndef CONFIG_I2C_POLLED + /* Enable transmit and receive interrupts here so when we send the start + * condition below the ISR will fire if the data was sent and some + * response from the slave received. All other interrupts relevant to + * our needs are enabled in stm32_i2c_sem_waitdone() below. + */ + + stm32_i2c_enableinterrupts(priv); +#endif + + /* Trigger START condition generation, which also sends the slave address + * with read/write flag and the data in the first message + */ + + stm32_i2c_sendstart(priv); + + /* Wait for the ISR to tell us that the transfer is complete by attempting + * to grab the semaphore that is initially locked by the ISR. If the ISR + * does not release the lock so we can obtain it here prior to the end of + * the timeout period waitdone returns error and we report a timeout. + */ + + waitrc = stm32_i2c_sem_waitdone(priv); + + cr1 = stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET); + cr2 = stm32_i2c_getreg32(priv, STM32_I2C_CR2_OFFSET); +#if !defined(CONFIG_DEBUG_I2C) + UNUSED(cr1); + UNUSED(cr2); +#endif + + /* Status after a normal / good exit is usually 0x00000001, meaning the TXE + * bit is set. That occurs as a result of the I2C_TXDR register being + * empty, and it naturally will be after the last byte is transmitted. + * This bit is cleared when we attempt communications again and re-enable + * the peripheral. The priv->status field can hold additional information + * like a NACK, so we reset the status field to include that information. + */ + + status = stm32_i2c_getstatus(priv); + + /* The priv->status field can hold additional information like a NACK + * event so we include that information. + */ + + status = priv->status & 0xffffffff; + + if (waitrc < 0) + { + /* Connection timed out */ + + errval = ETIMEDOUT; + i2cerr("ERROR: Waitdone timed out CR1: 0x%08" PRIx32 + " CR2: 0x%08" PRIx32 + " status: 0x%08" PRIx32 "\n", + cr1, cr2, status); + } + else + { + i2cinfo("Waitdone success: CR1: 0x%08" PRIx32 + " CR2: 0x%08" PRIx32 + " status: 0x%08" PRIx32 "\n", + cr1, cr2, status); + } + + UNUSED(cr1); + UNUSED(cr2); + + i2cinfo("priv->status: 0x%08" PRIx32 "\n", priv->status); + + /* Check for error status conditions */ + + if ((status & (I2C_INT_BERR | + I2C_INT_ARLO | + I2C_INT_OVR | + I2C_INT_PECERR | + I2C_INT_TIMEOUT | + I2C_INT_NACK)) != 0) + + { + /* one or more errors in the mask are present */ + + if (status & I2C_INT_BERR) + { + /* Bus Error, ignore it because of errata (revision A,Z) */ + + i2cerr("ERROR: I2C Bus Error\n"); + + /* errval = EIO; */ + } + else if (status & I2C_INT_ARLO) + { + /* Arbitration Lost (master mode) */ + + i2cerr("ERROR: I2C Arbitration Lost\n"); + errval = EAGAIN; + } + + else if (status & I2C_INT_OVR) + { + /* Overrun/Underrun */ + + i2cerr("ERROR: I2C Overrun/Underrun\n"); + errval = EIO; + } + else if (status & I2C_INT_PECERR) + { + /* PEC Error in reception (SMBus Only) */ + + i2cerr("ERROR: I2C PEC Error\n"); + errval = EPROTO; + } + else if (status & I2C_INT_TIMEOUT) + { + /* Timeout or Tlow Error (SMBus Only) */ + + i2cerr("ERROR: I2C Timeout / Tlow Error\n"); + errval = ETIME; + } + else if (status & I2C_INT_NACK) + { + /* NACK Received, flag as "communication error on send" */ + + if (priv->astart == TRUE) + { + i2cwarn("WARNING: I2C Address NACK\n"); + errval = EADDRNOTAVAIL; + } + else + { + i2cwarn("WARNING: I2C Data NACK\n"); + errval = ECOMM; + } + } + else + { + /* Unrecognized error */ + + i2cerr("ERROR: I2C Unrecognized Error"); + errval = EINTR; + } + } + + /* This is not an error, but should not happen. The BUSY signal can be + * present if devices on the bus are in an odd state and need to be reset. + * NOTE: We will only see this busy indication if stm32_i2c_sem_waitdone() + * fails above; Otherwise it is cleared. + */ + + else if ((status & I2C_ISR_BUSY) != 0) + { + /* I2C Bus Busy + * + * This is a status condition rather than an error. + * + * We will only see this busy indication if stm32_i2c_sem_waitdone() + * fails above; Otherwise it is cleared by the hardware when the ISR + * wraps up the transfer with a STOP condition. + */ + + clock_t start = clock_systime_ticks(); + clock_t timeout = USEC2TICK(USEC_PER_SEC / priv->frequency) + 1; + + status = stm32_i2c_getstatus(priv); + + while ((status & I2C_ISR_BUSY) != 0) + { + if ((clock_systime_ticks() - start) > timeout) + { + i2cerr("ERROR: I2C Bus busy"); + errval = EBUSY; + break; + } + + status = stm32_i2c_getstatus(priv); + } + } + + /* Dump the trace result */ + + stm32_i2c_tracedump(priv); + nxmutex_unlock(&priv->lock); + + return -errval; +} + +/**************************************************************************** + * Name: stm32_i2c_transfer + * + * Description: + * Generic I2C transfer function + * + ****************************************************************************/ + +static int stm32_i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count) +{ + struct stm32_i2c_priv_s *priv; + int ret; + + DEBUGASSERT(dev); + + /* Get I2C private structure */ + + priv = ((struct stm32_i2c_inst_s *)dev)->priv; + + /* Ensure that address or flags don't change meanwhile */ + + ret = nxmutex_lock(&priv->lock); + if (ret >= 0) + { + ret = stm32_i2c_process(dev, msgs, count); + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_i2c_reset + * + * Description: + * Reset an I2C bus + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int stm32_i2c_reset(struct i2c_master_s *dev) +{ + struct stm32_i2c_priv_s *priv; + unsigned int clock_count; + unsigned int stretch_count; + uint32_t scl_gpio; + uint32_t sda_gpio; + uint32_t frequency; + int ret; + + DEBUGASSERT(dev); + + /* Get I2C private structure */ + + priv = ((struct stm32_i2c_inst_s *)dev)->priv; + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = -EIO; + + /* Save the current frequency */ + + frequency = priv->frequency; + + /* De-init the port */ + + stm32_i2c_deinit(priv); + + /* Use GPIO configuration to un-wedge the bus */ + + scl_gpio = MKI2C_OUTPUT(priv->config->scl_pin); + sda_gpio = MKI2C_OUTPUT(priv->config->sda_pin); + + stm32_configgpio(sda_gpio); + stm32_configgpio(scl_gpio); + + /* Let SDA go high */ + + stm32_gpiowrite(sda_gpio, 1); + + /* Clock the bus until any slaves currently driving it let it go. */ + + clock_count = 0; + while (!stm32_gpioread(sda_gpio)) + { + /* Give up if we have tried too hard */ + + if (clock_count++ > 10) + { + goto out; + } + + /* Sniff to make sure that clock stretching has finished. + * + * If the bus never relaxes, the reset has failed. + */ + + stretch_count = 0; + while (!stm32_gpioread(scl_gpio)) + { + /* Give up if we have tried too hard */ + + if (stretch_count++ > 10) + { + goto out; + } + + up_udelay(10); + } + + /* Drive SCL low */ + + stm32_gpiowrite(scl_gpio, 0); + up_udelay(10); + + /* Drive SCL high again */ + + stm32_gpiowrite(scl_gpio, 1); + up_udelay(10); + } + + /* Generate a start followed by a stop to reset slave + * state machines. + */ + + stm32_gpiowrite(sda_gpio, 0); + up_udelay(10); + stm32_gpiowrite(scl_gpio, 0); + up_udelay(10); + stm32_gpiowrite(scl_gpio, 1); + up_udelay(10); + stm32_gpiowrite(sda_gpio, 1); + up_udelay(10); + + /* Revert the GPIO configuration. */ + + stm32_unconfiggpio(sda_gpio); + stm32_unconfiggpio(scl_gpio); + + /* Re-init the port */ + + stm32_i2c_init(priv); + + /* Restore the frequency */ + + stm32_i2c_setclock(priv, frequency); + ret = OK; + +out: + + /* Release the port for re-use by other clients */ + + nxmutex_unlock(&priv->lock); + return ret; +} +#endif /* CONFIG_I2C_RESET */ + +/**************************************************************************** + * Name: stm32_i2c_pm_prepare + * + * Description: + * Request the driver to prepare for a new power state. This is a + * warning that the system is about to enter into a new power state. The + * driver should begin whatever operations that may be required to enter + * power state. The driver may abort the state change mode by returning + * a non-zero value from the callback function. + * + * Input Parameters: + * cb - Returned to the driver. The driver version of the callback + * structure may include additional, driver-specific state + * data at the end of the structure. + * domain - Identifies the activity domain of the state change + * pmstate - Identifies the new PM state + * + * Returned Value: + * 0 (OK) means the event was successfully processed and that the driver + * is prepared for the PM state change. Non-zero means that the driver + * is not prepared to perform the tasks needed achieve this power setting + * and will cause the state change to be aborted. NOTE: The prepare + * method will also be recalled when reverting from lower back to higher + * power consumption modes (say because another driver refused a lower + * power state change). Drivers are not permitted to return non-zero + * values when reverting back to higher power consumption modes! + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static int stm32_i2c_pm_prepare(struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate) +{ + struct stm32_i2c_priv_s *priv = + (struct stm32_i2c_priv_s *)((char *)cb - + offsetof(struct stm32_i2c_priv_s, pm_cb)); + + /* Logic to prepare for a reduced power state goes here. */ + + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + break; + + case PM_STANDBY: + case PM_SLEEP: + + /* Check if exclusive lock for I2C bus is held. */ + + if (nxmutex_is_locked(&priv->lock)) + { + /* Exclusive lock is held, do not allow entry to deeper PM + * states. + */ + + return -EBUSY; + } + + break; + + default: + + /* Should not get here */ + + break; + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ****************************************************************************/ + +struct i2c_master_s *stm32_i2cbus_initialize(int port) +{ + struct stm32_i2c_priv_s *priv = NULL; /* private data of device with multiple instances */ + struct stm32_i2c_inst_s *inst = NULL; /* device, single instance */ + +#if defined(INVALID_CLOCK_SOURCE) +# warning STM32_I2C_INIT: Peripheral input clock must be HSI or the speed/timing calculations need to be redone. + return NULL; +#endif + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_STM32H5_I2C1 + case 1: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c1_priv; + break; +#endif +#ifdef CONFIG_STM32H5_I2C2 + case 2: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c2_priv; + break; +#endif +#ifdef CONFIG_STM32H5_I2C3 + case 3: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c3_priv; + break; +#endif +#ifdef CONFIG_STM32H5_I2C4 + case 4: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c4_priv; + break; +#endif + default: + return NULL; + } + + /* Allocate instance */ + + if (!(inst = kmm_malloc(sizeof(struct stm32_i2c_inst_s)))) + { + return NULL; + } + + /* Initialize instance */ + + inst->ops = &stm32_i2c_ops; + inst->priv = priv; + + /* Init private data for the first time, increment refs count, + * power-up hardware and configure GPIOs. + */ + + nxmutex_lock(&priv->lock); + if (priv->refs++ == 0) + { + stm32_i2c_init(priv); + +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + DEBUGVERIFY(pm_register(&priv->pm_cb)); +#endif + } + + nxmutex_unlock(&priv->lock); + return (struct i2c_master_s *)inst; +} + +/**************************************************************************** + * Name: stm32_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ****************************************************************************/ + +int stm32_i2cbus_uninitialize(struct i2c_master_s *dev) +{ + struct stm32_i2c_priv_s *priv; + + DEBUGASSERT(dev); + priv = ((struct stm32_i2c_inst_s *)dev)->priv; + + /* Decrement refs and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + nxmutex_lock(&priv->lock); + if (--priv->refs) + { + nxmutex_unlock(&priv->lock); + kmm_free(dev); + return OK; + } + +#ifdef CONFIG_PM + /* Unregister power management callbacks */ + + pm_unregister(&priv->pm_cb); +#endif + + /* Disable power and other HW resource (GPIO's) */ + + stm32_i2c_deinit(priv); + nxmutex_unlock(&priv->lock); + + kmm_free(dev); + return OK; +} + +#endif /* CONFIG_STM32H5_I2C1 || CONFIG_STM32H5_I2C2 || \ + * CONFIG_STM32H5_I2C3 || CONFIG_STM32H5_I2C4 */ diff --git a/arch/arm/src/stm32h5/stm32_i2c.h b/arch/arm/src/stm32h5/stm32_i2c.h new file mode 100644 index 00000000000..8f37a558f1d --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_i2c.h @@ -0,0 +1,89 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_i2c.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H5_STM32_I2C_H +#define __ARCH_ARM_SRC_STM32H5_STM32_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "chip.h" +#include "hardware/stm32h5xxx_i2c.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* If a dynamic timeout is selected, then a non-negative, non-zero micro- + * seconds per byte value must be provided as well. + */ + +#ifdef CONFIG_STM32H5_I2C_DYNTIMEO +# if CONFIG_STM32H5_I2C_DYNTIMEO_USECPERBYTE < 1 +# warning "Ignoring CONFIG_STM32H5_I2C_DYNTIMEO because of CONFIG_STM32H5_I2C_DYNTIMEO_USECPERBYTE" +# undef CONFIG_STM32H5_I2C_DYNTIMEO +# endif +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameters: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2c_master_s *stm32_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: stm32_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameters: + * Device structure as returned by the stm32_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int stm32_i2cbus_uninitialize(struct i2c_master_s *dev); + +#endif /* __ARCH_ARM_SRC_STM32H5_STM32_I2C_H */