From af8a002a10e28fdf625e60856af8ce24dbe679bb Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sun, 16 Sep 2018 15:58:25 +0000 Subject: [PATCH] Merged in raiden00/nuttx_h7 (pull request #720) I2C support for STM32H7 * stm32h7/chip.h: update peripherals * stm32h7x3xx_rcc.h: update definitions * stm32h7x3xx_rcc.c: remove some unused code and configure I2C clocks * stm32h7: add lower half I2C driver (based on F7 I2C driver) * configs/nucleo-h743zi: add LSM303AGR and LSM6DSL configuration * Remove whitespace * sensors/lsm303agr.c, sensors/lsm6dsl.c: add mising include, remove whitespace Approved-by: GregoryN --- arch/arm/include/stm32h7/chip.h | 15 +- arch/arm/src/stm32h7/Make.defs | 4 + arch/arm/src/stm32h7/chip/stm32_i2c.h | 54 + arch/arm/src/stm32h7/chip/stm32h7x3xx_i2c.h | 263 ++ arch/arm/src/stm32h7/chip/stm32h7x3xx_rcc.h | 166 +- arch/arm/src/stm32h7/stm32.h | 62 + arch/arm/src/stm32h7/stm32_i2c.c | 2838 +++++++++++++++++++ arch/arm/src/stm32h7/stm32_i2c.h | 104 + arch/arm/src/stm32h7/stm32h7x3xx_rcc.c | 143 +- configs/nucleo-h743zi/include/board.h | 18 +- configs/nucleo-h743zi/src/Makefile | 8 + configs/nucleo-h743zi/src/nucleo-h743zi.h | 29 + configs/nucleo-h743zi/src/stm32_bringup.c | 30 +- configs/nucleo-h743zi/src/stm32_lsm303agr.c | 94 + configs/nucleo-h743zi/src/stm32_lsm6dsl.c | 100 + configs/nucleo-l476rg/src/stm32_lsm303agr.c | 4 +- configs/nucleo-l476rg/src/stm32_lsm6dsl.c | 4 +- drivers/sensors/lsm303agr.c | 13 +- drivers/sensors/lsm6dsl.c | 15 +- 19 files changed, 3808 insertions(+), 156 deletions(-) create mode 100644 arch/arm/src/stm32h7/chip/stm32_i2c.h create mode 100644 arch/arm/src/stm32h7/chip/stm32h7x3xx_i2c.h create mode 100644 arch/arm/src/stm32h7/stm32.h create mode 100644 arch/arm/src/stm32h7/stm32_i2c.c create mode 100644 arch/arm/src/stm32h7/stm32_i2c.h create mode 100644 configs/nucleo-h743zi/src/stm32_lsm303agr.c create mode 100644 configs/nucleo-h743zi/src/stm32_lsm6dsl.c diff --git a/arch/arm/include/stm32h7/chip.h b/arch/arm/include/stm32h7/chip.h index b3c8c38c287..53b0a3252ce 100644 --- a/arch/arm/include/stm32h7/chip.h +++ b/arch/arm/include/stm32h7/chip.h @@ -92,8 +92,19 @@ /* Peripherals */ # define STM32H7_NGPIO (11) /* GPIOA-GPIOK */ -# define STM32H7_NUSART (4) /* USART1-3, 6 */ -# define STM32H7_NUART (4) /* UART4-5, 7-8 */ +# define STM32H7_NADC (3) /* (3) ADC1-3*/ +# define STM32H7_NDAC (2) /* (2) DAC1-2*/ +# define STM32H7_NCMP (2) /* (2) ultra-low power comparators */ +# define STM32H7_NPGA (2) /* (2) Operational amplifiers: OPAMP */ +# define STM32H7_NDFSDM (1) /* (1) digital filters for sigma delta modulator */ +# define STM32H7_NUSART (4) /* (4) USART1-3, 6 */ +# define STM32H7_NSPI (6) /* (6) SPI1-6 */ +# define STM32H7_NI2S (3) /* (3) I2S1-3 */ +# define STM32H7_NUART (4) /* (4) UART4-5, 7-8 */ +# define STM32H7_NI2C (4) /* (4) I2C1-4 */ +# define STM32H7_NSAI (4) /* (4) SAI1-4*/ +# define STM32H7_NCAN (2) /* (2) CAN1-2 */ +# define STM32H7_NSDIO (2) /* (2) SDIO */ #else # error STM32 H7 chip Family not identified #endif diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs index a2745531579..2df2647c8db 100644 --- a/arch/arm/src/stm32h7/Make.defs +++ b/arch/arm/src/stm32h7/Make.defs @@ -113,3 +113,7 @@ CHIP_CSRCS += stm32_start.c stm32_rcc.c stm32_lowputc.c stm32_serial.c ifneq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += stm32_timerisr.c endif + +ifeq ($(CONFIG_STM32H7_I2C),y) +CHIP_CSRCS += stm32_i2c.c +endif diff --git a/arch/arm/src/stm32h7/chip/stm32_i2c.h b/arch/arm/src/stm32h7/chip/stm32_i2c.h new file mode 100644 index 00000000000..2e9428e5f33 --- /dev/null +++ b/arch/arm/src/stm32h7/chip/stm32_i2c.h @@ -0,0 +1,54 @@ +/************************************************************************************ + * arch/arm/src/stm32h7/chip/stm32_i2c.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * Bob Feretich + * + * 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_STM32H7_CHIP_STM32_I2C_H +#define __ARCH_ARM_SRC_STM32H7_CHIP_STM32_I2C_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include "chip.h" + +#if defined(CONFIG_STM32H7_STM32H7X3XX) +# include "chip/stm32h7x3xx_i2c.h" +#else +# error "Unsupported STM32 H7 sub family" +#endif + +#endif /* __ARCH_ARM_SRC_STM32H7_CHIP_STM32_I2C_H */ diff --git a/arch/arm/src/stm32h7/chip/stm32h7x3xx_i2c.h b/arch/arm/src/stm32h7/chip/stm32h7x3xx_i2c.h new file mode 100644 index 00000000000..f51aeb7c1e0 --- /dev/null +++ b/arch/arm/src/stm32h7/chip/stm32h7x3xx_i2c.h @@ -0,0 +1,263 @@ +/************************************************************************************ + * arch/arm/src/stm32h7/chip/stm32h7x3xx_i2c.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * + * 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_STM32H7_STM32H7X3XX_I2C_H +#define __ARCH_ARM_SRC_STM32H7_STM32H7X3XX_I2C_H + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Register Offsets *****************************************************************/ + +#define STM32_I2C_CR1_OFFSET 0x0000 /* Control register 1 (32-bit) */ +#define STM32_I2C_CR2_OFFSET 0x0004 /* Control register 2 (32-bit) */ +#define STM32_I2C_OAR1_OFFSET 0x0008 /* Own address register 1 (16-bit) */ +#define STM32_I2C_OAR2_OFFSET 0x000c /* Own address register 2 (16-bit) */ +#define STM32_I2C_TIMINGR_OFFSET 0x0010 /* Timing register */ +#define STM32_I2C_TIMEOUTR_OFFSET 0x0014 /* Timeout register */ +#define STM32_I2C_ISR_OFFSET 0x0018 /* Interrupt and Status register */ +#define STM32_I2C_ICR_OFFSET 0x001c /* Interrupt clear register */ +#define STM32_I2C_PECR_OFFSET 0x0020 /* Packet error checking register */ +#define STM32_I2C_RXDR_OFFSET 0x0024 /* Receive data register */ +#define STM32_I2C_TXDR_OFFSET 0x0028 /* Transmit data register */ + +/* Register Addresses ***************************************************************/ + +#if STM32H7_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_TIMINGR (STM32_I2C1_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C1_TIMEOUTR (STM32_I2C1_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C1_ISR (STM32_I2C1_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C1_ICR (STM32_I2C1_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C1_PECR (STM32_I2C1_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C1_RXDR (STM32_I2C1_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C1_TXDR (STM32_I2C1_BASE+STM32_I2C_TXDR_OFFSET) +#endif + +#if STM32H7_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_TIMINGR (STM32_I2C2_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C2_TIMEOUTR (STM32_I2C2_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C2_ISR (STM32_I2C2_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C2_ICR (STM32_I2C2_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C2_PECR (STM32_I2C2_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C2_RXDR (STM32_I2C2_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C2_TXDR (STM32_I2C2_BASE+STM32_I2C_TXDR_OFFSET) +#endif + +#if STM32H7_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_TIMINGR (STM32_I2C3_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C3_TIMEOUTR (STM32_I2C3_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C3_ISR (STM32_I2C3_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C3_ICR (STM32_I2C3_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C3_PECR (STM32_I2C3_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C3_RXDR (STM32_I2C3_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C3_TXDR (STM32_I2C3_BASE+STM32_I2C_TXDR_OFFSET) +#endif + +#if STM32H7_NI2C > 3 +# define STM32_I2C4_CR1 (STM32_I2C4_BASE+STM32_I2C_CR1_OFFSET) +# define STM32_I2C4_CR2 (STM32_I2C4_BASE+STM32_I2C_CR2_OFFSET) +# define STM32_I2C4_OAR1 (STM32_I2C4_BASE+STM32_I2C_OAR1_OFFSET) +# define STM32_I2C4_OAR2 (STM32_I2C4_BASE+STM32_I2C_OAR2_OFFSET) +# define STM32_I2C4_TIMINGR (STM32_I2C4_BASE+STM32_I2C_TIMINGR_OFFSET) +# define STM32_I2C4_TIMEOUTR (STM32_I2C4_BASE+STM32_I2C_TIMEOUTR_OFFSET) +# define STM32_I2C4_ISR (STM32_I2C4_BASE+STM32_I2C_ISR_OFFSET) +# define STM32_I2C4_ICR (STM32_I2C4_BASE+STM32_I2C_ICR_OFFSET) +# define STM32_I2C4_PECR (STM32_I2C4_BASE+STM32_I2C_PECR_OFFSET) +# define STM32_I2C4_RXDR (STM32_I2C4_BASE+STM32_I2C_RXDR_OFFSET) +# define STM32_I2C4_TXDR (STM32_I2C4_BASE+STM32_I2C_TXDR_OFFSET) +#endif + +/* Register Bitfield Definitions ****************************************************/ + +/* Control register 1 */ + +#define I2C_CR1_PE (1 << 0) /* Bit 0: Peripheral Enable */ +#define I2C_CR1_TXIE (1 << 1) /* Bit 1: TX Interrupt enable */ +#define I2C_CR1_RXIE (1 << 2) /* Bit 2: RX Interrupt enable */ +#define I2C_CR1_ADDRIE (1 << 3) /* Bit 3: Address match interrupt enable (slave) */ +#define I2C_CR1_NACKIE (1 << 4) /* Bit 4: Not acknowledge received interrupt enable */ +#define I2C_CR1_STOPIE (1 << 5) /* Bit 5: STOP detection interrupt enable */ +#define I2C_CR1_TCIE (1 << 6) /* Bit 6: Transfer Complete interrupt enable */ +#define I2C_CR1_ERRIE (1 << 7) /* Bit 7: Error interrupts enable */ +#define I2C_CR1_DNF_SHIFT (8) /* Bits 8-11: Digital noise filter */ +#define I2C_CR1_DNF_MASK (0xf << I2C_CR1_DNF_SHIFT) +# define I2C_CR1_DNF_DISABLE (0 << I2C_CR1_DNF_SHIFT) +# define I2C_CR1_DNF(n) ((n) << I2C_CR1_DNF_SHIFT) /* Up to n * Ti2cclk, n=1..15 */ +#define I2C_CR1_ANFOFF (1 << 12) /* Bit 12: Analog noise filter OFF */ +#define I2C_CR1_TXDMAEN (1 << 14) /* Bit 14: DMA transmission requests enable */ +#define I2C_CR1_RXDMAEN (1 << 15) /* Bit 15: DMA reception requests enable */ +#define I2C_CR1_SBC (1 << 16) /* Bit 16: Slave byte control */ +#define I2C_CR1_NOSTRETCH (1 << 17) /* Bit 17: Clock stretching disable */ +#define I2C_CR1_GCEN (1 << 19) /* Bit 19: General call enable */ +#define I2C_CR1_SMBHEN (1 << 20) /* Bit 20: SMBus Host address enable */ +#define I2C_CR1_SMBDEN (1 << 21) /* Bit 21: SMBus Device Default address enable */ +#define I2C_CR1_ALERTEN (1 << 22) /* Bit 22: SMBus alert enable */ +#define I2C_CR1_PECEN (1 << 23) /* Bit 23: PEC enable */ + +/* Control register 2 */ + +#define I2C_CR2_SADD10_SHIFT (0) /* Bits 0-9: Slave 10-bit address (master) */ +#define I2C_CR2_SADD10_MASK (0x3ff << I2C_CR2_SADD10_SHIFT) +#define I2C_CR2_SADD7_SHIFT (1) /* Bits 1-7: Slave 7-bit address (master) */ +#define I2C_CR2_SADD7_MASK (0x7f << I2C_CR2_SADD7_SHIFT) +#define I2C_CR2_RD_WRN (1 << 10) /* Bit 10: Transfer direction (master) */ +#define I2C_CR2_ADD10 (1 << 11) /* Bit 11: 10-bit addressing mode (master) */ +#define I2C_CR2_HEAD10R (1 << 12) /* Bit 12: 10-bit address header only read direction (master) */ +#define I2C_CR2_START (1 << 13) /* Bit 13: Start generation */ +#define I2C_CR2_STOP (1 << 14) /* Bit 14: Stop generation (master) */ +#define I2C_CR2_NACK (1 << 15) /* Bit 15: NACK generation (slave) */ +#define I2C_CR2_NBYTES_SHIFT (16) /* Bits 16-23: Number of bytes */ +#define I2C_CR2_NBYTES_MASK (0xff << I2C_CR2_NBYTES_SHIFT) +#define I2C_CR2_RELOAD (1 << 24) /* Bit 24: NBYTES reload mode */ +#define I2C_CR2_AUTOEND (1 << 25) /* Bit 25: Automatic end mode (master) */ +#define I2C_CR2_PECBYTE (1 << 26) /* Bit 26: Packet error checking byte */ + +/* Own address register 1 */ + +#define I2C_OAR1_OA1_10_SHIFT (0) /* Bits 0-9: 10-bit interface address */ +#define I2C_OAR1_OA1_10_MASK (0x3ff << I2C_OAR1_OA1_10_SHIFT) +#define I2C_OAR1_OA1_7_SHIFT (1) /* Bits 1-7: 7-bit interface address */ +#define I2C_OAR1_OA1_7_MASK (0x7f << I2C_OAR1_OA1_7_SHIFT) +#define I2C_OAR1_OA1MODE (1 << 10) /* Bit 10: Own Address 1 10-bit mode */ +#define I2C_OAR1_OA1EN (1 << 15) /* Bit 15: Own Address 1 enable */ + +/* Own address register 2 */ + +#define I2C_OAR2_OA2_SHIFT (1) /* Bits 1-7: 7-bit interface address */ +#define I2C_OAR2_OA2_MASK (0x7f << I2C_OAR2_OA2_SHIFT) +#define I2C_OAR2_OA2MSK_SHIFT (8) /* Bits 8-10: Own Address 2 masks */ +#define I2C_OAR2_OA2MSK_MASK (7 << I2C_OAR2_OA2MSK_SHIFT) +# define I2C_OAR2_OA2MSK_NONE (0 << I2C_OAR2_OA2MSK_SHIFT) /* No mask */ +# define I2C_OAR2_OA2MSK_2_7 (1 << I2C_OAR2_OA2MSK_SHIFT) /* Only OA2[7:2] are compared */ +# define I2C_OAR2_OA2MSK_3_7 (2 << I2C_OAR2_OA2MSK_SHIFT) /* Only OA2[7:3] are compared */ +# define I2C_OAR2_OA2MSK_4_7 (3 << I2C_OAR2_OA2MSK_SHIFT) /* Only OA2[7:4] are compared */ +# define I2C_OAR2_OA2MSK_5_7 (4 << I2C_OAR2_OA2MSK_SHIFT) /* Only OA2[7:5] are compared */ +# define I2C_OAR2_OA2MSK_6_7 (5 << I2C_OAR2_OA2MSK_SHIFT) /* Only OA2[7:6] are compared */ +# define I2C_OAR2_OA2MSK_7 (6 << I2C_OAR2_OA2MSK_SHIFT) /* Only OA2[7] is compared */ +# define I2C_OAR2_OA2MSK_ALL (7 << I2C_OAR2_OA2MSK_SHIFT) /* All 7-bit addresses acknowledged */ +#define I2C_OAR2_OA2EN (1 << 15) /* Bit 15: Own Address 2 enable */ + +/* Timing register */ + +#define I2C_TIMINGR_SCLL_SHIFT (0) /* Bits 0-7: SCL low period (master) */ +#define I2C_TIMINGR_SCLL_MASK (0xff << I2C_TIMINGR_SCLL_SHIFT) +# define I2C_TIMINGR_SCLL(n) (((n)-1) << I2C_TIMINGR_SCLL_SHIFT) /* tSCLL = n x tPRESC */ + +#define I2C_TIMINGR_SCLH_SHIFT (8) /* Bits 8-15: SCL high period (master) */ +#define I2C_TIMINGR_SCLH_MASK (0xff << I2C_TIMINGR_SCLH_SHIFT) +# define I2C_TIMINGR_SCLH(n) (((n)-1) << I2C_TIMINGR_SCLH_SHIFT) /* tSCLH = n x tPRESC */ + +#define I2C_TIMINGR_SDADEL_SHIFT (16) /* Bits 16-19: Data hold time */ +#define I2C_TIMINGR_SDADEL_MASK (0xf << I2C_TIMINGR_SDADEL_SHIFT) +# define I2C_TIMINGR_SDADEL(n) ((n) << I2C_TIMINGR_SDADEL_SHIFT) /* tSDADEL= n x tPRESC */ + +#define I2C_TIMINGR_SCLDEL_SHIFT (20) /* Bits 20-23: Data setup time */ +#define I2C_TIMINGR_SCLDEL_MASK (0xf << I2C_TIMINGR_SCLDEL_SHIFT) +# define I2C_TIMINGR_SCLDEL(n) (((n)-1) << I2C_TIMINGR_SCLDEL_SHIFT) /* tSCLDEL = n x tPRESC */ + +#define I2C_TIMINGR_PRESC_SHIFT (28) /* Bits 28-31: Timing prescaler */ +#define I2C_TIMINGR_PRESC_MASK (0xf << I2C_TIMINGR_PRESC_SHIFT) +# define I2C_TIMINGR_PRESC(n) (((n)-1) << I2C_TIMINGR_PRESC_SHIFT) /* tPRESC = n x tI2CCLK */ + +/* Timeout register */ + +#define I2C_TIMEOUTR_A_SHIFT (0) /* Bits 0-11: Bus Timeout A */ +#define I2C_TIMEOUTR_A_MASK (0x0fff << I2C_TIMEOUTR_A_SHIFT) +# define I2C_TIMEOUTR_A(n) ((n) << I2C_TIMEOUTR_A_SHIFT) +#define I2C_TIMEOUTR_TIDLE (1 << 12) /* Bit 12: Idle clock timeout detection */ +#define I2C_TIMEOUTR_TIMOUTEN (1 << 15) /* Bit 15: Clock timeout enable */ +#define I2C_TIMEOUTR_B_SHIFT (16) /* Bits 16-27: Bus Timeout B */ +#define I2C_TIMEOUTR_B_MASK (0x0fff << I2C_TIMEOUTR_B_SHIFT) +# define I2C_TIMEOUTR_B(n) ((n) << I2C_TIMEOUTR_B_SHIFT) +#define I2C_TIMEOUTR_TEXTEN (1 << 31) /* Bits 31: Extended clock timeout enable */ + + +/* Fields unique to the Interrupt and Status register */ + +#define I2C_ISR_TXE (1 << 0) /* Bit 0: Transmit data register empty (transmitters) */ +#define I2C_ISR_TXIS (1 << 1) /* Bit 1: Transmit interrupt status (transmitters) */ +#define I2C_ISR_RXNE (1 << 2) /* Bit 2: Receive data register not empty (receivers) */ +#define I2C_ISR_TC (1 << 6) /* Bit 6: Transfer Complete (master) */ +#define I2C_ISR_TCR (1 << 7) /* Bit 7: Transfer Complete Reload */ +#define I2C_ISR_BUSY (1 << 15) /* Bit 15: Bus busy */ +#define I2C_ISR_DIR (1 << 16) /* Bit 16: Transfer direction (slave) */ +#define I2C_ISR_ADDCODE_SHIFT (17) /* Bits 17-23: Address match code (slave) */ +#define I2C_ISR_ADDCODE_MASK (0x7f << I2C_ISR_ADDCODE_SHIFT) + +/* Interrupt and Status register and interrupt clear register */ +/* Common interrupt bits */ + +#define I2C_INT_ADDR (1 << 3) /* Bit 3: Address matched (slave) */ +#define I2C_INT_NACK (1 << 4) /* Bit 4: Not Acknowledge received flag */ +#define I2C_INT_STOP (1 << 5) /* Bit 5: Stop detection flag */ +#define I2C_INT_BERR (1 << 8) /* Bit 8: Bus error */ +#define I2C_INT_ARLO (1 << 9) /* Bit 9: Arbitration lost */ +#define I2C_INT_OVR (1 << 10) /* Bit 10: Overrun/Underrun (slave) */ +#define I2C_INT_PECERR (1 << 11) /* Bit 11: PEC Error in reception */ +#define I2C_INT_TIMEOUT (1 << 12) /* Bit 12: Timeout or tLOW detection flag */ +#define I2C_INT_ALERT (1 << 13) /* Bit 13: SMBus alert */ + +#define I2C_ISR_ERRORMASK (I2C_INT_BERR | I2C_INT_ARLO | I2C_INT_OVR | I2C_INT_PECERR | I2C_INT_TIMEOUT) + +#define I2C_ICR_CLEARMASK (I2C_INT_ADDR | I2C_INT_NACK | I2C_INT_STOP | I2C_INT_BERR | I2C_INT_ARLO \ + | I2C_INT_OVR | I2C_INT_PECERR | I2C_INT_TIMEOUT | I2C_INT_ALERT) + +/* Packet error checking register */ + +#define I2C_PECR_MASK (0xff) + +/* Receive data register */ + +#define I2C_RXDR_MASK (0xff) + +/* Transmit data register */ + +#define I2C_TXDR_MASK (0xff) + +#endif /* __ARCH_ARM_SRC_STM32H7_CHIP_STM32H7X3XX_I2C_H */ diff --git a/arch/arm/src/stm32h7/chip/stm32h7x3xx_rcc.h b/arch/arm/src/stm32h7/chip/stm32h7x3xx_rcc.h index 50d46f59850..6a34ff411d3 100644 --- a/arch/arm/src/stm32h7/chip/stm32h7x3xx_rcc.h +++ b/arch/arm/src/stm32h7/chip/stm32h7x3xx_rcc.h @@ -67,15 +67,15 @@ #define STM32_RCC_PLL2FRACR_OFFSET 0x003c /* */ #define STM32_RCC_PLL3DIVR_OFFSET 0x0040 /* */ #define STM32_RCC_PLL3FRACR_OFFSET 0x0044 /* */ -#define STM32_RCC_D1CCIPR_OFFSET 0x004c /* */ -#define STM32_RCC_D2CCIP1R_OFFSET 0x0050 /* */ -#define STM32_RCC_D2CCIP2R_OFFSET 0x0054 /* */ -#define STM32_RCC_D3CCIPR_OFFSET 0x0058 /* */ -#define STM32_RCC_CIER_OFFSET 0x0060 /* */ -#define STM32_RCC_CIFR_OFFSET 0x0064 /* */ -#define STM32_RCC_CICR_OFFSET 0x0068 /* */ -#define STM32_RCC_BDCR_OFFSET 0x0070 /* */ -#define STM32_RCC_CSR_OFFSET 0x0074 /* */ +#define STM32_RCC_D1CCIPR_OFFSET 0x004c /* Domain 1 Kernel Clock configuration register */ +#define STM32_RCC_D2CCIP1R_OFFSET 0x0050 /* Domain 2 Kernel Clock configuration register 1 */ +#define STM32_RCC_D2CCIP2R_OFFSET 0x0054 /* Domain 2 Kernel Clock configuration register 2 */ +#define STM32_RCC_D3CCIPR_OFFSET 0x0058 /* Domain 3 Kernel Clock configuration register */ +#define STM32_RCC_CIER_OFFSET 0x0060 /* Clock Source Interrupt enable register */ +#define STM32_RCC_CIFR_OFFSET 0x0064 /* Clock Source Interrupt Flag register */ +#define STM32_RCC_CICR_OFFSET 0x0068 /* Clock Source Interrupt Clear register */ +#define STM32_RCC_BDCR_OFFSET 0x0070 /* Backup Domain Control register */ +#define STM32_RCC_CSR_OFFSET 0x0074 /* Clock Control and Status register */ #define STM32_RCC_AHB1RSTR_OFFSET 0x0080 /* AHB1 peripheral reset register */ #define STM32_RCC_AHB2RSTR_OFFSET 0x0084 /* AHB2 peripheral reset register */ #define STM32_RCC_AHB3RSTR_OFFSET 0x007c /* AHB3 peripheral reset register */ @@ -316,19 +316,19 @@ /* Bits 0-3: Reserved */ #define RCC_D2CFGR_D2PPRE1_SHIFT (4) /* Bits 4-6: D2 domain APB1 prescaler */ #define RCC_D2CFGR_D2PPRE1_MASK (7 << RCC_D2CFGR_D2PPRE1_SHIFT) -#define RCC_D2CFGR_D2PPRE1_HCLK (0 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 0xx: */ -#define RCC_D2CFGR_D2PPRE1_HCLKd2 (4 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 100: */ -#define RCC_D2CFGR_D2PPRE1_HCLKd4 (5 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 101: */ -#define RCC_D2CFGR_D2PPRE1_HCLKd8 (6 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 110: */ -#define RCC_D2CFGR_D2PPRE1_HCLKd16 (7 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 111: */ +# define RCC_D2CFGR_D2PPRE1_HCLK (0 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 0xx: */ +# define RCC_D2CFGR_D2PPRE1_HCLKd2 (4 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 100: */ +# define RCC_D2CFGR_D2PPRE1_HCLKd4 (5 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 101: */ +# define RCC_D2CFGR_D2PPRE1_HCLKd8 (6 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 110: */ +# define RCC_D2CFGR_D2PPRE1_HCLKd16 (7 << RCC_D2CFGR_D2PPRE1_SHIFT) /* 111: */ /* Bit 7: Reserved */ #define RCC_D2CFGR_D2PPRE2_SHIFT (8) /* Bits 8-10: D2 domain APB2 prescaler */ #define RCC_D2CFGR_D2PPRE2_MASK (7 << RCC_D2CFGR_D2PPRE2_SHIFT) -#define RCC_D2CFGR_D2PPRE2_HCLK (0 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 0xx: */ -#define RCC_D2CFGR_D2PPRE2_HCLKd2 (4 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 100: */ -#define RCC_D2CFGR_D2PPRE2_HCLKd4 (5 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 101: */ -#define RCC_D2CFGR_D2PPRE2_HCLKd8 (6 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 110: */ -#define RCC_D2CFGR_D2PPRE2_HCLKd16 (7 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 111: */ +# define RCC_D2CFGR_D2PPRE2_HCLK (0 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 0xx: */ +# define RCC_D2CFGR_D2PPRE2_HCLKd2 (4 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 100: */ +# define RCC_D2CFGR_D2PPRE2_HCLKd4 (5 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 101: */ +# define RCC_D2CFGR_D2PPRE2_HCLKd8 (6 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 110: */ +# define RCC_D2CFGR_D2PPRE2_HCLKd16 (7 << RCC_D2CFGR_D2PPRE2_SHIFT) /* 111: */ /* Bits 11-31: Reserved */ /* Bit definitions for RCC_D3CFGR */ @@ -336,11 +336,11 @@ /* Bits 0-3: Reserved */ #define RCC_D3CFGR_D3PPRE_SHIFT (4) /* Bits 4-6: D3 domain APB4 prescaler */ #define RCC_D3CFGR_D3PPRE_MASK (7 << RCC_D3CFGR_D3PPRE_SHIFT) -#define RCC_D3CFGR_D3PPRE_HCLK (0 << RCC_D3CFGR_D3PPRE_SHIFT) /* 0xx: */ -#define RCC_D3CFGR_D3PPRE_HCLKd2 (4 << RCC_D3CFGR_D3PPRE_SHIFT) /* 100: */ -#define RCC_D3CFGR_D3PPRE_HCLKd4 (5 << RCC_D3CFGR_D3PPRE_SHIFT) /* 101: */ -#define RCC_D3CFGR_D3PPRE_HCLKd8 (6 << RCC_D3CFGR_D3PPRE_SHIFT) /* 110: */ -#define RCC_D3CFGR_D3PPRE_HCLKd16 (7 << RCC_D3CFGR_D3PPRE_SHIFT) /* 111: */ +# define RCC_D3CFGR_D3PPRE_HCLK (0 << RCC_D3CFGR_D3PPRE_SHIFT) /* 0xx: */ +# define RCC_D3CFGR_D3PPRE_HCLKd2 (4 << RCC_D3CFGR_D3PPRE_SHIFT) /* 100: */ +# define RCC_D3CFGR_D3PPRE_HCLKd4 (5 << RCC_D3CFGR_D3PPRE_SHIFT) /* 101: */ +# define RCC_D3CFGR_D3PPRE_HCLKd8 (6 << RCC_D3CFGR_D3PPRE_SHIFT) /* 110: */ +# define RCC_D3CFGR_D3PPRE_HCLKd16 (7 << RCC_D3CFGR_D3PPRE_SHIFT) /* 111: */ /* Bits 7-31: Reserved */ /* Bit definitions for RCC_PLLCKSELR register */ @@ -494,7 +494,123 @@ #define RCC_PLL3FRACR_FRACN3_MASK (0x1FFFul << RCC_PLL3FRACR_FRACN3_SHIFT) /* 0x0000FFF8 */ #define RCC_PLL3FRACR_FRACN3 RCC_PLL3FRACR_FRACN3_MASK -/* CSR */ + +/* Bit definitions for RCC_D1CCIPR reigster */ + +#define RCC_D1CCIPR_FMCSEL_SHIFT (0) /* Bits 0-1: */ +#define RCC_D1CCIPR_FMCSEL_MASK (3 << RCC_D1CCIPR_FMCSEL_SHIFT) +# define RCC_D1CCIPR_FMCSEL_HCLK (0 << RCC_D1CCIPR_FMCSEL_SHIFT) /* 00: */ +# define RCC_D1CCIPR_FMCSEL_PLL1 (1 << RCC_D1CCIPR_FMCSEL_SHIFT) /* 01: */ +# define RCC_D1CCIPR_FMCSEL_PLL2 (2 << RCC_D1CCIPR_FMCSEL_SHIFT) /* 10: */ +# define RCC_D1CCIPR_FMCSEL_PER (3 << RCC_D1CCIPR_FMCSEL_SHIFT) /* 11: */ + /* Bits 2-3: Reserved */ +#define RCC_D1CCIPR_QSPISEL_SHIFT (0) /* Bits 4-5: */ +#define RCC_D1CCIPR_QSPISEL_MASK (3 << RCC_D1CCIPR_QSPISEL_SHIFT) +# define RCC_D1CCIPR_QSPISEL_HCLK (0 << RCC_D1CCIPR_QSPISEL_SHIFT) /* 00: */ +# define RCC_D1CCIPR_QSPISEL_PLL1 (1 << RCC_D1CCIPR_QSPISEL_SHIFT) /* 01: */ +# define RCC_D1CCIPR_QSPISEL_PLL2 (2 << RCC_D1CCIPR_QSPISEL_SHIFT) /* 10: */ +# define RCC_D1CCIPR_QSPISEL_PER (3 << RCC_D1CCIPR_QSPISEL_SHIFT) /* 11: */ + /* Bits 6-15: Reserved */ +#define RCC_D1CCIPR_SDMMC_SHIFT (16) /* Bit 15: */ +#define RCC_D1CCIPR_SDMMC_MASK (1 << RCC_D1CCIPR_SDMMC_SHIFT) +# define RCC_D1CCIPR_SDMMC_PLL1 (0 << RCC_D1CCIPR_SDMMC_SHIFT) /* 0: */ +# define RCC_D1CCIPR_SDMMC_PLL2 (1 << RCC_D1CCIPR_SDMMC_SHIFT) /* 1: */ + /* Bits 17-27: Reserved */ + /* Bits 17-27: Reserved */ +#define RCC_D1CCIPR_CKPERSEL_SHIFT (0) /* Bits 28-29: */ +#define RCC_D1CCIPR_CKPERSEL_MASK (3 << RCC_D1CCIPR_CKPERSEL_SHIFT) +# define RCC_D1CCIPR_CKPERSEL_HSI (0 << RCC_D1CCIPR_CKPERSEL_SHIFT) /* 00: */ +# define RCC_D1CCIPR_CKPERSEL_CSI (1 << RCC_D1CCIPR_CKPERSEL_SHIFT) /* 01: */ +# define RCC_D1CCIPR_CKPERSEL_HSE (2 << RCC_D1CCIPR_CKPERSEL_SHIFT) /* 10: */ + /* 11: Reserved */ + /* Bits 30-31: Reserved */ + +/* Bit definitions for RCC_D2CCIP1R reigster */ + +#define RCC_D2CCIP1R_SAI1SEL_SHIFT (0) /* Bits 0-2 */ +#define RCC_D2CCIP1R_SAI1SEL_MASK (7 << RCC_D2CCIP1R_SAI1SEL_MASK) +# define RCC_D2CCIP1R_SAI1SEL_PLL1 (0 << RCC_D2CCIP1R_SAI1SEL_SHIFT) /* 000 */ +# define RCC_D2CCIP1R_SAI1SEL_PLL2 (1 << RCC_D2CCIP1R_SAI1SEL_SHIFT) /* 001 */ +# define RCC_D2CCIP1R_SAI1SEL_PLL3 (2 << RCC_D2CCIP1R_SAI1SEL_SHIFT) /* 010 */ +# define RCC_D2CCIP1R_SAI1SEL_I2SCKIN (3 << RCC_D2CCIP1R_SAI1SEL_SHIFT) /* 011 */ +# define RCC_D2CCIP1R_SAI1SEL_PER (4 << RCC_D2CCIP1R_SAI1SEL_SHIFT) /* 100 */ + /* Bits 3-5: Reserved */ +#define RCC_D2CCIP1R_SAI23SEL_SHIFT (6) /* Bits 6-8 */ +#define RCC_D2CCIP1R_SAI23SEL_MASK (7 << RCC_D2CCIP1R_SAI23SEL_SHIFT) +# define RCC_D2CCIP1R_SAI23SEL_PLL1 (0 << RCC_D2CCIP1R_SAI23SEL_SHIFT) /* 000 */ +# define RCC_D2CCIP1R_SAI23SEL_PLL2 (1 << RCC_D2CCIP1R_SAI23SEL_SHIFT) /* 001 */ +# define RCC_D2CCIP1R_SAI23SEL_PLL3 (2 << RCC_D2CCIP1R_SAI23SEL_SHIFT) /* 010 */ +# define RCC_D2CCIP1R_SAI23SEL_I2SCKIN (3 << RCC_D2CCIP1R_SAI23SEL_SHIFT) /* 011 */ +# define RCC_D2CCIP1R_SAI23SEL_PER (4 << RCC_D2CCIP1R_SAI23SEL_SHIFT) /* 100 */ + /* Bits 9-11: Reserved */ +#define RCC_D2CCIP1R_SPI123SEL_SHIFT (12) /* Bits 12-14 */ +#define RCC_D2CCIP1R_SPI123SEL_MASK (7 << RCC_D2CCIP1R_SPI123SEL_SHIFT) +# define RCC_D2CCIP1R_SPI123SEL_PLL1 (0 << RCC_D2CCIP1R_SPI123SEL_SHIFT) /* 000 */ +# define RCC_D2CCIP1R_SPI123SEL_PLL2 (1 << RCC_D2CCIP1R_SPI123SEL_SHIFT) /* 001 */ +# define RCC_D2CCIP1R_SPI123SEL_PLL3 (2 << RCC_D2CCIP1R_SPI123SEL_SHIFT) /* 010 */ +# define RCC_D2CCIP1R_SPI123SEL_I2SCKIN (3 << RCC_D2CCIP1R_SPI123SEL_SHIFT) /* 011 */ +# define RCC_D2CCIP1R_SPI123SEL_PER (4 << RCC_D2CCIP1R_SPI123SEL_SHIFT) /* 100 */ + /* Bit 15: Reserved */ +#define RCC_D2CCIP1R_SPI45SEL_SHIFT (16) /* Bits 16-18 */ +#define RCC_D2CCIP1R_SPI45SEL_MASK (7 << RCC_D2CCIP1R_SPI45SEL_SHIFT) +# define RCC_D2CCIP1R_SPI45SEL_APB (0 << RCC_D2CCIP1R_SPI45SEL_SHIFT) /* 000 */ +# define RCC_D2CCIP1R_SPI45SEL_PLL2 (1 << RCC_D2CCIP1R_SPI45SEL_SHIFT) /* 001 */ +# define RCC_D2CCIP1R_SPI45SEL_PLL3 (2 << RCC_D2CCIP1R_SPI45SEL_SHIFT) /* 010 */ +# define RCC_D2CCIP1R_SPI45SEL_HSI (3 << RCC_D2CCIP1R_SPI45SEL_SHIFT) /* 011 */ +# define RCC_D2CCIP1R_SPI45SEL_CSI (4 << RCC_D2CCIP1R_SPI45SEL_SHIFT) /* 100 */ +# define RCC_D2CCIP1R_SPI45SEL_HSE (5 << RCC_D2CCIP1R_SPI45SEL_SHIFT) /* 101 */ + /* Bit 19: Reserved */ +#define RCC_D2CCIP1R_SPDIFSEL_SHIFT (20) /* Bits 20-21 */ +#define RCC_D2CCIP1R_SPDIFSEL_MASK (3 << RCC_D2CCIP1R_SPDIFSEL_SHIFT) +# define RCC_D2CCIP1R_SPDIFSEL_PLL1 (0 << RCC_D2CCIP1R_SPDIFSEL_SHIFT) /* 00 */ +# define RCC_D2CCIP1R_SPDIFSEL_PLL2 (1 << RCC_D2CCIP1R_SPDIFSEL_SHIFT) /* 01 */ +# define RCC_D2CCIP1R_SPDIFSEL_PLL3 (2 << RCC_D2CCIP1R_SPDIFSEL_SHIFT) /* 10 */ +# define RCC_D2CCIP1R_SPDIFSEL_HSI (3 << RCC_D2CCIP1R_SPDIFSEL_SHIFT) /* 11 */ + /* Bits 22-23: Reserved */ +#define RCC_D2CCIP1R_DFSDM1SEL_SHIFT (24) /* Bit 24 */ +#define RCC_D2CCIP1R_DFSDM1SEL_MASK (1 << RCC_D2CCIP1R_DFSDM1SEL_SHIFT) +# define RCC_D2CCIP1R_DFSDM1SEL_PCLK2 (0 << RCC_D2CCIP1R_DFSDM1SEL_SHIFT) /* 0 */ +# define RCC_D2CCIP1R_DFSDM1SEL_SYSCLK (1 << RCC_D2CCIP1R_DFSDM1SEL_SHIFT) /* 1 */ + /* Bits 25-27: Reserved */ +#define RCC_D2CCIP1R_FDCANSEL_SHIFT (28) /* Bits 28-29 */ +#define RCC_D2CCIP1R_FDCANSEL_MASK (3 << RCC_D2CCIP1R_FDCANSEL_SHIFT) +# define RCC_D2CCIP1R_FDCANSEL_HSE (0 << RCC_D2CCIP1R_FDCANSEL_SHIFT) /* 00 */ +# define RCC_D2CCIP1R_FDCANSEL_PLL1 (1 << RCC_D2CCIP1R_FDCANSEL_SHIFT) /* 01 */ +# define RCC_D2CCIP1R_FDCANSEL_PLL2 (2 << RCC_D2CCIP1R_FDCANSEL_SHIFT) /* 10 */ + /* Bit 30: Reserved */ +#define RCC_D2CCIP1R_SWPSEL_SHIFT (31) /* Bit 31 */ +#define RCC_D2CCIP1R_SWPSEL_MASK (1 << RCC_D2CCIP1R_SWPSEL_SHIFT) +# define RCC_D2CCIP1R_SWPSEL_PCLK (0 << RCC_D2CCIP1R_SWPSEL_SHIFT) /* 0 */ +# define RCC_D2CCIP1R_SWPSEL_HSI (1 << RCC_D2CCIP1R_SWPSEL_SHIFT) /* 1 */ + +/* TODO: Bit definitions for RCC_D2CCIP2R reigster */ + +/* ... */ +#define RCC_D2CCIP2R_I2C123SEL_SHIFT (12) /* Bits 12-13 */ +#define RCC_D2CCIP2R_I2C123SEL_MASK (3 << RCC_D2CCIP2R_I2C123SEL_SHIFT) +# define RCC_D2CCIP2R_I2C123SEL_PCLK1 (0 << RCC_D2CCIP2R_I2C123SEL_SHIFT) /* 00 */ +# define RCC_D2CCIP2R_I2C123SEL_PLL3 (1 << RCC_D2CCIP2R_I2C123SEL_SHIFT) /* 01 */ +# define RCC_D2CCIP2R_I2C123SEL_HSI (2 << RCC_D2CCIP2R_I2C123SEL_SHIFT) /* 10 */ +# define RCC_D2CCIP2R_I2C123SEL_CSI (3 << RCC_D2CCIP2R_I2C123SEL_SHIFT) /* 11 */ +/* ... */ + +/* TODO: Bit definitions for RCC_D3CCIPR reigster */ + +/* ... */ +#define RCC_D3CCIPR_I2C4SEL_SHIFT (8) /* Bits 8-9 */ +#define RCC_D3CCIPR_I2C4SEL_MASK (3 << RCC_D3CCIPR_I2C4SEL_SHIFT) +# define RCC_D3CCIPR_I2C4SEL_PCLK4 (0 << RCC_D3CCIPR_I2C4SEL_SHIFT) /* 00 */ +# define RCC_D3CCIPR_I2C4SEL_PLL3 (1 << RCC_D3CCIPR_I2C4SEL_SHIFT) /* 01 */ +# define RCC_D3CCIPR_I2C4SEL_HSI (2 << RCC_D3CCIPR_I2C4SEL_SHIFT) /* 10 */ +# define RCC_D3CCIPR_I2C4SEL_CSI (3 << RCC_D3CCIPR_I2C4SEL_SHIFT) /* 11 */ +/* ... */ + +/* TODO: CIER */ +/* TODO: CIFR */ +/* TODO: CICR */ +/* TODO: BDCR */ + +/* Bit definitions for RCC_CSR register */ #define RCC_CSR_LSION_SHIFT (0ul) /* RCC CSR: LSION (Bit 0) */ #define RCC_CSR_LSION (0x1ul) /* RCC CSR: LSION (Bitfield-Mask: 0x01) */ diff --git a/arch/arm/src/stm32h7/stm32.h b/arch/arm/src/stm32h7/stm32.h new file mode 100644 index 00000000000..400dee56bb0 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32.h @@ -0,0 +1,62 @@ +/************************************************************************************ + * arch/arm/src/stm32h7/stm32.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Authors: 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_STM32H7_H +#define __ARCH_ARM_SRC_STM32H7_STM32H7_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include +#include + +#include "up_internal.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Peripherals **********************************************************************/ + +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_i2c.h" +#include "stm32_rcc.h" +#include "stm32_lowputc.h" + +#endif /* __ARCH_ARM_SRC_STM32H7_STM32_H */ diff --git a/arch/arm/src/stm32h7/stm32_i2c.c b/arch/arm/src/stm32h7/stm32_i2c.c new file mode 100644 index 00000000000..14ea09e6313 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_i2c.c @@ -0,0 +1,2838 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32h7_i2c.c + * STM32 I2C Hardware Layer - Device Driver + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Author: Uros Platise + * + * With extensions and modifications for the F1, F2, and F4 by: + * + * Copyright (C) 2016-2017 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * John Wharington + * David Sidrane + * Bob Feretich + * Modified for STM32H7 by Mateusz Szafoni + * + * Major rewrite of ISR and supporting methods, including support + * for NACK and RELOAD by: + * + * Copyright (c) 2016 Doug Vetter. All rights reserved. + * Author: Doug Vetter + * + * + * 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 H7 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_D2CCIP2R_I2C123SRC + * and STM32_RCC_D3CCIPR_I2C4SRC + * 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: + * + * - STM32H743ZI on ST Nucleo-144 Board + * + * Operational Status: + * + * All supported features have been tested and found to be operational. + * + * Although the RELOAD capability has been tested as it was required to + * implement the I2C_M_NOSTART flag on F3 hardware, the associated + * logic to support the transfer messages with more than 255 byte + * payloads has not been tested as the author lacked access to a real + * device supporting these types of transfers. + * + * Performance Benchmarks: TBD + * + * Time to transfer two messages, each a byte in length, in addition to the + * START condition, in interrupt mode: + * + * DEBUG enabled (development): TBDms + * Excessive delay here is caused by printing to the console and + * is of no concern. + * + * DEBUG disabled (production): TBSus + * Between Messages: TBDus + * Between Bytes: TBDus + * + * Implementation: + * + * - Device: structure as defined by the nuttx/i2c/i2c.h + * + * - Instance: represents each individual access to the I2C driver, obtained by + * the i2c_init(); it extends the Device structure from the nuttx/i2c/i2c.h; + * Instance points to OPS, to common I2C Hardware private data and contains + * its own private data including frequency, address and mode of operation. + * + * - Private: Private data of an I2C Hardware + * + * High Level Functional Desecription + * + * 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 acknowedged) + * NACKF - Not Acknowledge Received + * (data transmitted to bus and NOT acknowledged) + * RXNE - Receive interrupt + * (data received from bus) + * TC - Transfer Complete + * (All bytes in message transferred) + * TCR - Transfer Complete (Reload) + * (Current batch of bytes in message transferred) + * + * The driver currently supports Single Master mode only. Slave mode is not + * supported. Additionally, the driver runs in Software End Mode (AUTOEND + * disabled) so the driver is responsible for telling the hardware what to + * do at the end of a transfer. + * + * ------------------------------------------------------------------------------ + * + * Configuration: + * + * To use this driver, enable the following configuration variable: + * + * One of: + * + * CONFIG_STM32H7_STM32H7X3XX + * + * and one or more interfaces: + * + * CONFIG_STM32H7_I2C1 + * CONFIG_STM32H7_I2C2 + * CONFIG_STM32H7_I2C3 + * CONFIG_STM32H7_I2C4 + * + * To configure the ISR timeout using fixed values (CONFIG_STM32H7_I2C_DYNTIMEO=n): + * + * CONFIG_STM32H7_I2CTIMEOSEC (Timeout in seconds) + * CONFIG_STM32H7_I2CTIMEOMS (Timeout in milliseconds) + * CONFIG_STM32H7_I2CTIMEOTICKS (Timeout in ticks) + * + * To configure the ISR timeout using dynamic values (CONFIG_STM32H7_I2C_DYNTIMEO=y): + * + * CONFIG_STM32H7_I2C_DYNTIMEO_USECPERBYTE (Timeout in microseconds per byte) + * CONFIG_STM32H7_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 + */ + +/************************************************************************************ + * 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" +#include "chip/stm32_pinmap.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_STM32H7_I2C1) || defined(CONFIG_STM32H7_I2C2) || \ + defined(CONFIG_STM32H7_I2C3) || defined(CONFIG_STM32H7_I2C4) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#undef INVALID_CLOCK_SOURCE + +#if defined(CONFIG_STM32H7_I2C1) || defined(CONFIG_STM32H7_I2C2) || defined(CONFIG_STM32H7_I2C3) +# if STM32_RCC_D2CCIP2R_I2C123SRC != RCC_D2CCIP2R_I2C123SEL_HSI +# warning "Clock Source STM32_RCC_D2CCIP2R_I2C123SRC must be HSI" +# define INVALID_CLOCK_SOURCE +# endif +#endif +#ifdef CONFIG_STM32H7_I2C4 +# if STM32_RCC_D3CCIPR_I2C4SRC != RCC_D3CCIPR_I2C4SEL_HSI +# warning "Clock Source STM32_RCC_D3CCIPR_I2C4SRC must be HSI" +# define INVALID_CLOCK_SOURCE +# endif +#endif + +/* 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_STM32H7_I2CTIMEOSEC) && !defined(CONFIG_STM32H7_I2CTIMEOMS) +# define CONFIG_STM32H7_I2CTIMEOSEC 0 +# define CONFIG_STM32H7_I2CTIMEOMS 500 /* Default is 500 milliseconds */ +# warning "Using Default 500 Ms Timeout" +#elif !defined(CONFIG_STM32H7_I2CTIMEOSEC) +# define CONFIG_STM32H7_I2CTIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_STM32H7_I2CTIMEOMS) +# define CONFIG_STM32H7_I2CTIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_STM32H7_I2CTIMEOTICKS +# define CONFIG_STM32H7_I2CTIMEOTICKS \ + (SEC2TICK(CONFIG_STM32H7_I2CTIMEOSEC) + MSEC2TICK(CONFIG_STM32H7_I2CTIMEOMS)) +#endif + +#ifndef CONFIG_STM32H7_I2C_DYNTIMEO_STARTSTOP +# define CONFIG_STM32H7_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_STM32H7_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) + +/* 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 */ + uint32_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; /* 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 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 */ + uint32_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_STM32H7_I2C_DYNTIMEO +static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); +#endif /* CONFIG_STM32H7_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_STM32H7_I2C1 +static const struct stm32_i2c_config_s stm32_i2c1_config = +{ + .base = STM32_I2C1_BASE, + .clk_bit = RCC_APB1LENR_I2C1EN, + .reset_bit = RCC_APB1LRSTR_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_STM32H7_I2C2 +static const struct stm32_i2c_config_s stm32_i2c2_config = +{ + .base = STM32_I2C2_BASE, + .clk_bit = RCC_APB1LENR_I2C2EN, + .reset_bit = RCC_APB1LRSTR_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_STM32H7_I2C3 +static const struct stm32_i2c_config_s stm32_i2c3_config = +{ + .base = STM32_I2C3_BASE, + .clk_bit = RCC_APB1LENR_I2C3EN, + .reset_bit = RCC_APB1LRSTR_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_STM32H7_I2C4 +static const struct stm32_i2c_config_s stm32_i2c4_config = +{ + .base = STM32_I2C4_BASE, + .clk_bit = RCC_APB1LENR_I2C4EN, + .reset_bit = RCC_APB1LRSTR_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_STM32H7_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_STM32H7_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_STM32H7_I2CTIMEOSEC > 0 + abstime.tv_sec += CONFIG_STM32H7_I2CTIMEOSEC; +#endif + + /* Add a value proportional to the number of bytes in the transfer */ + +#ifdef CONFIG_STM32H7_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_STM32H7_I2CTIMEOMS > 0 + abstime.tv_nsec += CONFIG_STM32H7_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) +{ + uint32_t timeout; + uint32_t start; + uint32_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_STM32H7_I2C_DYNTIMEO + timeout = USEC2TICK(stm32_i2c_tousecs(priv->msgc, priv->msgv)); +#else + timeout = CONFIG_STM32H7_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: %d threshold: %d status: 0x%08x\n", + priv->intstate, elapsed, 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) +{ + uint32_t start; + uint32_t elapsed; + uint32_t timeout; + uint32_t cr; + uint32_t sr; + + /* Select a timeout */ + +#ifdef CONFIG_STM32H7_I2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_STM32H7_I2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_STM32H7_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", + 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: + * + * 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 scl_delay; + uint8_t sda_delay; + uint8_t scl_h_period; + uint8_t scl_l_period; + + if (frequency != priv->frequency) + { + /* I2C peripheral must be disabled to update clocking configuration */ + + 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); + } + + /* The Sppeed 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); + + if (pe) + { + stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE); + } + + priv->frequency = frequency; + } +} + +/************************************************************************************ + * 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. + */ + + if (priv->msgc > 0) + { + 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 */ + + priv->status = status; + + /* 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 acknowledgement. + * 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) == 0) + { + 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); + } + + 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->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 */ + + priv->msgc--; + + /* if 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 should not happen in interrupt based operation. + * + * 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->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 + + status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET); + + /* Update private state to capture NACK which is used in combination + * with the astart flag to report the type of NACK received (address + * vs data) to the upper layers once we exit the ISR. + * + * Note: We do this prior to clearing interrupts because the NACKF + * flag will naturally be cleared by that process. + */ + + priv->status = status; + + /* Clear all interrupts */ + + stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); + + /* 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_APB1LENR, 0, priv->config->clk_bit); + modifyreg32(STM32_RCC_APB1LRSTR, 0, priv->config->reset_bit); + modifyreg32(STM32_RCC_APB1LRSTR, 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); + + /* Enable I2C peripheral */ + + 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); + +#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_APB1LENR, 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 (on change it toggles I2C_CR1_PE !) */ + + 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. + */ + + uint32_t start = clock_systimer(); + uint32_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; + } + + 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 != 16000000 || 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_STM32H7_I2C1 + case 1: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c1_priv; + break; +#endif +#ifdef CONFIG_STM32H7_I2C2 + case 2: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c2_priv; + break; +#endif +#ifdef CONFIG_STM32H7_I2C3 + case 3: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c3_priv; + break; +#endif +#ifdef CONFIG_STM32H7_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_STM32H7_I2C1 || CONFIG_STM32H7_I2C2 || \ + CONFIG_STM32H7_I2C3 || CONFIG_STM32H7_I2C4 */ diff --git a/arch/arm/src/stm32h7/stm32_i2c.h b/arch/arm/src/stm32h7/stm32_i2c.h new file mode 100644 index 00000000000..7c6847f8302 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_i2c.h @@ -0,0 +1,104 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/stm32_i2c.h + * + * Copyright (C) 2018 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_STM32H7_STM32H7_I2C_H +#define __ARCH_ARM_SRC_STM32H7_STM32H7_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "chip.h" +#include "chip/stm32_i2c.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* If a dynamic timeout is selected, then a non-negative, non-zero micro- + * seconds per byte value must be provided as well. + */ + +#ifdef CONFIG_STM32H7_I2C_DYNTIMEO +# if CONFIG_STM32H7_I2C_DYNTIMEO_USECPERBYTE < 1 +# warning "Ignoring CONFIG_STM32H7_I2C_DYNTIMEO because of CONFIG_STM32H7_I2C_DYNTIMEO_USECPERBYTE" +# undef CONFIG_STM32H7_I2C_DYNTIMEO +# endif +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameters: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct i2c_master_s *stm32_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: stm32_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameters: + * Device structure as returned by the stm32_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int stm32_i2cbus_uninitialize(FAR struct i2c_master_s *dev); + +#endif /* __ARCH_ARM_SRC_STM32H7_STM32H7_I2C_H */ diff --git a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c index eaf0d6f7992..0a7563d9027 100644 --- a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c +++ b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c @@ -289,15 +289,40 @@ static inline void rcc_enableapb1(void) regval = getreg32(STM32_RCC_APB1LENR); + +#ifdef CONFIG_STM32F7_I2C1 + /* I2C1 clock enable */ + + regval |= RCC_APB1LENR_I2C1EN; +#endif + +#ifdef CONFIG_STM32F7_I2C2 + /* I2C2 clock enable */ + + regval |= RCC_APB1LENR_I2C2EN; +#endif + +#ifdef CONFIG_STM32F7_I2C3 + /* I2C3 clock enable */ + + regval |= RCC_APB1LENR_I2C3EN; +#endif + +#ifdef CONFIG_STM32F7_I2C4 + /* I2C4 clock enable */ + + regval |= RCC_APB1LENR_I2C4EN; +#endif + // TODO: ... - putreg32(regval, STM32_RCC_APB1LENR); /* Enable peripherals */ + putreg32(regval, STM32_RCC_APB1LENR); /* Enable APB1L peripherals */ regval = getreg32(STM32_RCC_APB1HENR); // TODO: ... - putreg32(regval, STM32_RCC_APB1HENR); /* Enable peripherals */ + putreg32(regval, STM32_RCC_APB1HENR); /* Enable APB1H peripherals */ } /**************************************************************************** @@ -577,112 +602,20 @@ static void stm32_stdclockconfig(void) { } -#if defined(CONFIG_STM32H7_LTDC) || defined(CONFIG_STM32H7_PLLSAI) + /* Configure I2C source clock */ - /* Configure PLLSAI */ - - regval = getreg32(STM32_RCC_PLLSAICFGR); - regval &= ~( RCC_PLLSAICFGR_PLLSAIN_MASK - | RCC_PLLSAICFGR_PLLSAIP_MASK - | RCC_PLLSAICFGR_PLLSAIQ_MASK - | RCC_PLLSAICFGR_PLLSAIR_MASK); - regval |= (STM32_RCC_PLLSAICFGR_PLLSAIN - | STM32_RCC_PLLSAICFGR_PLLSAIP - | STM32_RCC_PLLSAICFGR_PLLSAIQ - | STM32_RCC_PLLSAICFGR_PLLSAIR); - putreg32(regval, STM32_RCC_PLLSAICFGR); - - regval = getreg32(STM32_RCC_DCKCFGR1); - regval &= ~(RCC_DCKCFGR1_PLLI2SDIVQ_MASK - | RCC_DCKCFGR1_PLLSAIDIVQ_MASK - | RCC_DCKCFGR1_PLLSAIDIVR_MASK - | RCC_DCKCFGR1_SAI1SEL_MASK - | RCC_DCKCFGR1_SAI2SEL_MASK - | RCC_DCKCFGR1_TIMPRESEL); - - regval |= (STM32_RCC_DCKCFGR1_PLLI2SDIVQ - | STM32_RCC_DCKCFGR1_PLLSAIDIVQ - | STM32_RCC_DCKCFGR1_PLLSAIDIVR - | STM32_RCC_DCKCFGR1_SAI1SRC - | STM32_RCC_DCKCFGR1_SAI2SRC - | STM32_RCC_DCKCFGR1_TIMPRESRC); - - putreg32(regval, STM32_RCC_DCKCFGR1); - - - /* Enable PLLSAI */ - - regval = getreg32(STM32_RCC_CR); - regval |= RCC_CR_PLLSAION; - putreg32(regval, STM32_RCC_CR); - - /* Wait until the PLLSAI is ready */ - - while ((getreg32(STM32_RCC_CR) & RCC_CR_PLLSAIRDY) == 0) - { - } +#if defined(STM32_RCC_D2CCIP2R_I2C123SRC) + regval = getreg32(STM32_RCC_D2CCIP2R); + regval &= ~RCC_D2CCIP2R_I2C123SEL_MASK; + regval |= STM32_RCC_D2CCIP2R_I2C123SRC; + putreg32(regval, STM32_RCC_D2CCIP2R); #endif -#if defined(CONFIG_STM32H7_LTDC) || defined(CONFIG_STM32H7_PLLI2S) - /* Configure PLLI2S */ - - regval = getreg32(STM32_RCC_PLLI2SCFGR); - regval &= ~( RCC_PLLI2SCFGR_PLLI2SN_MASK - | RCC_PLLI2SCFGR_PLLI2SP_MASK - | RCC_PLLI2SCFGR_PLLI2SQ_MASK - | RCC_PLLI2SCFGR_PLLI2SR_MASK); - regval |= (STM32_RCC_PLLI2SCFGR_PLLI2SN - | STM32_RCC_PLLI2SCFGR_PLLI2SP - | STM32_RCC_PLLI2SCFGR_PLLI2SQ - | STM32_RCC_PLLI2SCFGR_PLLI2SR); - putreg32(regval, STM32_RCC_PLLI2SCFGR); - - regval = getreg32(STM32_RCC_DCKCFGR2); - regval &= ~( RCC_DCKCFGR2_USART1SEL_MASK - | RCC_DCKCFGR2_USART2SEL_MASK - | RCC_DCKCFGR2_UART4SEL_MASK - | RCC_DCKCFGR2_UART5SEL_MASK - | RCC_DCKCFGR2_USART6SEL_MASK - | RCC_DCKCFGR2_UART7SEL_MASK - | RCC_DCKCFGR2_UART8SEL_MASK - | RCC_DCKCFGR2_I2C1SEL_MASK - | RCC_DCKCFGR2_I2C2SEL_MASK - | RCC_DCKCFGR2_I2C3SEL_MASK - | RCC_DCKCFGR2_I2C4SEL_MASK - | RCC_DCKCFGR2_LPTIM1SEL_MASK - | RCC_DCKCFGR2_CECSEL_MASK - | RCC_DCKCFGR2_CK48MSEL_MASK - | RCC_DCKCFGR2_SDMMCSEL_MASK); - - regval |= ( STM32_RCC_DCKCFGR2_USART1SRC - | STM32_RCC_DCKCFGR2_USART2SRC - | STM32_RCC_DCKCFGR2_UART4SRC - | STM32_RCC_DCKCFGR2_UART5SRC - | STM32_RCC_DCKCFGR2_USART6SRC - | STM32_RCC_DCKCFGR2_UART7SRC - | STM32_RCC_DCKCFGR2_UART8SRC - | STM32_RCC_DCKCFGR2_I2C1SRC - | STM32_RCC_DCKCFGR2_I2C2SRC - | STM32_RCC_DCKCFGR2_I2C3SRC - | STM32_RCC_DCKCFGR2_I2C4SRC - | STM32_RCC_DCKCFGR2_LPTIM1SRC - | STM32_RCC_DCKCFGR2_CECSRC - | STM32_RCC_DCKCFGR2_CK48MSRC - | STM32_RCC_DCKCFGR2_SDMMCSRC); - - putreg32(regval, STM32_RCC_DCKCFGR2); - - /* Enable PLLI2S */ - - regval = getreg32(STM32_RCC_CR); - regval |= RCC_CR_PLLI2SON; - putreg32(regval, STM32_RCC_CR); - - /* Wait until the PLLI2S is ready */ - - while ((getreg32(STM32_RCC_CR) & RCC_CR_PLLI2SRDY) == 0) - { - } +#if defined(STM32_RCC_D3CCIPR_I2C4SRC) + regval = getreg32(STM32_RCC_D3CCIPR); + regval &= ~RCC_D3CCIPR_I2C4SEL_MASK; + regval |= STM32_RCC_D3CCIPR_I2C4SRC; + putreg32(regval, STM32_RCC_D3CCIPR); #endif #if defined(CONFIG_STM32H7_IWDG) || defined(CONFIG_STM32H7_RTC_LSICLOCK) diff --git a/configs/nucleo-h743zi/include/board.h b/configs/nucleo-h743zi/include/board.h index 48fe9e12dfa..32935872a7e 100644 --- a/configs/nucleo-h743zi/include/board.h +++ b/configs/nucleo-h743zi/include/board.h @@ -184,6 +184,15 @@ #define STM32_RCC_D3CFGR_D3PPRE RCC_D3CFGR_D3PPRE_HCLKd4 /* PCLK1 = HCLK / 4 */ #define STM32_PCLK4_FREQUENCY (STM32_HCLK_FREQUENCY/4) + +/* I2C123 clock source */ + +#define STM32_RCC_D2CCIP2R_I2C123SRC RCC_D2CCIP2R_I2C123SEL_HSI + +/* I2C4 clock source */ + +#define STM32_RCC_D2CCIP3R_I2C4SRC RCC_D2CCIP3R_I2C4SEL_HSI + /* FLASH wait states * * ------------ ---------- ----------- @@ -266,7 +275,7 @@ #define NUM_BUTTONS 1 #define BUTTON_USER_BIT (1 << BUTTON_USER) -/* Pin Disambiguation ***************************************************************/ +/* Alternate function pin selections ************************************************/ /* USART3 (Nucleo Virtual Console) */ #define GPIO_USART3_RX GPIO_USART3_RX_3 /* PD9 */ @@ -277,6 +286,13 @@ #define GPIO_USART6_RX GPIO_USART6_RX_2 /* PG9 */ #define GPIO_USART6_TX GPIO_USART6_TX_2 /* PG14 */ +/* I2C1 Use Nucleo I2C pins */ + +#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* PB8 */ +#define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* PB9 */ + +/* */ + /************************************************************************************ * Public Data ************************************************************************************/ diff --git a/configs/nucleo-h743zi/src/Makefile b/configs/nucleo-h743zi/src/Makefile index 19c49f4322a..bb3ae6b906f 100644 --- a/configs/nucleo-h743zi/src/Makefile +++ b/configs/nucleo-h743zi/src/Makefile @@ -52,6 +52,14 @@ ifeq ($(CONFIG_STM32H7_SPI),y) CSRCS += stm32_spi.c endif +ifeq ($(CONFIG_SENSORS_LSM6DSL),y) +CSRCS += stm32_lsm6dsl.c +endif + +ifeq ($(CONFIG_SENSORS_LSM303AGR),y) +CSRCS += stm32_lsm303agr.c +endif + ifeq ($(CONFIG_LIB_BOARDCTL),y) CSRCS += stm32_appinitialize.c endif diff --git a/configs/nucleo-h743zi/src/nucleo-h743zi.h b/configs/nucleo-h743zi/src/nucleo-h743zi.h index b5039af3f6e..bf1671f3c0d 100644 --- a/configs/nucleo-h743zi/src/nucleo-h743zi.h +++ b/configs/nucleo-h743zi/src/nucleo-h743zi.h @@ -82,6 +82,12 @@ #define GPIO_BTN_USER (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI | GPIO_PORTC | GPIO_PIN13) +/* X-NUCLEO IKS01A2 */ + +#define GPIO_LPS22HB_INT1 (GPIO_INPUT | GPIO_FLOAT | GPIO_PORTB | GPIO_PIN10) +#define GPIO_LSM6DSL_INT1 (GPIO_INPUT | GPIO_FLOAT | GPIO_PORTB | GPIO_PIN4) +#define GPIO_LSM6DSL_INT2 (GPIO_INPUT | GPIO_FLOAT | GPIO_PORTB | GPIO_PIN5) + /************************************************************************************ * Public Functions ************************************************************************************/ @@ -128,4 +134,27 @@ void stm32_spidev_initialize(void); int stm32_adc_setup(void); #endif + +/***************************************************************************** + * Name: stm32_lsm6dsl_initialize + * + * Description: + * Initialize I2C-based LSM6DSL. + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_LSM303AGR +int stm32_lsm6dsl_initialize(char *devpath); +#endif + +/***************************************************************************** + * Name: stm32_lsm303agr_initialize + * + * Description: + * Initialize I2C-based LSM303AGR. + ****************************************************************************/ + +#ifdef CONFIG_SENSORS_LSM6DSL +int stm32_lsm303agr_initialize(char *devpath); +#endif + #endif /* __CONFIGS_NUCLEO_H743ZI_SRC_NUCLEO_H743ZI_H */ diff --git a/configs/nucleo-h743zi/src/stm32_bringup.c b/configs/nucleo-h743zi/src/stm32_bringup.c index 2921100bf67..8749c2fab2f 100644 --- a/configs/nucleo-h743zi/src/stm32_bringup.c +++ b/configs/nucleo-h743zi/src/stm32_bringup.c @@ -63,23 +63,25 @@ * CONFIG_BOARD_INITIALIZE=y : * Called from board_initialize(). * - * CONFIG_BOARD_INITIALIZE=n && CONFIG_LIB_BOARDCTL=y : + * CONFIG_BOARD_INITIALIZE=n && CONFIG_LIB_BOARDCTL=y && CONFIG_NSH_ARCHINIT: * Called from the NSH library * ****************************************************************************/ int stm32_bringup(void) { -#ifdef CONFIG_FS_PROCFS int ret; + UNUSED(ret); + +#ifdef CONFIG_FS_PROCFS #ifdef CONFIG_STM32_CCM_PROCFS /* Register the CCM procfs entry. This must be done before the procfs is * mounted. */ (void)ccm_procfs_register(); -#endif +#endif /* CONFIG_STM32_CCM_PROCFS */ /* Mount the procfs file system */ @@ -90,7 +92,7 @@ int stm32_bringup(void) "ERROR: Failed to mount the PROC filesystem: %d (%d)\n", ret, errno); } -#endif +#endif /* CONFIG_FS_PROCFS */ #ifdef CONFIG_BUTTONS /* Register the BUTTON driver */ @@ -100,7 +102,7 @@ int stm32_bringup(void) { syslog(LOG_ERR, "ERROR: btn_lower_initialize() failed: %d\n", ret); } -#endif +#endif /* CONFIG_BUTTONS */ #ifdef CONFIG_ADC /* Initialize ADC and register the ADC driver. */ @@ -110,7 +112,23 @@ int stm32_bringup(void) { syslog(LOG_ERR, "ERROR: stm32_adc_setup failed: %d\n", ret); } -#endif +#endif /* CONFIG_ADC */ + +#ifdef CONFIG_SENSORS_LSM6DSL + ret = stm32_lsm6dsl_initialize("/dev/lsm6dsl0"); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize LSM6DSL driver: %d\n", ret); + } +#endif /* CONFIG_SENSORS_LSM6DSL */ + +#ifdef CONFIG_SENSORS_LSM303AGR + ret = stm32_lsm303agr_initialize("/dev/lsm303mag0"); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize LSM303AGR driver: %d\n", ret); + } +#endif /* CONFIG_SENSORS_LSM303AGR */ return OK; } diff --git a/configs/nucleo-h743zi/src/stm32_lsm303agr.c b/configs/nucleo-h743zi/src/stm32_lsm303agr.c new file mode 100644 index 00000000000..751f2e6ef81 --- /dev/null +++ b/configs/nucleo-h743zi/src/stm32_lsm303agr.c @@ -0,0 +1,94 @@ +/***************************************************************************** + * configs/nucleo-h743zi/src/stm32_lsm303agr.c + * + * Copyright (C) 2018 Greg 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 "stm32.h" +#include +#include + +/***************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_STM32H7_I2C1 +# error "LSM303AGR driver requires CONFIG_STM32H7_I2C1 to be enabled" +#endif + +/***************************************************************************** + * Public Functions + ****************************************************************************/ + +/***************************************************************************** + * Name: stm32_lsm303agr_initialize + * + * Description: + * Initialize I2C-based LSM303AGR. + ****************************************************************************/ + +int stm32_lsm303agr_initialize(char *devpath) +{ + FAR struct i2c_master_s *i2c; + int ret = OK; + + sninfo("INFO: Initializing LMS303AGR sensor over I2C\n"); + +#if defined(CONFIG_STM32H7_I2C1) + i2c = stm32_i2cbus_initialize(1); + if (i2c == NULL) + { + return -ENODEV; + } + + ret = lsm303agr_sensor_register("/dev/lsm303agr0", i2c, LSM303AGRMAGNETO_ADDR); + if (ret < 0) + { + snerr("ERROR: Failed to initialize LMS303AGR magneto driver %s\n", devpath); + return -ENODEV; + } + + sninfo("INFO: LMS303AGR sensor has been initialized successfully\n"); +#endif + + return ret; +} diff --git a/configs/nucleo-h743zi/src/stm32_lsm6dsl.c b/configs/nucleo-h743zi/src/stm32_lsm6dsl.c new file mode 100644 index 00000000000..826695c6dd6 --- /dev/null +++ b/configs/nucleo-h743zi/src/stm32_lsm6dsl.c @@ -0,0 +1,100 @@ +/***************************************************************************** + * configs/nucleo-h743zi/src/stm32_lsm6dsl.c + * + * Copyright (C) 2018 Greg 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 "stm32.h" +#include +#include + +/***************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_STM32H7_I2C1 +# error "LSM6DSL driver requires CONFIG_STM32H7_I2C1 to be enabled" +#endif + +/***************************************************************************** + * Public Functions + ****************************************************************************/ + +/***************************************************************************** + * Name: stm32_lsm6dsl_initialize + * + * Description: + * Initialize I2C-based LSM6DSL. + ****************************************************************************/ + +int stm32_lsm6dsl_initialize(char *devpath) +{ + FAR struct i2c_master_s *i2c; + int ret = OK; + + sninfo("Initializing LMS6DSL!\n"); + + /* Configure the GPIO interrupt */ + + stm32_configgpio(GPIO_LPS22HB_INT1); + +#if defined(CONFIG_STM32H7_I2C1) + i2c = stm32_i2cbus_initialize(1); + if (i2c == NULL) + { + return -ENODEV; + } + + sninfo("INFO: Initializing LMS6DSL accelero-gyro sensor over I2C%d\n", ret); + + ret = lsm6dsl_sensor_register("/dev/lsm6dsl0", i2c, LSM6DSLACCEL_ADDR1); + if (ret < 0) + { + snerr("ERROR: Failed to initialize LMS6DSL accelero-gyro driver %s\n", devpath); + return -ENODEV; + } + + sninfo("INFO: LMS6DSL sensor has been initialized successfully\n"); +#endif + + return ret; +} diff --git a/configs/nucleo-l476rg/src/stm32_lsm303agr.c b/configs/nucleo-l476rg/src/stm32_lsm303agr.c index eb8ad2c5b9d..6c44e02b914 100644 --- a/configs/nucleo-l476rg/src/stm32_lsm303agr.c +++ b/configs/nucleo-l476rg/src/stm32_lsm303agr.c @@ -70,7 +70,7 @@ int stm32l4_lsm303agr_initialize(char *devpath) { FAR struct i2c_master_s *i2c; int ret = OK; - + sninfo("INFO: Initializing LMS303AGR sensor over I2C\n"); #if defined(CONFIG_STM32L4_I2C1) @@ -79,7 +79,7 @@ int stm32l4_lsm303agr_initialize(char *devpath) { return -ENODEV; } - + ret = lsm303agr_sensor_register("/dev/lsm303mag0", i2c, LSM303AGRMAGNETO_ADDR); if (ret < 0) { diff --git a/configs/nucleo-l476rg/src/stm32_lsm6dsl.c b/configs/nucleo-l476rg/src/stm32_lsm6dsl.c index 5c2030dc333..c70424b18a4 100644 --- a/configs/nucleo-l476rg/src/stm32_lsm6dsl.c +++ b/configs/nucleo-l476rg/src/stm32_lsm6dsl.c @@ -70,7 +70,7 @@ int stm32l4_lsm6dsl_initialize(char *devpath) { FAR struct i2c_master_s *i2c; int ret = OK; - + sninfo("Initializing LMS6DSL!\n"); /* Configure the GPIO interrupt */ @@ -83,7 +83,7 @@ int stm32l4_lsm6dsl_initialize(char *devpath) { return -ENODEV; } - + sninfo("INFO: Initializing LMS6DSL accelero-gyro sensor over I2C%d\n", ret); ret = lsm6dsl_sensor_register("/dev/lsm6dsl0", i2c, LSM6DSLACCEL_ADDR1); diff --git a/drivers/sensors/lsm303agr.c b/drivers/sensors/lsm303agr.c index 29e0a619c73..7a2b3340bb5 100644 --- a/drivers/sensors/lsm303agr.c +++ b/drivers/sensors/lsm303agr.c @@ -2,9 +2,9 @@ * drivers/sensors/lsm303agr.c * * Copyright (C) 2018 Inc. All rights reserved. - * Author: Ben vd Veen + * Author: Ben vd Veen * Alias: DisruptiveNL - * + * * Based on: * * Copyright (C) 2016 Omni Hoverboards Inc. All rights reserved. @@ -57,6 +57,7 @@ #include #include #include +#include #if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_LSM303AGR) @@ -869,10 +870,10 @@ static int lsm303agr_selftest(FAR struct lsm303agr_dev_s *priv, uint32_t mode) * * Description: * Read the sensor. - * A sensor in a steady state on a horizontal surface will - * measure 0 g on both the X-axis and Y-axis, whereas the Z-axis will - * measure 1 g. (page 30 datasheet). The X- and Y-axis have an offset - * of 40 mg/LSB + * A sensor in a steady state on a horizontal surface will + * measure 0 g on both the X-axis and Y-axis, whereas the Z-axis will + * measure 1 g. (page 30 datasheet). The X- and Y-axis have an offset + * of 40 mg/LSB * ****************************************************************************/ diff --git a/drivers/sensors/lsm6dsl.c b/drivers/sensors/lsm6dsl.c index d699f9d3eef..d0458b97348 100644 --- a/drivers/sensors/lsm6dsl.c +++ b/drivers/sensors/lsm6dsl.c @@ -2,9 +2,9 @@ * drivers/sensors/lsm6dsl.c * * Copyright (C) 2018 Inc. All rights reserved. - * Author: Ben vd Veen + * Author: Ben vd Veen * Alias: DisruptiveNL - * + * * Based on: * * Copyright (C) 2016 Omni Hoverboards Inc. All rights reserved. @@ -57,6 +57,7 @@ #include #include #include +#include #if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_LSM6DSL) @@ -889,10 +890,10 @@ static int lsm6dsl_selftest(FAR struct lsm6dsl_dev_s *priv, uint32_t mode) * * Description: * Read the sensor. - * A sensor in a steady state on a horizontal surface will - * measure 0 g on both the X-axis and Y-axis, whereas the Z-axis will - * measure 1 g. (page 30 datasheet). The X- and Y-axis have an offset - * of 40 mg/LSB + * A sensor in a steady state on a horizontal surface will + * measure 0 g on both the X-axis and Y-axis, whereas the Z-axis will + * measure 1 g. (page 30 datasheet). The X- and Y-axis have an offset + * of 40 mg/LSB * ****************************************************************************/ @@ -1132,7 +1133,7 @@ static ssize_t lsm6dsl_read(FAR struct file *filep, } /* The value is negative, so find its absolute value by taking the - * two's complement + * two's complement */ else if (data > 0x8000)