From c00bb5d4a761a0119abc3c58febd689fadfeee3b Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 27 May 2016 00:16:55 +0200 Subject: [PATCH] i2c --- arch/arm/src/stm32f7/stm32_i2c.c | 2226 +++++++++++------ configs/stm32f746-ws/nsh/defconfig | 6 +- .../stm32f746-ws/src/stm32_appinitialize.c | 34 +- 3 files changed, 1475 insertions(+), 791 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32_i2c.c b/arch/arm/src/stm32f7/stm32_i2c.c index 3b9fc8270b..e5a9604294 100644 --- a/arch/arm/src/stm32f7/stm32_i2c.c +++ b/arch/arm/src/stm32f7/stm32_i2c.c @@ -1,22 +1,24 @@ /************************************************************************************ - * arch/arm/src/stm32f7/stm32f3xx_i2c.c - * STM32F7 I2C driver - based on STM32L4 I2C Hardware Layer - Device Driver + * arch/arm/src/stm32/stm32f3xx_i2c.c + * STM32 F3 I2C Hardware Layer - Device Driver * * Copyright (C) 2011 Uros Platise. All rights reserved. * Author: Uros Platise * * With extensions and modifications for the F1, F2, and F4 by: * - * Copyright (C) 2011-2013, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved. * Author: Gregroy Nutt * * And this version for the STM32 F3 by * * Author: John Wharington * - * Modified for STM32L4 by + * Major rewrite of ISR and supporting methods, including support + * for NACK and RELOAD by: * - * Author: Sebastien Lorquet + * 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 @@ -47,29 +49,162 @@ * ************************************************************************************/ -/* Supports: - * - Master operation, 100 kHz (standard) and 400 kHz (full speed) +/* ------------------------------------------------------------------------------ + * + * STM32 F3 I2C Driver + * + * Supports: + * - STM32 F30xxx + * - Internal Oscillator (HSI) running at 8 Mhz + * - Master operation at up to 400Khz (Fast Mode) * - Multiple instances (shared bus) * - Interrupt based operation + * - RELOAD support + * + * Unsupported, possible future work: + * - More effective error reporting to higher layers + * - Fast mode plus (1Mhz) + * - Slave operation + * - Peripheral clock source selection (SYSCLK vs HSI) + * - Support of SYSCLK frequencies other than 8Mhz + * - Polled operation (code present but untested) + * - SMBus support + * - Multi-master support + * - IPMI + * + * Test Environment: + * + * - STM32F303VC on ST F3 Discovery Board (ST Part STM32F3DISCOVERY) + * 256K Flash, 40K SRAM available for all operations, 8K SRAM CCM + * + * 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_NORESTART 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: + * + * Time to transfer two messages, each a byte in length, in addition to the + * START condition, in interrupt mode: + * + * DEBUG enabled (development): 80ms + * Excessive delay here is caused by printing to the console and + * is of no concern. + * + * DEBUG disabled (production): 120us + * Between Messages: 38us + * Between Bytes: 7us + * + * Implementation: * - * Structure naming: * - Device: structure as defined by the nuttx/i2c/i2c.h + * * - Instance: represents each individual access to the I2C driver, obtained by * the i2c_init(); it extends the Device structure from the nuttx/i2c/i2c.h; * Instance points to OPS, to common I2C Hardware private data and contains - * its own private data, as frequency, address, mode of operation (in the future) + * its own private data including frequency, address and mode of operation. + * * - Private: Private data of an I2C Hardware * - * TODO - * - Check for all possible deadlocks (as BUSY='1' I2C needs to be reset in HW using the I2C_CR1_SWRST) - * - SMBus support (hardware layer timings are already supported) and add SMBA gpio pin - * - Slave support with multiple addresses (on multiple instances): - * - 2 x 7-bit address or - * - 1 x 10 bit adresses + 1 x 7 bit address (?) - * - plus the broadcast address (general call) - * - Multi-master support - * - DMA (to get rid of too many CPU wake-ups and interventions) - * - Be ready for IPMI + * High Level Functional 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: + * + * CONFIG_STM32F7_STM32F30XX + * + * and one or more interfaces: + * + * CONFIG_STM32F7_I2C1 + * CONFIG_STM32F7_I2C2 + * CONFIG_STM32F7_I2C3 + * + * To configure the ISR timeout using fixed values (STM32_I2C_DYNTIMEO=n): + * + * CONFIG_STM32F7_I2CTIMEOMS (Timeout in milliseconds) + * CONFIG_STM32F7_I2CTIMEOSEC (Timeout in seconds) + * CONFIG_STM32F7_I2CTIMEOTICKS (Timeout in ticks) + * + * To configure the ISR timeout using dynamic values (STM32_I2C_DYNTIMEO=y): + * + * STM32_I2C_DYNTIMEO_USECPERBYTE (Timeout in microseconds per byte) + * STM32_I2C_DYNTIMEO_STARTSTOP (Timeout for start/stop in milliseconds) + * + * Debugging output enabled with: + * + * CONFIG_DEBUG and CONFIG_DEBUG_I2C + * + * ISR Debugging output may be enabled with: + * + * CONFIG_DEBUG_VERBOSE + * + * ------------------------------------------------------------------------------ + * + * References: + * + * RM0316: + * ST STM32F303xB/C/D/E, etc. Reference Manual + * Document ID: DM00043574, Revision 6, August 2015. + * + * DATASHEET: + * ST STM32F303xB/STM32F303xC Datasheet + * Document ID: DM00058181, Revision 12, December 2015. + * + * 303ZYERRATA: + * STM32F303xB/C Rev Z and Y device limitations + * Document ID: DM00063985, Revision 7, September 2015. + * + * I2CSPEC: + * I2C Bus Specification and User Manual + * Document ID: UM10204, Revision 6, April 2014. + * + * ------------------------------------------------------------------------------ */ /************************************************************************************ @@ -90,6 +225,7 @@ #include #include #include +#include #include #include @@ -104,13 +240,13 @@ /* At least one I2C peripheral must be enabled */ #if defined(CONFIG_STM32F7_I2C1) || defined(CONFIG_STM32F7_I2C2) || defined(CONFIG_STM32F7_I2C3) -/* This implementation is for the STM32F7 only */ +/* This implementation is for the STM32 F1, F2, and F4 only */ /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ -/* Configuration ********************************************************************/ + /* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. Instead, * CPU-intensive polling will be used. */ @@ -147,18 +283,9 @@ #define I2C_CR1_TXRX \ (I2C_CR1_RXIE | I2C_CR1_TXIE) #define I2C_CR1_ALLINTS \ - (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ADDRIE | I2C_CR1_ERRIE) + (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ERRIE) -#define STATUS_NACK(status) (status & I2C_INT_NACK) -#define STATUS_ADDR(status) (status & I2C_INT_ADDR) -#define STATUS_ADDR_TX(status) (status & (I2C_INT_ADDR | I2C_ISR_TXIS)) -#define STATUS_ADD10(status) (0) -#define STATUS_RXNE(status) (status & I2C_ISR_RXNE) -#define STATUS_TC(status) (status & I2C_ISR_TC) -#define STATUS_BUSY(status) (status & I2C_ISR_BUSY) - -/* Debug ****************************************************************************/ -/* CONFIG_DEBUG_I2C + CONFIG_DEBUG enables general I2C debug output. */ +/* I2C Debugging */ #ifdef CONFIG_DEBUG_I2C # define i2cdbg dbg @@ -168,16 +295,22 @@ # define i2cvdbg(x...) #endif -/* I2C event trace logic. NOTE: trace uses the internal, non-standard, low-level - * debug interface syslog() but does not require that any other debug - * is enabled. +/* 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 stm32f7_i2c_tracereset(p) -# define stm32f7_i2c_tracenew(p,s) -# define stm32f7_i2c_traceevent(p,e,a) -# define stm32f7_i2c_tracedump(p) +# 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 @@ -187,9 +320,10 @@ /************************************************************************************ * Private Types ************************************************************************************/ + /* Interrupt state */ -enum stm32f7_intstate_e +enum stm32_intstate_e { INTSTATE_IDLE = 0, /* No I2C activity */ INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ @@ -198,35 +332,46 @@ enum stm32f7_intstate_e /* Trace events */ -enum stm32f7_trace_e +enum stm32_trace_e { - I2CEVENT_NONE = 0, /* No events have occurred with this status */ - I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ - I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ - I2CEVENT_ITBUFEN, /* Enable buffer interrupts, param = 0 */ - I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ - I2CEVENT_REITBUFEN, /* Re-enable buffer interrupts, param = 0 */ - I2CEVENT_DISITBUFEN, /* Disable buffer interrupts, param = 0 */ - I2CEVENT_BTFNOSTART, /* BTF on last byte with no restart, param = msgc */ - I2CEVENT_BTFRESTART, /* Last byte sent, re-starting, param = msgc */ - I2CEVENT_BTFSTOP, /* Last byte sten, send stop, param = 0 */ - I2CEVENT_ERROR /* Error occurred, param = 0 */ + I2CEVENT_NONE = 0, + I2CEVENT_STATE_ERROR, + I2CEVENT_ISR_SHUTDOWN, + I2CEVENT_ISR_CALL, + I2CEVENT_ISR_EMPTY_CALL, + I2CEVENT_MSG_HANDLING, + I2CEVENT_POLL_NOT_READY, + I2CEVENT_EMPTY_MSG, + I2CEVENT_START, + I2CEVENT_ADDRESS_ACKED, + I2CEVENT_ADDRESS_NACKED, + I2CEVENT_NACK, + I2CEVENT_READ, + I2CEVENT_READ_ERROR, + I2CEVENT_WRITE_TO_DR, + I2CEVENT_WRITE_STOP, + I2CEVENT_WRITE_RESTART, + I2CEVENT_WRITE_NO_RESTART, + I2CEVENT_WRITE_ERROR, + I2CEVENT_WRITE_FLAG_ERROR, + I2CEVENT_TC_RESTART, + I2CEVENT_TC_NO_RESTART }; /* Trace data */ -struct stm32f7_trace_s +struct stm32_trace_s { uint32_t status; /* I2C 32-bit SR2|SR1 status */ uint32_t count; /* Interrupt count when status change */ - enum stm32f7_intstate_e event; /* Last event that occurred with this status */ + enum stm32_intstate_e event; /* Last event that occurred with this status */ uint32_t parm; /* Parameter associated with the event */ - systime_t time; /* First of event or first status */ + uint32_t time; /* First of event or first status */ }; /* I2C Device hardware configuration */ -struct stm32f7_i2c_config_s +struct stm32_i2c_config_s { uint32_t base; /* I2C base address */ uint32_t clk_bit; /* Clock enable bit */ @@ -242,22 +387,20 @@ struct stm32f7_i2c_config_s /* I2C Device Private Data */ -struct stm32f7_i2c_priv_s +struct stm32_i2c_priv_s { - const struct i2c_ops_s *ops; /* Standard I2C operations */ - const struct stm32f7_i2c_config_s *config; /* Port configuration */ + 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 stm32f7_intstate_e) */ + volatile uint8_t intstate; /* Interrupt handshake (see enum stm32_intstate_e) */ uint8_t msgc; /* Message count */ struct i2c_msg_s *msgv; /* Message list */ uint8_t *ptr; /* Current message buffer */ - uint32_t frequency; /* Current I2C frequency */ - int dcnt; /* Current message length */ + int dcnt; /* Current message bytes remaining to transfer */ uint16_t flags; /* Current message flags */ bool astart; /* START sent */ @@ -265,90 +408,89 @@ struct stm32f7_i2c_priv_s #ifdef CONFIG_I2C_TRACE int tndx; /* Trace array index */ - systime_t start_time; /* Time when the trace was started */ + uint32_t start_time; /* Time when the trace was started */ /* The actual trace data */ - struct stm32f7_trace_s trace[CONFIG_I2C_NTRACE]; + struct stm32_trace_s trace[CONFIG_I2C_NTRACE]; #endif uint32_t status; /* End of transfer SR2|SR1 status */ }; +/* I2C Device, Instance */ + +struct stm32_i2c_inst_s +{ + struct i2c_ops_s *ops; /* Standard I2C operations */ + struct stm32_i2c_priv_s *priv; /* Common driver private data structure */ + + uint32_t frequency; /* Frequency used in this instantiation */ + int address; /* Address used in this instantiation */ + uint16_t flags; /* Flags used in this instantiation */ +}; + /************************************************************************************ * Private Function Prototypes ************************************************************************************/ -static inline uint16_t stm32f7_i2c_getreg(FAR struct stm32f7_i2c_priv_s *priv, +static inline uint16_t stm32_i2c_getreg(FAR struct stm32_i2c_priv_s *priv, uint8_t offset); -static inline void stm32f7_i2c_putreg(FAR struct stm32f7_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 stm32f7_i2c_putreg32(FAR struct stm32f7_i2c_priv_s *priv, uint8_t offset, +static inline void stm32_i2c_putreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, uint32_t value); -static inline void stm32f7_i2c_modifyreg(FAR struct stm32f7_i2c_priv_s *priv, - uint8_t offset, uint16_t clearbits, - uint16_t setbits); -static inline void stm32f7_i2c_modifyreg32(FAR struct stm32f7_i2c_priv_s *priv, +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 stm32f7_i2c_sem_wait(FAR struct stm32f7_i2c_priv_s *priv); +static inline void stm32_i2c_sem_wait(FAR struct i2c_master_s *dev); #ifdef CONFIG_STM32F7_I2C_DYNTIMEO -static useconds_t stm32f7_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); +static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); #endif /* CONFIG_STM32F7_I2C_DYNTIMEO */ -static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv); -static inline void stm32f7_i2c_sem_waitstop(FAR struct stm32f7_i2c_priv_s *priv); -static inline void stm32f7_i2c_sem_post(FAR struct stm32f7_i2c_priv_s *priv); -static inline void stm32f7_i2c_sem_init(FAR struct stm32f7_i2c_priv_s *priv); -static inline void stm32f7_i2c_sem_destroy(FAR struct stm32f7_i2c_priv_s *priv); +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 stm32f7_i2c_tracereset(FAR struct stm32f7_i2c_priv_s *priv); -static void stm32f7_i2c_tracenew(FAR struct stm32f7_i2c_priv_s *priv, uint32_t status); -static void stm32f7_i2c_traceevent(FAR struct stm32f7_i2c_priv_s *priv, - enum stm32f7_trace_e event, uint32_t parm); -static void stm32f7_i2c_tracedump(FAR struct stm32f7_i2c_priv_s *priv); +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 stm32f7_i2c_setclock(FAR struct stm32f7_i2c_priv_s *priv, +static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency); -static inline void stm32f7_i2c_sendstart(FAR struct stm32f7_i2c_priv_s *priv); -static inline void stm32f7_i2c_clrstart(FAR struct stm32f7_i2c_priv_s *priv); -static inline void stm32f7_i2c_sendstop(FAR struct stm32f7_i2c_priv_s *priv); -static inline uint32_t stm32f7_i2c_getstatus(FAR struct stm32f7_i2c_priv_s *priv); -static int stm32f7_i2c_isr(struct stm32f7_i2c_priv_s * priv); +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(struct stm32_i2c_priv_s * priv); #ifndef CONFIG_I2C_POLLED #ifdef CONFIG_STM32F7_I2C1 -static int stm32f7_i2c1_isr(int irq, void *context); +static int stm32_i2c1_isr(int irq, void *context); #endif #ifdef CONFIG_STM32F7_I2C2 -static int stm32f7_i2c2_isr(int irq, void *context); +static int stm32_i2c2_isr(int irq, void *context); #endif #ifdef CONFIG_STM32F7_I2C3 -static int stm32f7_i2c3_isr(int irq, void *context); +static int stm32_i2c3_isr(int irq, void *context); #endif #endif -static int stm32f7_i2c_init(FAR struct stm32f7_i2c_priv_s *priv); -static int stm32f7_i2c_deinit(FAR struct stm32f7_i2c_priv_s *priv); -static int stm32f7_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, +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 stm32f7_i2c_reset(FAR struct i2c_master_s *dev); -#endif + /************************************************************************************ * Private Data ************************************************************************************/ -/* Device Structures, Instantiation */ - -const struct i2c_ops_s stm32f7_i2c_ops = -{ - .transfer = stm32f7_i2c_transfer -#ifdef CONFIG_I2C_RESET - , .reset = stm32f7_i2c_reset -#endif -}; - #ifdef CONFIG_STM32F7_I2C1 -static const struct stm32f7_i2c_config_s stm32f7_i2c1_config = +static const struct stm32_i2c_config_s stm32_i2c1_config = { .base = STM32_I2C1_BASE, .clk_bit = RCC_APB1ENR_I2C1EN, @@ -356,16 +498,15 @@ static const struct stm32f7_i2c_config_s stm32f7_i2c1_config = .scl_pin = GPIO_I2C1_SCL, .sda_pin = GPIO_I2C1_SDA, #ifndef CONFIG_I2C_POLLED - .isr = stm32f7_i2c1_isr, + .isr = stm32_i2c1_isr, .ev_irq = STM32_IRQ_I2C1EV, .er_irq = STM32_IRQ_I2C1ER #endif }; -struct stm32f7_i2c_priv_s stm32f7_i2c1_priv = +struct stm32_i2c_priv_s stm32_i2c1_priv = { - .ops = &stm32f7_i2c_ops, - .config = &stm32f7_i2c1_config, + .config = &stm32_i2c1_config, .refs = 0, .intstate = INTSTATE_IDLE, .msgc = 0, @@ -378,7 +519,7 @@ struct stm32f7_i2c_priv_s stm32f7_i2c1_priv = #endif #ifdef CONFIG_STM32F7_I2C2 -static const struct stm32f7_i2c_config_s stm32f7_i2c2_config = +static const struct stm32_i2c_config_s stm32_i2c2_config = { .base = STM32_I2C2_BASE, .clk_bit = RCC_APB1ENR_I2C2EN, @@ -386,16 +527,15 @@ static const struct stm32f7_i2c_config_s stm32f7_i2c2_config = .scl_pin = GPIO_I2C2_SCL, .sda_pin = GPIO_I2C2_SDA, #ifndef CONFIG_I2C_POLLED - .isr = stm32f7_i2c2_isr, + .isr = stm32_i2c2_isr, .ev_irq = STM32_IRQ_I2C2EV, .er_irq = STM32_IRQ_I2C2ER #endif }; -struct stm32f7_i2c_priv_s stm32f7_i2c2_priv = +struct stm32_i2c_priv_s stm32_i2c2_priv = { - .ops = &stm32f7_i2c_ops, - .config = &stm32f7_i2c2_config, + .config = &stm32_i2c2_config, .refs = 0, .intstate = INTSTATE_IDLE, .msgc = 0, @@ -408,7 +548,7 @@ struct stm32f7_i2c_priv_s stm32f7_i2c2_priv = #endif #ifdef CONFIG_STM32F7_I2C3 -static const struct stm32f7_i2c_config_s stm32f7_i2c3_config = +static const struct stm32_i2c_config_s stm32_i2c3_config = { .base = STM32_I2C3_BASE, .clk_bit = RCC_APB1ENR_I2C3EN, @@ -416,16 +556,15 @@ static const struct stm32f7_i2c_config_s stm32f7_i2c3_config = .scl_pin = GPIO_I2C3_SCL, .sda_pin = GPIO_I2C3_SDA, #ifndef CONFIG_I2C_POLLED - .isr = stm32f7_i2c3_isr, + .isr = stm32_i2c3_isr, .ev_irq = STM32_IRQ_I2C3EV, .er_irq = STM32_IRQ_I2C3ER #endif }; -struct stm32f7_i2c_priv_s stm32f7_i2c3_priv = +struct stm32_i2c_priv_s stm32_i2c3_priv = { - .ops = &stm32f7_i2c_ops, - .config = &stm32f7_i2c3_config, + .config = &stm32_i2c3_config, .refs = 0, .intstate = INTSTATE_IDLE, .msgc = 0, @@ -437,90 +576,86 @@ struct stm32f7_i2c_priv_s stm32f7_i2c3_priv = }; #endif +/* Device Structures, Instantiation */ + +struct i2c_ops_s stm32_i2c_ops = +{ + .transfer = stm32_i2c_transfer +#ifdef CONFIG_I2C_RESET + , .reset = stm32f7_i2c_reset +#endif +}; + /************************************************************************************ * Private Functions ************************************************************************************/ /************************************************************************************ - * Name: stm32f7_i2c_getreg + * Name: stm32_i2c_getreg * * Description: * Get a 16-bit register value by offset * ************************************************************************************/ -static inline uint16_t stm32f7_i2c_getreg(FAR struct stm32f7_i2c_priv_s *priv, +static inline uint16_t stm32_i2c_getreg(FAR struct stm32_i2c_priv_s *priv, uint8_t offset) { return getreg16(priv->config->base + offset); } /************************************************************************************ - * Name: stm32f7_i2c_getreg32 + * Name: stm32_i2c_getreg32 * * Description: * Get a 32-bit register value by offset * ************************************************************************************/ -static inline uint32_t stm32f7_i2c_getreg32(FAR struct stm32f7_i2c_priv_s *priv, +static inline uint32_t stm32_i2c_getreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset) { return getreg32(priv->config->base + offset); } /************************************************************************************ - * Name: stm32f7_i2c_putreg + * Name: stm32_i2c_putreg * * Description: * Put a 16-bit register value by offset * ************************************************************************************/ -static inline void stm32f7_i2c_putreg(FAR struct stm32f7_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) { putreg16(value, priv->config->base + offset); } /************************************************************************************ - * Name: stm32f7_i2c_putreg32 + * Name: stm32_i2c_putreg32 * * Description: * Put a 32-bit register value by offset * ************************************************************************************/ -static inline void stm32f7_i2c_putreg32(FAR struct stm32f7_i2c_priv_s *priv, +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: stm32f7_i2c_modifyreg - * - * Description: - * Modify a 16-bit register value by offset - * - ************************************************************************************/ - -static inline void stm32f7_i2c_modifyreg(FAR struct stm32f7_i2c_priv_s *priv, - uint8_t offset, uint16_t clearbits, - uint16_t setbits) -{ - modifyreg16(priv->config->base + offset, clearbits, setbits); -} /************************************************************************************ - * Name: stm32f7_i2c_modifyreg32 + * Name: stm32_i2c_modifyreg32 * * Description: * Modify a 32-bit register value by offset * ************************************************************************************/ -static inline void stm32f7_i2c_modifyreg32(FAR struct stm32f7_i2c_priv_s *priv, +static inline void stm32_i2c_modifyreg32(FAR struct stm32_i2c_priv_s *priv, uint8_t offset, uint32_t clearbits, uint32_t setbits) { @@ -528,23 +663,23 @@ static inline void stm32f7_i2c_modifyreg32(FAR struct stm32f7_i2c_priv_s *priv, } /************************************************************************************ - * Name: stm32f7_i2c_sem_wait + * Name: stm32_i2c_sem_wait * * Description: * Take the exclusive access, waiting as necessary * ************************************************************************************/ -static inline void stm32f7_i2c_sem_wait(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sem_wait(FAR struct i2c_master_s *dev) { - while (sem_wait(&priv->sem_excl) != 0) + while (sem_wait(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl) != 0) { ASSERT(errno == EINTR); } } /************************************************************************************ - * Name: stm32f7_i2c_tousecs + * Name: stm32_i2c_tousecs * * Description: * Return a micro-second delay based on the number of bytes left to be processed. @@ -552,7 +687,7 @@ static inline void stm32f7_i2c_sem_wait(FAR struct stm32f7_i2c_priv_s *priv) ************************************************************************************/ #ifdef CONFIG_STM32F7_I2C_DYNTIMEO -static useconds_t stm32f7_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) +static useconds_t stm32_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) { size_t bytecount = 0; int i; @@ -573,7 +708,7 @@ static useconds_t stm32f7_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) #endif /************************************************************************************ - * Name: stm32f7_i2c_enableinterrupts + * Name: stm32_i2c_enableinterrupts * * Description: * Enable I2C interrupts @@ -581,37 +716,26 @@ static useconds_t stm32f7_i2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) ************************************************************************************/ #ifndef CONFIG_I2C_POLLED -static inline void stm32f7_i2c_enableinterrupts(struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_enableinterrupts(struct stm32_i2c_priv_s *priv) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, I2C_CR1_TXRX); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, (I2C_CR1_TXRX | I2C_CR1_NACKIE)); } #endif -/************************************************************************************ - * Name: stm32f7_i2c_disableinterrupts - * - * Description: - * Enable I2C interrupts - * - ************************************************************************************/ - -#ifndef CONFIG_I2C_POLLED -static inline void stm32f7_i2c_disableinterrupts(struct stm32f7_i2c_priv_s *priv) -{ - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, I2C_CR1_TXRX, 0); -} -#endif /************************************************************************************ - * Name: stm32f7_i2c_sem_waitdone + * 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 stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) +static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv) { struct timespec abstime; irqstate_t flags; @@ -621,13 +745,14 @@ static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) /* Enable I2C interrupts */ - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, + /* 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, STM32F7_I2C_CR1_OFFSET, 0, (I2C_CR1_ALLINTS & ~I2C_CR1_TXRX)); - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * sem_timedwait() sleeps. - */ + /* Signal the interrupt handler that we are waiting */ priv->intstate = INTSTATE_WAITING; do @@ -645,7 +770,7 @@ static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) /* Add a value proportional to the number of bytes in the transfer */ #ifdef CONFIG_STM32F7_I2C_DYNTIMEO - abstime.tv_nsec += 1000 * stm32f7_i2c_tousecs(priv->msgc, priv->msgv); + abstime.tv_nsec += 1000 * stm32_i2c_tousecs(priv->msgc, priv->msgv); if (abstime.tv_nsec >= 1000 * 1000 * 1000) { abstime.tv_sec++; @@ -684,23 +809,23 @@ static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) /* Disable I2C interrupts */ - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, I2C_CR1_ALLINTS, 0); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, I2C_CR1_ALLINTS, 0); leave_critical_section(flags); return ret; } #else -static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) +static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv) { - systime_t timeout; - systime_t start; - systime_t elapsed; + uint32_t timeout; + uint32_t start; + uint32_t elapsed; int ret; /* Get the timeout value */ #ifdef CONFIG_STM32F7_I2C_DYNTIMEO - timeout = USEC2TICK(stm32f7_i2c_tousecs(priv->msgc, priv->msgv)); + timeout = USEC2TICK(stm32_i2c_tousecs(priv->msgc, priv->msgv)); #else timeout = CONFIG_STM32F7_I2CTIMEOTICKS; #endif @@ -719,7 +844,7 @@ static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) * reports that it is done. */ - stm32f7_i2c_isr(priv); + stm32_i2c_isr(priv); /* Calculate the elapsed time */ @@ -730,8 +855,8 @@ static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) while (priv->intstate != INTSTATE_DONE && elapsed < timeout); - i2cvdbg("intstate: %d elapsed: %ld threshold: %ld status: %08x\n", - priv->intstate, (long)elapsed, (long)timeout, priv->status); + i2cvdbg("intstate: %d elapsed: %d threshold: %d status: 0x%08x\n", + priv->intstate, elapsed, timeout, priv->status); /* Set the interrupt state back to IDLE */ @@ -742,99 +867,100 @@ static inline int stm32f7_i2c_sem_waitdone(FAR struct stm32f7_i2c_priv_s *priv) #endif /************************************************************************************ - * Name: stm32f7_i2c_set_7bit_address + * Name: stm32_i2c_set_7bit_address * * Description: * ************************************************************************************/ static inline void -stm32f7_i2c_set_7bit_address(FAR struct stm32f7_i2c_priv_s *priv) +stm32_i2c_set_7bit_address(FAR struct stm32_i2c_priv_s *priv) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_SADD7_MASK, + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_SADD7_MASK, ((priv->msgv->addr & 0x7F) << I2C_CR2_SADD7_SHIFT)); } /************************************************************************************ - * Name: stm32f7_i2c_set_bytes_to_transfer + * Name: stm32_i2c_set_bytes_to_transfer * * Description: * ************************************************************************************/ static inline void -stm32f7_i2c_set_bytes_to_transfer(FAR struct stm32f7_i2c_priv_s *priv, +stm32_i2c_set_bytes_to_transfer(FAR struct stm32_i2c_priv_s *priv, uint8_t n_bytes) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_NBYTES_MASK, + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_NBYTES_MASK, (n_bytes << I2C_CR2_NBYTES_SHIFT)); } /************************************************************************************ - * Name: stm32f7_i2c_set_write_transfer_dir + * Name: stm32_i2c_set_write_transfer_dir * * Description: * ************************************************************************************/ static inline void -stm32f7_i2c_set_write_transfer_dir(FAR struct stm32f7_i2c_priv_s *priv) +stm32_i2c_set_write_transfer_dir(FAR struct stm32_i2c_priv_s *priv) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_RD_WRN, 0); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_RD_WRN, 0); } /************************************************************************************ - * Name: stm32f7_i2c_set_read_transfer_dir + * Name: stm32_i2c_set_read_transfer_dir * * Description: * ************************************************************************************/ static inline void -stm32f7_i2c_set_read_transfer_dir(FAR struct stm32f7_i2c_priv_s *priv) +stm32_i2c_set_read_transfer_dir(FAR struct stm32_i2c_priv_s *priv) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_RD_WRN); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_RD_WRN); } /************************************************************************************ - * Name: stm32f7_i2c_enable_autoend + * Name: stm32_i2c_enable_reload * * Description: * ************************************************************************************/ static inline void -stm32f7_i2c_enable_autoend(FAR struct stm32f7_i2c_priv_s *priv) +stm32_i2c_enable_reload(FAR struct stm32_i2c_priv_s *priv) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_AUTOEND); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_RELOAD); } /************************************************************************************ - * Name: stm32f7_i2c_disable_autoend + * Name: stm32_i2c_disable_reload * * Description: * ************************************************************************************/ static inline void -stm32f7_i2c_disable_autoend(FAR struct stm32f7_i2c_priv_s *priv) +stm32_i2c_disable_reload(FAR struct stm32_i2c_priv_s *priv) { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_AUTOEND, 0); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, I2C_CR2_RELOAD, 0); } + /************************************************************************************ - * Name: stm32f7_i2c_sem_waitstop + * Name: stm32_i2c_sem_waitstop * * Description: * Wait for a STOP to complete * ************************************************************************************/ -static inline void stm32f7_i2c_sem_waitstop(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv) { - systime_t start; - systime_t elapsed; - systime_t timeout; + uint32_t start; + uint32_t elapsed; + uint32_t timeout; uint32_t cr; uint32_t sr; @@ -846,18 +972,14 @@ static inline void stm32f7_i2c_sem_waitstop(FAR struct stm32f7_i2c_priv_s *priv) timeout = CONFIG_STM32F7_I2CTIMEOTICKS; #endif - /* Wait as stop might still be in progress; but stop might also - * be set because of a timeout error: "The [STOP] bit is set and - * cleared by software, cleared by hardware when a Stop condition is - * detected, set by hardware when a timeout error is detected." - */ + /* Wait as stop might still be in progress */ start = clock_systimer(); do { /* Check for STOP condition */ - cr = stm32f7_i2c_getreg32(priv, STM32F7_I2C_CR2_OFFSET); + cr = stm32_i2c_getreg32(priv, STM32F7_I2C_CR2_OFFSET); if ((cr & I2C_CR2_STOP) == 0) { return; @@ -865,7 +987,7 @@ static inline void stm32f7_i2c_sem_waitstop(FAR struct stm32f7_i2c_priv_s *priv) /* Check for timeout error */ - sr = stm32f7_i2c_getreg(priv, STM32F7_I2C_ISR_OFFSET); + sr = stm32_i2c_getreg(priv, STM32F7_I2C_ISR_OFFSET); if ((sr & I2C_INT_TIMEOUT) != 0) { return; @@ -888,52 +1010,52 @@ static inline void stm32f7_i2c_sem_waitstop(FAR struct stm32f7_i2c_priv_s *priv) } /************************************************************************************ - * Name: stm32f7_i2c_sem_post + * Name: stm32_i2c_sem_post * * Description: * Release the mutual exclusion semaphore * ************************************************************************************/ -static inline void stm32f7_i2c_sem_post(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sem_post(FAR struct i2c_master_s *dev) { - sem_post(&priv->sem_excl); + sem_post(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); } /************************************************************************************ - * Name: stm32f7_i2c_sem_init + * Name: stm32_i2c_sem_init * * Description: * Initialize semaphores * ************************************************************************************/ -static inline void stm32f7_i2c_sem_init(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sem_init(FAR struct i2c_master_s *dev) { - sem_init(&priv->sem_excl, 0, 1); + sem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl, 0, 1); #ifndef CONFIG_I2C_POLLED - sem_init(&priv->sem_isr, 0, 0); + sem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, 0, 0); #endif } /************************************************************************************ - * Name: stm32f7_i2c_sem_destroy + * Name: stm32_i2c_sem_destroy * * Description: * Destroy semaphores. * ************************************************************************************/ -static inline void stm32f7_i2c_sem_destroy(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sem_destroy(FAR struct i2c_master_s *dev) { - sem_destroy(&priv->sem_excl); + sem_destroy(&((struct stm32_i2c_inst_s *)dev)->priv->sem_excl); #ifndef CONFIG_I2C_POLLED - sem_destroy(&priv->sem_isr); + sem_destroy(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr); #endif } /************************************************************************************ - * Name: stm32f7_i2c_trace* + * Name: stm32_i2c_trace* * * Description: * I2C trace instrumentation @@ -941,30 +1063,30 @@ static inline void stm32f7_i2c_sem_destroy(FAR struct stm32f7_i2c_priv_s *priv) ************************************************************************************/ #ifdef CONFIG_I2C_TRACE -static void stm32f7_i2c_traceclear(FAR struct stm32f7_i2c_priv_s *priv) +static void stm32_i2c_traceclear(FAR struct stm32_i2c_priv_s *priv) { - struct stm32f7_trace_s *trace = &priv->trace[priv->tndx]; + struct stm32_trace_s *trace = &priv->trace[priv->tndx]; - trace->status = 0; /* I2C 32-bit SR2|SR1 status */ + trace->status = 0; /* I2C 32-bit status */ trace->count = 0; /* Interrupt count when status change */ trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ trace->parm = 0; /* Parameter associated with the event */ trace->time = 0; /* Time of first status or event */ } -static void stm32f7_i2c_tracereset(FAR struct stm32f7_i2c_priv_s *priv) +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(); - stm32f7_i2c_traceclear(priv); + stm32_i2c_traceclear(priv); } -static void stm32f7_i2c_tracenew(FAR struct stm32f7_i2c_priv_s *priv, +static void stm32_i2c_tracenew(FAR struct stm32_i2c_priv_s *priv, uint32_t status) { - struct stm32f7_trace_s *trace = &priv->trace[priv->tndx]; + struct stm32_trace_s *trace = &priv->trace[priv->tndx]; /* Is the current entry uninitialized? Has the status changed? */ @@ -988,7 +1110,7 @@ static void stm32f7_i2c_tracenew(FAR struct stm32f7_i2c_priv_s *priv, /* Initialize the new trace entry */ - stm32f7_i2c_traceclear(priv); + stm32_i2c_traceclear(priv); trace->status = status; trace->count = 1; trace->time = clock_systimer(); @@ -1001,10 +1123,10 @@ static void stm32f7_i2c_tracenew(FAR struct stm32f7_i2c_priv_s *priv, } } -static void stm32f7_i2c_traceevent(FAR struct stm32f7_i2c_priv_s *priv, - enum stm32f7_trace_e event, uint32_t parm) +static void stm32_i2c_traceevent(FAR struct stm32_i2c_priv_s *priv, + enum stm32_trace_e event, uint32_t parm) { - struct stm32f7_trace_s *trace; + struct stm32_trace_s *trace; if (event != I2CEVENT_NONE) { @@ -1024,17 +1146,17 @@ static void stm32f7_i2c_traceevent(FAR struct stm32f7_i2c_priv_s *priv, } priv->tndx++; - stm32f7_i2c_traceclear(priv); + stm32_i2c_traceclear(priv); } } -static void stm32f7_i2c_tracedump(FAR struct stm32f7_i2c_priv_s *priv) +static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv) { - struct stm32f7_trace_s *trace; + struct stm32_trace_s *trace; int i; - syslog(LOG_DEBUG, "Elapsed time: %ld\n", - (long)(clock_systimer() - priv->start_time)); + syslog(LOG_DEBUG, "Elapsed time: %d\n", + clock_systimer() - priv->start_time); for (i = 0; i <= priv->tndx; i++) { @@ -1048,437 +1170,919 @@ static void stm32f7_i2c_tracedump(FAR struct stm32f7_i2c_priv_s *priv) #endif /* CONFIG_I2C_TRACE */ /************************************************************************************ - * Name: stm32f7_i2c_setclock + * Name: stm32_i2c_setclock * * Description: - * Set the I2C clock + * + * Sets the I2C bus clock frequency by configuring the I2C_TIMINGR register. + * + * This function supports bus clock frequencies of: + * + * 500Khz + * 400Khz + * 100Khz + * 10Khz + * + * 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 SYSCLK or the HSI. + * + * SYSCLK >------|\ I2CCLK + * | |---------> + * HSI >------|/ + * + * HSI is the default and is always 8Mhz. + * + * SYSCLK can, in turn, be derived from the HSI, PLL or HSE. + * + * HSI >------|\ + * | | SYSCLK + * PLL >------| |---------> + * | | + * HSE >------|/ + * + * The ability to select the I2C peripheral clock source is not yet supported + * so all of this really only works at 8Mhz. The values provided are copied + * directly from the 8Mhz example table in RM0316 (See References). + * + * TODO: + * + * - Add support for peripheral clock source selection (SYSCLK vs HSI). + * - Calculate values for a given SYSCLK frequency. + * - Add support for Fast Mode Plus (up to 1Mhz) + * + * References: + * + * RM0316: Section: 28.4.9: I2C_TIMINGR register configuration examples * ************************************************************************************/ -static void stm32f7_i2c_setclock(FAR struct stm32f7_i2c_priv_s *priv, uint32_t frequency) +static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency) { uint32_t pe; uint8_t presc; - uint8_t s_time; - uint8_t h_time; + uint8_t scl_delay; + uint8_t sda_delay; uint8_t scl_h_period; uint8_t scl_l_period; - /* Has the I2C bus frequency changed? */ + /* I2C peripheral must be disabled to update clocking configuration */ - if (frequency != priv->frequency) + pe = (stm32_i2c_getreg32(priv, STM32F7_I2C_CR1_OFFSET) & I2C_CR1_PE); + if (pe) { - /* Disable the selected I2C peripheral to configure TRISE */ + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, I2C_CR1_PE, 0); + } - pe = (stm32f7_i2c_getreg32(priv, STM32F7_I2C_CR1_OFFSET) & I2C_CR1_PE); - if (pe) - { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, I2C_CR1_PE, 0); - } + /* TODO: speed/timing calcs, at the moment 45Mhz = STM32_PCLK1_FREQUENCY, analog filter is on, + digital off from STM32F0-F3_AN4235_V1.0.1 */ - /* Update timing and control registers */ + if (frequency == 100000) + { + presc = 0x06; + scl_delay = 0x02; + sda_delay = 0x00; + scl_h_period = 0x1e; + scl_l_period = 0x2b; - /* TODO: speed/timing calcs, at the moment 45Mhz = STM32_PCLK1_FREQUENCY, analog filter is on, - digital off from STM32F0-F3_AN4235_V1.0.1 */ + } + else if (frequency == 400000) + { + presc = 0x00; + scl_delay = 0x0A; + sda_delay = 0x00; + scl_h_period = 0x1b; + scl_l_period = 0x5b; + } + else + { + presc = 0x00; + scl_delay = 0x08; + sda_delay = 0x00; + scl_h_period = 0x09; + scl_l_period = 0x1c; + } - if (frequency == 100000) - { - presc = 0x06; - s_time = 0x02; - h_time = 0x00; - scl_h_period = 0x1e; - scl_l_period = 0x2b; - - } - else if (frequency == 400000) - { - presc = 0x00; - s_time = 0x0A; - h_time = 0x00; - scl_h_period = 0x1b; - scl_l_period = 0x5b; - } - else - { - presc = 0x00; - s_time = 0x08; - h_time = 0x00; - scl_h_period = 0x09; - scl_l_period = 0x1c; - } + 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); - uint32_t timingr = - (presc << I2C_TIMINGR_PRESC_SHIFT) | - (s_time << I2C_TIMINGR_SCLDEL_SHIFT) | - (h_time << I2C_TIMINGR_SDADEL_SHIFT) | - (scl_h_period << I2C_TIMINGR_SCLH_SHIFT) | - (scl_l_period << I2C_TIMINGR_SCLL_SHIFT); + stm32_i2c_putreg32(priv, STM32F7_I2C_TIMINGR_OFFSET, timingr); - stm32f7_i2c_putreg32(priv, STM32F7_I2C_TIMINGR_OFFSET, timingr); - - /* Re-enable the peripheral (or not) */ - - if (pe) - { - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, I2C_CR1_PE); - } - - /* Save the new I2C frequency */ - - priv->frequency = frequency; + if (pe) + { + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, I2C_CR1_PE); } } /************************************************************************************ - * Name: stm32f7_i2c_sendstart + * Name: stm32_i2c_sendstart * * Description: - * Send the START conditions/force Master mode + * Send the START condition / force Master mode + * + * A START condition in I2C consists of a single byte that contains both the + * 7 bit slave address and a read/write bit (0 = WRITE, 1 = READ). If the + * address is recognized by one of the slave devices that slave device will + * ACK the byte so that data transfers can begin. + * + * A RESTART (or repeated START per the I2CSPEC) is simply a START condition + * issued in the middle of a transfer (i.e. after the initial START and before + * a STOP). A RESTART sends a new address byte and R/W bit to the bus. A + * RESTART is optional in most cases but mandatory in the event the transfer + * direction is changed. + * + * Most of the time reading data from an I2C slave requires a WRITE of the + * subaddress followed by a READ (and hence a RESTART in between). Writing + * to an I2C slave typically requires only WRITE operations and hence no + * RESTARTs. + * + * This function is therefore called both at the beginning of a transfer + * (START) and at appropriate times during a transfer (RESTART). * ************************************************************************************/ -static inline void stm32f7_i2c_sendstart(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv) { - /* Get run-time data */ + + /* Flag the first byte as an address byte */ priv->astart = true; + + /* 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_NORESTART: 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; - /* Disable ACK on receive by default and generate START */ - - stm32f7_i2c_set_bytes_to_transfer(priv, priv->dcnt); - stm32f7_i2c_set_7bit_address(priv); - if (priv->flags & I2C_M_READ) - { - stm32f7_i2c_set_read_transfer_dir(priv); - } - else - { - stm32f7_i2c_set_write_transfer_dir(priv); - } - - if (priv->msgc == 1) - { - /* stm32f7_i2c_enable_autoend(priv); */ - } - else - { - /* stm32f7_i2c_disable_autoend(priv); */ - } - - /* TODO check NACK */ - /* TODO handle NACKR? */ - - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_START); -} - -/************************************************************************************ - * Name: stm32f7_i2c_clrstart - * - * Description: - * Clear the STOP, START or PEC condition on certain error recovery steps. - * - ************************************************************************************/ - -static inline void stm32f7_i2c_clrstart(FAR struct stm32f7_i2c_priv_s *priv) -{ - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." + /* Enabling RELOAD allows the transfer of: * - * "The [STOP] bit is set and cleared by software, cleared by hardware - * when a Stop condition is detected, set by hardware when a timeout - * error is detected. + * - individual messages with a payload exceeding 255 bytes + * - multiple messages back to back without a RESTART in between * - * "This [START] bit is set and cleared by software and cleared by hardware - * when start is sent or PE=0." The bit must be cleared by software if the - * START is never sent. - * - * "This [PEC] bit is set and cleared by software, and cleared by hardware - * when PEC is transferred or by a START or Stop condition or when PE=0." + * so we enable it if either of those conditions exist and disable + * it otherwise. */ - /* TODO check PEC (32 bit separate reg) */ + if ((priv->flags & I2C_M_NORESTART) || priv->dcnt > 255) { + i2cdbg("RELOAD enabled: dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + stm32_i2c_enable_reload(priv); + } else { + i2cdbg("RELOAD disable: dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + stm32_i2c_disable_reload(priv); + } - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, - I2C_CR2_START | I2C_CR2_STOP, 0); + /* 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. + */ + + i2cdbg("Sending START: dcnt=%i msgc=%i flags=0x%04x\n", + priv->dcnt, priv->msgc, priv->flags); + + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_START); } /************************************************************************************ - * Name: stm32f7_i2c_sendstop + * 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 stm32f7_i2c_sendstop(FAR struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv) { - /* TODO check NACK */ + i2cdbg("Sending STOP\n"); + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_STOP, 0); - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_STOP); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR2_OFFSET, 0, I2C_CR2_STOP); } /************************************************************************************ - * Name: stm32f7_i2c_getstatus + * Name: stm32_i2c_getstatus * * Description: * Get 32-bit status (SR1 and SR2 combined) * ************************************************************************************/ -static inline uint32_t stm32f7_i2c_getstatus(FAR struct stm32f7_i2c_priv_s *priv) +static inline uint32_t stm32_i2c_getstatus(FAR struct stm32_i2c_priv_s *priv) { return getreg32(priv->config->base + STM32F7_I2C_ISR_OFFSET); } /************************************************************************************ - * Name: stm32f7_i2c_isr - * - * Description: - * Common logic when a message is started. Just adds the even to the trace buffer - * if enabled and adjusts the message pointer and count. - * - ************************************************************************************/ - -static inline void stm32f7_i2c_isr_startmessage(struct stm32f7_i2c_priv_s *priv) -{ - stm32f7_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgc); - - /* Increment to next pointer and decrement message count */ - - priv->msgv++; - priv->msgc--; -} - -/************************************************************************************ - * Name: stm32f7_i2c_clearinterrupts + * Name: stm32_i2c_clearinterrupts * * Description: * Clear all interrupts * ************************************************************************************/ -static inline void stm32f7_i2c_clearinterrupts(struct stm32f7_i2c_priv_s *priv) +static inline void stm32_i2c_clearinterrupts(struct stm32_i2c_priv_s *priv) { -#warning "check this clears interrupts?" - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); + stm32_i2c_modifyreg32(priv, STM32F7_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); } /************************************************************************************ - * Name: stm32f7_i2c_isr + * Name: stm32_i2c_isr * * Description: - * Common Interrupt Service Routine + * Common interrupt service routine (ISR) that handles I2C protocol logic. + * This is instantiated for each configured I2C interface (I2C1, I2C2, I2C3). + * + * This ISR is activated and deactivated by: + * + * stm32_i2c_process + * and + * stm32_i2c_waitdone + * + * Input Parameters: + * priv - The private struct of the I2C driver. * ************************************************************************************/ -static int stm32f7_i2c_isr(struct stm32f7_i2c_priv_s *priv) +static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) { - uint32_t status = stm32f7_i2c_getstatus(priv); - /* Check for new trace setup */ + uint32_t status; - stm32f7_i2c_tracenew(priv, status); + /* Get state of the I2C controller */ -#warning "TODO: check clear interrupts after all actions" + status = stm32_i2c_getreg32(priv, STM32F7_I2C_ISR_OFFSET); - if (STATUS_NACK(status)) + i2cvdbg("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) { - /* wait, reset this? */ - } - else if (priv->astart) - { - stm32f7_i2c_isr_startmessage(priv); - priv->astart = false; - } - /* Was address sent, continue with either sending or reading data */ - - if ((priv->flags & I2C_M_READ) == 0 && STATUS_ADDR_TX(status)) - { -#warning "TODO: ADDRCF clear address interrupt flag" - if (priv->dcnt > 0) + if (priv->astart == true) { - /* Send a byte */ - stm32f7_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); - stm32f7_i2c_putreg(priv, STM32F7_I2C_TXDR_OFFSET, *priv->ptr++); - priv->dcnt--; + /* NACK received on first (address) byte: address is invalid */ + + i2cvdbg("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 */ + + i2cvdbg("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); + i2cvdbg("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) + { + i2cvdbg("TXIS: Address Valid\n"); + stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_ACKED, priv->msgv->addr); + priv->astart = false; } - } - else if ((priv->flags & I2C_M_READ) != 0 && STATUS_ADDR(status)) - { - /* Enable RxNE and TxE buffers in order to receive one or multiple bytes */ - -#warning "TODO: ADDRCF clear address interrupt flag" - -#ifndef CONFIG_I2C_POLLED - stm32f7_i2c_traceevent(priv, I2CEVENT_ITBUFEN, 0); - stm32f7_i2c_enableinterrupts(priv); -#endif - } - - /* More bytes to read */ - else if (STATUS_RXNE(status)) - { - /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ + /* If one or more bytes in the current message are ready to transmit */ if (priv->dcnt > 0) { - stm32f7_i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); + + /* Prepare to transmit the current byte */ + + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_TO_DR, priv->dcnt); + i2cvdbg("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, STM32F7_I2C_TXDR_OFFSET, *priv->ptr); + + /* Advance to next byte */ + + priv->ptr++; + + } + else + { + /* Unsupported state */ + + i2cdbg("TXIS: UNSUPPORTED STATE DETECTED, dcnt=%i, status 0x%08x\n", + priv->dcnt, status); + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_ERROR, 0); + + } + + i2cvdbg("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); + i2cvdbg("RXNE: ENTER dcnt = %i msgc = %i status 0x%08x\n", + priv->dcnt, priv->msgc, status); + + /* If more bytes in the current message */ + + if (priv->dcnt > 0) + { + + stm32_i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); /* No interrupts or context switches may occur in the following - * sequence. Otherwise, additional bytes may be sent by the - * device. + * sequence. Otherwise, additional bytes may be received. */ #ifdef CONFIG_I2C_POLLED - irqstate_t flags = enter_critical_section(); + irqstate_t state = enter_critical_section(); #endif /* Receive a byte */ - *priv->ptr++ = stm32f7_i2c_getreg(priv, STM32F7_I2C_RXDR_OFFSET); + *priv->ptr = stm32_i2c_getreg(priv, STM32F7_I2C_RXDR_OFFSET); - /* Disable acknowledge when last byte is to be received */ + i2cvdbg("RXNE: Read Data 0x%02x\n", *priv->ptr); + + /* Advance buffer to the next byte in the message */ + + priv->ptr++; + + /* Signal byte received */ priv->dcnt--; - if (priv->dcnt == 1) - { - /* autoend? */ - } #ifdef CONFIG_I2C_POLLED - leave_critical_section(flags); + leave_critical_section(state); #endif + } + else + { + + /* Unsupported state */ + + stm32_i2c_traceevent(priv, I2CEVENT_READ_ERROR, 0); + status = stm32_i2c_getreg(priv, STM32F7_I2C_ISR_OFFSET); + i2cdbg("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; + + } + + i2cvdbg("RXNE: EXIT dcnt = %i msgc = %i status 0x%08x\n", + priv->dcnt, priv->msgc, status); } - /* Do we have more bytes to send, enable/disable buffer interrupts - * (these ISRs could be replaced by DMAs) + /* Transfer Complete (TC) State Handler + * + * This branch is only triggered when the TC interrupt occurs. This + * interrupt will only fire when: + * + * I2C_CR1->TCIE = 1 (Transfer Complete Interrupts Enabled) + * I2C_CR2->RELOAD = 0 (Reload Mode Disabled) + * I2C_CR2->AUTOEND = 0 (Autoend Mode Disabled, i.e. Software End Mode) + * + * This event indicates that the number of bytes initially defined + * in NBYTES, meaning, the number of bytes in the current message (priv->dcnt) + * has been successfully transmitted or received. + * + * When the TC interrupt occurs we have two choices to clear it and move + * on, regardless of the transfer direction: + * + * - if more messages follow, perform a repeated START if required + * and then fall through to transmit or receive the next message. + * + * - if no messages follow, perform a STOP and set flags needed to + * exit the ISR. + * + * The fact that the hardware must either RESTART or STOP when a TC + * event occurs explains why, when messages must be sent back to back + * (i.e. without a restart by specifying the I2C_M_NORESTART flag), + * RELOAD mode must be enabled and TCR event(s) must be generated + * instead. See the TCR handler for more. */ -#ifndef CONFIG_I2C_POLLED - if (priv->dcnt > 0) + else if ((status & I2C_ISR_TC) != 0) { - stm32f7_i2c_traceevent(priv, I2CEVENT_REITBUFEN, 0); - stm32f7_i2c_enableinterrupts(priv); - } - else if ((priv->dcnt == 0) && (priv->msgc == 0)) - { - stm32f7_i2c_traceevent(priv, I2CEVENT_DISITBUFEN, 0); - stm32f7_i2c_disableinterrupts(priv); - } -#endif + i2cvdbg("TC: ENTER dcnt = %i msgc = %i status 0x%08x\n", + priv->dcnt, priv->msgc, status); - /* Was last byte received or sent? Hmmm... the F2 and F4 seems to differ from - * the F1 in that BTF is not set after data is received (only RXNE). + /* Prior message has been sent successfully */ + + priv->msgc--; + + /* if additional messages remain to be transmitted / received */ + + if (priv->msgc > 0) { + + i2cvdbg("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. + */ + + i2cvdbg("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; + + } + + i2cvdbg("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_NORESTART 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. */ - if (priv->dcnt <= 0 && STATUS_TC(status)) + else if ((status & I2C_ISR_TCR) != 0) { - /* ??? */ + i2cvdbg("TCR: ENTER dcnt = %i msgc = %i status 0x%08x\n", + priv->dcnt, priv->msgc, status); - /* Do we need to terminate or restart after this byte? - * If there are more messages to send, then we may: - * - * - continue with repeated start - * - or just continue sending writeable part - * - or we close down by sending the stop bit - */ + /* If no more bytes in the current message to transfer */ - if (priv->msgc > 0) - { - if (priv->msgv->flags & I2C_M_NORESTART) - { - stm32f7_i2c_traceevent(priv, I2CEVENT_BTFNOSTART, priv->msgc); - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; - priv->msgv++; - priv->msgc--; + if (priv->dcnt == 0) { - /* Restart this ISR! */ + /* Prior message has been sent successfully */ -#ifndef CONFIG_I2C_POLLED - stm32f7_i2c_enableinterrupts(priv); -#endif - } - else - { - stm32f7_i2c_traceevent(priv, I2CEVENT_BTFRESTART, priv->msgc); - /* ??? */ - stm32f7_i2c_sendstart(priv); - } - } - else if (priv->msgv) - { - stm32f7_i2c_traceevent(priv, I2CEVENT_BTFSTOP, 0); + priv->msgc--; - stm32f7_i2c_sendstop(priv); + /* Advance to the next message in the list */ - /* Is there a thread waiting for this event (there should be) */ + priv->msgv++; -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) - { - /* Yes.. inform the thread that the transfer is complete - * and wake it up. - */ + /* Update current message data */ - sem_post(&priv->sem_isr); - priv->intstate = INTSTATE_DONE; - } + 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) { + i2cvdbg("TCR: DISABLE RELOAD: dcnt = %i msgc = %i\n", + priv->dcnt, priv->msgc); + + stm32_i2c_disable_reload(priv); + } + + /* Update NBYTES with length of current message */ + + i2cvdbg("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) { + + i2cvdbg("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. + */ + + i2cvdbg("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); + + } + + i2cvdbg("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, STM32F7_I2C_ISR_OFFSET); + i2cdbg("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 - priv->intstate = INTSTATE_DONE; + /* Read rest of the state */ + + status = stm32_i2c_getreg(priv, STM32F7_I2C_ISR_OFFSET); + + i2cdbg("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 - - /* Mark that we have stopped with this transaction */ - - priv->msgv = NULL; - } } - /* Check for errors, in which case, stop the transfer and return - * Note that in master reception mode AF becomes set on last byte - * since ACK is not returned. We should ignore this error. + /* --------------------- 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 ((status & I2C_ISR_ERRORMASK) != 0) + if (priv->dcnt == -1 && priv->msgc == 0) { - stm32f7_i2c_traceevent(priv, I2CEVENT_ERROR, 0); + i2cvdbg("MSG: Shutting down I2C ISR\n"); - /* Clear interrupt flags */ + stm32_i2c_traceevent(priv, I2CEVENT_ISR_SHUTDOWN, 0); - stm32f7_i2c_clearinterrupts(priv); + /* clear pointer to message content to reflect we are done + * with the current transaction */ - /* Is there a thread waiting for this event (there should be) */ + priv->msgv = NULL; + +#ifdef CONFIG_I2C_POLLED + priv->intstate = INTSTATE_DONE; +#else + + status = stm32_i2c_getreg32(priv, STM32F7_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, STM32F7_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK); + + /* If a thread is waiting then inform it transfer is complete */ -#ifndef CONFIG_I2C_POLLED if (priv->intstate == INTSTATE_WAITING) { - /* Yes.. inform the thread that the transfer is complete - * and wake it up. - */ - sem_post(&priv->sem_isr); priv->intstate = INTSTATE_DONE; } -#else - priv->intstate = INTSTATE_DONE; #endif + } - priv->status = status; + status = stm32_i2c_getreg32(priv, STM32F7_I2C_ISR_OFFSET); + i2cvdbg("EXIT: status = 0x%08x\n", status); + return OK; } /************************************************************************************ - * Name: stm32f7_i2c1_isr + * Name: stm32_i2c1_isr * * Description: * I2C1 interrupt service routine @@ -1487,14 +2091,14 @@ static int stm32f7_i2c_isr(struct stm32f7_i2c_priv_s *priv) #ifndef CONFIG_I2C_POLLED #ifdef CONFIG_STM32F7_I2C1 -static int stm32f7_i2c1_isr(int irq, void *context) +static int stm32_i2c1_isr(int irq, void *context) { - return stm32f7_i2c_isr(&stm32f7_i2c1_priv); + return stm32_i2c_isr(&stm32_i2c1_priv); } #endif /************************************************************************************ - * Name: stm32f7_i2c2_isr + * Name: stm32_i2c2_isr * * Description: * I2C2 interrupt service routine @@ -1502,14 +2106,14 @@ static int stm32f7_i2c1_isr(int irq, void *context) ************************************************************************************/ #ifdef CONFIG_STM32F7_I2C2 -static int stm32f7_i2c2_isr(int irq, void *context) +static int stm32_i2c2_isr(int irq, void *context) { - return stm32f7_i2c_isr(&stm32f7_i2c2_priv); + return stm32_i2c_isr(&stm32_i2c2_priv); } #endif /************************************************************************************ - * Name: stm32f7_i2c3_isr + * Name: stm32_i2c3_isr * * Description: * I2C2 interrupt service routine @@ -1517,9 +2121,9 @@ static int stm32f7_i2c2_isr(int irq, void *context) ************************************************************************************/ #ifdef CONFIG_STM32F7_I2C3 -static int stm32f7_i2c3_isr(int irq, void *context) +static int stm32_i2c3_isr(int irq, void *context) { - return stm32f7_i2c_isr(&stm32f7_i2c3_priv); + return stm32_i2c_isr(&stm32_i2c3_priv); } #endif #endif @@ -1529,14 +2133,14 @@ static int stm32f7_i2c3_isr(int irq, void *context) ************************************************************************************/ /************************************************************************************ - * Name: stm32f7_i2c_init + * Name: stm32_i2c_init * * Description: * Setup the I2C hardware, ready for operation with defaults * ************************************************************************************/ -static int stm32f7_i2c_init(FAR struct stm32f7_i2c_priv_s *priv) +static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv) { /* Power-up and configure GPIOs */ @@ -1559,56 +2163,53 @@ static int stm32f7_i2c_init(FAR struct stm32f7_i2c_priv_s *priv) return ERROR; } - /* Attach ISRs */ - #ifndef CONFIG_I2C_POLLED + + /* Attach error and event interrupts to the ISRs */ + irq_attach(priv->config->ev_irq, priv->config->isr); irq_attach(priv->config->er_irq, priv->config->isr); up_enable_irq(priv->config->ev_irq); up_enable_irq(priv->config->er_irq); #endif - /* Set peripheral frequency, where it must be at least 2 MHz for 100 kHz - * or 4 MHz for 400 kHz. This also disables all I2C interrupts. + /* TODO: + * - Provide means to set peripheral clock source via RCC_CFGR3_I2CxSW + * - Set to HSI by default, make Kconfig option */ - /* Force a frequency update */ + stm32_i2c_setclock(priv, 100000); - priv->frequency = 0; + /* Enable I2C peripheral */ - /* TODO: f303 i2c clock source RCC_CFGR3 */ - /* RCC_CFGR3_I2C1SW (default is HSI clock) */ + stm32_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, I2C_CR1_PE); - stm32f7_i2c_setclock(priv, 100000); - - /* Enable I2C */ - - stm32f7_i2c_modifyreg32(priv, STM32F7_I2C_CR1_OFFSET, 0, I2C_CR1_PE); return OK; } /************************************************************************************ - * Name: stm32f7_i2c_deinit + * Name: stm32_i2c_deinit * * Description: * Shutdown the I2C hardware * ************************************************************************************/ -static int stm32f7_i2c_deinit(FAR struct stm32f7_i2c_priv_s *priv) +static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv) { /* Disable I2C */ - stm32f7_i2c_putreg32(priv, STM32F7_I2C_CR1_OFFSET, 0); + stm32_i2c_putreg32(priv, STM32F7_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 */ -#ifndef CONFIG_I2C_POLLED up_disable_irq(priv->config->ev_irq); up_disable_irq(priv->config->er_irq); irq_detach(priv->config->ev_irq); @@ -1618,50 +2219,41 @@ static int stm32f7_i2c_deinit(FAR struct stm32f7_i2c_priv_s *priv) /* Disable clocking */ modifyreg32(STM32_RCC_APB1ENR, priv->config->clk_bit, 0); + return OK; } -/************************************************************************************ - * Device Driver Operations - ************************************************************************************/ /************************************************************************************ - * Name: stm32f7_i2c_transfer + * Name: stm32_i2c_process * * Description: - * Generic I2C transfer function + * 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 stm32f7_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, - int count) +static int stm32_i2c_process(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) { - FAR struct stm32f7_i2c_priv_s *priv = (struct stm32f7_i2c_priv_s *)dev; - uint32_t status = 0; - int ret = OK; + 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 = 0; + uint32_t cr2 = 0; + int errval = 0; + int waitrc = 0; - DEBUGASSERT(dev != NULL && msgs != NULL && count > 0); + ASSERT(count); - /* Ensure that address or flags don't change meanwhile */ + /* Wait for any STOP in progress */ - stm32f7_i2c_sem_wait(priv); - - /* Wait for any STOP in progress. */ - - stm32f7_i2c_sem_waitstop(priv); + stm32_i2c_sem_waitstop(priv); /* Clear any pending error interrupts */ - stm32f7_i2c_clearinterrupts(priv); - - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." However, if the bits are - * not cleared by hardware, then we will have to do that from hardware. - */ - - stm32f7_i2c_clrstart(priv); + stm32_i2c_clearinterrupts(priv); /* Old transfers are done */ @@ -1670,184 +2262,354 @@ static int stm32f7_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg /* Reset I2C trace logic */ - stm32f7_i2c_tracereset(priv); + stm32_i2c_tracereset(priv); - /* Set I2C clock frequency (on change it toggles I2C_CR1_PE !) - * REVISIT: Note that the frequency is set only on the first message. - * This could be extended to support different transfer frequencies for - * each message segment. - */ + /* Set I2C clock frequency (on change it toggles I2C_CR1_PE !) */ - stm32f7_i2c_setclock(priv, msgs->frequency); + stm32_i2c_setclock(priv, msgs->frequency); /* Trigger start condition, then the process moves into the ISR. I2C - * interrupts will be enabled within stm32f7_i2c_waitdone(). + * interrupts will be enabled within stm32_i2c_waitdone(). */ priv->status = 0; #ifndef CONFIG_I2C_POLLED - stm32f7_i2c_enableinterrupts(priv); -#endif - stm32f7_i2c_sendstart(priv); - - /* Wait for an ISR, if there was a timeout, fetch latest status to get - * the BUSY flag. + /* 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. */ - if (stm32f7_i2c_sem_waitdone(priv) < 0) + 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, STM32F7_I2C_CR1_OFFSET); + cr2 = stm32_i2c_getreg32(priv, STM32F7_I2C_CR2_OFFSET); + + /* 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) { - status = stm32f7_i2c_getstatus(priv); - ret = -ETIMEDOUT; + /* Connection timed out */ - i2cdbg("Timed out: CR1: %04x status: %08x\n", - stm32f7_i2c_getreg(priv, STM32F7_I2C_CR1_OFFSET), status); - - /* "Note: When the STOP, START or PEC bit is set, the software must - * not perform any write access to I2C_CR1 before this bit is - * cleared by hardware. Otherwise there is a risk of setting a - * second STOP, START or PEC request." - */ - - stm32f7_i2c_clrstart(priv); - - /* Clear busy flag in case of timeout */ - - status = priv->status & 0xffff; + errval = ETIMEDOUT; + i2cdbg("Waitdone timed out: CR1: 0x%08x CR2: 0x%08x status: 0x%08x\n", + cr1, cr2,status); } else { - /* clear SR2 (BUSY flag) as we've done successfully */ - - status = priv->status & 0xffff; + i2cdbg("Waitdone success: CR1: 0x%08x CR2: 0x%08x status: 0x%08x\n", + cr1, cr2,status ); } - status &= ~I2C_ISR_BUSY; -#if 0 - /* Refresh status */ - do - { - status = stm32f7_i2c_getstatus(priv); - } - while (STATUS_BUSY(status)); -#endif + i2cdbg("priv->status: 0x%08x\n", priv->status); /* Check for error status conditions */ - if ((status & I2C_ISR_ERRORMASK) != 0) + if ((status & (I2C_INT_BERR | + I2C_INT_ARLO | + I2C_INT_OVR | + I2C_INT_PECERR | + I2C_INT_TIMEOUT | + I2C_INT_NACK)) != 0) + { - /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ + /* one or more errors in the mask are present */ if (status & I2C_INT_BERR) { /* Bus Error */ - ret = -EIO; + i2cdbg("I2C: Bus Error\n"); + errval = EIO; } else if (status & I2C_INT_ARLO) { /* Arbitration Lost (master mode) */ - ret = -EAGAIN; + i2cdbg("I2C: Arbitration Lost\n"); + errval = EAGAIN; } - /* TODO Acknowledge failure */ - else if (status & I2C_INT_OVR) { /* Overrun/Underrun */ - ret = -EIO; + i2cdbg("I2C: Overrun/Underrun\n"); + errval = EIO; } else if (status & I2C_INT_PECERR) { - /* PEC Error in reception */ + /* PEC Error in reception (SMBus Only) */ - ret = -EPROTO; + i2cdbg("I2C: PEC Error\n"); + errval = EPROTO; } else if (status & I2C_INT_TIMEOUT) { - /* Timeout or Tlow Error */ + /* Timeout or Tlow Error (SMBus Only) */ - ret = -ETIME; + i2cdbg("I2C: Timeout / Tlow Error\n"); + errval = ETIME; } + else if (status & I2C_INT_NACK) + { + /* NACK Received, flag as "communication error on send" */ - /* This is not an error and should never happen since SMBus is not - * enabled - */ - - else /* if (status & I2C_INT_ALERT) */ + if (priv->astart == TRUE) + { + i2cdbg("I2C: Address NACK\n"); + errval = EADDRNOTAVAIL; + } + else + { + i2cdbg("I2C: Data NACK\n"); + errval = ECOMM; + } + } + else { - /* SMBus alert is an optional signal with an interrupt line for devices - * that want to trade their ability to master for a pin. - */ + /* Unrecognized error */ - ret = -EINTR; + i2cdbg("I2C: Unrecognized Error"); + errval = EINTR; } } - /* This is not an error, but should not happen. The BUSY signal can hang, - * however, if there are unhealthy devices on the bus that need to be reset. - * NOTE: We will only see this buy indication if stm32f7_i2c_sem_waitdone() + /* 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 is for some reason busy */ - ret = -EBUSY; + /* 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. + */ + i2cdbg("I2C: Bus busy"); + errval = EBUSY; } /* Dump the trace result */ - stm32f7_i2c_tracedump(priv); - stm32f7_i2c_sem_post(priv); - return ret; + stm32_i2c_tracedump(priv); + stm32_i2c_sem_post(dev); + + return -errval; } /************************************************************************************ - * Name: stm32f7_i2c_reset + * Name: stm32_i2c_transfer * * Description: - * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. + * Generic I2C transfer function * - * Input Parameters: - * dev - Device-specific state data + ************************************************************************************/ + +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); +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: up_i2cinitialize * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. + * Description: + * Initialize one I2C bus + * + ************************************************************************************/ + +FAR struct i2c_master_s *stm32f7_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 */ + int irqs; + +#if STM32_PCLK1_FREQUENCY < 4000000 +# warning STM32_I2C_INIT: Peripheral clock must be at least 4 MHz to support 400 kHz operation. +#endif + +#if STM32_PCLK1_FREQUENCY < 2000000 +# warning STM32_I2C_INIT: Peripheral clock must be at least 2 MHz to support 100 kHz operation. + return NULL; +#endif + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_STM32F7_I2C1 + case 1: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c1_priv; + break; +#endif +#ifdef CONFIG_STM32F7_I2C2 + case 2: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c2_priv; + break; +#endif +#ifdef CONFIG_STM32F7_I2C3 + case 3: + priv = (struct stm32_i2c_priv_s *)&stm32_i2c3_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; + inst->frequency = 0; + inst->address = 0; + inst->flags = 0; + + /* 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); + } + + leave_critical_section(irqs); + return (struct i2c_master_s *)inst; +} + +/************************************************************************************ + * Name: up_i2cuninitialize + * + * Description: + * Uninitialize an I2C bus + * + ************************************************************************************/ + +int stm32f7_i2cbus_uninitialize(FAR struct i2c_master_s * dev) +{ + int irqs; + + ASSERT(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); + + /* 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; +} + +/************************************************************************************ + * Name: up_i2creset + * + * Description: + * Reset an I2C bus * ************************************************************************************/ #ifdef CONFIG_I2C_RESET -static int stm32f7_i2c_reset(FAR struct i2c_master_s * dev) +int up_i2creset(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; ASSERT(dev); + /* Get I2C private structure */ + + priv = ((struct stm32_i2c_inst_s *)dev)->priv; + /* Our caller must own a ref */ ASSERT(priv->refs > 0); /* Lock out other clients */ - stm32f7_i2c_sem_wait(priv); - - /* Save the current frequency */ - - frequency = priv->frequency; + stm32_i2c_sem_wait(dev); /* De-init the port */ - stm32f7_i2c_deinit(priv); + stm32_i2c_deinit(priv); /* Use GPIO configuration to un-wedge the bus */ @@ -1856,12 +2618,12 @@ static int stm32f7_i2c_reset(FAR struct i2c_master_s * dev) /* Let SDA go high */ - stm32f7_gpiowrite(sda_gpio, 1); + stm32_gpiowrite(sda_gpio, 1); /* Clock the bus until any slaves currently driving it let it go. */ clock_count = 0; - while (!stm32f7_gpioread(sda_gpio)) + while (!stm32_gpioread(sda_gpio)) { /* Give up if we have tried too hard */ @@ -1876,7 +2638,7 @@ static int stm32f7_i2c_reset(FAR struct i2c_master_s * dev) */ stretch_count = 0; - while (!stm32f7_gpioread(scl_gpio)) + while (!stm32_gpioread(scl_gpio)) { /* Give up if we have tried too hard */ @@ -1890,12 +2652,12 @@ static int stm32f7_i2c_reset(FAR struct i2c_master_s * dev) /* Drive SCL low */ - stm32f7_gpiowrite(scl_gpio, 0); + stm32_gpiowrite(scl_gpio, 0); up_udelay(10); /* Drive SCL high again */ - stm32f7_gpiowrite(scl_gpio, 1); + stm32_gpiowrite(scl_gpio, 1); up_udelay(10); } @@ -1903,13 +2665,13 @@ static int stm32f7_i2c_reset(FAR struct i2c_master_s * dev) * state machines. */ - stm32f7_gpiowrite(sda_gpio, 0); + stm32_gpiowrite(sda_gpio, 0); up_udelay(10); - stm32f7_gpiowrite(scl_gpio, 0); + stm32_gpiowrite(scl_gpio, 0); up_udelay(10); - stm32f7_gpiowrite(scl_gpio, 1); + stm32_gpiowrite(scl_gpio, 1); up_udelay(10); - stm32f7_gpiowrite(sda_gpio, 1); + stm32_gpiowrite(sda_gpio, 1); up_udelay(10); /* Revert the GPIO configuration. */ @@ -1919,129 +2681,17 @@ static int stm32f7_i2c_reset(FAR struct i2c_master_s * dev) /* Re-init the port */ - stm32f7_i2c_init(priv); - - /* Restore the frequency */ - - stm32f7_i2c_setclock(priv, frequency); + stm32_i2c_init(priv); ret = OK; out: /* Release the port for re-use by other clients */ - stm32f7_i2c_sem_post(priv); + stm32_i2c_sem_post(dev); return ret; } #endif /* CONFIG_I2C_RESET */ -/************************************************************************************ - * Public Functions - ************************************************************************************/ - -/************************************************************************************ - * Name: stm32f7_i2cbus_initialize - * - * Description: - * Initialize one I2C bus - * - ************************************************************************************/ - -FAR struct i2c_master_s *stm32f7_i2cbus_initialize(int port) -{ - struct stm32f7_i2c_priv_s * priv = NULL; /* private data of device with multiple instances */ - irqstate_t flags; - -#if STM32_PCLK1_FREQUENCY < 4000000 -# warning STM32F7_I2C_INIT: Peripheral clock must be at least 4 MHz to support 400 kHz operation. -#endif - -#if STM32_PCLK1_FREQUENCY < 2000000 -# warning STM32F7_I2C_INIT: Peripheral clock must be at least 2 MHz to support 100 kHz operation. - return NULL; -#endif - - /* Get I2C private structure */ - - switch (port) - { -#ifdef CONFIG_STM32F7_I2C1 - case 1: - priv = (struct stm32f7_i2c_priv_s *)&stm32f7_i2c1_priv; - break; -#endif -#ifdef CONFIG_STM32F7_I2C2 - case 2: - priv = (struct stm32f7_i2c_priv_s *)&stm32f7_i2c2_priv; - break; -#endif -#ifdef CONFIG_STM32F7_I2C3 - case 3: - priv = (struct stm32f7_i2c_priv_s *)&stm32f7_i2c3_priv; - break; -#endif - default: - return NULL; - } - - /* Init private data for the first time, increment refs count, - * power-up hardware and configure GPIOs. - */ - - flags = enter_critical_section(); - - if ((volatile int)priv->refs++ == 0) - { - stm32f7_i2c_sem_init(priv); - stm32f7_i2c_init(priv); - } - - leave_critical_section(flags); - return (struct i2c_master_s *)priv; -} - -/************************************************************************************ - * Name: stm32f7_i2cbus_uninitialize - * - * Description: - * Uninitialize an I2C bus - * - ************************************************************************************/ - -int stm32f7_i2cbus_uninitialize(FAR struct i2c_master_s * dev) -{ - FAR struct stm32f7_i2c_priv_s *priv = (struct stm32f7_i2c_priv_s *)dev; - irqstate_t flags; - - ASSERT(dev); - - /* Decrement refs and check for underflow */ - - if (priv->refs == 0) - { - return ERROR; - } - - flags = enter_critical_section(); - - if (--priv->refs) - { - leave_critical_section(flags); - return OK; - } - - leave_critical_section(flags); - - /* Disable power and other HW resource (GPIO's) */ - - stm32f7_i2c_deinit(priv); - - /* Release unused resources */ - - stm32f7_i2c_sem_destroy(priv); - return OK; -} - #endif /* CONFIG_STM32F7_I2C1 || CONFIG_STM32F7_I2C2 || CONFIG_STM32F7_I2C3 */ - diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index 1be6e1b081..73e832d3fd 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -318,7 +318,7 @@ CONFIG_NSH_MMCSDMINOR=0 # # Board-Specific Options # -# CONFIG_LIB_BOARDCTL is not set +CONFIG_LIB_BOARDCTL=y # # RTOS Features @@ -907,8 +907,10 @@ CONFIG_READLINE_ECHO=y # CONFIG_SYSTEM_UBLOXMODEM is not set # CONFIG_SYSTEM_ZMODEM is not set +CONFIG_I2C_DRIVER=y + CONFIG_SYSTEM_I2CTOOL=y -CONFIG_I2CTOOL_MINBUS=0 +CONFIG_I2CTOOL_MINBUS=1 CONFIG_I2CTOOL_MAXBUS=1 CONFIG_I2CTOOL_MINADDR=0x03 CONFIG_I2CTOOL_MAXADDR=0x77 diff --git a/configs/stm32f746-ws/src/stm32_appinitialize.c b/configs/stm32f746-ws/src/stm32_appinitialize.c index 610f4978e2..0df665374e 100644 --- a/configs/stm32f746-ws/src/stm32_appinitialize.c +++ b/configs/stm32f746-ws/src/stm32_appinitialize.c @@ -40,9 +40,38 @@ #include #include +#include +#include +#include #include "stm32f746-ws.h" -#include +#include "stm32_i2c.h" + +static void stm32f7_i2c_register(int bus) +{ + FAR struct i2c_master_s *i2c; + int ret; + + i2c = stm32f7_i2cbus_initialize(bus); + if (i2c == NULL) + { + dbg("ERROR: Failed to get I2C%d interface\n", bus); + } + else + { + ret = i2c_register(i2c, bus); + if (ret < 0) + { + dbg("ERROR: Failed to register I2C%d driver: %d\n", bus, ret); + stm32f7_i2cbus_uninitialize(i2c); + } + } +} + +static void stm32f7_i2ctool(void) +{ + stm32f7_i2c_register(1); +} /**************************************************************************** * Public Functions @@ -60,6 +89,9 @@ int board_app_initialize(void) { + /* Register I2C drivers on behalf of the I2C tool */ + + stm32f7_i2ctool(); return OK; }