From 8ce1f1a67b6df6beba214b5bae23eadc8d47cab9 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Wed, 15 May 2019 08:20:28 +0000 Subject: [PATCH] Merged in raiden00/nuttx_lora/lora (pull request #869) Port STM32F7 I2C to STM32F3 and STM32F0L0 arch/arm/src/stm32: port I2C IPv2 driver from F7 (only F3 chips) arch/arm/src/stm32f0l0: port I2C IPv2 driver from F7 configs/b-l072z-lrwan1: nxlines_oled example (ssd1306) configs/b-l072z-lrwan1: support for the I2C tool configs/nucleo-f303ze: nxlines_oled example (ssd1306) arch/arm/src/stm32h7/chip.h: cosmetics arch/arm/src/stm32/chip/stm32_tim.h: cosmetics Approved-by: Gregory Nutt --- arch/arm/src/stm32/Kconfig | 15 + arch/arm/src/stm32/Make.defs | 8 +- arch/arm/src/stm32/chip.h | 100 +- arch/arm/src/stm32/chip/stm32_i2c.h | 180 +- arch/arm/src/stm32/chip/stm32_i2c_v1.h | 214 ++ .../{stm32f30xxx_i2c.h => stm32_i2c_v2.h} | 13 +- arch/arm/src/stm32/chip/stm32_pinmap.h | 137 + arch/arm/src/stm32/chip/stm32_tim.h | 22 +- arch/arm/src/stm32/stm32_i2c.h | 7 +- arch/arm/src/stm32/stm32_i2c_v2.c | 2872 +++++++++++++++++ arch/arm/src/stm32/stm32f30xxx_i2c.c | 2019 ------------ arch/arm/src/stm32f0l0/stm32_i2c.c | 2270 +++++++++---- arch/arm/src/stm32h7/chip.h | 8 +- configs/b-l072z-lrwan1/include/board.h | 2 +- configs/b-l072z-lrwan1/nxlines_oled/defconfig | 73 + configs/b-l072z-lrwan1/src/Makefile | 4 + configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h | 4 + configs/b-l072z-lrwan1/src/stm32_bringup.c | 59 + configs/b-l072z-lrwan1/src/stm32_ssd1306.c | 124 + configs/nucleo-f303ze/include/board.h | 11 + configs/nucleo-f303ze/nxlines_oled/defconfig | 61 + configs/nucleo-f303ze/src/Makefile | 4 + configs/nucleo-f303ze/src/nucleo-f303ze.h | 4 + configs/nucleo-f303ze/src/stm32_ssd1306.c | 124 + 24 files changed, 5315 insertions(+), 3020 deletions(-) create mode 100644 arch/arm/src/stm32/chip/stm32_i2c_v1.h rename arch/arm/src/stm32/chip/{stm32f30xxx_i2c.h => stm32_i2c_v2.h} (98%) create mode 100644 arch/arm/src/stm32/chip/stm32_pinmap.h create mode 100644 arch/arm/src/stm32/stm32_i2c_v2.c delete mode 100644 arch/arm/src/stm32/stm32f30xxx_i2c.c create mode 100644 configs/b-l072z-lrwan1/nxlines_oled/defconfig create mode 100644 configs/b-l072z-lrwan1/src/stm32_ssd1306.c create mode 100644 configs/nucleo-f303ze/nxlines_oled/defconfig create mode 100644 configs/nucleo-f303ze/src/stm32_ssd1306.c diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index 096c932f5c..a9e9b5cac0 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -1325,6 +1325,7 @@ config STM32_STM32L15XX select STM32_HAVE_IP_TIMERS_V1 select STM32_HAVE_IP_ADC_V1 select STM32_HAVE_IP_DMA_V1 + select STM32_HAVE_IP_I2C_V1 config STM32_ENERGYLITE bool @@ -1343,6 +1344,7 @@ config STM32_STM32F10XX select STM32_HAVE_IP_TIMERS_V1 select STM32_HAVE_IP_ADC_V1_BASIC select STM32_HAVE_IP_DMA_V1 + select STM32_HAVE_IP_I2C_V1 config STM32_VALUELINE bool @@ -1457,6 +1459,7 @@ config STM32_STM32F20XX select STM32_HAVE_IP_TIMERS_V1 select STM32_HAVE_IP_ADC_V1 select STM32_HAVE_IP_DMA_V2 + select STM32_HAVE_IP_I2C_V1 config STM32_STM32F205 bool @@ -1541,6 +1544,7 @@ config STM32_STM32F30XX select STM32_HAVE_IP_TIMERS_V2 select STM32_HAVE_IP_ADC_V2 select STM32_HAVE_IP_DMA_V1 + select STM32_HAVE_IP_I2C_V2 config STM32_STM32F302 bool @@ -1587,6 +1591,7 @@ config STM32_STM32F33XX select STM32_HAVE_IP_TIMERS_V2 select STM32_HAVE_IP_ADC_V2 select STM32_HAVE_IP_DMA_V1 + select STM32_HAVE_IP_I2C_V2 config STM32_STM32F37XX bool @@ -1614,6 +1619,7 @@ config STM32_STM32F37XX select STM32_HAVE_IP_TIMERS_V1 select STM32_HAVE_IP_ADC_V1_BASIC select STM32_HAVE_IP_DMA_V1 + select STM32_HAVE_IP_I2C_V2 config STM32_STM32F4XXX bool @@ -1625,6 +1631,7 @@ config STM32_STM32F4XXX select STM32_HAVE_IP_TIMERS_V1 select STM32_HAVE_IP_ADC_V1 select STM32_HAVE_IP_DMA_V2 + select STM32_HAVE_IP_I2C_V1 config STM32_STM32F401xBC bool @@ -2214,6 +2221,14 @@ config STM32_HAVE_OPAMP4 # These are STM32 peripherals IP blocks +config STM32_HAVE_IP_I2C_V1 + bool + default n + +config STM32_HAVE_IP_I2C_V2 + bool + default n + config STM32_HAVE_IP_DMA_V1 bool default n diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs index 39699c6a21..f5950ab2e0 100644 --- a/arch/arm/src/stm32/Make.defs +++ b/arch/arm/src/stm32/Make.defs @@ -119,17 +119,17 @@ ifeq ($(CONFIG_STM32_CCM_PROCFS),y) CHIP_CSRCS += stm32_procfs_ccm.c endif +ifeq ($(CONFIG_STM32_HAVE_IP_I2C_V1),y) ifeq ($(CONFIG_STM32_I2C_ALT),y) CHIP_CSRCS += stm32_i2c_alt.c -else ifeq ($(CONFIG_STM32_STM32F30XX),y) -CHIP_CSRCS += stm32f30xxx_i2c.c -else ifeq ($(CONFIG_STM32_STM32F37XX),y) -CHIP_CSRCS += stm32f30xxx_i2c.c else ifeq ($(CONFIG_STM32_STM32F4XXX),y) CHIP_CSRCS += stm32f40xxx_i2c.c else CHIP_CSRCS += stm32_i2c.c endif +else ifeq ($(CONFIG_STM32_HAVE_IP_I2C_V2),y) +CHIP_CSRCS += stm32_i2c_v2.c +endif ifeq ($(CONFIG_USBDEV),y) ifeq ($(CONFIG_STM32_USB),y) diff --git a/arch/arm/src/stm32/chip.h b/arch/arm/src/stm32/chip.h index 36f6254ae8..f65832b1d2 100644 --- a/arch/arm/src/stm32/chip.h +++ b/arch/arm/src/stm32/chip.h @@ -50,104 +50,14 @@ #include -/* Include the chip pin configuration file */ - -/* STM32L EnergyLite Line ***********************************************************/ - -#if defined(CONFIG_STM32_ENERGYLITE) - -/* STM32L15xx family */ - -# if defined(CONFIG_STM32_STM32L15XX) -# include "chip/stm32l15xxx_pinmap.h" -# else -# error "Unsupported EnergyLite chip" -# endif - -/* STM32 F1 Family ******************************************************************/ - -#elif defined(CONFIG_STM32_STM32F10XX) - -/* STM32F100 Value Line */ - -# if defined(CONFIG_STM32_VALUELINE) -# include "chip/stm32f100_pinmap.h" - -/* STM32 F102 USB Access Medium Density Family */ -# elif defined(CONFIG_ARCH_CHIP_STM32F102CB) -# include "chip/stm32f102_pinmap.h" - -/* STM32 F103 Low / Medium Density Family */ -# elif defined(CONFIG_ARCH_CHIP_STM32F103C4) || \ - defined(CONFIG_ARCH_CHIP_STM32F103C8) || \ - defined(CONFIG_ARCH_CHIP_STM32F103CB) -# include "chip/stm32f103c_pinmap.h" - -/* STM32 F103 High Density Family */ -/* STM32F103RC, STM32F103RD, and STM32F103RE are all provided in 64 pin packages and differ - * only in the available FLASH and SRAM. - */ - -# elif defined(CONFIG_ARCH_CHIP_STM32F103RB) || \ - defined(CONFIG_ARCH_CHIP_STM32F103RC) || \ - defined(CONFIG_ARCH_CHIP_STM32F103RD) || \ - defined(CONFIG_ARCH_CHIP_STM32F103RE) || \ - defined(CONFIG_ARCH_CHIP_STM32F103RG) -# include "chip/stm32f103r_pinmap.h" - -/* STM32F103VC, STM32F103VD, and STM32F103VE are all provided in 100 pin packages and differ - * only in the available FLASH and SRAM. - */ - -# elif defined(CONFIG_ARCH_CHIP_STM32F103VC) || defined(CONFIG_ARCH_CHIP_STM32F103VE) -# include "chip/stm32f103v_pinmap.h" - -/* STM32F103ZC, STM32F103ZD, and STM32F103ZE are all provided in 144 pin packages and differ - * only in the available FLASH and SRAM. - */ -# elif defined(CONFIG_ARCH_CHIP_STM32F103ZE) -# include "chip/stm32f103z_pinmap.h" - -/* STM32 F105/F107 Connectivity Line */ - -# elif defined(CONFIG_ARCH_CHIP_STM32F105VB) -# include "chip/stm32f105v_pinmap.h" - -# elif defined(CONFIG_ARCH_CHIP_STM32F105RB) -# include "chip/stm32f105r_pinmap.h" - -# elif defined(CONFIG_ARCH_CHIP_STM32F107VC) -# include "chip/stm32f107v_pinmap.h" -# else -# error "Unsupported STM32F10XXX chip" -# endif - -/* STM32 F2 Family ******************************************************************/ - -#elif defined(CONFIG_STM32_STM32F20XX) -# include "chip/stm32f20xxx_pinmap.h" - -/* STM32 F3 Family ******************************************************************/ - -#elif defined(CONFIG_STM32_STM32F30XX) -# include "chip/stm32f30xxx_pinmap.h" -#elif defined(CONFIG_STM32_STM32F33XX) -# include "chip/stm32f33xxx_pinmap.h" -#elif defined(CONFIG_STM32_STM32F37XX) -# include "chip/stm32f37xxx_pinmap.h" - -/* STM32 F4 Family ******************************************************************/ - -#elif defined(CONFIG_STM32_STM32F4XXX) -# include "chip/stm32f40xxx_pinmap.h" -#else -# error "No pinmap file for this STM32 chip" -#endif - -/* Include the chip memory map. */ +/* Include the chip memory map */ #include "chip/stm32_memorymap.h" +/* Include the chip pinmap */ + +#include "chip/stm32_pinmap.h" + /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ diff --git a/arch/arm/src/stm32/chip/stm32_i2c.h b/arch/arm/src/stm32/chip/stm32_i2c.h index 682059772c..49c3591cad 100644 --- a/arch/arm/src/stm32/chip/stm32_i2c.h +++ b/arch/arm/src/stm32/chip/stm32_i2c.h @@ -36,177 +36,17 @@ #ifndef __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_H #define __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_H -/************************************************************************************ - * Pre-processor Definitions - ************************************************************************************/ +/* There are 2 main types of I2C IP cores among STM32 chips: + * 1. STM32 ADC IPv1 - F1, F2, F4 and L1 + * 2. STM32 ADC IPv2 - G0, L0, F0, F3, F7, H7 and L4 + */ -/* Register Offsets *****************************************************************/ - -#define STM32_I2C_CR1_OFFSET 0x0000 /* Control register 1 (16-bit) */ -#define STM32_I2C_CR2_OFFSET 0x0004 /* Control register 2 (16-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_DR_OFFSET 0x0010 /* Data register (16-bit) */ -#define STM32_I2C_SR1_OFFSET 0x0014 /* Status register 1 (16-bit) */ -#define STM32_I2C_SR2_OFFSET 0x0018 /* Status register 2 (16-bit) */ -#define STM32_I2C_CCR_OFFSET 0x001c /* Clock control register (16-bit) */ -#define STM32_I2C_TRISE_OFFSET 0x0020 /* TRISE Register (16-bit) */ -#if defined(CONFIG_STM32_STM32F427) || defined(CONFIG_STM32_STM32F429) || \ - defined(CONFIG_STM32_STM32F446) -# define STM32_I2C_FLTR_OFFSET 0x0024 /* FLTR Register (16-bit) */ -#endif - -/* Register Addresses ***************************************************************/ - -#if STM32_NI2C > 0 -# 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_DR (STM32_I2C1_BASE+STM32_I2C_DR_OFFSET) -# define STM32_I2C1_SR1 (STM32_I2C1_BASE+STM32_I2C_SR1_OFFSET) -# define STM32_I2C1_SR2 (STM32_I2C1_BASE+STM32_I2C_SR2_OFFSET) -# define STM32_I2C1_CCR (STM32_I2C1_BASE+STM32_I2C_CCR_OFFSET) -# define STM32_I2C1_TRISE (STM32_I2C1_BASE+STM32_I2C_TRISE_OFFSET) -# ifdef STM32_I2C_FLTR_OFFSET -# define STM32_I2C1_FLTR (STM32_I2C1_BASE+STM32_I2C_FLTR_OFFSET) -# endif -#endif - -#if STM32_NI2C > 1 -# 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_DR (STM32_I2C2_BASE+STM32_I2C_DR_OFFSET) -# define STM32_I2C2_SR1 (STM32_I2C2_BASE+STM32_I2C_SR1_OFFSET) -# define STM32_I2C2_SR2 (STM32_I2C2_BASE+STM32_I2C_SR2_OFFSET) -# define STM32_I2C2_CCR (STM32_I2C2_BASE+STM32_I2C_CCR_OFFSET) -# define STM32_I2C2_TRISE (STM32_I2C2_BASE+STM32_I2C_TRISE_OFFSET) -# ifdef STM32_I2C_FLTR_OFFSET -# define STM32_I2C2_FLTR (STM32_I2C2_BASE+STM32_I2C_FLTR_OFFSET) -# endif -#endif - -#if STM32_NI2C > 2 -# 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_DR (STM32_I2C3_BASE+STM32_I2C_DR_OFFSET) -# define STM32_I2C3_SR1 (STM32_I2C3_BASE+STM32_I2C_SR1_OFFSET) -# define STM32_I2C3_SR2 (STM32_I2C3_BASE+STM32_I2C_SR2_OFFSET) -# define STM32_I2C3_CCR (STM32_I2C3_BASE+STM32_I2C_CCR_OFFSET) -# define STM32_I2C3_TRISE (STM32_I2C3_BASE+STM32_I2C_TRISE_OFFSET) -# ifdef STM32_I2C_FLTR_OFFSET -# define STM32_I2C3_FLTR (STM32_I2C3_BASE+STM32_I2C_FLTR_OFFSET) -# endif -#endif - -/* Register Bitfield Definitions ****************************************************/ - -/* Control register 1 */ - -#define I2C_CR1_PE (1 << 0) /* Bit 0: Peripheral Enable */ -#define I2C_CR1_SMBUS (1 << 1) /* Bit 1: SMBus Mode */ -#define I2C_CR1_SMBTYPE (1 << 3) /* Bit 3: SMBus Type */ -#define I2C_CR1_ENARP (1 << 4) /* Bit 4: ARP Enable */ -#define I2C_CR1_ENPEC (1 << 5) /* Bit 5: PEC Enable */ -#define I2C_CR1_ENGC (1 << 6) /* Bit 6: General Call Enable */ -#define I2C_CR1_NOSTRETCH (1 << 7) /* Bit 7: Clock Stretching Disable (Slave mode) */ -#define I2C_CR1_START (1 << 8) /* Bit 8: Start Generation */ -#define I2C_CR1_STOP (1 << 9) /* Bit 9: Stop Generation */ -#define I2C_CR1_ACK (1 << 10) /* Bit 10: Acknowledge Enable */ -#define I2C_CR1_POS (1 << 11) /* Bit 11: Acknowledge/PEC Position (for data reception) */ -#define I2C_CR1_PEC (1 << 12) /* Bit 12: Packet Error Checking */ -#define I2C_CR1_ALERT (1 << 13) /* Bit 13: SMBus Alert */ -#define I2C_CR1_SWRST (1 << 15) /* Bit 15: Software Reset */ - -/* Control register 2 */ - -#define I2C_CR2_FREQ_SHIFT (0) /* Bits 5-0: Peripheral Clock Frequency */ -#define I2C_CR2_FREQ_MASK (0x3f << I2C_CR2_FREQ_SHIFT) -#define I2C_CR2_ITERREN (1 << 8) /* Bit 8: Error Interrupt Enable */ -#define I2C_CR2_ITEVFEN (1 << 9) /* Bit 9: Event Interrupt Enable */ -#define I2C_CR2_ITBUFEN (1 << 10) /* Bit 10: Buffer Interrupt Enable */ -#define I2C_CR2_DMAEN (1 << 11) /* Bit 11: DMA Requests Enable */ -#define I2C_CR2_LAST (1 << 12) /* Bit 12: DMA Last Transfer */ - -#define I2C_CR2_ALLINTS (I2C_CR2_ITERREN|I2C_CR2_ITEVFEN|I2C_CR2_ITBUFEN) - -/* Own address register 1 */ - -#define I2C_OAR1_ADD0 (1 << 0) /* Bit 0: Interface Address */ -#define I2C_OAR1_ADD8_SHIFT (1) /* Bits 7-1: Interface Address */ -#define I2C_OAR1_ADD8_MASK (0x007f << I2C_OAR1_ADD8_SHIFT) -#define I2C_OAR1_ADD10_SHIFT (1) /* Bits 9-1: Interface Address (10-bit addressing mode)*/ -#define I2C_OAR1_ADD10_MASK (0x01ff << I2C_OAR1_ADD10_SHIFT) -#define I2C_OAR1_ONE (1 << 14) /* Bit 14: Must be configured and kept at 1 */ -#define I2C_OAR1_ADDMODE (1 << 15) /* Bit 15: Addressing Mode (Slave mode) */ - -/* Own address register 2 */ - -#define I2C_OAR2_ENDUAL (1 << 0) /* Bit 0: Dual addressing mode enable */ -#define I2C_OAR2_ADD2_SHIFT (1) /* Bits 7-1: Interface address */ -#define I2C_OAR2_ADD2_MASK (0x7f << I2C_OAR2_ADD2_SHIFT) - -/* Data register */ - -#define I2C_DR_SHIFT (0) /* Bits 7-0: 8-bit Data Register */ -#define I2C_DR_MASK (0x00ff << I2C_DR_SHIFT) - -/* Status register 1 */ - -#define I2C_SR1_SB (1 << 0) /* Bit 0: Start Bit (Master mode) */ -#define I2C_SR1_ADDR (1 << 1) /* Bit 1: Address sent (master mode)/matched (slave mode) */ -#define I2C_SR1_BTF (1 << 2) /* Bit 2: Byte Transfer Finished */ -#define I2C_SR1_ADD10 (1 << 3) /* Bit 3: 10-bit header sent (Master mode) */ -#define I2C_SR1_STOPF (1 << 4) /* Bit 4: Stop detection (Slave mode) */ - /* Bit 5: Reserved */ -#define I2C_SR1_RXNE (1 << 6) /* Bit 6: Data Register not Empty (receivers) */ -#define I2C_SR1_TXE (1 << 7) /* Bit 7: Data Register Empty (transmitters) */ -#define I2C_SR1_BERR (1 << 8) /* Bit 8: Bus Error */ -#define I2C_SR1_ARLO (1 << 9) /* Bit 9: Arbitration Lost (master mode) */ -#define I2C_SR1_AF (1 << 10) /* Bit 10: Acknowledge Failure */ -#define I2C_SR1_OVR (1 << 11) /* Bit 11: Overrun/Underrun */ -#define I2C_SR1_PECERR (1 << 12) /* Bit 12: PEC Error in reception */ - /* Bit 13: Reserved */ -#define I2C_SR1_TIMEOUT (1 << 14) /* Bit 14: Timeout or Tlow Error */ -#define I2C_SR1_SMBALERT (1 << 15) /* Bit 15: SMBus Alert */ - -#define I2C_SR1_ERRORMASK (I2C_SR1_BERR|I2C_SR1_ARLO|I2C_SR1_AF|I2C_SR1_OVR|\ - I2C_SR1_PECERR|I2C_SR1_TIMEOUT|I2C_SR1_SMBALERT) - -/* Status register 2 */ - -#define I2C_SR2_MSL (1 << 0) /* Bit 0: Master/Slave */ -#define I2C_SR2_BUSY (1 << 1) /* Bit 1: Bus Busy */ -#define I2C_SR2_TRA (1 << 2) /* Bit 2: Transmitter/Receiver */ -#define I2C_SR2_GENCALL (1 << 4) /* Bit 4: General Call Address (Slave mode) */ -#define I2C_SR2_SMBDEFAULT (1 << 5) /* Bit 5: SMBus Device Default Address (Slave mode) */ -#define I2C_SR2_SMBHOST (1 << 6) /* Bit 6: SMBus Host Header (Slave mode) */ -#define I2C_SR2_DUALF (1 << 7) /* Bit 7: Dual Flag (Slave mode) */ -#define I2C_SR2_PEC_SHIFT (8) /* Bits 15-8: Packet Error Checking Register */ -#define I2C_SR2_PEC_MASK (0xff << I2C_SR2_PEC_SHIFT) - -/* Clock control register */ - -#define I2C_CCR_CCR_SHIFT (0) /* Bits 11-0: Clock Control Register in Fast/Standard mode (Master mode) */ -#define I2C_CCR_CCR_MASK (0x0fff << I2C_CCR_CCR_SHIFT) -#define I2C_CCR_DUTY (1 << 14) /* Bit 14: Fast Mode Duty Cycle */ -#define I2C_CCR_FS (1 << 15) /* Bit 15: Fast Mode Selection */ - -/* TRISE Register */ - -#define I2C_TRISE_SHIFT (0) /* Bits 5-0: Maximum Rise Time in Fast/Standard mode (Master mode) */ -#define I2C_TRISE_MASK (0x3f << I2C_TRISE_SHIFT) - -/* FLTR Register */ - -#ifdef STM32_I2C_FLTR_OFFSET -# define I2C_FLTR_ANOFF (1 << 4) /* Bit 4: Analog noise filter disable */ -# define I2C_FLTR_DNF_SHIFT 0 /* Bits 0-3: Digital noise filter */ -# define I2C_FLTR_DNF_MASK (0xf << I2C_FLTR_DNF_SHIFT) +#if defined(CONFIG_STM32_HAVE_IP_I2C_V1) +# include "stm32_i2c_v1.h" +#elif defined(CONFIG_STM32_HAVE_IP_I2C_V2) +# include "stm32_i2c_v2.h" +#else +# error STM32 I2C IP version not specified #endif #endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_H */ diff --git a/arch/arm/src/stm32/chip/stm32_i2c_v1.h b/arch/arm/src/stm32/chip/stm32_i2c_v1.h new file mode 100644 index 0000000000..51483f01ca --- /dev/null +++ b/arch/arm/src/stm32/chip/stm32_i2c_v1.h @@ -0,0 +1,214 @@ +/************************************************************************************ + * arch/arm/src/stm32/chip/stm32_i2c_v1.h + * + * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_V1_H +#define __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_V1_H + +/* This file provide definitions for the STM32 I2C IP core 1 (F1, F2, F4 and L1) */ + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Register Offsets *****************************************************************/ + +#define STM32_I2C_CR1_OFFSET 0x0000 /* Control register 1 (16-bit) */ +#define STM32_I2C_CR2_OFFSET 0x0004 /* Control register 2 (16-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_DR_OFFSET 0x0010 /* Data register (16-bit) */ +#define STM32_I2C_SR1_OFFSET 0x0014 /* Status register 1 (16-bit) */ +#define STM32_I2C_SR2_OFFSET 0x0018 /* Status register 2 (16-bit) */ +#define STM32_I2C_CCR_OFFSET 0x001c /* Clock control register (16-bit) */ +#define STM32_I2C_TRISE_OFFSET 0x0020 /* TRISE Register (16-bit) */ +#if defined(CONFIG_STM32_STM32F427) || defined(CONFIG_STM32_STM32F429) || \ + defined(CONFIG_STM32_STM32F446) +# define STM32_I2C_FLTR_OFFSET 0x0024 /* FLTR Register (16-bit) */ +#endif + +/* Register Addresses ***************************************************************/ + +#if STM32_NI2C > 0 +# 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_DR (STM32_I2C1_BASE+STM32_I2C_DR_OFFSET) +# define STM32_I2C1_SR1 (STM32_I2C1_BASE+STM32_I2C_SR1_OFFSET) +# define STM32_I2C1_SR2 (STM32_I2C1_BASE+STM32_I2C_SR2_OFFSET) +# define STM32_I2C1_CCR (STM32_I2C1_BASE+STM32_I2C_CCR_OFFSET) +# define STM32_I2C1_TRISE (STM32_I2C1_BASE+STM32_I2C_TRISE_OFFSET) +# ifdef STM32_I2C_FLTR_OFFSET +# define STM32_I2C1_FLTR (STM32_I2C1_BASE+STM32_I2C_FLTR_OFFSET) +# endif +#endif + +#if STM32_NI2C > 1 +# 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_DR (STM32_I2C2_BASE+STM32_I2C_DR_OFFSET) +# define STM32_I2C2_SR1 (STM32_I2C2_BASE+STM32_I2C_SR1_OFFSET) +# define STM32_I2C2_SR2 (STM32_I2C2_BASE+STM32_I2C_SR2_OFFSET) +# define STM32_I2C2_CCR (STM32_I2C2_BASE+STM32_I2C_CCR_OFFSET) +# define STM32_I2C2_TRISE (STM32_I2C2_BASE+STM32_I2C_TRISE_OFFSET) +# ifdef STM32_I2C_FLTR_OFFSET +# define STM32_I2C2_FLTR (STM32_I2C2_BASE+STM32_I2C_FLTR_OFFSET) +# endif +#endif + +#if STM32_NI2C > 2 +# 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_DR (STM32_I2C3_BASE+STM32_I2C_DR_OFFSET) +# define STM32_I2C3_SR1 (STM32_I2C3_BASE+STM32_I2C_SR1_OFFSET) +# define STM32_I2C3_SR2 (STM32_I2C3_BASE+STM32_I2C_SR2_OFFSET) +# define STM32_I2C3_CCR (STM32_I2C3_BASE+STM32_I2C_CCR_OFFSET) +# define STM32_I2C3_TRISE (STM32_I2C3_BASE+STM32_I2C_TRISE_OFFSET) +# ifdef STM32_I2C_FLTR_OFFSET +# define STM32_I2C3_FLTR (STM32_I2C3_BASE+STM32_I2C_FLTR_OFFSET) +# endif +#endif + +/* Register Bitfield Definitions ****************************************************/ + +/* Control register 1 */ + +#define I2C_CR1_PE (1 << 0) /* Bit 0: Peripheral Enable */ +#define I2C_CR1_SMBUS (1 << 1) /* Bit 1: SMBus Mode */ +#define I2C_CR1_SMBTYPE (1 << 3) /* Bit 3: SMBus Type */ +#define I2C_CR1_ENARP (1 << 4) /* Bit 4: ARP Enable */ +#define I2C_CR1_ENPEC (1 << 5) /* Bit 5: PEC Enable */ +#define I2C_CR1_ENGC (1 << 6) /* Bit 6: General Call Enable */ +#define I2C_CR1_NOSTRETCH (1 << 7) /* Bit 7: Clock Stretching Disable (Slave mode) */ +#define I2C_CR1_START (1 << 8) /* Bit 8: Start Generation */ +#define I2C_CR1_STOP (1 << 9) /* Bit 9: Stop Generation */ +#define I2C_CR1_ACK (1 << 10) /* Bit 10: Acknowledge Enable */ +#define I2C_CR1_POS (1 << 11) /* Bit 11: Acknowledge/PEC Position (for data reception) */ +#define I2C_CR1_PEC (1 << 12) /* Bit 12: Packet Error Checking */ +#define I2C_CR1_ALERT (1 << 13) /* Bit 13: SMBus Alert */ +#define I2C_CR1_SWRST (1 << 15) /* Bit 15: Software Reset */ + +/* Control register 2 */ + +#define I2C_CR2_FREQ_SHIFT (0) /* Bits 5-0: Peripheral Clock Frequency */ +#define I2C_CR2_FREQ_MASK (0x3f << I2C_CR2_FREQ_SHIFT) +#define I2C_CR2_ITERREN (1 << 8) /* Bit 8: Error Interrupt Enable */ +#define I2C_CR2_ITEVFEN (1 << 9) /* Bit 9: Event Interrupt Enable */ +#define I2C_CR2_ITBUFEN (1 << 10) /* Bit 10: Buffer Interrupt Enable */ +#define I2C_CR2_DMAEN (1 << 11) /* Bit 11: DMA Requests Enable */ +#define I2C_CR2_LAST (1 << 12) /* Bit 12: DMA Last Transfer */ + +#define I2C_CR2_ALLINTS (I2C_CR2_ITERREN|I2C_CR2_ITEVFEN|I2C_CR2_ITBUFEN) + +/* Own address register 1 */ + +#define I2C_OAR1_ADD0 (1 << 0) /* Bit 0: Interface Address */ +#define I2C_OAR1_ADD8_SHIFT (1) /* Bits 7-1: Interface Address */ +#define I2C_OAR1_ADD8_MASK (0x007f << I2C_OAR1_ADD8_SHIFT) +#define I2C_OAR1_ADD10_SHIFT (1) /* Bits 9-1: Interface Address (10-bit addressing mode)*/ +#define I2C_OAR1_ADD10_MASK (0x01ff << I2C_OAR1_ADD10_SHIFT) +#define I2C_OAR1_ONE (1 << 14) /* Bit 14: Must be configured and kept at 1 */ +#define I2C_OAR1_ADDMODE (1 << 15) /* Bit 15: Addressing Mode (Slave mode) */ + +/* Own address register 2 */ + +#define I2C_OAR2_ENDUAL (1 << 0) /* Bit 0: Dual addressing mode enable */ +#define I2C_OAR2_ADD2_SHIFT (1) /* Bits 7-1: Interface address */ +#define I2C_OAR2_ADD2_MASK (0x7f << I2C_OAR2_ADD2_SHIFT) + +/* Data register */ + +#define I2C_DR_SHIFT (0) /* Bits 7-0: 8-bit Data Register */ +#define I2C_DR_MASK (0x00ff << I2C_DR_SHIFT) + +/* Status register 1 */ + +#define I2C_SR1_SB (1 << 0) /* Bit 0: Start Bit (Master mode) */ +#define I2C_SR1_ADDR (1 << 1) /* Bit 1: Address sent (master mode)/matched (slave mode) */ +#define I2C_SR1_BTF (1 << 2) /* Bit 2: Byte Transfer Finished */ +#define I2C_SR1_ADD10 (1 << 3) /* Bit 3: 10-bit header sent (Master mode) */ +#define I2C_SR1_STOPF (1 << 4) /* Bit 4: Stop detection (Slave mode) */ + /* Bit 5: Reserved */ +#define I2C_SR1_RXNE (1 << 6) /* Bit 6: Data Register not Empty (receivers) */ +#define I2C_SR1_TXE (1 << 7) /* Bit 7: Data Register Empty (transmitters) */ +#define I2C_SR1_BERR (1 << 8) /* Bit 8: Bus Error */ +#define I2C_SR1_ARLO (1 << 9) /* Bit 9: Arbitration Lost (master mode) */ +#define I2C_SR1_AF (1 << 10) /* Bit 10: Acknowledge Failure */ +#define I2C_SR1_OVR (1 << 11) /* Bit 11: Overrun/Underrun */ +#define I2C_SR1_PECERR (1 << 12) /* Bit 12: PEC Error in reception */ + /* Bit 13: Reserved */ +#define I2C_SR1_TIMEOUT (1 << 14) /* Bit 14: Timeout or Tlow Error */ +#define I2C_SR1_SMBALERT (1 << 15) /* Bit 15: SMBus Alert */ + +#define I2C_SR1_ERRORMASK (I2C_SR1_BERR|I2C_SR1_ARLO|I2C_SR1_AF|I2C_SR1_OVR|\ + I2C_SR1_PECERR|I2C_SR1_TIMEOUT|I2C_SR1_SMBALERT) + +/* Status register 2 */ + +#define I2C_SR2_MSL (1 << 0) /* Bit 0: Master/Slave */ +#define I2C_SR2_BUSY (1 << 1) /* Bit 1: Bus Busy */ +#define I2C_SR2_TRA (1 << 2) /* Bit 2: Transmitter/Receiver */ +#define I2C_SR2_GENCALL (1 << 4) /* Bit 4: General Call Address (Slave mode) */ +#define I2C_SR2_SMBDEFAULT (1 << 5) /* Bit 5: SMBus Device Default Address (Slave mode) */ +#define I2C_SR2_SMBHOST (1 << 6) /* Bit 6: SMBus Host Header (Slave mode) */ +#define I2C_SR2_DUALF (1 << 7) /* Bit 7: Dual Flag (Slave mode) */ +#define I2C_SR2_PEC_SHIFT (8) /* Bits 15-8: Packet Error Checking Register */ +#define I2C_SR2_PEC_MASK (0xff << I2C_SR2_PEC_SHIFT) + +/* Clock control register */ + +#define I2C_CCR_CCR_SHIFT (0) /* Bits 11-0: Clock Control Register in Fast/Standard mode (Master mode) */ +#define I2C_CCR_CCR_MASK (0x0fff << I2C_CCR_CCR_SHIFT) +#define I2C_CCR_DUTY (1 << 14) /* Bit 14: Fast Mode Duty Cycle */ +#define I2C_CCR_FS (1 << 15) /* Bit 15: Fast Mode Selection */ + +/* TRISE Register */ + +#define I2C_TRISE_SHIFT (0) /* Bits 5-0: Maximum Rise Time in Fast/Standard mode (Master mode) */ +#define I2C_TRISE_MASK (0x3f << I2C_TRISE_SHIFT) + +/* FLTR Register */ + +#ifdef STM32_I2C_FLTR_OFFSET +# define I2C_FLTR_ANOFF (1 << 4) /* Bit 4: Analog noise filter disable */ +# define I2C_FLTR_DNF_SHIFT 0 /* Bits 0-3: Digital noise filter */ +# define I2C_FLTR_DNF_MASK (0xf << I2C_FLTR_DNF_SHIFT) +#endif + +#endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_V1_H */ diff --git a/arch/arm/src/stm32/chip/stm32f30xxx_i2c.h b/arch/arm/src/stm32/chip/stm32_i2c_v2.h similarity index 98% rename from arch/arm/src/stm32/chip/stm32f30xxx_i2c.h rename to arch/arm/src/stm32/chip/stm32_i2c_v2.h index 40a0bee37e..f25fb33bcd 100644 --- a/arch/arm/src/stm32/chip/stm32f30xxx_i2c.h +++ b/arch/arm/src/stm32/chip/stm32_i2c_v2.h @@ -1,5 +1,5 @@ /************************************************************************************ - * arch/arm/src/stm32/chip/stm32f30xxx_i2c.h + * arch/arm/src/stm32/chip/stm32_i2c_v2.h * * Copyright (C) 2009, 2011, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -33,11 +33,11 @@ * ************************************************************************************/ -#ifndef __ARCH_ARM_SRC_STM32_CHIP_STM32F30XXX_I2C_H -#define __ARCH_ARM_SRC_STM32_CHIP_STM32F30XXX_I2C_H +#ifndef __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_V2_H +#define __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_V2_H -/* This file provide definitions for the STM32 I2C IP core 2 (F0, F3, F7, H7, and - * L4). +/* This file provide definitions for the STM32 I2C IP core 2 (G0, L0, F0, F3, F7, + * H7, and L4). */ /************************************************************************************ @@ -250,5 +250,4 @@ #define I2C_TXDR_MASK (0xff) -#endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32F30XXX_I2C_H */ - +#endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32_I2C_V2_H */ diff --git a/arch/arm/src/stm32/chip/stm32_pinmap.h b/arch/arm/src/stm32/chip/stm32_pinmap.h new file mode 100644 index 0000000000..dcefbba1cd --- /dev/null +++ b/arch/arm/src/stm32/chip/stm32_pinmap.h @@ -0,0 +1,137 @@ +/************************************************************************************ + * arch/arm/src/stm32/chip/stm32_pinmap.h + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_CHIP_STM32_PINMAP_H +#define __ARCH_ARM_SRC_STM32_CHIP_STM32_PINMAP_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +/* STM32L EnergyLite Line ***********************************************************/ + +#if defined(CONFIG_STM32_ENERGYLITE) + +/* STM32L15xx family */ + +# if defined(CONFIG_STM32_STM32L15XX) +# include "chip/stm32l15xxx_pinmap.h" +# else +# error "Unsupported EnergyLite chip" +# endif + +/* STM32 F1 Family ******************************************************************/ + +#elif defined(CONFIG_STM32_STM32F10XX) + +/* STM32F100 Value Line */ + +# if defined(CONFIG_STM32_VALUELINE) +# include "chip/stm32f100_pinmap.h" + +/* STM32 F102 USB Access Medium Density Family */ +# elif defined(CONFIG_ARCH_CHIP_STM32F102CB) +# include "chip/stm32f102_pinmap.h" + +/* STM32 F103 Low / Medium Density Family */ +# elif defined(CONFIG_ARCH_CHIP_STM32F103C4) || \ + defined(CONFIG_ARCH_CHIP_STM32F103C8) || \ + defined(CONFIG_ARCH_CHIP_STM32F103CB) +# include "chip/stm32f103c_pinmap.h" + +/* STM32 F103 High Density Family */ +/* STM32F103RC, STM32F103RD, and STM32F103RE are all provided in 64 pin packages and differ + * only in the available FLASH and SRAM. + */ + +# elif defined(CONFIG_ARCH_CHIP_STM32F103RB) || \ + defined(CONFIG_ARCH_CHIP_STM32F103RC) || \ + defined(CONFIG_ARCH_CHIP_STM32F103RD) || \ + defined(CONFIG_ARCH_CHIP_STM32F103RE) || \ + defined(CONFIG_ARCH_CHIP_STM32F103RG) +# include "chip/stm32f103r_pinmap.h" + +/* STM32F103VC, STM32F103VD, and STM32F103VE are all provided in 100 pin packages and differ + * only in the available FLASH and SRAM. + */ + +# elif defined(CONFIG_ARCH_CHIP_STM32F103VC) || defined(CONFIG_ARCH_CHIP_STM32F103VE) +# include "chip/stm32f103v_pinmap.h" + +/* STM32F103ZC, STM32F103ZD, and STM32F103ZE are all provided in 144 pin packages and differ + * only in the available FLASH and SRAM. + */ +# elif defined(CONFIG_ARCH_CHIP_STM32F103ZE) +# include "chip/stm32f103z_pinmap.h" + +/* STM32 F105/F107 Connectivity Line */ + +# elif defined(CONFIG_ARCH_CHIP_STM32F105VB) +# include "chip/stm32f105v_pinmap.h" + +# elif defined(CONFIG_ARCH_CHIP_STM32F105RB) +# include "chip/stm32f105r_pinmap.h" + +# elif defined(CONFIG_ARCH_CHIP_STM32F107VC) +# include "chip/stm32f107v_pinmap.h" +# else +# error "Unsupported STM32F10XXX chip" +# endif + +/* STM32 F2 Family ******************************************************************/ + +#elif defined(CONFIG_STM32_STM32F20XX) +# include "chip/stm32f20xxx_pinmap.h" + +/* STM32 F3 Family ******************************************************************/ + +#elif defined(CONFIG_STM32_STM32F30XX) +# include "chip/stm32f30xxx_pinmap.h" +#elif defined(CONFIG_STM32_STM32F33XX) +# include "chip/stm32f33xxx_pinmap.h" +#elif defined(CONFIG_STM32_STM32F37XX) +# include "chip/stm32f37xxx_pinmap.h" + +/* STM32 F4 Family ******************************************************************/ + +#elif defined(CONFIG_STM32_STM32F4XXX) +# include "chip/stm32f40xxx_pinmap.h" +#else +# error "No pinmap file for this STM32 chip" +#endif + +#endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32_PINMAP_H */ diff --git a/arch/arm/src/stm32/chip/stm32_tim.h b/arch/arm/src/stm32/chip/stm32_tim.h index 1f91e5c2d3..2c8093ddb5 100644 --- a/arch/arm/src/stm32/chip/stm32_tim.h +++ b/arch/arm/src/stm32/chip/stm32_tim.h @@ -47,17 +47,17 @@ /* TIM version **************************************************************************************/ /* Chip has extended version of ADV Timers (F3/F7/H7/L4/L4+): - - CCMR3, CCR5 and CCR6 registers - - OC5 and OC6 - - 32-bit CCMR register - - UIFREMAP bit in CR1 register - - TRGO2 configuration in CR2 register - - OCCS bit and 4-bit SMS in SMCR register - - Chip has extended version of General Timers 2-5 (F3/F7/H7/L4/L4+): - - UIFREMAP bit in CR1 register - - 4-bit SMS in SMCR register -*/ + * - CCMR3, CCR5 and CCR6 registers + * - OC5 and OC6 + * - 32-bit CCMR register + * - UIFREMAP bit in CR1 register + * - TRGO2 configuration in CR2 register + * - OCCS bit and 4-bit SMS in SMCR register + * + * Chip has extended version of General Timers 2-5 (F3/F7/H7/L4/L4+): + * - UIFREMAP bit in CR1 register + * - 4-bit SMS in SMCR register + */ #if defined(CONFIG_STM32_HAVE_IP_TIMERS_V2) # define HAVE_IP_TIMERS_V2 diff --git a/arch/arm/src/stm32/stm32_i2c.h b/arch/arm/src/stm32/stm32_i2c.h index c61f17332b..5118f26cc1 100644 --- a/arch/arm/src/stm32/stm32_i2c.h +++ b/arch/arm/src/stm32/stm32_i2c.h @@ -44,12 +44,7 @@ #include #include "chip.h" -#if defined(CONFIG_STM32_STM32F30XX) || defined(CONFIG_STM32_STM32F37XX) || \ - defined(CONFIG_STM32_STM32F33XX) -# include "chip/stm32f30xxx_i2c.h" -#else -# include "chip/stm32_i2c.h" -#endif +#include "chip/stm32_i2c.h" /**************************************************************************** * Pre-processor Definitions diff --git a/arch/arm/src/stm32/stm32_i2c_v2.c b/arch/arm/src/stm32/stm32_i2c_v2.c new file mode 100644 index 0000000000..213a8c42d2 --- /dev/null +++ b/arch/arm/src/stm32/stm32_i2c_v2.c @@ -0,0 +1,2872 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32_i2c.c + * STM32 I2C IPv2 Hardware Layer - Device Driver ported from STM32F7 + * + * 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 + * + * 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 + * + * 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 I2C IPv2 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_DCKCFGR2_I2CxSRC + * being set to HSI and the calculations are based on STM32_HSI_FREQUENCY + * of 16mHz + * + * - 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 + * - Polled operation (code present but untested) + * - SMBus support + * - Multi-master support + * - IPMI + * + * Test Environment: + * + * - NUCLEO-F303ZE + * + * 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: + * + * CONFIG_STM32_I2C1 + * CONFIG_STM32_I2C2 + * CONFIG_STM32_I2C3 + * CONFIG_STM32_I2C4 + * + * To configure the ISR timeout using fixed values (CONFIG_STM32_I2C_DYNTIMEO=n): + * + * CONFIG_STM32_I2CTIMEOSEC (Timeout in seconds) + * CONFIG_STM32_I2CTIMEOMS (Timeout in milliseconds) + * CONFIG_STM32_I2CTIMEOTICKS (Timeout in ticks) + * + * To configure the ISR timeout using dynamic values (CONFIG_STM32_I2C_DYNTIMEO=y): + * + * CONFIG_STM32_I2C_DYNTIMEO_USECPERBYTE (Timeout in microseconds per byte) + * CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP (Timeout for start/stop in milliseconds) + * + * 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 + * + * ------------------------------------------------------------------------------ + * + * References: + * + * RM0431: + * ST STM322xxx and STM323xxx Reference Manual + * Document ID: DocID029480 Revision 1, Jan 2017. + * + * RM0316: + * ST STM326xxx and STM327xxx Reference Manual + * Document ID: DocID028270 Revision 2, April 2016. + * + * DATASHEET: + * ST STM3277xx/STM3278Ax/STM3279x Datasheet + * Document ID: DocID028294, Revision 3, May 2016. + * + * ERRATA: + * STM326xxx/STM327xxx Errata sheet Rev A device limitations + * Document ID: DocID028806, Revision 2, April 2016. + * + * I2CSPEC: + * I2C Bus Specification and User Manual + * Document ID: UM10204, Revision 6, April 2014. + * + * ------------------------------------------------------------------------------ + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "up_arch.h" + +#include "stm32_rcc.h" +#include "stm32_i2c.h" +#include "stm32_gpio.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_STM32_I2C1) || defined(CONFIG_STM32_I2C2) || \ + defined(CONFIG_STM32_I2C3) || defined(CONFIG_STM32_I2C4) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#undef INVALID_CLOCK_SOURCE + +#warning TODO: check I2C clock source. It must be HSI! + +/* 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_STM32_I2CTIMEOSEC) && !defined(CONFIG_STM32_I2CTIMEOMS) +# define CONFIG_STM32_I2CTIMEOSEC 0 +# define CONFIG_STM32_I2CTIMEOMS 500 /* Default is 500 milliseconds */ +# warning "Using Default 500 Ms Timeout" +#elif !defined(CONFIG_STM32_I2CTIMEOSEC) +# define CONFIG_STM32_I2CTIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_STM32_I2CTIMEOMS) +# define CONFIG_STM32_I2CTIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_STM32_I2CTIMEOTICKS +# define CONFIG_STM32_I2CTIMEOTICKS \ + (SEC2TICK(CONFIG_STM32_I2CTIMEOSEC) + MSEC2TICK(CONFIG_STM32_I2CTIMEOMS)) +#endif + +#ifndef CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP +# define CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_STM32_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 + +/************************************************************************************ + * 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_bit; /* Clock enable bit */ + 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 +{ + const struct stm32_i2c_config_s *config; /* Port configuration */ + int refs; /* Reference count */ + sem_t sem_excl; /* Mutual exclusion semaphore */ +#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(FAR struct stm32_i2c_priv_s *priv, + uint8_t offset); +static inline void stm32_i2c_putreg(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, + uint16_t value); +static inline void stm32_i2c_putreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, + uint32_t value); +static inline void stm32_i2c_modifyreg32(FAR struct stm32_i2c_priv_s *priv, + uint8_t offset, uint32_t clearbits, + uint32_t setbits); +static inline void stm32_i2c_sem_wait(FAR struct i2c_master_s *dev); +#ifdef CONFIG_STM32_I2C_DYNTIMEO +static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); +#endif /* CONFIG_STM32_I2C_DYNTIMEO */ +static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sem_post(FAR struct i2c_master_s *dev); +static inline void stm32_i2c_sem_init(FAR struct i2c_master_s *dev); +static inline void stm32_i2c_sem_destroy(FAR struct i2c_master_s *dev); +#ifdef CONFIG_I2C_TRACE +static void stm32_i2c_tracereset(FAR struct stm32_i2c_priv_s *priv); +static void stm32_i2c_tracenew(FAR struct stm32_i2c_priv_s *priv, uint32_t status); +static void stm32_i2c_traceevent(FAR struct stm32_i2c_priv_s *priv, + enum stm32_trace_e event, uint32_t parm); +static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ +static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, + uint32_t frequency); +static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv); +static inline uint32_t stm32_i2c_getstatus(FAR 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, FAR void *arg); +#endif +static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv); +static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv); + +static int stm32_i2c_process(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, + int count); +static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, + int count); +#ifdef CONFIG_I2C_RESET +static int stm32_i2c_reset(FAR struct i2c_master_s * dev); +#endif +#ifdef CONFIG_PM +static int stm32_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); +#endif + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +#ifdef CONFIG_STM32_I2C1 +static const struct stm32_i2c_config_s stm32_i2c1_config = +{ + .base = STM32_I2C1_BASE, + .clk_bit = RCC_APB1ENR_I2C1EN, + .reset_bit = RCC_APB1RSTR_I2C1RST, + .scl_pin = GPIO_I2C1_SCL, + .sda_pin = GPIO_I2C1_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C1EV, + .er_irq = STM32_IRQ_I2C1ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c1_priv = +{ + .config = &stm32_i2c1_config, + .refs = 0, + .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_STM32_I2C2 +static const struct stm32_i2c_config_s stm32_i2c2_config = +{ + .base = STM32_I2C2_BASE, + .clk_bit = RCC_APB1ENR_I2C2EN, + .reset_bit = RCC_APB1RSTR_I2C2RST, + .scl_pin = GPIO_I2C2_SCL, + .sda_pin = GPIO_I2C2_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C2EV, + .er_irq = STM32_IRQ_I2C2ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c2_priv = +{ + .config = &stm32_i2c2_config, + .refs = 0, + .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_STM32_I2C3 +static const struct stm32_i2c_config_s stm32_i2c3_config = +{ + .base = STM32_I2C3_BASE, + .clk_bit = RCC_APB1ENR_I2C3EN, + .reset_bit = RCC_APB1RSTR_I2C3RST, + .scl_pin = GPIO_I2C3_SCL, + .sda_pin = GPIO_I2C3_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C3EV, + .er_irq = STM32_IRQ_I2C3ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c3_priv = +{ + .config = &stm32_i2c3_config, + .refs = 0, + .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_STM32_I2C4 +static const struct stm32_i2c_config_s stm32_i2c4_config = +{ + .base = STM32_I2C4_BASE, + .clk_bit = RCC_APB1ENR_I2C4EN, + .reset_bit = RCC_APB1RSTR_I2C4RST, + .scl_pin = GPIO_I2C4_SCL, + .sda_pin = GPIO_I2C4_SDA, +#ifndef CONFIG_I2C_POLLED + .ev_irq = STM32_IRQ_I2C4EV, + .er_irq = STM32_IRQ_I2C4ER +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c4_priv = +{ + .config = &stm32_i2c4_config, + .refs = 0, + .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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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_sem_wait + * + * Description: + * Take the exclusive access, waiting as necessary + * + ************************************************************************************/ + +static inline void stm32_i2c_sem_wait(FAR struct i2c_master_s *dev) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +/************************************************************************************ + * Name: stm32_i2c_tousecs + * + * Description: + * Return a micro-second delay based on the number of bytes left to be processed. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32_I2C_DYNTIMEO +static useconds_t stm32_i2c_tousecs(int msgc, FAR 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 (useconds_t)(CONFIG_STM32_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(FAR struct stm32_i2c_priv_s *priv) +{ + struct timespec abstime; + 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 + { + /* Get the current time */ + + (void)clock_gettime(CLOCK_REALTIME, &abstime); + + /* Calculate a time in the future */ + +#if CONFIG_STM32_I2CTIMEOSEC > 0 + abstime.tv_sec += CONFIG_STM32_I2CTIMEOSEC; +#endif + + /* Add a value proportional to the number of bytes in the transfer */ + +#ifdef CONFIG_STM32_I2C_DYNTIMEO + abstime.tv_nsec += 1000 * stm32_i2c_tousecs(priv->msgc, priv->msgv); + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } + +#elif CONFIG_STM32_I2CTIMEOMS > 0 + abstime.tv_nsec += CONFIG_STM32_I2CTIMEOMS * 1000 * 1000; + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } +#endif + /* Wait until either the transfer is complete or the timeout expires */ + + ret = nxsem_timedwait(&priv->sem_isr, &abstime); + if (ret < 0 && ret != -EINTR) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by nxsem_timedwait. + * NOTE that we try again if we are awakened by a signal (EINTR). + */ + + 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(FAR struct stm32_i2c_priv_s *priv) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_STM32_I2C_DYNTIMEO + timeout = USEC2TICK(stm32_i2c_tousecs(priv->msgc, priv->msgv)); +#else + timeout = CONFIG_STM32_I2CTIMEOTICKS; +#endif + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_timedwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + start = clock_systimer(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systimer() - 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%08x\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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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_STM32_I2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_STM32_I2CTIMEOTICKS; +#endif + + /* Wait as stop might still be in progress */ + + start = clock_systimer(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systimer() - 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: %04x SR: %04x\n", cr, sr); +} + +/************************************************************************************ + * Name: stm32_i2c_sem_post + * + * Description: + * Release the mutual exclusion semaphore + * + ************************************************************************************/ + +static inline void stm32_i2c_sem_post(FAR struct i2c_master_s *dev) +{ + nxsem_post(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); +} + +/************************************************************************************ + * Name: stm32_i2c_sem_init + * + * Description: + * Initialize semaphores + * + ************************************************************************************/ + +static inline void stm32_i2c_sem_init(FAR struct i2c_master_s *dev) +{ + nxsem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl, 0, 1); + +#ifndef CONFIG_I2C_POLLED + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, 0, 0); + nxsem_setprotocol(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, SEM_PRIO_NONE); +#endif +} + +/************************************************************************************ + * Name: stm32_i2c_sem_destroy + * + * Description: + * Destroy semaphores. + * + ************************************************************************************/ + +static inline void stm32_i2c_sem_destroy(FAR struct i2c_master_s *dev) +{ + nxsem_destroy(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); +#ifndef CONFIG_I2C_POLLED + nxsem_destroy(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr); +#endif +} + +/************************************************************************************ + * Name: stm32_i2c_trace* + * + * Description: + * I2C trace instrumentation + * + ************************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void stm32_i2c_traceclear(FAR 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(FAR struct stm32_i2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systimer(); + stm32_i2c_traceclear(priv); +} + +static void stm32_i2c_tracenew(FAR 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_systimer(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void stm32_i2c_traceevent(FAR 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(FAR struct stm32_i2c_priv_s *priv) +{ + struct stm32_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %d\n", + (int)(clock_systimer() - 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: + * + * 1000Khz (Fast Mode+) + * 400Khz (Fast Mode) + * 100Khz (Standard Mode) + * 10Khz (Standard Mode) + * + * Attempts to set a different frequency will quietly provision the default + * of 10Khz. + * + * The only differences between the various modes of operation (std, fast, + * fast+) are the bus clock speed and setup/hold times. Setup/hold times are + * specified as a MINIMUM time for the given mode, and naturally std mode + * has the longest minimum times. As a result, by provisioning setup/hold + * times for std mode they are also compatible with fast/fast+, though some + * performance degradation occurs in fast/fast+ as a result of the times + * being somewhat longer than strictly required. The values remain as they + * are because reliability is favored over performance. + * + * Clock Selection: + * + * The I2C peripheral clock can be provided by either PCLK1, SYSCLK or the HSI. + * + * PCLK1 >------|\ I2CCLK + * SYSCLK >------| |---------> + * HSI >------|/ + * + * HSI is the default and is always 8Mhz. + * + * SYSCLK can, in turn, be derived from the HSI, HSE, PPLCLK. + * + * HSI >------|\ + * | | SYSCLK + * PLL >------| |---------> + * | | + * HSE >------|/ + * + * + * References: + * + * App Note AN4235 and the associated software STSW-STM32126. + * + ************************************************************************************/ + +static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency) +{ + uint8_t presc; + uint8_t scl_delay; + uint8_t sda_delay; + uint8_t scl_h_period; + uint8_t scl_l_period; + + /* 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); + + if (frequency != priv->frequency) + { + + /* The Speed and timing calculation are based on the following + * fI2CCLK = HSI and is 16Mhz + * Analog filter is on, + * Digital filter off + * Rise Time is 120 ns and fall is 10ns + * Mode is FastMode + */ + + if (frequency == 100000) + { + presc = 0; + scl_delay = 5; + sda_delay = 0; + scl_h_period = 61; + scl_l_period = 89; + + } + else if (frequency == 400000) + { + presc = 0; + scl_delay = 3; + sda_delay = 0; + scl_h_period = 6; + scl_l_period = 24; + } + else if (frequency == 1000000) + { + presc = 0; + scl_delay = 2; + sda_delay = 0; + scl_h_period = 1; + scl_l_period = 5; + } + else + { + presc = 7; + scl_delay = 0; + sda_delay = 0; + scl_h_period = 35; + scl_l_period = 162; + } + + 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; + } + + /* 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(FAR 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(FAR 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(FAR 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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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%08x\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_disable_reload(priv); + + stm32_i2c_set_bytes_to_transfer(priv, priv->dcnt); + } + + i2cinfo("TCR: EXIT dcnt = %i msgc = %i status 0x%08x\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%08x\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%08x\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 + + /* We will 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: status is captured 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%08x\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, FAR 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(FAR struct stm32_i2c_priv_s *priv) +{ + /* Power-up and configure GPIOs */ + + /* Enable power and reset the peripheral */ + + modifyreg32(STM32_RCC_APB1ENR, 0, priv->config->clk_bit); + modifyreg32(STM32_RCC_APB1RSTR, 0, priv->config->reset_bit); + modifyreg32(STM32_RCC_APB1RSTR, 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 + + /* TODO: + * - Provide means to set peripheral clock source via RCC_CFGR3_I2CxSW + * - Set to HSI by default, make Kconfig option + */ + + /* 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(FAR 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(STM32_RCC_APB1ENR, 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(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) +{ + struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev; + FAR 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%08x CR2: 0x%08x status: 0x%08x\n", + cr1, cr2,status); + } + else + { + i2cinfo("Waitdone success: CR1: 0x%08x CR2: 0x%08x status: 0x%08x\n", + cr1, cr2,status ); + } + + UNUSED(cr1); + UNUSED(cr2); + + i2cinfo("priv->status: 0x%08x\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_systimer(); + clock_t timeout = USEC2TICK(USEC_PER_SEC/priv->frequency) + 1; + + status = stm32_i2c_getstatus(priv); + + while (status & I2C_ISR_BUSY) + { + if ((clock_systimer() - start) > timeout) + { + i2cerr("ERROR: I2C Bus busy"); + errval = EBUSY; + break; + } + + status = stm32_i2c_getstatus(priv); + } + } + + /* Dump the trace result */ + + stm32_i2c_tracedump(priv); + stm32_i2c_sem_post(dev); + + return -errval; +} + +/************************************************************************************ + * Name: stm32_i2c_transfer + * + * Description: + * Generic I2C transfer function + * + ************************************************************************************/ + +static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, + int count) +{ + stm32_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */ + return stm32_i2c_process(dev, msgs, count); +} + +/************************************************************************************ + * Name: stm32_i2c_reset + * + * Description: + * Reset an I2C bus + * + ************************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int stm32_i2c_reset(FAR 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 = ERROR; + + 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 */ + + stm32_i2c_sem_wait(dev); + + /* 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 */ + + stm32_i2c_sem_post(dev); + 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(FAR 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)); + int sval; + + /* 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 (nxsem_getvalue(&priv->sem_excl, &sval) < 0) + { + DEBUGASSERT(false); + return -EINVAL; + } + + if (sval <= 0) + { + /* 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 + * + ************************************************************************************/ + +FAR 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 */ + irqstate_t irqs; +#ifdef CONFIG_PM + int ret; +#endif + +#if STM32_HSI_FREQUENCY != 8000000 || defined(INVALID_CLOCK_SOURCE) +# warning STM32_I2C_INIT: Peripheral clock is HSI and it must be 16mHz or the speed/timing calculations need to be redone. + return NULL; +#endif + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_STM32_I2C1 + case 1: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c1_priv; + break; +#endif +#ifdef CONFIG_STM32_I2C2 + case 2: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c2_priv; + break; +#endif +#ifdef CONFIG_STM32_I2C3 + case 3: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c3_priv; + break; +#endif +#ifdef CONFIG_STM32_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. + */ + + irqs = enter_critical_section(); + + if ((volatile int)priv->refs++ == 0) + { + stm32_i2c_sem_init((struct i2c_master_s *)inst); + stm32_i2c_init(priv); + +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + ret = pm_register(&priv->pm_cb); + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif + } + + leave_critical_section(irqs); + return (struct i2c_master_s *)inst; +} + +/************************************************************************************ + * Name: stm32_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ************************************************************************************/ + +int stm32_i2cbus_uninitialize(FAR struct i2c_master_s * dev) +{ + irqstate_t irqs; + + DEBUGASSERT(dev); + + /* Decrement refs and check for underflow */ + + if (((struct stm32_i2c_inst_s *)dev)->priv->refs == 0) + { + return ERROR; + } + + irqs = enter_critical_section(); + + if (--((struct stm32_i2c_inst_s *)dev)->priv->refs) + { + leave_critical_section(irqs); + kmm_free(dev); + return OK; + } + + leave_critical_section(irqs); + +#ifdef CONFIG_PM + /* Unregister power management callbacks */ + + pm_unregister(&((struct stm32_i2c_inst_s *)dev)->priv->pm_cb); +#endif + + /* Disable power and other HW resource (GPIO's) */ + + stm32_i2c_deinit(((struct stm32_i2c_inst_s *)dev)->priv); + + /* Release unused resources */ + + stm32_i2c_sem_destroy((struct i2c_master_s *)dev); + + kmm_free(dev); + return OK; +} + +#endif /* CONFIG_STM32_I2C1 || CONFIG_STM32_I2C2 || \ + CONFIG_STM32_I2C3 || CONFIG_STM32_I2C4 */ diff --git a/arch/arm/src/stm32/stm32f30xxx_i2c.c b/arch/arm/src/stm32/stm32f30xxx_i2c.c deleted file mode 100644 index 29f084590b..0000000000 --- a/arch/arm/src/stm32/stm32f30xxx_i2c.c +++ /dev/null @@ -1,2019 +0,0 @@ -/************************************************************************************ - * arch/arm/src/stm32/stm32f3xx_i2c.c - * STM32 F3 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) 2011-2013, 2016-2017 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * - * And this version for the STM32 F3 by - * - * Author: John Wharington - * - * 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. - * - ************************************************************************************/ - -/* Supports: - * - Master operation, 100 kHz (standard) and 400 kHz (full speed) - * - Multiple instances (shared bus) - * - Interrupt based operation - * - * Structure naming: - * - 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, as frequency, address, mode of operation (in the future) - * - Private: Private data of an I2C Hardware - * - * TODO - * - Check for all possible deadlocks (as BUSY='1' I2C needs to be reset in HW using the I2C_CR1_SWRST) - * - SMBus support (hardware layer timings are already supported) and add SMBA gpio pin - * - Slave support with multiple addresses (on multiple instances): - * - 2 x 7-bit address or - * - 1 x 10 bit adresses + 1 x 7 bit address (?) - * - plus the broadcast address (general call) - * - Multi-master support - * - DMA (to get rid of too many CPU wake-ups and interventions) - * - Be ready for IPMI - */ - -/************************************************************************************ - * Included Files - ************************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "up_arch.h" - -#include "stm32_rcc.h" -#include "stm32_i2c.h" -#include "stm32_waste.h" - -/* At least one I2C peripheral must be enabled */ - -#if defined(CONFIG_STM32_I2C1) || defined(CONFIG_STM32_I2C2) || defined(CONFIG_STM32_I2C3) -/* This implementation is for the STM32 F1, F2, and F4 only */ - -#if defined(CONFIG_STM32_STM32F30XX) || defined(CONFIG_STM32_STM32F37XX) - -/************************************************************************************ - * Pre-processor Definitions - ************************************************************************************/ -/* Configuration ********************************************************************/ -/* 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_STM32_I2CTIMEOSEC) && !defined(CONFIG_STM32_I2CTIMEOMS) -# define CONFIG_STM32_I2CTIMEOSEC 0 -# define CONFIG_STM32_I2CTIMEOMS 500 /* Default is 500 milliseconds */ -#elif !defined(CONFIG_STM32_I2CTIMEOSEC) -# define CONFIG_STM32_I2CTIMEOSEC 0 /* User provided milliseconds */ -#elif !defined(CONFIG_STM32_I2CTIMEOMS) -# define CONFIG_STM32_I2CTIMEOMS 0 /* User provided seconds */ -#endif - -/* Interrupt wait time timeout in system timer ticks */ - -#ifndef CONFIG_STM32_I2CTIMEOTICKS -# define CONFIG_STM32_I2CTIMEOTICKS \ - (SEC2TICK(CONFIG_STM32_I2CTIMEOSEC) + MSEC2TICK(CONFIG_STM32_I2CTIMEOMS)) -#endif - -#ifndef CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP -# define CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_STM32_I2CTIMEOTICKS) -#endif - -#define I2C_OUTPUT \ - (GPIO_OUTPUT | GPIO_OUTPUT_SET | GPIO_OPENDRAIN | GPIO_SPEED_50MHz) -#define MKI2C_OUTPUT(p) \ - (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT) - -/* Register setting unique to the STM32F30xx */ - -#define I2C_CR1_TXRX \ - (I2C_CR1_RXIE | I2C_CR1_TXIE) -#define I2C_CR1_ALLINTS \ - (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ADDRIE | I2C_CR1_ERRIE) - -#define STATUS_NACK(status) (status & I2C_INT_NACK) -#define STATUS_ADDR(status) (status & I2C_INT_ADDR) -#define STATUS_ADDR_TX(status) (status & (I2C_INT_ADDR | I2C_ISR_TXIS)) -#define STATUS_ADD10(status) (0) -#define STATUS_RXNE(status) (status & I2C_ISR_RXNE) -#define STATUS_TC(status) (status & I2C_ISR_TC) -#define STATUS_BUSY(status) (status & I2C_ISR_BUSY) - -/* Debug ****************************************************************************/ - -/* I2C event trace logic. NOTE: trace uses the internal, non-standard, low-level - * debug interface syslog() but does not require that any other debug - * is enabled. - */ - -#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 - -/************************************************************************************ - * 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, /* No events have occurred with this status */ - I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ - I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ - I2CEVENT_ITBUFEN, /* Enable buffer interrupts, param = 0 */ - I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ - I2CEVENT_REITBUFEN, /* Re-enable buffer interrupts, param = 0 */ - I2CEVENT_DISITBUFEN, /* Disable buffer interrupts, param = 0 */ - I2CEVENT_BTFNOSTART, /* BTF on last byte with no restart, param = msgc */ - I2CEVENT_BTFRESTART, /* Last byte sent, re-starting, param = msgc */ - I2CEVENT_BTFSTOP, /* Last byte sten, send stop, param = 0 */ - I2CEVENT_ERROR /* Error occurred, param = 0 */ -}; - -/* 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_bit; /* Clock enable bit */ - 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 -{ - const struct i2c_ops_s *ops; /* Standard I2C operations */ - const struct stm32_i2c_config_s *config; /* Port configuration */ - int refs; /* Referernce count */ - sem_t sem_excl; /* Mutual exclusion semaphore */ -#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 length */ - 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 */ -}; - -/************************************************************************************ - * Private Function Prototypes - ************************************************************************************/ - -static inline uint16_t stm32_i2c_getreg(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset); -static inline void stm32_i2c_putreg(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, - uint16_t value); -static inline void stm32_i2c_putreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, - uint32_t value); -static inline void stm32_i2c_modifyreg(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset, uint16_t clearbits, - uint16_t setbits); -static inline void stm32_i2c_modifyreg32(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset, uint32_t clearbits, - uint32_t setbits); -static inline void stm32_i2c_sem_wait(FAR struct stm32_i2c_priv_s *priv); -#ifdef CONFIG_STM32_I2C_DYNTIMEO -static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); -#endif /* CONFIG_STM32_I2C_DYNTIMEO */ -static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_post(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_init(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_destroy(FAR struct stm32_i2c_priv_s *priv); -#ifdef CONFIG_I2C_TRACE -static void stm32_i2c_tracereset(FAR struct stm32_i2c_priv_s *priv); -static void stm32_i2c_tracenew(FAR struct stm32_i2c_priv_s *priv, uint32_t status); -static void stm32_i2c_traceevent(FAR struct stm32_i2c_priv_s *priv, - enum stm32_trace_e event, uint32_t parm); -static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv); -#endif /* CONFIG_I2C_TRACE */ -static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, - uint32_t frequency); -static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv); -static inline uint32_t stm32_i2c_getstatus(FAR 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, FAR void *arg); -#endif -static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv); -static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv); -static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, - int count); -#ifdef CONFIG_I2C_RESET -static int stm32_i2c_reset(FAR struct i2c_master_s *dev); -#endif - -/************************************************************************************ - * Private Data - ************************************************************************************/ - -/* 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 -}; - -#ifdef CONFIG_STM32_I2C1 -static const struct stm32_i2c_config_s stm32_i2c1_config = -{ - .base = STM32_I2C1_BASE, - .clk_bit = RCC_APB1ENR_I2C1EN, - .reset_bit = RCC_APB1RSTR_I2C1RST, - .scl_pin = GPIO_I2C1_SCL, - .sda_pin = GPIO_I2C1_SDA, -#ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C1EV, - .er_irq = STM32_IRQ_I2C1ER -#endif -}; - -static struct stm32_i2c_priv_s stm32_i2c1_priv = -{ - .ops = &stm32_i2c_ops, - .config = &stm32_i2c1_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -#ifdef CONFIG_STM32_I2C2 -static const struct stm32_i2c_config_s stm32_i2c2_config = -{ - .base = STM32_I2C2_BASE, - .clk_bit = RCC_APB1ENR_I2C2EN, - .reset_bit = RCC_APB1RSTR_I2C2RST, - .scl_pin = GPIO_I2C2_SCL, - .sda_pin = GPIO_I2C2_SDA, -#ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C2EV, - .er_irq = STM32_IRQ_I2C2ER -#endif -}; - -static struct stm32_i2c_priv_s stm32_i2c2_priv = -{ - .ops = &stm32_i2c_ops, - .config = &stm32_i2c2_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -#ifdef CONFIG_STM32_I2C3 -static const struct stm32_i2c_config_s stm32_i2c3_config = -{ - .base = STM32_I2C3_BASE, - .clk_bit = RCC_APB1ENR_I2C3EN, - .reset_bit = RCC_APB1RSTR_I2C3RST, - .scl_pin = GPIO_I2C3_SCL, - .sda_pin = GPIO_I2C3_SDA, -#ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C3EV, - .er_irq = STM32_IRQ_I2C3ER -#endif -}; - -static struct stm32_i2c_priv_s stm32_i2c3_priv = -{ - .ops = &stm32_i2c_ops, - .config = &stm32_i2c3_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -/************************************************************************************ - * Private Functions - ************************************************************************************/ - -/************************************************************************************ - * Name: stm32_i2c_getreg - * - * Description: - * Get a 16-bit register value by offset - * - ************************************************************************************/ - -static inline uint16_t stm32_i2c_getreg(FAR 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(FAR 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(FAR 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(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset, uint32_t value) -{ - putreg32(value, priv->config->base + offset); -} - -/************************************************************************************ - * Name: stm32_i2c_modifyreg - * - * Description: - * Modify a 16-bit register value by offset - * - ************************************************************************************/ - -static inline void stm32_i2c_modifyreg(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset, uint16_t clearbits, - uint16_t setbits) -{ - modifyreg16(priv->config->base + offset, clearbits, setbits); -} - -/************************************************************************************ - * Name: stm32_i2c_modifyreg32 - * - * Description: - * Modify a 32-bit register value by offset - * - ************************************************************************************/ - -static inline void stm32_i2c_modifyreg32(FAR 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_sem_wait - * - * Description: - * Take the exclusive access, waiting as necessary - * - ************************************************************************************/ - -static inline void stm32_i2c_sem_wait(FAR struct stm32_i2c_priv_s *priv) -{ - int ret; - - do - { - /* Take the semaphore (perhaps waiting) */ - - ret = nxsem_wait(&priv->sem_excl); - - /* The only case that an error should occur here is if the wait was - * awakened by a signal. - */ - - DEBUGASSERT(ret == OK || ret == -EINTR); - } - while (ret == -EINTR); -} - -/************************************************************************************ - * Name: stm32_i2c_tousecs - * - * Description: - * Return a micro-second delay based on the number of bytes left to be processed. - * - ************************************************************************************/ - -#ifdef CONFIG_STM32_I2C_DYNTIMEO -static useconds_t stm32_i2c_tousecs(int msgc, FAR 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 (useconds_t)(CONFIG_STM32_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); -} -#endif - -/************************************************************************************ - * Name: stm32_i2c_disableinterrupts - * - * Description: - * Enable I2C interrupts - * - ************************************************************************************/ - -#ifndef CONFIG_I2C_POLLED -static inline void stm32_i2c_disableinterrupts(struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_TXRX, 0); -} -#endif - -/************************************************************************************ - * Name: stm32_i2c_sem_waitdone - * - * Description: - * Wait for a transfer to complete - * - ************************************************************************************/ - -#ifndef CONFIG_I2C_POLLED -static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv) -{ - struct timespec abstime; - irqstate_t flags; - int ret; - - flags = enter_critical_section(); - - /* Enable I2C interrupts */ - - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, - (I2C_CR1_ALLINTS & ~I2C_CR1_TXRX)); - - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_timedwait() sleeps. - */ - - priv->intstate = INTSTATE_WAITING; - do - { - /* Get the current time */ - - (void)clock_gettime(CLOCK_REALTIME, &abstime); - - /* Calculate a time in the future */ - -#if CONFIG_STM32_I2CTIMEOSEC > 0 - abstime.tv_sec += CONFIG_STM32_I2CTIMEOSEC; -#endif - - /* Add a value proportional to the number of bytes in the transfer */ - -#ifdef CONFIG_STM32_I2C_DYNTIMEO - abstime.tv_nsec += 1000 * stm32_i2c_tousecs(priv->msgc, priv->msgv); - if (abstime.tv_nsec >= 1000 * 1000 * 1000) - { - abstime.tv_sec++; - abstime.tv_nsec -= 1000 * 1000 * 1000; - } - -#elif CONFIG_STM32_I2CTIMEOMS > 0 - abstime.tv_nsec += CONFIG_STM32_I2CTIMEOMS * 1000 * 1000; - if (abstime.tv_nsec >= 1000 * 1000 * 1000) - { - abstime.tv_sec++; - abstime.tv_nsec -= 1000 * 1000 * 1000; - } -#endif - /* Wait until either the transfer is complete or the timeout expires */ - - ret = nxsem_timedwait(&priv->sem_isr, &abstime); - if (ret < 0 && ret != -EINTR) - { - /* Break out of the loop on irrecoverable errors. This would - * include timeouts and mystery errors reported by nxsem_timedwait. - * NOTE that we try again if we are awakened by a signal (EINTR). - */ - - 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(FAR struct stm32_i2c_priv_s *priv) -{ - clock_t timeout; - clock_t start; - clock_t elapsed; - int ret; - - /* Get the timeout value */ - -#ifdef CONFIG_STM32_I2C_DYNTIMEO - timeout = USEC2TICK(stm32_i2c_tousecs(priv->msgc, priv->msgv)); -#else - timeout = CONFIG_STM32_I2CTIMEOTICKS; -#endif - - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_timedwait() sleeps. - */ - - priv->intstate = INTSTATE_WAITING; - start = clock_systimer(); - - do - { - /* Calculate the elapsed time */ - - elapsed = clock_systimer() - 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: %08x\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(FAR 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(FAR 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(FAR 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(FAR struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_RD_WRN); -} - -/************************************************************************************ - * Name: stm32_i2c_enable_autoend - * - * Description: - * - ************************************************************************************/ - -static inline void -stm32_i2c_enable_autoend(FAR struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_AUTOEND); -} - -/************************************************************************************ - * Name: stm32_i2c_disable_autoend - * - * Description: - * - ************************************************************************************/ - -static inline void -stm32_i2c_disable_autoend(FAR struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_AUTOEND, 0); -} - -/************************************************************************************ - * Name: stm32_i2c_sem_waitstop - * - * Description: - * Wait for a STOP to complete - * - ************************************************************************************/ - -static inline void stm32_i2c_sem_waitstop(FAR 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_STM32_I2C_DYNTIMEO - timeout = USEC2TICK(CONFIG_STM32_I2C_DYNTIMEO_STARTSTOP); -#else - timeout = CONFIG_STM32_I2CTIMEOTICKS; -#endif - - /* Wait as stop might still be in progress; but stop might also - * be set because of a timeout error: "The [STOP] bit is set and - * cleared by software, cleared by hardware when a Stop condition is - * detected, set by hardware when a timeout error is detected." - */ - - start = clock_systimer(); - do - { - /* Calculate the elapsed time */ - - elapsed = clock_systimer() - 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: %04x SR: %04x\n", cr, sr); -} - -/************************************************************************************ - * Name: stm32_i2c_sem_post - * - * Description: - * Release the mutual exclusion semaphore - * - ************************************************************************************/ - -static inline void stm32_i2c_sem_post(FAR struct stm32_i2c_priv_s *priv) -{ - nxsem_post(&priv->sem_excl); -} - -/************************************************************************************ - * Name: stm32_i2c_sem_init - * - * Description: - * Initialize semaphores - * - ************************************************************************************/ - -static inline void stm32_i2c_sem_init(FAR struct stm32_i2c_priv_s *priv) -{ - nxsem_init(&priv->sem_excl, 0, 1); - -#ifndef CONFIG_I2C_POLLED - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - nxsem_init(&priv->sem_isr, 0, 0); - nxsem_setprotocol(&priv->sem_isr, SEM_PRIO_NONE); -#endif -} - -/************************************************************************************ - * Name: stm32_i2c_sem_destroy - * - * Description: - * Destroy semaphores. - * - ************************************************************************************/ - -static inline void stm32_i2c_sem_destroy(FAR struct stm32_i2c_priv_s *priv) -{ - nxsem_destroy(&priv->sem_excl); -#ifndef CONFIG_I2C_POLLED - nxsem_destroy(&priv->sem_isr); -#endif -} - -/************************************************************************************ - * Name: stm32_i2c_trace* - * - * Description: - * I2C trace instrumentation - * - ************************************************************************************/ - -#ifdef CONFIG_I2C_TRACE -static void stm32_i2c_traceclear(FAR struct stm32_i2c_priv_s *priv) -{ - struct stm32_trace_s *trace = &priv->trace[priv->tndx]; - - trace->status = 0; /* I2C 32-bit SR2|SR1 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(FAR struct stm32_i2c_priv_s *priv) -{ - /* Reset the trace info for a new data collection */ - - priv->tndx = 0; - priv->start_time = clock_systimer(); - stm32_i2c_traceclear(priv); -} - -static void stm32_i2c_tracenew(FAR 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_systimer(); - } - else - { - /* Just increment the count of times that we have seen this status */ - - trace->count++; - } -} - -static void stm32_i2c_traceevent(FAR 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(FAR struct stm32_i2c_priv_s *priv) -{ - struct stm32_trace_s *trace; - int i; - - syslog(LOG_DEBUG, "Elapsed time: %ld\n", - (long)(clock_systimer() - 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, - trace->time - priv->start_time); - } -} -#endif /* CONFIG_I2C_TRACE */ - -/************************************************************************************ - * Name: stm32_i2c_setclock - * - * Description: - * Set the I2C clock - * - ************************************************************************************/ - -static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency) -{ - uint32_t pe; - uint8_t presc; - uint8_t s_time; - uint8_t h_time; - uint8_t scl_h_period; - uint8_t scl_l_period; - - /* Has the I2C bus frequency changed? */ - - if (frequency != priv->frequency) - { - /* Disable the selected I2C peripheral to configure TRISE */ - - pe = (stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET) & I2C_CR1_PE); - if (pe) - { - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0); - } - - /* Update timing and control registers */ - - /* TODO: speed/timing calcs */ -#warning "check set filters before timing, see RM0316" - - /* values from 100khz at 8mhz i2c clock */ - - /* prescaler */ - /* t_presc= (presc+1)*t_i2cclk */ - /* RM0316 */ - - if (frequency == 10000) - { - presc = 0x01; - scl_l_period = 0xc7; - scl_h_period = 0xc3; - h_time = 0x02; - s_time = 0x04; - } - else if (frequency == 100000) - { - /* values from datasheet with clock 8mhz */ - - presc = 0x01; - scl_l_period = 0x13; - scl_h_period = 0x0f; - h_time = 0x02; - s_time = 0x04; - } - else - { - presc = 0x00; - scl_l_period = 0x09; - scl_h_period = 0x03; - h_time = 0x01; - s_time = 0x03; - } - - uint32_t timingr = - (presc << I2C_TIMINGR_PRESC_SHIFT) | - (s_time << I2C_TIMINGR_SCLDEL_SHIFT) | - (h_time << 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); - - /* Bit 14 of OAR1 must be configured and kept at 1 */ - - stm32_i2c_putreg(priv, STM32_I2C_OAR1_OFFSET, I2C_OAR1_ONE); - - /* Re-enable the peripheral (or not) */ - - if (pe) - { - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); - } - - /* Save the new I2C frequency */ - - priv->frequency = frequency; - } -} - -/************************************************************************************ - * Name: stm32_i2c_sendstart - * - * Description: - * Send the START conditions/force Master mode - * - ************************************************************************************/ - -static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv) -{ - /* Get run-time data */ - - priv->astart = true; - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; - - /* Disable ACK on receive by default and generate START */ - - stm32_i2c_set_bytes_to_transfer(priv, priv->dcnt); - stm32_i2c_set_7bit_address(priv); - if (priv->flags & I2C_M_READ) - { - stm32_i2c_set_read_transfer_dir(priv); - } - else - { - stm32_i2c_set_write_transfer_dir(priv); - } - - if (priv->msgc == 1) - { - /* stm32_i2c_enable_autoend(priv); */ - } - else - { - /* stm32_i2c_disable_autoend(priv); */ - } - - /* TODO check NACK */ - /* TODO handle NACKR? */ - - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_START); -} - -/************************************************************************************ - * Name: stm32_i2c_clrstart - * - * Description: - * Clear the STOP, START or PEC condition on certain error recovery steps. - * - ************************************************************************************/ - -static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv) -{ - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." - * - * "The [STOP] bit is set and cleared by software, cleared by hardware - * when a Stop condition is detected, set by hardware when a timeout - * error is detected. - * - * "This [START] bit is set and cleared by software and cleared by hardware - * when start is sent or PE=0." The bit must be cleared by software if the - * START is never sent. - * - * "This [PEC] bit is set and cleared by software, and cleared by hardware - * when PEC is transferred or by a START or Stop condition or when PE=0." - */ - - /* TODO check PEC (32 bit separate reg) */ - - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, - I2C_CR2_START | I2C_CR2_STOP, 0); -} - -/************************************************************************************ - * Name: stm32_i2c_sendstop - * - * Description: - * Send the STOP conditions - * - ************************************************************************************/ - -static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv) -{ - /* TODO check NACK */ - - 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(FAR struct stm32_i2c_priv_s *priv) -{ - return getreg32(priv->config->base + STM32_I2C_ISR_OFFSET); -} - -/************************************************************************************ - * Name: stm32_i2c_isr_startmessage - * - * Description: - * Common logic when a message is started. Just adds the even to the trace buffer - * if enabled and adjusts the message pointer and count. - * - ************************************************************************************/ - -static inline void stm32_i2c_isr_startmessage(struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgc); - - /* Increment to next pointer and decrement message count */ - - priv->msgv++; - priv->msgc--; -} - -/************************************************************************************ - * Name: stm32_i2c_clearinterrupts - * - * Description: - * Clear all interrupts - * - ************************************************************************************/ - -static inline void stm32_i2c_clearinterrupts(struct stm32_i2c_priv_s *priv) -{ -#warning "check this clears interrupts?" - stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); -} - -/************************************************************************************ - * Name: stm32_i2c_isr_process - * - * Description: - * Common Interrupt Service Routine - * - ************************************************************************************/ - -static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv) -{ - uint32_t status = stm32_i2c_getstatus(priv); - - /* Check for new trace setup */ - - stm32_i2c_tracenew(priv, status); - -#warning "TODO: check clear interrupts after all actions" - - if (STATUS_NACK(status)) - { - /* wait, reset this? */ - } - else if (priv->astart) - { - stm32_i2c_isr_startmessage(priv); - priv->astart = false; - } - - /* Was address sent, continue with either sending or reading data */ - - if ((priv->flags & I2C_M_READ) == 0 && STATUS_ADDR_TX(status)) - { -#warning "TODO: ADDRCF clear address interrupt flag" - if (priv->dcnt > 0) - { - /* Send a byte */ - - stm32_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); - stm32_i2c_putreg(priv, STM32_I2C_TXDR_OFFSET, *priv->ptr++); - priv->dcnt--; - } - } - - else if ((priv->flags & I2C_M_READ) != 0 && STATUS_ADDR(status)) - { - /* Enable RxNE and TxE buffers in order to receive one or multiple bytes */ - -#warning "TODO: ADDRCF clear address interrupt flag" - -#ifndef CONFIG_I2C_POLLED - stm32_i2c_traceevent(priv, I2CEVENT_ITBUFEN, 0); - stm32_i2c_enableinterrupts(priv); -#endif - } - - /* More bytes to read */ - else if (STATUS_RXNE(status)) - { - /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ - - 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 sent by the - * device. - */ - -#ifdef CONFIG_I2C_POLLED - irqstate_t flags = enter_critical_section(); -#endif - /* Receive a byte */ - - *priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_RXDR_OFFSET); - - /* Disable acknowledge when last byte is to be received */ - - priv->dcnt--; - if (priv->dcnt == 1) - { - /* autoend? */ - } - -#ifdef CONFIG_I2C_POLLED - leave_critical_section(flags); -#endif - } - } - - /* Do we have more bytes to send, enable/disable buffer interrupts - * (these ISRs could be replaced by DMAs) - */ - -#ifndef CONFIG_I2C_POLLED - if (priv->dcnt > 0) - { - stm32_i2c_traceevent(priv, I2CEVENT_REITBUFEN, 0); - stm32_i2c_enableinterrupts(priv); - } - else if ((priv->dcnt == 0) && (priv->msgc == 0)) - { - stm32_i2c_traceevent(priv, I2CEVENT_DISITBUFEN, 0); - stm32_i2c_disableinterrupts(priv); - } -#endif - - /* Was last byte received or sent? Hmmm... the F2 and F4 seems to differ from - * the F1 in that BTF is not set after data is received (only RXNE). - */ - - if (priv->dcnt <= 0 && STATUS_TC(status)) - { - /* ??? */ - - /* Do we need to terminate or restart after this byte? - * If there are more messages to send, then we may: - * - * - continue with repeated start - * - or just continue sending writeable part - * - or we close down by sending the stop bit - */ - - if (priv->msgc > 0) - { - if (priv->msgv->flags & I2C_M_NOSTART) - { - stm32_i2c_traceevent(priv, I2CEVENT_BTFNOSTART, priv->msgc); - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; - priv->msgv++; - priv->msgc--; - - /* Restart this ISR! */ - -#ifndef CONFIG_I2C_POLLED - stm32_i2c_enableinterrupts(priv); -#endif - } - else - { - stm32_i2c_traceevent(priv, I2CEVENT_BTFRESTART, priv->msgc); - /* ??? */ - stm32_i2c_sendstart(priv); - } - } - else if (priv->msgv) - { - stm32_i2c_traceevent(priv, I2CEVENT_BTFSTOP, 0); - - stm32_i2c_sendstop(priv); - - /* Is there a thread waiting for this event (there should be) */ - -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) - { - /* Yes.. inform the thread that the transfer is complete - * and wake it up. - */ - - nxsem_post(&priv->sem_isr); - priv->intstate = INTSTATE_DONE; - } -#else - priv->intstate = INTSTATE_DONE; -#endif - - /* Mark that we have stopped with this transaction */ - - priv->msgv = NULL; - } - } - - /* Check for errors, in which case, stop the transfer and return - * Note that in master reception mode AF becomes set on last byte - * since ACK is not returned. We should ignore this error. - */ - - if ((status & I2C_ISR_ERRORMASK) != 0) - { - stm32_i2c_traceevent(priv, I2CEVENT_ERROR, 0); - - /* Clear interrupt flags */ - - stm32_i2c_clearinterrupts(priv); - - /* Is there a thread waiting for this event (there should be) */ - -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) - { - /* Yes.. inform the thread that the transfer is complete - * and wake it up. - */ - - nxsem_post(&priv->sem_isr); - priv->intstate = INTSTATE_DONE; - } -#else - priv->intstate = INTSTATE_DONE; -#endif - } - - priv->status = 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, FAR 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(FAR struct stm32_i2c_priv_s *priv) -{ - /* Power-up and configure GPIOs */ - - /* Enable power and reset the peripheral */ - - modifyreg32(STM32_RCC_APB1ENR, 0, priv->config->clk_bit); - modifyreg32(STM32_RCC_APB1RSTR, 0, priv->config->reset_bit); - modifyreg32(STM32_RCC_APB1RSTR, 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; - } - - /* Attach ISRs */ - -#ifndef CONFIG_I2C_POLLED - 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 - - /* Set peripheral frequency, where it must be at least 2 MHz for 100 kHz - * or 4 MHz for 400 kHz. This also disables all I2C interrupts. - */ - - /* Force a frequency update */ - - priv->frequency = 0; - - /* TODO: f303 i2c clock source RCC_CFGR3 */ - /* RCC_CFGR3_I2C1SW (default is HSI clock) */ - - stm32_i2c_setclock(priv, 100000); - - /* Enable I2C */ - - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); - return OK; -} - -/************************************************************************************ - * Name: stm32_i2c_deinit - * - * Description: - * Shutdown the I2C hardware - * - ************************************************************************************/ - -static int stm32_i2c_deinit(FAR 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); - - /* Disable and detach interrupts */ - -#ifndef CONFIG_I2C_POLLED - 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(STM32_RCC_APB1ENR, priv->config->clk_bit, 0); - return OK; -} - -/************************************************************************************ - * Device Driver Operations - ************************************************************************************/ - -/************************************************************************************ - * Name: stm32_i2c_transfer - * - * Description: - * Generic I2C transfer function - * - ************************************************************************************/ - -static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, - int count) -{ - FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)dev; - uint32_t status = 0; - int ret = OK; - - DEBUGASSERT(dev != NULL && msgs != NULL && count > 0); - - /* Ensure that address or flags don't change meanwhile */ - - stm32_i2c_sem_wait(priv); - - /* Wait for any STOP in progress. */ - - stm32_i2c_sem_waitstop(priv); - - /* Clear any pending error interrupts */ - - stm32_i2c_clearinterrupts(priv); - - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." However, if the bits are - * not cleared by hardware, then we will have to do that from hardware. - */ - - stm32_i2c_clrstart(priv); - - /* Old transfers are done */ - - priv->msgv = msgs; - priv->msgc = count; - - /* Reset I2C trace logic */ - - stm32_i2c_tracereset(priv); - - /* Set I2C clock frequency (on change it toggles I2C_CR1_PE !) - * REVISIT: Note that the frequency is set only on the first message. - * This could be extended to support different transfer frequencies for - * each message segment. - */ - - 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 - stm32_i2c_enableinterrupts(priv); -#endif - - stm32_i2c_sendstart(priv); - - /* Wait for an ISR, if there was a timeout, fetch latest status to get - * the BUSY flag. - */ - - if (stm32_i2c_sem_waitdone(priv) < 0) - { - status = stm32_i2c_getstatus(priv); - ret = -ETIMEDOUT; - - i2cerr("ERROR: Timed out: CR1: %04x status: %08x\n", - stm32_i2c_getreg(priv, STM32_I2C_CR1_OFFSET), status); - - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." - */ - - stm32_i2c_clrstart(priv); - - /* Clear busy flag in case of timeout */ - - status = priv->status & 0xffff; - } - else - { - /* clear SR2 (BUSY flag) as we've done successfully */ - - status = priv->status & 0xffff; - } - - status &= ~I2C_ISR_BUSY; -#if 0 - /* Refresh status */ - do - { - status = stm32_i2c_getstatus(priv); - } - while (STATUS_BUSY(status)); -#endif - - /* Check for error status conditions */ - - if ((status & I2C_ISR_ERRORMASK) != 0) - { - /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ - - if (status & I2C_INT_BERR) - { - /* Bus Error */ - - ret = -EIO; - } - else if (status & I2C_INT_ARLO) - { - /* Arbitration Lost (master mode) */ - - ret = -EAGAIN; - } - - /* TODO Acknowledge failure */ - - else if (status & I2C_INT_OVR) - { - /* Overrun/Underrun */ - - ret = -EIO; - } - else if (status & I2C_INT_PECERR) - { - /* PEC Error in reception */ - - ret = -EPROTO; - } - else if (status & I2C_INT_TIMEOUT) - { - /* Timeout or Tlow Error */ - - ret = -ETIME; - } - - /* This is not an error and should never happen since SMBus is not - * enabled - */ - - else /* if (status & I2C_INT_ALERT) */ - { - /* SMBus alert is an optional signal with an interrupt line for devices - * that want to trade their ability to master for a pin. - */ - - ret = -EINTR; - } - } - - /* This is not an error, but should not happen. The BUSY signal can hang, - * however, if there are unhealthy devices on the bus that need to be reset. - * NOTE: We will only see this buy indication if stm32_i2c_sem_waitdone() - * fails above; Otherwise it is cleared. - */ - - else if ((status & I2C_ISR_BUSY) != 0) - { - /* I2C Bus is for some reason busy */ - - ret = -EBUSY; - } - - /* Dump the trace result */ - - stm32_i2c_tracedump(priv); - stm32_i2c_sem_post(priv); - return ret; -} - -/************************************************************************************ - * Name: stm32_i2c_reset - * - * Description: - * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. - * - * Input Parameters: - * dev - Device-specific state data - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ************************************************************************************/ - -#ifdef CONFIG_I2C_RESET -static int stm32_i2c_reset(FAR struct i2c_master_s * dev) -{ - FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)dev; - unsigned int clock_count; - unsigned int stretch_count; - uint32_t scl_gpio; - uint32_t sda_gpio; - uint32_t frequency; - int ret = ERROR; - - DEBUGASSERT(dev); - - /* Our caller must own a ref */ - - DEBUGASSERT(priv->refs > 0); - - /* Lock out other clients */ - - stm32_i2c_sem_wait(priv); - - /* 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); - - /* 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 */ - - stm32_i2c_sem_post(priv); - return ret; -} -#endif /* CONFIG_I2C_RESET */ - -/************************************************************************************ - * Public Functions - ************************************************************************************/ - -/************************************************************************************ - * Name: stm32_i2cbus_initialize - * - * Description: - * Initialize one I2C bus - * - ************************************************************************************/ - -FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) -{ - struct stm32_i2c_priv_s * priv = NULL; /* private data of device with multiple instances */ - irqstate_t flags; - -#if STM32_PCLK1_FREQUENCY < 4000000 -# warning STM32_I2C_INIT: Peripheral clock must be at least 4 MHz to support 400 kHz operation. -#endif - -#if STM32_PCLK1_FREQUENCY < 2000000 -# warning STM32_I2C_INIT: Peripheral clock must be at least 2 MHz to support 100 kHz operation. - return NULL; -#endif - - /* Get I2C private structure */ - - switch (port) - { -#ifdef CONFIG_STM32_I2C1 - case 1: - priv = (struct stm32_i2c_priv_s *)&stm32_i2c1_priv; - break; -#endif -#ifdef CONFIG_STM32_I2C2 - case 2: - priv = (struct stm32_i2c_priv_s *)&stm32_i2c2_priv; - break; -#endif -#ifdef CONFIG_STM32_I2C3 - case 3: - priv = (struct stm32_i2c_priv_s *)&stm32_i2c3_priv; - break; -#endif - default: - return NULL; - } - - /* Init private data for the first time, increment refs count, - * power-up hardware and configure GPIOs. - */ - - flags = enter_critical_section(); - - if ((volatile int)priv->refs++ == 0) - { - stm32_i2c_sem_init(priv); - stm32_i2c_init(priv); - } - - leave_critical_section(flags); - return (struct i2c_master_s *)priv; -} - -/************************************************************************************ - * Name: stm32_i2cbus_uninitialize - * - * Description: - * Uninitialize an I2C bus - * - ************************************************************************************/ - -int stm32_i2cbus_uninitialize(FAR struct i2c_master_s * dev) -{ - FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)dev; - irqstate_t flags; - - DEBUGASSERT(dev); - - /* Decrement refs and check for underflow */ - - if (priv->refs == 0) - { - return ERROR; - } - - flags = enter_critical_section(); - - if (--priv->refs) - { - leave_critical_section(flags); - return OK; - } - - leave_critical_section(flags); - - /* Disable power and other HW resource (GPIO's) */ - - stm32_i2c_deinit(priv); - - /* Release unused resources */ - - stm32_i2c_sem_destroy(priv); - return OK; -} - -#endif /* CONFIG_STM32_STM32F30XX */ -#endif /* CONFIG_STM32_I2C1 || CONFIG_STM32_I2C2 || CONFIG_STM32_I2C3 */ diff --git a/arch/arm/src/stm32f0l0/stm32_i2c.c b/arch/arm/src/stm32f0l0/stm32_i2c.c index d3f4a705c0..ac61504072 100644 --- a/arch/arm/src/stm32f0l0/stm32_i2c.c +++ b/arch/arm/src/stm32f0l0/stm32_i2c.c @@ -1,14 +1,23 @@ /************************************************************************************ - * arch/arm/src/stm32f0l0/stm32_i2c.c - * STM32L4 I2C driver - based on STM32F3 I2C Hardware Layer - Device Driver + * arch/arm/src/stm32/stm32_i2c.c + * STM32 I2C IPv2 Hardware Layer - Device Driver ported from STM32F7 * * Copyright (C) 2011 Uros Platise. All rights reserved. * Author: Uros Platise - * Copyright (C) 2011-2013, 2016-2017 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * Author: John Wharington - * Author: Sebastien Lorquet - * Author: dev@ziggurat29.com + * + * 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 + * + * 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 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,31 +48,165 @@ * ************************************************************************************/ -/* Supports: - * - Master operation, 100 kHz (standard) and 400 kHz (full speed) +/* ------------------------------------------------------------------------------ + * + * STM32 I2C IPv2 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_DCKCFGR2_I2CxSRC + * being set to HSI and the calculations are based on STM32_HSI_FREQUENCY + * of 16mHz + * * - 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 16Mhz + * - Polled operation (code present but untested) + * - SMBus support + * - Multi-master support + * - IPMI + * + * Test Environment: + * + * - B-L072Z-LRWAN1 + * + * 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: * - * Structure naming: * - 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, as frequency, address, mode of operation (in the future) + * its own private data including frequency, address and mode of operation. + * * - Private: Private data of an I2C Hardware * - * TODO - * - Check for all possible deadlocks (as BUSY='1' I2C needs to be reset in HW using - * the I2C_CR1_SWRST) - * - SMBus support (hardware layer timings are already supported) and add SMBA gpio - * pin - * - Slave support with multiple addresses (on multiple instances): - * - 2 x 7-bit address or - * - 1 x 10 bit adresses + 1 x 7 bit address (?) - * - plus the broadcast address (general call) - * - Multi-master support - * - DMA (to get rid of too many CPU wake-ups and interventions) - * - Be ready for IPMI + * 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: + * + * CONFIG_STM32F0L0_I2C1 + * CONFIG_STM32F0L0_I2C2 + * CONFIG_STM32F0L0_I2C3 + * CONFIG_STM32F0L0_I2C4 + * + * To configure the ISR timeout using fixed values (CONFIG_STM32F0L0_I2C_DYNTIMEO=n): + * + * CONFIG_STM32F0L0_I2CTIMEOSEC (Timeout in seconds) + * CONFIG_STM32F0L0_I2CTIMEOMS (Timeout in milliseconds) + * CONFIG_STM32F0L0_I2CTIMEOTICKS (Timeout in ticks) + * + * To configure the ISR timeout using dynamic values (CONFIG_STM32F0L0_I2C_DYNTIMEO=y): + * + * CONFIG_STM32F0L0_I2C_DYNTIMEO_USECPERBYTE (Timeout in microseconds per byte) + * CONFIG_STM32F0L0_I2C_DYNTIMEO_STARTSTOP (Timeout for start/stop in milliseconds) + * + * 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 + * + * ------------------------------------------------------------------------------ + * + * References: + * + * RM0431: + * ST STM322xxx and STM323xxx Reference Manual + * Document ID: DocID029480 Revision 1, Jan 2017. + * + * RM0316: + * ST STM326xxx and STM327xxx Reference Manual + * Document ID: DocID028270 Revision 2, April 2016. + * + * DATASHEET: + * ST STM3277xx/STM3278Ax/STM3279x Datasheet + * Document ID: DocID028294, Revision 3, May 2016. + * + * ERRATA: + * STM326xxx/STM327xxx Errata sheet Rev A device limitations + * Document ID: DocID028806, Revision 2, April 2016. + * + * I2CSPEC: + * I2C Bus Specification and User Manual + * Document ID: UM10204, Revision 6, April 2014. + * + * ------------------------------------------------------------------------------ */ /************************************************************************************ @@ -77,33 +220,40 @@ #include #include #include +#include #include #include #include #include #include -#include #include +#include +#include +#include #include #include #include "up_arch.h" -#include "stm32_gpio.h" #include "stm32_rcc.h" #include "stm32_i2c.h" +#include "stm32_gpio.h" /* At least one I2C peripheral must be enabled */ -#if defined(CONFIG_STM32F0L0_I2C1) || defined(CONFIG_STM32F0L0_I2C2) || defined(CONFIG_STM32F0L0_I2C3) +#if defined(CONFIG_STM32F0L0_I2C1) || defined(CONFIG_STM32F0L0_I2C2) || \ + defined(CONFIG_STM32F0L0_I2C3) || defined(CONFIG_STM32F0L0_I2C4) /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ -/* Configuration ********************************************************************/ +#undef INVALID_CLOCK_SOURCE + +#warning TODO: check I2C clock source. It must be HSI! + /* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. Instead, * CPU-intensive polling will be used. */ @@ -113,6 +263,7 @@ #if !defined(CONFIG_STM32F0L0_I2CTIMEOSEC) && !defined(CONFIG_STM32F0L0_I2CTIMEOMS) # define CONFIG_STM32F0L0_I2CTIMEOSEC 0 # define CONFIG_STM32F0L0_I2CTIMEOMS 500 /* Default is 500 milliseconds */ +# warning "Using Default 500 Ms Timeout" #elif !defined(CONFIG_STM32F0L0_I2CTIMEOSEC) # define CONFIG_STM32F0L0_I2CTIMEOSEC 0 /* User provided milliseconds */ #elif !defined(CONFIG_STM32F0L0_I2CTIMEOMS) @@ -130,29 +281,31 @@ # define CONFIG_STM32F0L0_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_STM32F0L0_I2CTIMEOTICKS) #endif -#define I2C_OUTPUT \ - (GPIO_OUTPUT | GPIO_OUTPUT_SET | GPIO_OPENDRAIN | GPIO_SPEED_50MHz) -#define MKI2C_OUTPUT(p) \ - (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT) +/* Macros to convert a I2C pin to a GPIO output */ -#define I2C_CR1_TXRX \ - (I2C_CR1_RXIE | I2C_CR1_TXIE) -#define I2C_CR1_ALLINTS \ - (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ADDRIE | I2C_CR1_ERRIE) +#define I2C_OUTPUT (GPIO_OUTPUT | GPIO_FLOAT | GPIO_OPENDRAIN |\ + GPIO_SPEED_50MHz | GPIO_OUTPUT_SET) -#define STATUS_NACK(status) (status & I2C_INT_NACK) -#define STATUS_ADDR(status) (status & I2C_INT_ADDR) -#define STATUS_ADDR_TX(status) (status & (I2C_INT_ADDR | I2C_ISR_TXIS)) -#define STATUS_ADD10(status) (0) -#define STATUS_RXNE(status) (status & I2C_ISR_RXNE) -#define STATUS_TC(status) (status & I2C_ISR_TC) -#define STATUS_BUSY(status) (status & I2C_ISR_BUSY) +#define MKI2C_OUTPUT(p) (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT) -/* Debug ****************************************************************************/ +#define I2C_CR1_TXRX (I2C_CR1_RXIE | I2C_CR1_TXIE) +#define I2C_CR1_ALLINTS (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ERRIE) -/* I2C event trace logic. NOTE: trace uses the internal, non-standard, low-level - * debug interface syslog() but does not require that any other debug - * is enabled. +/* 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 @@ -169,6 +322,7 @@ /************************************************************************************ * Private Types ************************************************************************************/ + /* Interrupt state */ enum stm32_intstate_e @@ -182,28 +336,39 @@ enum stm32_intstate_e enum stm32_trace_e { - I2CEVENT_NONE = 0, /* No events have occurred with this status */ - I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ - I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ - I2CEVENT_ITBUFEN, /* Enable buffer interrupts, param = 0 */ - I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ - I2CEVENT_REITBUFEN, /* Re-enable buffer interrupts, param = 0 */ - I2CEVENT_DISITBUFEN, /* Disable buffer interrupts, param = 0 */ - I2CEVENT_BTFNOSTART, /* BTF on last byte with no restart, param = msgc */ - I2CEVENT_BTFRESTART, /* Last byte sent, re-starting, param = msgc */ - I2CEVENT_BTFSTOP, /* Last byte sten, send stop, param = 0 */ - I2CEVENT_ERROR /* Error occurred, param = 0 */ + 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 */ + 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 */ @@ -224,7 +389,6 @@ struct stm32_i2c_config_s struct stm32_i2c_priv_s { - const struct i2c_ops_s *ops; /* Standard I2C operations */ const struct stm32_i2c_config_s *config; /* Port configuration */ int refs; /* Reference count */ sem_t sem_excl; /* Mutual exclusion semaphore */ @@ -237,7 +401,7 @@ struct stm32_i2c_priv_s struct i2c_msg_s *msgv; /* Message list */ uint8_t *ptr; /* Current message buffer */ uint32_t frequency; /* Current I2C frequency */ - int dcnt; /* Current message length */ + int dcnt; /* Current message bytes remaining to transfer */ uint16_t flags; /* Current message flags */ bool astart; /* START sent */ @@ -253,40 +417,52 @@ struct stm32_i2c_priv_s #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 uint32_t stm32_i2c_getreg32(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset); -static inline void stm32_i2c_putreg32(FAR struct stm32_i2c_priv_s *priv, - uint8_t offset, uint32_t value); +static inline uint16_t stm32_i2c_getreg(FAR struct stm32_i2c_priv_s *priv, + uint8_t offset); +static inline void stm32_i2c_putreg(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, + uint16_t value); +static inline void stm32_i2c_putreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, + uint32_t value); static inline void stm32_i2c_modifyreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, uint32_t clearbits, uint32_t setbits); -static inline void stm32_i2c_sem_wait(FAR struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sem_wait(FAR struct i2c_master_s *dev); #ifdef CONFIG_STM32F0L0_I2C_DYNTIMEO static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); #endif /* CONFIG_STM32F0L0_I2C_DYNTIMEO */ static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv); static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_post(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_init(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_sem_destroy(FAR struct stm32_i2c_priv_s *priv); +static inline void stm32_i2c_sem_post(FAR struct i2c_master_s *dev); +static inline void stm32_i2c_sem_init(FAR struct i2c_master_s *dev); +static inline void stm32_i2c_sem_destroy(FAR struct i2c_master_s *dev); #ifdef CONFIG_I2C_TRACE static void stm32_i2c_tracereset(FAR struct stm32_i2c_priv_s *priv); -static void stm32_i2c_tracenew(FAR struct stm32_i2c_priv_s *priv, - uint32_t status); +static void stm32_i2c_tracenew(FAR struct stm32_i2c_priv_s *priv, uint32_t status); static void stm32_i2c_traceevent(FAR struct stm32_i2c_priv_s *priv, - enum stm32_trace_e event, uint32_t parm); + enum stm32_trace_e event, uint32_t parm); static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv); #endif /* CONFIG_I2C_TRACE */ static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency); static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv); -static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv); static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv); static inline uint32_t stm32_i2c_getstatus(FAR struct stm32_i2c_priv_s *priv); static int stm32_i2c_isr_process(struct stm32_i2c_priv_s * priv); @@ -295,34 +471,31 @@ static int stm32_i2c_isr(int irq, void *context, FAR void *arg); #endif static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv); static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv); -static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, - FAR struct i2c_msg_s *msgs, int count); + +static int stm32_i2c_process(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, + int count); +static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, + int count); #ifdef CONFIG_I2C_RESET -static int stm32_i2c_reset(FAR struct i2c_master_s *dev); +static int stm32_i2c_reset(FAR struct i2c_master_s * dev); +#endif +#ifdef CONFIG_PM +static int stm32_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); #endif /************************************************************************************ * Private Data ************************************************************************************/ -/* 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 -}; - #ifdef CONFIG_STM32F0L0_I2C1 static const struct stm32_i2c_config_s stm32_i2c1_config = { - .base = STM32_I2C1_BASE, - .clk_bit = RCC_APB1ENR_I2C1EN, - .reset_bit = RCC_APB1RSTR_I2C1RST, - .scl_pin = GPIO_I2C1_SCL, - .sda_pin = GPIO_I2C1_SDA, + .base = STM32_I2C1_BASE, + .clk_bit = RCC_APB1ENR_I2C1EN, + .reset_bit = RCC_APB1RSTR_I2C1RST, + .scl_pin = GPIO_I2C1_SCL, + .sda_pin = GPIO_I2C1_SDA, #ifndef CONFIG_I2C_POLLED .irq = STM32_IRQ_I2C1 #endif @@ -330,79 +503,143 @@ static const struct stm32_i2c_config_s stm32_i2c1_config = static struct stm32_i2c_priv_s stm32_i2c1_priv = { - .ops = &stm32_i2c_ops, - .config = &stm32_i2c1_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c1_config, + .refs = 0, + .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_STM32F0L0_I2C2 static const struct stm32_i2c_config_s stm32_i2c2_config = { - .base = STM32_I2C2_BASE, - .clk_bit = RCC_APB1ENR1_I2C2EN, - .reset_bit = RCC_APB1RSTR1_I2C2RST, - .scl_pin = GPIO_I2C2_SCL, - .sda_pin = GPIO_I2C2_SDA, + .base = STM32_I2C2_BASE, + .clk_bit = RCC_APB1ENR_I2C2EN, + .reset_bit = RCC_APB1RSTR_I2C2RST, + .scl_pin = GPIO_I2C2_SCL, + .sda_pin = GPIO_I2C2_SDA, #ifndef CONFIG_I2C_POLLED - .irq = STM32_IRQ_I2C2 + .irq = STM32_IRQ_I2C1 #endif }; static struct stm32_i2c_priv_s stm32_i2c2_priv = { - .ops = &stm32_i2c_ops, - .config = &stm32_i2c2_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c2_config, + .refs = 0, + .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_STM32F0L0_I2C3 static const struct stm32_i2c_config_s stm32_i2c3_config = { - .base = STM32_I2C3_BASE, - .clk_bit = RCC_APB1ENR1_I2C3EN, - .reset_bit = RCC_APB1RSTR1_I2C3RST, - .scl_pin = GPIO_I2C3_SCL, - .sda_pin = GPIO_I2C3_SDA, + .base = STM32_I2C3_BASE, + .clk_bit = RCC_APB1ENR_I2C3EN, + .reset_bit = RCC_APB1RSTR_I2C3RST, + .scl_pin = GPIO_I2C3_SCL, + .sda_pin = GPIO_I2C3_SDA, #ifndef CONFIG_I2C_POLLED - .irq = STM32_IRQ_I2C3 + .irq = STM32_IRQ_I2C1 #endif }; static struct stm32_i2c_priv_s stm32_i2c3_priv = { - .ops = &stm32_i2c_ops, - .config = &stm32_i2c3_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c3_config, + .refs = 0, + .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_STM32F0L0_I2C4 +static const struct stm32_i2c_config_s stm32_i2c4_config = +{ + .base = STM32_I2C4_BASE, + .clk_bit = RCC_APB1ENR_I2C4EN, + .reset_bit = RCC_APB1RSTR_I2C4RST, + .scl_pin = GPIO_I2C4_SCL, + .sda_pin = GPIO_I2C4_SDA, +#ifndef CONFIG_I2C_POLLED + .irq = STM32_IRQ_I2C1 +#endif +}; + +static struct stm32_i2c_priv_s stm32_i2c4_priv = +{ + .config = &stm32_i2c4_config, + .refs = 0, + .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(FAR struct stm32_i2c_priv_s *priv, + uint8_t offset) +{ + return getreg16(priv->config->base + offset); +} + /************************************************************************************ * Name: stm32_i2c_getreg32 * @@ -417,6 +654,20 @@ static inline uint32_t stm32_i2c_getreg32(FAR struct stm32_i2c_priv_s *priv, 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(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, + uint16_t value) +{ + putreg16(value, priv->config->base + offset); +} + /************************************************************************************ * Name: stm32_i2c_putreg32 * @@ -431,6 +682,7 @@ static inline void stm32_i2c_putreg32(FAR struct stm32_i2c_priv_s *priv, putreg32(value, priv->config->base + offset); } + /************************************************************************************ * Name: stm32_i2c_modifyreg32 * @@ -454,7 +706,7 @@ static inline void stm32_i2c_modifyreg32(FAR struct stm32_i2c_priv_s *priv, * ************************************************************************************/ -static inline void stm32_i2c_sem_wait(FAR struct stm32_i2c_priv_s *priv) +static inline void stm32_i2c_sem_wait(FAR struct i2c_master_s *dev) { int ret; @@ -462,7 +714,7 @@ static inline void stm32_i2c_sem_wait(FAR struct stm32_i2c_priv_s *priv) { /* Take the semaphore (perhaps waiting) */ - ret = nxsem_wait(&priv->sem_excl); + ret = nxsem_wait(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); /* The only case that an error should occur here is if the wait was * awakened by a signal. @@ -513,22 +765,7 @@ static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) #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); -} -#endif - -/************************************************************************************ - * Name: stm32_i2c_disableinterrupts - * - * Description: - * Enable I2C interrupts - * - ************************************************************************************/ - -#ifndef CONFIG_I2C_POLLED -static inline void stm32_i2c_disableinterrupts(struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_TXRX, 0); + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, (I2C_CR1_TXRX | I2C_CR1_NACKIE)); } #endif @@ -538,6 +775,9 @@ static inline void stm32_i2c_disableinterrupts(struct stm32_i2c_priv_s *priv) * 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 @@ -551,16 +791,17 @@ static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv) /* 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. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_timedwait() sleeps. - */ + /* Signal the interrupt handler that we are waiting */ - priv->intstate = INTSTATE_WAITING; - do + priv->intstate = INTSTATE_WAITING; + do { /* Get the current time */ @@ -660,7 +901,7 @@ static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv) while (priv->intstate != INTSTATE_DONE && elapsed < timeout); - i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08x\n", + i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: 0x%08x\n", priv->intstate, (long)elapsed, (long)timeout, priv->status); /* Set the interrupt state back to IDLE */ @@ -694,7 +935,7 @@ stm32_i2c_set_7bit_address(FAR struct stm32_i2c_priv_s *priv) static inline void stm32_i2c_set_bytes_to_transfer(FAR struct stm32_i2c_priv_s *priv, - uint8_t n_bytes) + uint8_t n_bytes) { stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_NBYTES_MASK, (n_bytes << I2C_CR2_NBYTES_SHIFT)); @@ -727,31 +968,32 @@ stm32_i2c_set_read_transfer_dir(FAR struct stm32_i2c_priv_s *priv) } /************************************************************************************ - * Name: stm32_i2c_enable_autoend + * Name: stm32_i2c_enable_reload * * Description: * ************************************************************************************/ static inline void -stm32_i2c_enable_autoend(FAR struct stm32_i2c_priv_s *priv) +stm32_i2c_enable_reload(FAR struct stm32_i2c_priv_s *priv) { - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_AUTOEND); + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_RELOAD); } /************************************************************************************ - * Name: stm32_i2c_disable_autoend + * Name: stm32_i2c_disable_reload * * Description: * ************************************************************************************/ static inline void -stm32_i2c_disable_autoend(FAR struct stm32_i2c_priv_s *priv) +stm32_i2c_disable_reload(FAR struct stm32_i2c_priv_s *priv) { - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_AUTOEND, 0); + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_RELOAD, 0); } + /************************************************************************************ * Name: stm32_i2c_sem_waitstop * @@ -776,11 +1018,7 @@ static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv) timeout = CONFIG_STM32F0L0_I2CTIMEOTICKS; #endif - /* Wait as stop might still be in progress; but stop might also - * be set because of a timeout error: "The [STOP] bit is set and - * cleared by software, cleared by hardware when a Stop condition is - * detected, set by hardware when a timeout error is detected." - */ + /* Wait as stop might still be in progress */ start = clock_systimer(); do @@ -799,11 +1037,12 @@ static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv) /* Check for timeout error */ - sr = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + 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. */ @@ -825,9 +1064,9 @@ static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv) * ************************************************************************************/ -static inline void stm32_i2c_sem_post(FAR struct stm32_i2c_priv_s *priv) +static inline void stm32_i2c_sem_post(FAR struct i2c_master_s *dev) { - nxsem_post(&priv->sem_excl); + nxsem_post(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); } /************************************************************************************ @@ -838,17 +1077,17 @@ static inline void stm32_i2c_sem_post(FAR struct stm32_i2c_priv_s *priv) * ************************************************************************************/ -static inline void stm32_i2c_sem_init(FAR struct stm32_i2c_priv_s *priv) +static inline void stm32_i2c_sem_init(FAR struct i2c_master_s *dev) { - nxsem_init(&priv->sem_excl, 0, 1); + nxsem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl, 0, 1); #ifndef CONFIG_I2C_POLLED /* This semaphore is used for signaling and, hence, should not have * priority inheritance enabled. */ - nxsem_init(&priv->sem_isr, 0, 0); - nxsem_setprotocol(&priv->sem_isr, SEM_PRIO_NONE); + nxsem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, 0, 0); + nxsem_setprotocol(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, SEM_PRIO_NONE); #endif } @@ -860,11 +1099,11 @@ static inline void stm32_i2c_sem_init(FAR struct stm32_i2c_priv_s *priv) * ************************************************************************************/ -static inline void stm32_i2c_sem_destroy(FAR struct stm32_i2c_priv_s *priv) +static inline void stm32_i2c_sem_destroy(FAR struct i2c_master_s *dev) { - nxsem_destroy(&priv->sem_excl); + nxsem_destroy(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); #ifndef CONFIG_I2C_POLLED - nxsem_destroy(&priv->sem_isr); + nxsem_destroy(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr); #endif } @@ -881,7 +1120,7 @@ static void stm32_i2c_traceclear(FAR struct stm32_i2c_priv_s *priv) { struct stm32_trace_s *trace = &priv->trace[priv->tndx]; - trace->status = 0; /* I2C 32-bit SR2|SR1 status */ + 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 */ @@ -969,8 +1208,8 @@ static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv) struct stm32_trace_s *trace; int i; - syslog(LOG_DEBUG, "Elapsed time: %ld\n", - (long)(clock_systimer() - priv->start_time)); + syslog(LOG_DEBUG, "Elapsed time: %d\n", + (int)(clock_systimer() - priv->start_time)); for (i = 0; i < priv->tndx; i++) { @@ -978,7 +1217,7 @@ static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv) 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, - trace->time - priv->start_time); + (int)(trace->time - priv->start_time)); } } #endif /* CONFIG_I2C_TRACE */ @@ -987,149 +1226,249 @@ static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv) * Name: stm32_i2c_setclock * * Description: - * Set the I2C clock + * + * Sets the I2C bus clock frequency by configuring the I2C_TIMINGR register. + * + * This function supports bus clock frequencies of: + * + * 1000Khz (Fast Mode+) + * 400Khz (Fast Mode) + * 100Khz (Standard Mode) + * 10Khz (Standard Mode) + * + * Attempts to set a different frequency will quietly provision the default + * of 10Khz. + * + * The only differences between the various modes of operation (std, fast, + * fast+) are the bus clock speed and setup/hold times. Setup/hold times are + * specified as a MINIMUM time for the given mode, and naturally std mode + * has the longest minimum times. As a result, by provisioning setup/hold + * times for std mode they are also compatible with fast/fast+, though some + * performance degradation occurs in fast/fast+ as a result of the times + * being somewhat longer than strictly required. The values remain as they + * are because reliability is favored over performance. + * + * Clock Selection: + * + * The I2C peripheral clock can be provided by either PCLK1, SYSCLK or the HSI. + * + * PCLK1 >------|\ I2CCLK + * SYSCLK >------| |---------> + * HSI >------|/ + * + * HSI is the default and is always 16Mhz. + * + * SYSCLK can, in turn, be derived from the HSI, HSE, PPLCLK. + * + * HSI >------|\ + * | | SYSCLK + * PLL >------| |---------> + * | | + * HSE >------|/ + * + * + * References: + * + * App Note AN4235 and the associated software STSW-STM32126. * ************************************************************************************/ static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency) { - uint32_t pe; uint8_t presc; - uint8_t s_time; - uint8_t h_time; + uint8_t scl_delay; + uint8_t sda_delay; uint8_t scl_h_period; uint8_t scl_l_period; - /* XXX haque; these are the only freqs we support at the moment, until we can - * compute the values ourself. Pick the highest supported frequency that does - * not exceed the requested frequency. + /* I2C peripheral must be disabled to update clocking configuration. + * This will SW reset the device. */ - if (frequency < 100000) - { - frequency = 10000; /* 0Hz <= frequency < 100KHz: Use 10Khz */ - } - else if (frequency < 400000) - { - frequency = 100000; /* 100KHz <= frequency < 400KHz: Use 100KHz */ - } - else if (frequency < 1000000) - { - frequency = 400000; /* 400KHz <= frequency < 1MHz: Use 400Khz */ - } - else - { - frequency = 1000000; /* 1MHz <= frequency: Use 1Mhz */ - } - - /* Has the I2C bus frequency changed? */ + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0); if (frequency != priv->frequency) { - /* Disable the selected I2C peripheral to configure TRISE */ - pe = (stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET) & I2C_CR1_PE); - if (pe) - { - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0); - } - - /* Update timing and control registers */ - - /* TODO: speed/timing calcs, taking into consideration - * STM32_PCLK1_FREQUENCY, or SYSCLK, or HSI16 - * clock source, RCC_CCIPR, I2CxSEL, 0 = PCKL, 1 = SCLK, 2 = HSI16, 3 = reserved -#warning "check set filters before timing, see RM0351 35.4.4 p 1112" - * analog filter; suppress spikes up to 50 ns in fast-mode and fast-mode plus - * ANFOFF cr1 - * DNF cr1; 1-15 I2CCLK periods + /* The Speed and timing calculation are based on the following + * fI2CCLK = HSI and is 16Mhz + * Analog filter is on, + * Digital filter off + * Rise Time is 120 ns and fall is 10ns + * Mode is FastMode */ - /* RM0351 35.4.9 p 1140 */ - if (frequency == 10000) - { - /* 10 KHz values from I2C timing tool with clock 8mhz */ + if (frequency == 100000) + { + presc = 0; + scl_delay = 5; + sda_delay = 0; + scl_h_period = 61; + scl_l_period = 89; - presc = 0x01; /* PRESC - (+1) prescale I2CCLK */ - scl_l_period = 0xc7; /* SCLL - SCL low period in master mode */ - scl_h_period = 0xc3; /* SCLH - SCL high period in master mode */ - h_time = 0x02; /* SDADEL - (+1) data hold time after SCL falling edge */ - s_time = 0x04; /* SCLDEL - (+1) data setup time from SDA edge to SCL rising edge */ - } - else if (frequency == 100000) - { - /* 100 KHz values from I2C timing tool with clock 8mhz */ - - presc = 0x01; /* PRESC - (+1) prescale I2CCLK */ - scl_l_period = 0x13; /* SCLL - SCL low period in master mode */ - scl_h_period = 0x0f; /* SCLH - SCL high period in master mode */ - h_time = 0x02; /* SDADEL - (+1) data hold time after SCL falling edge */ - s_time = 0x04; /* SCLDEL - (+1) data setup time from SDA edge to SCL rising edge */ - } - else if (frequency == 400000) - { - /* 400 KHz values from I2C timing tool for clock of 8mhz */ - - presc = 0x00; /* PRESC - (+1) prescale I2CCLK */ - scl_l_period = 0x09; /* SCLL - SCL low period in master mode */ - scl_h_period = 0x03; /* SCLH - SCL high period in master mode */ - h_time = 0x01; /* SDADEL - (+1) data hold time after SCL falling edge */ - s_time = 0x03; /* SCLDEL - (+1) data setup time from SDA edge to SCL rising edge */ - } - else - { - /* 1000 KHhz values from I2C timing tool for clock of 8mhz */ - - presc = 0x00; /* PRESC - (+1) prescale I2CCLK */ - scl_l_period = 0x06; /* SCLL - SCL low period in master mode */ - scl_h_period = 0x03; /* SCLH - SCL high period in master mode */ - h_time = 0x00; /* SDADEL - (+1) data hold time after SCL falling edge */ - s_time = 0x01; /* SCLDEL - (+1) data setup time from SDA edge to SCL rising edge */ - } + } + else if (frequency == 400000) + { + presc = 0; + scl_delay = 3; + sda_delay = 0; + scl_h_period = 6; + scl_l_period = 24; + } + else if (frequency == 1000000) + { + presc = 0; + scl_delay = 2; + sda_delay = 0; + scl_h_period = 1; + scl_l_period = 5; + } + else + { + presc = 7; + scl_delay = 0; + sda_delay = 0; + scl_h_period = 35; + scl_l_period = 162; + } uint32_t timingr = (presc << I2C_TIMINGR_PRESC_SHIFT) | - (s_time << I2C_TIMINGR_SCLDEL_SHIFT) | - (h_time << I2C_TIMINGR_SDADEL_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); - - /* Re-enable the peripheral (or not) */ - - if (pe) - { - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); - } - - /* Save the new I2C frequency */ - priv->frequency = frequency; } + + /* Enable I2C peripheral */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); } /************************************************************************************ * Name: stm32_i2c_sendstart * * Description: - * Send the START conditions/force Master mode + * 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(FAR struct stm32_i2c_priv_s *priv) { - /* Get run-time data */ + 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->astart = true; priv->ptr = priv->msgv->buffer; priv->dcnt = priv->msgv->length; priv->flags = priv->msgv->flags; - /* Disable ACK on receive by default and generate START */ + 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_bytes_to_transfer(priv, priv->dcnt); 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); @@ -1139,52 +1478,14 @@ static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv) stm32_i2c_set_write_transfer_dir(priv); } - if (priv->msgc == 1) - { - /* stm32_i2c_enable_autoend(priv); */ - } - else - { - /* stm32_i2c_disable_autoend(priv); */ - } - - /* TODO check NACK */ - /* TODO handle NACKR? */ - - stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_START); -} - -/************************************************************************************ - * Name: stm32_i2c_clrstart - * - * Description: - * Clear the STOP, START or PEC condition on certain error recovery steps. - * - ************************************************************************************/ - -static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv) -{ - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." - * - * "The [STOP] bit is set and cleared by software, cleared by hardware - * when a Stop condition is detected, set by hardware when a timeout - * error is detected. - * - * "This [START] bit is set and cleared by software and cleared by hardware - * when start is sent or PE=0." The bit must be cleared by software if the - * START is never sent. - * - * "This [PEC] bit is set and cleared by software, and cleared by hardware - * when PEC is transferred or by a START or Stop condition or when PE=0." + /* 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. */ - /* TODO check PEC (32 bit separate reg) */ + 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, - I2C_CR2_START | I2C_CR2_STOP, 0); + stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_START); } /************************************************************************************ @@ -1193,11 +1494,16 @@ static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv) * 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(FAR struct stm32_i2c_priv_s *priv) { - /* TODO check NACK */ + i2cinfo("Sending STOP\n"); + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_STOP, 0); stm32_i2c_modifyreg32(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_STOP); } @@ -1215,25 +1521,6 @@ static inline uint32_t stm32_i2c_getstatus(FAR struct stm32_i2c_priv_s *priv) return getreg32(priv->config->base + STM32_I2C_ISR_OFFSET); } -/************************************************************************************ - * Name: stm32_i2c_isr_startmessage - * - * Description: - * Common logic when a message is started. Just adds the even to the trace buffer - * if enabled and adjusts the message pointer and count. - * - ************************************************************************************/ - -static inline void stm32_i2c_isr_startmessage(struct stm32_i2c_priv_s *priv) -{ - stm32_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgc); - - /* Increment to next pointer and decrement message count */ - - priv->msgv++; - priv->msgc--; -} - /************************************************************************************ * Name: stm32_i2c_clearinterrupts * @@ -1244,218 +1531,641 @@ static inline void stm32_i2c_isr_startmessage(struct stm32_i2c_priv_s *priv) static inline void stm32_i2c_clearinterrupts(struct stm32_i2c_priv_s *priv) { -#warning "check this clears interrupts?" - stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); + stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); } /************************************************************************************ * Name: stm32_i2c_isr_process * * Description: - * Common Interrupt Service Routine + * 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 = stm32_i2c_getstatus(priv); + uint32_t status; - /* Check for new trace setup */ + /* Get state of the I2C controller */ + + status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + + i2cinfo("ENTER: status = 0x%08x\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); -#warning "TODO: check clear interrupts after all actions" + /* --------------------- Start of I2C protocol handling -------------------- */ - if (STATUS_NACK(status)) + /* 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) { - /* wait, reset this? */ - } - else if (priv->astart) - { - stm32_i2c_isr_startmessage(priv); - priv->astart = false; + + if (priv->astart == true) + { + + /* NACK received on first (address) byte: address is invalid */ + + i2cinfo("NACK: Address invalid: dcnt=%i msgc=%i status=0x%08x\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%08x\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; } - /* Was address sent, continue with either sending or reading data */ + /* 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. + */ - if ((priv->flags & I2C_M_READ) == 0 && STATUS_ADDR_TX(status)) + else if ((priv->flags & (I2C_M_READ)) == 0 && (status & (I2C_ISR_TXIS)) != 0) { -#warning "TODO: ADDRCF clear address interrupt flag" + + /* TXIS interrupt occurred, address valid, ready to transmit */ + + stm32_i2c_traceevent(priv, I2CEVENT_WRITE, 0); + i2cinfo("TXIS: ENTER dcnt = %i msgc = %i status 0x%08x\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) { - /* Send a byte */ + /* 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 */ - stm32_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); - stm32_i2c_putreg32(priv, STM32_I2C_TXDR_OFFSET, *priv->ptr++); 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 if ((priv->flags & I2C_M_READ) != 0 && STATUS_ADDR(status)) - { - /* Enable RxNE and TxE buffers in order to receive one or multiple bytes */ + else + { + /* Unsupported state */ -#warning "TODO: ADDRCF clear address interrupt flag" + i2cerr("ERROR: TXIS Unsupported state detected, dcnt=%i, status 0x%08x\n", + priv->dcnt, status); + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_ERROR, 0); -#ifndef CONFIG_I2C_POLLED - stm32_i2c_traceevent(priv, I2CEVENT_ITBUFEN, 0); - stm32_i2c_enableinterrupts(priv); -#endif + /* 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%08x\n", + priv->dcnt, priv->msgc, status); } - /* More bytes to read */ + /* 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 (STATUS_RXNE(status)) + else if ((priv->flags & (I2C_M_READ)) != 0 && (status & I2C_ISR_RXNE) != 0) { - /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ + /* 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%08x\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 sent by the - * device. + * sequence. Otherwise, additional bytes may be received. */ #ifdef CONFIG_I2C_POLLED - irqstate_t flags = enter_critical_section(); + irqstate_t state = enter_critical_section(); #endif /* Receive a byte */ - *priv->ptr++ = (uint8_t) stm32_i2c_getreg32(priv, STM32_I2C_RXDR_OFFSET); + *priv->ptr = stm32_i2c_getreg(priv, STM32_I2C_RXDR_OFFSET); - /* Disable acknowledge when last byte is to be received */ + 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--; - if (priv->dcnt == 1) - { - /* autoend? */ - } #ifdef CONFIG_I2C_POLLED - leave_critical_section(flags); + 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%08x\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%08x\n", + priv->dcnt, priv->msgc, status); } - /* Do we have more bytes to send, enable/disable buffer interrupts - * (these ISRs could be replaced by DMAs) + /* 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. */ -#ifndef CONFIG_I2C_POLLED - if (priv->dcnt > 0) + else if ((status & I2C_ISR_TC) != 0) { - stm32_i2c_traceevent(priv, I2CEVENT_REITBUFEN, 0); - stm32_i2c_enableinterrupts(priv); - } - else if ((priv->dcnt == 0) && (priv->msgc == 0)) - { - stm32_i2c_traceevent(priv, I2CEVENT_DISITBUFEN, 0); - stm32_i2c_disableinterrupts(priv); - } -#endif + i2cinfo("TC: ENTER dcnt = %i msgc = %i status 0x%08x\n", + priv->dcnt, priv->msgc, status); - /* Was last byte received or sent? Hmmm... the F2 and F4 seems to differ from - * the F1 in that BTF is not set after data is received (only RXNE). - */ - - if (priv->dcnt <= 0 && STATUS_TC(status)) - { - /* Do we need to terminate or restart after this byte? - * If there are more messages to send, then we may: - * - * - continue with repeated start - * - or just continue sending writeable part - * - or we close down by sending the stop bit + /* 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) { - if (priv->msgv->flags & I2C_M_NOSTART) - { - stm32_i2c_traceevent(priv, I2CEVENT_BTFNOSTART, priv->msgc); - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; - priv->msgv++; - priv->msgc--; - - /* Restart this ISR! */ - -#ifndef CONFIG_I2C_POLLED - stm32_i2c_enableinterrupts(priv); -#endif - } - else - { - stm32_i2c_traceevent(priv, I2CEVENT_BTFRESTART, priv->msgc); - stm32_i2c_sendstart(priv); - } + priv->msgc--; } - else if (priv->msgv) + + /* Are there additional messages remain to be transmitted / received? */ + + if (priv->msgc > 0) { - stm32_i2c_traceevent(priv, I2CEVENT_BTFSTOP, 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); - /* Is there a thread waiting for this event (there should be) */ + /* Set signals that will terminate ISR and wake waiting thread */ -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) + priv->dcnt = -1; + priv->msgc = 0; + } + + i2cinfo("TC: EXIT dcnt = %i msgc = %i status 0x%08x\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%08x\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) { - /* Yes.. inform the thread that the transfer is complete - * and wake it up. + 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. */ - nxsem_post(&priv->sem_isr); - priv->intstate = INTSTATE_DONE; + stm32_i2c_enable_reload(priv); + + stm32_i2c_set_bytes_to_transfer(priv, 255); } -#else - priv->intstate = INTSTATE_DONE; -#endif + 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. + */ - /* Mark that we have stopped with this transaction */ + i2cinfo("TCR: DISABLE RELOAD: NBYTES = dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); - priv->msgv = NULL; + stm32_i2c_disable_reload(priv); + + stm32_i2c_set_bytes_to_transfer(priv, priv->dcnt); + } + + i2cinfo("TCR: EXIT dcnt = %i msgc = %i status 0x%08x\n", + priv->dcnt, priv->msgc, status); } } - /* Check for errors, in which case, stop the transfer and return - * Note that in master reception mode AF becomes set on last byte - * since ACK is not returned. We should ignore this error. + /* Empty call handler + * + * Case to handle an empty call to the ISR where it has nothing to + * do and should exit immediately. */ - if ((status & I2C_ISR_ERRORMASK) != 0) + else if (priv->dcnt == -1 && priv->msgc == 0) { - stm32_i2c_traceevent(priv, I2CEVENT_ERROR, 0); + status = stm32_i2c_getreg(priv, STM32_I2C_ISR_OFFSET); + i2cwarn("WARNING: EMPTY CALL: Stopping ISR: status 0x%08x\n", status); + stm32_i2c_traceevent(priv, I2CEVENT_ISR_EMPTY_CALL, 0); + } - /* Clear interrupt flags */ + /* 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. + */ - stm32_i2c_clearinterrupts(priv); + else + { +#ifdef CONFIG_I2C_POLLED + stm32_i2c_traceevent(priv, I2CEVENT_POLL_DEV_NOT_RDY, 0); +#else + /* Read rest of the state */ - /* Is there a thread waiting for this event (there should be) */ + status = stm32_i2c_getreg(priv, STM32_I2C_ISR_OFFSET); + + i2cerr("ERROR: Invalid state detected, status 0x%08x\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 + + /* We will 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: status is captured 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 */ -#ifndef CONFIG_I2C_POLLED if (priv->intstate == INTSTATE_WAITING) { - /* Yes.. inform the thread that the transfer is complete - * and wake it up. - */ - nxsem_post(&priv->sem_isr); priv->intstate = INTSTATE_DONE; } -#else - priv->intstate = INTSTATE_DONE; #endif } - priv->status = status; + status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + i2cinfo("EXIT: status = 0x%08x\n", status); + return OK; } /************************************************************************************ - * Name: stm32_i2c_isr_process + * Name: stm32_i2c_isr * * Description: * Common I2C interrupt service routine @@ -1472,10 +2182,6 @@ static int stm32_i2c_isr(int irq, void *context, FAR void *arg) } #endif -/************************************************************************************ - * Private Initialization and Deinitialization - ************************************************************************************/ - /************************************************************************************ * Name: stm32_i2c_init * @@ -1486,9 +2192,8 @@ static int stm32_i2c_isr(int irq, void *context, FAR void *arg) static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv) { - int ret; - /* Power-up and configure GPIOs */ + /* Enable power and reset the peripheral */ modifyreg32(STM32_RCC_APB1ENR, 0, priv->config->clk_bit); @@ -1497,42 +2202,34 @@ static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv) /* Configure pins */ - ret = stm32_configgpio(priv->config->scl_pin); - if (ret < 0) + if (stm32_configgpio(priv->config->scl_pin) < 0) { - return ret; + return ERROR; } - ret = stm32_configgpio(priv->config->sda_pin); - if (ret < 0) + if (stm32_configgpio(priv->config->sda_pin) < 0) { stm32_unconfiggpio(priv->config->scl_pin); - return ret; + return ERROR; } #ifndef CONFIG_I2C_POLLED - /* Attach and enable the I2C interrupt */ + /* Attach error and event interrupts to the ISRs */ irq_attach(priv->config->irq, stm32_i2c_isr, priv); up_enable_irq(priv->config->irq); #endif - /* Set peripheral frequency, where it must be at least 2 MHz for 100 kHz - * or 4 MHz for 400 kHz. This also disables all I2C interrupts. + /* TODO: + * - Provide means to set peripheral clock source via RCC_CFGR3_I2CxSW + * - Set to HSI by default, make Kconfig option */ /* Force a frequency update */ priv->frequency = 0; - - /* TODO: i2c clock source RCC_CCIPR */ - /* RCC_CCIPR I2CxSEL (default is PCLK clock) */ - stm32_i2c_setclock(priv, 100000); - /* Enable I2C */ - - stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); return OK; } @@ -1555,9 +2252,10 @@ static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv) stm32_unconfiggpio(priv->config->scl_pin); stm32_unconfiggpio(priv->config->sda_pin); +#ifndef CONFIG_I2C_POLLED + /* Disable and detach interrupts */ -#ifndef CONFIG_I2C_POLLED up_disable_irq(priv->config->irq); irq_detach(priv->config->irq); #endif @@ -1565,13 +2263,242 @@ static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv) /* Disable clocking */ modifyreg32(STM32_RCC_APB1ENR, priv->config->clk_bit, 0); + return OK; } /************************************************************************************ - * Device Driver Operations + * 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(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) +{ + struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev; + FAR 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%08x CR2: 0x%08x status: 0x%08x\n", + cr1, cr2,status); + } + else + { + i2cinfo("Waitdone success: CR1: 0x%08x CR2: 0x%08x status: 0x%08x\n", + cr1, cr2,status ); + } + + UNUSED(cr1); + UNUSED(cr2); + + i2cinfo("priv->status: 0x%08x\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_systimer(); + clock_t timeout = USEC2TICK(USEC_PER_SEC/priv->frequency) + 1; + + status = stm32_i2c_getstatus(priv); + + while (status & I2C_ISR_BUSY) + { + if ((clock_systimer() - start) > timeout) + { + i2cerr("ERROR: I2C Bus busy"); + errval = EBUSY; + break; + } + + status = stm32_i2c_getstatus(priv); + } + } + + /* Dump the trace result */ + + stm32_i2c_tracedump(priv); + stm32_i2c_sem_post(dev); + + return -errval; +} + /************************************************************************************ * Name: stm32_i2c_transfer * @@ -1583,211 +2510,42 @@ static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv) static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) { - FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)dev; - uint32_t status = 0; - int ret = OK; - - DEBUGASSERT(dev != NULL && msgs != NULL && count > 0); - - /* Ensure that address or flags don't change meanwhile */ - - stm32_i2c_sem_wait(priv); - - /* Wait for any STOP in progress. */ - - stm32_i2c_sem_waitstop(priv); - - /* Clear any pending error interrupts */ - - stm32_i2c_clearinterrupts(priv); - - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." However, if the bits are - * not cleared by hardware, then we will have to do that from hardware. - */ - - stm32_i2c_clrstart(priv); - - /* Old transfers are done */ - - priv->msgv = msgs; - priv->msgc = count; - - /* Reset I2C trace logic */ - - stm32_i2c_tracereset(priv); - - /* Set I2C clock frequency (on change it toggles I2C_CR1_PE !) - * REVISIT: Note that the frequency is set only on the first message. - * This could be extended to support different transfer frequencies for - * each message segment. - */ - - 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 - stm32_i2c_enableinterrupts(priv); -#endif - - stm32_i2c_sendstart(priv); - - /* Wait for an ISR, if there was a timeout, fetch latest status to get - * the BUSY flag. - */ - - if (stm32_i2c_sem_waitdone(priv) < 0) - { - status = stm32_i2c_getstatus(priv); - ret = -ETIMEDOUT; - - i2cerr("ERROR: Timed out: CR1: %08x status: %08x\n", - stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET), status); - - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." - */ - - stm32_i2c_clrstart(priv); - - /* Clear busy flag in case of timeout */ - - status = priv->status & 0xffff; - } - else - { - /* clear SR2 (BUSY flag) as we've done successfully */ - - status = priv->status & 0xffff; - } - - status &= ~I2C_ISR_BUSY; -#if 0 - /* Refresh status */ - do - { - status = stm32_i2c_getstatus(priv); - } - while (STATUS_BUSY(status)); -#endif - - /* Check for error status conditions */ - - if ((status & I2C_ISR_ERRORMASK) != 0) - { - /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ - - if (status & I2C_INT_BERR) - { - /* Bus Error */ - - ret = -EIO; - } - else if (status & I2C_INT_ARLO) - { - /* Arbitration Lost (master mode) */ - - ret = -EAGAIN; - } - - /* TODO Acknowledge failure */ - - else if (status & I2C_INT_OVR) - { - /* Overrun/Underrun */ - - ret = -EIO; - } - else if (status & I2C_INT_PECERR) - { - /* PEC Error in reception */ - - ret = -EPROTO; - } - else if (status & I2C_INT_TIMEOUT) - { - /* Timeout or Tlow Error */ - - ret = -ETIME; - } - - /* This is not an error and should never happen since SMBus is not - * enabled - */ - - else /* if (status & I2C_INT_ALERT) */ - { - /* SMBus alert is an optional signal with an interrupt line for devices - * that want to trade their ability to master for a pin. - */ - - ret = -EINTR; - } - } - - /* This is not an error, but should not happen. The BUSY signal can hang, - * however, if there are unhealthy devices on the bus that need to be reset. - * NOTE: We will only see this buy indication if stm32_i2c_sem_waitdone() - * fails above; Otherwise it is cleared. - */ - - else if ((status & I2C_ISR_BUSY) != 0) - { - /* I2C Bus is for some reason busy */ - - ret = -EBUSY; - } - - /* Dump the trace result */ - - stm32_i2c_tracedump(priv); - stm32_i2c_sem_post(priv); - return ret; + stm32_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */ + return stm32_i2c_process(dev, msgs, count); } /************************************************************************************ * Name: stm32_i2c_reset * * Description: - * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. - * - * Input Parameters: - * dev - Device-specific state data - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. + * Reset an I2C bus * ************************************************************************************/ #ifdef CONFIG_I2C_RESET static int stm32_i2c_reset(FAR struct i2c_master_s * dev) { - FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_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 = -EIO; + int ret = ERROR; 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 */ - stm32_i2c_sem_wait(priv); + stm32_i2c_sem_wait(dev); /* Save the current frequency */ @@ -1802,6 +2560,9 @@ static int stm32_i2c_reset(FAR struct i2c_master_s * dev) 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); @@ -1867,25 +2628,97 @@ static int stm32_i2c_reset(FAR struct i2c_master_s * dev) /* Re-init the port */ - ret = stm32_i2c_init(priv); - if (ret < 0) - { - i2cerr("ERROR: stm32_i2c_init failed: %d\n", ret); - } + stm32_i2c_init(priv); /* Restore the frequency */ stm32_i2c_setclock(priv, frequency); + ret = OK; out: /* Release the port for re-use by other clients */ - stm32_i2c_sem_post(priv); + stm32_i2c_sem_post(dev); 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(FAR 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)); + int sval; + + /* 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 (nxsem_getvalue(&priv->sem_excl, &sval) < 0) + { + DEBUGASSERT(false); + return -EINVAL; + } + + if (sval <= 0) + { + /* 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 ************************************************************************************/ @@ -1900,20 +2733,21 @@ out: FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) { - struct stm32_i2c_priv_s *priv = NULL; - irqstate_t flags; + struct stm32_i2c_priv_s * priv = NULL; /* private data of device with multiple instances */ + struct stm32_i2c_inst_s * inst = NULL; /* device, single instance */ + irqstate_t irqs; +#ifdef CONFIG_PM int ret; - -#if STM32_PCLK1_FREQUENCY < 4000000 -# warning STM32_I2C_INIT: Peripheral clock must be at least 4 MHz to support 400 kHz operation. #endif -#if STM32_PCLK1_FREQUENCY < 2000000 -# warning STM32_I2C_INIT: Peripheral clock must be at least 2 MHz to support 100 kHz operation. - return NULL; +#if 0 /* REVISIT: this is not true for all STM32 M0 */ +#if STM32_HSI_FREQUENCY != 8000000 || defined(INVALID_CLOCK_SOURCE) +# warning STM32_I2C_INIT: Peripheral clock is HSI and it must be 16mHz or the speed/timing calculations need to be redone. + return NULL; +#endif #endif - /* Get I2C private structure. */ + /* Get I2C private structure */ switch (port) { @@ -1931,29 +2765,50 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) case 3: priv = (struct stm32_i2c_priv_s *)&stm32_i2c3_priv; break; +#endif +#ifdef CONFIG_STM32F0L0_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. */ - flags = enter_critical_section(); + irqs = enter_critical_section(); if ((volatile int)priv->refs++ == 0) { - stm32_i2c_sem_init(priv); - ret = stm32_i2c_init(priv); - if (ret < 0) - { - i2cerr("ERROR: stm32_i2c_init failed: %d\n", ret); - } + stm32_i2c_sem_init((struct i2c_master_s *)inst); + stm32_i2c_init(priv); + +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + ret = pm_register(&priv->pm_cb); + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif } - leave_critical_section(flags); - return (struct i2c_master_s *)priv; + leave_critical_section(irqs); + return (struct i2c_master_s *)inst; } /************************************************************************************ @@ -1966,36 +2821,45 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) int stm32_i2cbus_uninitialize(FAR struct i2c_master_s * dev) { - FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)dev; - irqstate_t flags; + irqstate_t irqs; DEBUGASSERT(dev); /* Decrement refs and check for underflow */ - if (priv->refs == 0) + if (((struct stm32_i2c_inst_s *)dev)->priv->refs == 0) { - return -ENODEV; + return ERROR; } - flags = enter_critical_section(); + irqs = enter_critical_section(); - if (--priv->refs) + if (--((struct stm32_i2c_inst_s *)dev)->priv->refs) { - leave_critical_section(flags); + leave_critical_section(irqs); + kmm_free(dev); return OK; } - leave_critical_section(flags); + leave_critical_section(irqs); + +#ifdef CONFIG_PM + /* Unregister power management callbacks */ + + pm_unregister(&((struct stm32_i2c_inst_s *)dev)->priv->pm_cb); +#endif /* Disable power and other HW resource (GPIO's) */ - stm32_i2c_deinit(priv); + stm32_i2c_deinit(((struct stm32_i2c_inst_s *)dev)->priv); /* Release unused resources */ - stm32_i2c_sem_destroy(priv); + stm32_i2c_sem_destroy((struct i2c_master_s *)dev); + + kmm_free(dev); return OK; } -#endif /* CONFIG_STM32F0L0_I2C1 || CONFIG_STM32F0L0_I2C2 || CONFIG_STM32F0L0_I2C3 */ +#endif /* CONFIG_STM32F0L0_I2C1 || CONFIG_STM32F0L0_I2C2 || \ + CONFIG_STM32F0L0_I2C3 || CONFIG_STM32F0L0_I2C4 */ diff --git a/arch/arm/src/stm32h7/chip.h b/arch/arm/src/stm32h7/chip.h index 127f5c2a66..dd89846335 100644 --- a/arch/arm/src/stm32h7/chip.h +++ b/arch/arm/src/stm32h7/chip.h @@ -51,6 +51,10 @@ #include "chip/stm32_pinmap.h" #include "chip/stm32_memorymap.h" +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + /* If the common ARMv7-M vector handling logic is used, then it expects the * following definition in this file that provides the number of supported external * interrupts which, for this architecture, is provided in the arch/stm32h7/chip.h @@ -64,10 +68,6 @@ #define ARMV7M_DCACHE_LINESIZE 32 /* 32 bytes (8 words) */ #define ARMV7M_ICACHE_LINESIZE 32 /* 32 bytes (8 words) */ -/************************************************************************************ - * Pre-processor Definitions - ************************************************************************************/ - /************************************************************************************ * Public Types ************************************************************************************/ diff --git a/configs/b-l072z-lrwan1/include/board.h b/configs/b-l072z-lrwan1/include/board.h index 262ea1de76..e6f35afae0 100644 --- a/configs/b-l072z-lrwan1/include/board.h +++ b/configs/b-l072z-lrwan1/include/board.h @@ -243,7 +243,7 @@ /* I2C1 */ #define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* PB9 */ -#define GPIO_I2C1_SCLK GPIO_I2C1_SCL_2 /* PB8 */ +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* PB8 */ /* DMA channels *************************************************************/ /* ADC */ diff --git a/configs/b-l072z-lrwan1/nxlines_oled/defconfig b/configs/b-l072z-lrwan1/nxlines_oled/defconfig new file mode 100644 index 0000000000..cc396affb7 --- /dev/null +++ b/configs/b-l072z-lrwan1/nxlines_oled/defconfig @@ -0,0 +1,73 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_EXAMPLES_NXLINES_DEFAULT_COLORS is not set +# CONFIG_LIBC_LONG_LONG is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NX_DISABLE_1BPP is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="b-l072z-lrwan1" +CONFIG_ARCH_BOARD_B_L072Z_LRWAN1=y +CONFIG_ARCH_CHIP_STM32L072CZ=y +CONFIG_ARCH_CHIP_STM32L072XX=y +CONFIG_ARCH_CHIP_STM32L0=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=2796 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DISABLE_POLL=y +CONFIG_EXAMPLES_NXLINES=y +CONFIG_EXAMPLES_NXLINES_BORDERWIDTH=1 +CONFIG_EXAMPLES_NXLINES_BPP=1 +CONFIG_EXAMPLES_NXLINES_LINECOLOR=0xff +CONFIG_EXAMPLES_NXLINES_LINEWIDTH=1 +CONFIG_EXPERIMENTAL=y +CONFIG_INTELHEX_BINARY=y +CONFIG_LCD=y +CONFIG_LCD_MAXCONTRAST=255 +CONFIG_LCD_SH1106_OLED_132=y +CONFIG_LCD_SSD1306_I2C=y +CONFIG_MAX_TASKS=8 +CONFIG_MAX_WDOGPARMS=2 +CONFIG_MQ_MAXMSGSIZE=64 +CONFIG_NFILE_DESCRIPTORS=6 +CONFIG_NFILE_STREAMS=6 +CONFIG_NPTHREAD_KEYS=0 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=64 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_NUNGET_CHARS=0 +CONFIG_NX=y +CONFIG_NXFONT_MONO5X8=y +CONFIG_NX_BLOCKING=y +CONFIG_PREALLOC_TIMERS=0 +CONFIG_PREALLOC_WDOGS=4 +CONFIG_PTHREAD_MUTEX_UNSAFE=y +CONFIG_PTHREAD_STACK_DEFAULT=1536 +CONFIG_RAM_SIZE=20480 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_START_DAY=19 +CONFIG_START_MONTH=5 +CONFIG_START_YEAR=2013 +CONFIG_STDIO_DISABLE_BUFFERING=y +CONFIG_STM32F0L0_I2C1=y +CONFIG_STM32F0L0_PWR=y +CONFIG_STM32F0L0_USART2=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_NSH_STACKSIZE=1024 +CONFIG_TASK_NAME_SIZE=0 +CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=1536 +CONFIG_USART2_SERIAL_CONSOLE=y +CONFIG_USERMAIN_STACKSIZE=1536 +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WDOG_INTRESERVE=0 diff --git a/configs/b-l072z-lrwan1/src/Makefile b/configs/b-l072z-lrwan1/src/Makefile index c6fbe3a069..31ab0fd412 100644 --- a/configs/b-l072z-lrwan1/src/Makefile +++ b/configs/b-l072z-lrwan1/src/Makefile @@ -64,4 +64,8 @@ ifeq ($(CONFIG_ADC),y) CSRCS += stm32_adc.c endif +ifeq ($(CONFIG_LCD_SSD1306),y) +CSRCS += stm32_ssd1306.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h b/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h index a91df6e665..3c861c2d0c 100644 --- a/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h +++ b/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h @@ -114,6 +114,10 @@ #define GPIO_SX127X_CRF2 (GPIO_SPEED_HIGH | GPIO_PORTC | GPIO_PIN2) #define GPIO_SX127X_CRF3 (GPIO_SPEED_HIGH | GPIO_PORTC | GPIO_PIN1) +/* Oled configuration */ + +#define OLED_I2C_PORT 1 + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/configs/b-l072z-lrwan1/src/stm32_bringup.c b/configs/b-l072z-lrwan1/src/stm32_bringup.c index 322d621f40..0764d56d45 100644 --- a/configs/b-l072z-lrwan1/src/stm32_bringup.c +++ b/configs/b-l072z-lrwan1/src/stm32_bringup.c @@ -63,6 +63,65 @@ # define HAVE_DAC2 1 #endif +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_i2c_register + * + * Description: + * Register one I2C drivers for the I2C tool. + * + ****************************************************************************/ + +#if defined(CONFIG_I2C) && defined(CONFIG_SYSTEM_I2CTOOL) +static void stm32_i2c_register(int bus) +{ + FAR struct i2c_master_s *i2c; + int ret; + + i2c = stm32_i2cbus_initialize(bus); + if (i2c == NULL) + { + syslog(LOG_ERR, "ERROR: Failed to get I2C%d interface\n", bus); + } + else + { + ret = i2c_register(i2c, bus); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to register I2C%d driver: %d\n", + bus, ret); + stm32_i2cbus_uninitialize(i2c); + } + } +} +#endif + +/**************************************************************************** + * Name: stm32_i2ctool + * + * Description: + * Register I2C drivers for the I2C tool. + * + ****************************************************************************/ + +#if defined(CONFIG_I2C) && defined(CONFIG_SYSTEM_I2CTOOL) +static void stm32_i2ctool(void) +{ +#ifdef CONFIG_STM32F0L0_I2C1 + stm32_i2c_register(1); +#endif +#ifdef CONFIG_STM32F0L0_I2C2 + stm32_i2c_register(2); +#endif +#ifdef CONFIG_STM32F0L0_I2C3 + stm32_i2c_register(3); +#endif +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/configs/b-l072z-lrwan1/src/stm32_ssd1306.c b/configs/b-l072z-lrwan1/src/stm32_ssd1306.c new file mode 100644 index 0000000000..6234d0a80b --- /dev/null +++ b/configs/b-l072z-lrwan1/src/stm32_ssd1306.c @@ -0,0 +1,124 @@ +/**************************************************************************** + * config/b-l072z-lrwan1/src/stm32_ssd1306.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#include "stm32.h" +#include "b-l072z-lrwan1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_LCD_MAXPOWER +# define CONFIG_LCD_MAXPOWER 1 +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +FAR struct i2c_master_s *g_i2c; +FAR struct lcd_dev_s *g_lcddev; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_lcd_initialize + ****************************************************************************/ + +int board_lcd_initialize(void) +{ + /* Initialize I2C */ + + g_i2c = stm32_i2cbus_initialize(OLED_I2C_PORT); + if (!g_i2c) + { + lcderr("ERROR: Failed to initialize I2C port %d\n", OLED_I2C_PORT); + return -ENODEV; + } + + return OK; +} + +/**************************************************************************** + * Name: board_lcd_getdev + ****************************************************************************/ + +FAR struct lcd_dev_s *board_lcd_getdev(int devno) +{ + /* Bind the I2C port to the OLED */ + + g_lcddev = ssd1306_initialize(g_i2c, NULL, devno); + if (!g_lcddev) + { + lcderr("ERROR: Failed to bind I2C port 1 to OLED %d: %d\n", devno); + } + else + { + lcdinfo("Bound I2C port %d to OLED %d\n", OLED_I2C_PORT, devno); + + /* And turn the OLED on */ + + (void)g_lcddev->setpower(g_lcddev, CONFIG_LCD_MAXPOWER); + return g_lcddev; + } + + return NULL; +} + +/**************************************************************************** + * Name: board_lcd_uninitialize + ****************************************************************************/ + +void board_lcd_uninitialize(void) +{ + /* TO-FIX */ +} diff --git a/configs/nucleo-f303ze/include/board.h b/configs/nucleo-f303ze/include/board.h index a6bcc5f9ba..9ecfa8d26d 100644 --- a/configs/nucleo-f303ze/include/board.h +++ b/configs/nucleo-f303ze/include/board.h @@ -199,6 +199,17 @@ #define GPIO_USART3_RX GPIO_USART3_RX_3 /* PD9 */ #define GPIO_USART3_TX GPIO_USART3_TX_3 /* PD8 */ +/* I2C1 Use Nucleo I2C1 pins */ + +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_3 /* PB8 - D15 */ +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_3 /* PB9 - D14 */ + +/* I2C2 Use Nucleo I2C2 pins */ + +#define GPIO_I2C2_SCL GPIO_I2C2_SCL_2 /* PF1 - D69 */ +#define GPIO_I2C2_SDA GPIO_I2C2_SDA_2 /* PF0 - D68 */ +#define GPIO_I2C2_SMBA GPIO_I2C2_SMBA_2 /* PF2 - D70 */ + /* DMA **********************************************************************/ #define ADC1_DMA_CHAN DMACHAN_ADC1 diff --git a/configs/nucleo-f303ze/nxlines_oled/defconfig b/configs/nucleo-f303ze/nxlines_oled/defconfig new file mode 100644 index 0000000000..80e56a0b5f --- /dev/null +++ b/configs/nucleo-f303ze/nxlines_oled/defconfig @@ -0,0 +1,61 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +# CONFIG_EXAMPLES_NXLINES_DEFAULT_COLORS is not set +# CONFIG_NX_DISABLE_1BPP is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="nucleo-f303ze" +CONFIG_ARCH_BOARD_NUCLEO_F303ZE=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP_STM32=y +CONFIG_ARCH_CHIP_STM32F303ZE=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=6522 +CONFIG_BUILTIN=y +CONFIG_DISABLE_POLL=y +CONFIG_EXAMPLES_NXLINES=y +CONFIG_EXAMPLES_NXLINES_BORDERWIDTH=1 +CONFIG_EXAMPLES_NXLINES_BPP=1 +CONFIG_EXAMPLES_NXLINES_LINECOLOR=0xff +CONFIG_EXAMPLES_NXLINES_LINEWIDTH=1 +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_INTELHEX_BINARY=y +CONFIG_LCD=y +CONFIG_LCD_MAXCONTRAST=255 +CONFIG_LCD_SH1106_OLED_132=y +CONFIG_LCD_SSD1306_I2C=y +CONFIG_MAX_TASKS=16 +CONFIG_MAX_WDOGPARMS=2 +CONFIG_MM_REGIONS=2 +CONFIG_MQ_MAXMSGSIZE=64 +CONFIG_NFILE_DESCRIPTORS=8 +CONFIG_NFILE_STREAMS=8 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NX=y +CONFIG_NXFONT_MONO5X8=y +CONFIG_NX_BLOCKING=y +CONFIG_PREALLOC_MQ_MSGS=4 +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PREALLOC_WDOGS=8 +CONFIG_RAM_SIZE=65536 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_START_DAY=27 +CONFIG_START_YEAR=2013 +CONFIG_STM32_I2C1=y +CONFIG_STM32_JTAG_SW_ENABLE=y +CONFIG_STM32_USART3=y +CONFIG_SYSLOG_NONE=y +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=0 +CONFIG_USART3_SERIAL_CONSOLE=y +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WDOG_INTRESERVE=1 diff --git a/configs/nucleo-f303ze/src/Makefile b/configs/nucleo-f303ze/src/Makefile index 9f6c5cb074..7da547c2f8 100644 --- a/configs/nucleo-f303ze/src/Makefile +++ b/configs/nucleo-f303ze/src/Makefile @@ -56,4 +56,8 @@ ifeq ($(CONFIG_ADC),y) CSRCS += stm32_adc.c endif +ifeq ($(CONFIG_LCD_SSD1306),y) +CSRCS += stm32_ssd1306.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/nucleo-f303ze/src/nucleo-f303ze.h b/configs/nucleo-f303ze/src/nucleo-f303ze.h index 8e0f4cfa8f..fd7fa80d15 100644 --- a/configs/nucleo-f303ze/src/nucleo-f303ze.h +++ b/configs/nucleo-f303ze/src/nucleo-f303ze.h @@ -84,6 +84,10 @@ #define GPIO_BTN_USER (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTC|GPIO_PIN13) +/* Oled configuration */ + +#define OLED_I2C_PORT 1 + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/configs/nucleo-f303ze/src/stm32_ssd1306.c b/configs/nucleo-f303ze/src/stm32_ssd1306.c new file mode 100644 index 0000000000..133222c244 --- /dev/null +++ b/configs/nucleo-f303ze/src/stm32_ssd1306.c @@ -0,0 +1,124 @@ +/**************************************************************************** + * config/nucleo-f303ze/src/stm32_ssd1306.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#include "stm32.h" +#include "nucleo-f303ze.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_LCD_MAXPOWER +# define CONFIG_LCD_MAXPOWER 1 +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +FAR struct i2c_master_s *g_i2c; +FAR struct lcd_dev_s *g_lcddev; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_lcd_initialize + ****************************************************************************/ + +int board_lcd_initialize(void) +{ + /* Initialize I2C */ + + g_i2c = stm32_i2cbus_initialize(OLED_I2C_PORT); + if (!g_i2c) + { + lcderr("ERROR: Failed to initialize I2C port %d\n", OLED_I2C_PORT); + return -ENODEV; + } + + return OK; +} + +/**************************************************************************** + * Name: board_lcd_getdev + ****************************************************************************/ + +FAR struct lcd_dev_s *board_lcd_getdev(int devno) +{ + /* Bind the I2C port to the OLED */ + + g_lcddev = ssd1306_initialize(g_i2c, NULL, devno); + if (!g_lcddev) + { + lcderr("ERROR: Failed to bind I2C port 1 to OLED %d: %d\n", devno); + } + else + { + lcdinfo("Bound I2C port %d to OLED %d\n", OLED_I2C_PORT, devno); + + /* And turn the OLED on */ + + (void)g_lcddev->setpower(g_lcddev, CONFIG_LCD_MAXPOWER); + return g_lcddev; + } + + return NULL; +} + +/**************************************************************************** + * Name: board_lcd_uninitialize + ****************************************************************************/ + +void board_lcd_uninitialize(void) +{ + /* TO-FIX */ +}