You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2020/09/05 08:41:09 UTC
[incubator-nuttx] branch master updated: stm32h7:i2c driver fixed
iterrupt storm
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 719246e stm32h7:i2c driver fixed iterrupt storm
719246e is described below
commit 719246eddcbc7837d85d86e7d761e32617c20897
Author: David Sidrane <Da...@NscDg.com>
AuthorDate: Fri Sep 4 08:39:29 2020 -0700
stm32h7:i2c driver fixed iterrupt storm
Driver was getting into a state that it would keep
generating interrups and not service them.
---
arch/arm/src/stm32h7/stm32_i2c.c | 114 +++++++++++++++++++++++----------------
1 file changed, 67 insertions(+), 47 deletions(-)
diff --git a/arch/arm/src/stm32h7/stm32_i2c.c b/arch/arm/src/stm32h7/stm32_i2c.c
index dc577af..67b3613 100644
--- a/arch/arm/src/stm32h7/stm32_i2c.c
+++ b/arch/arm/src/stm32h7/stm32_i2c.c
@@ -20,7 +20,6 @@
* Copyright (c) 2016 Doug Vetter. All rights reserved.
* Author: Doug Vetter <os...@aileronlabs.com>
*
- *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -115,7 +114,7 @@
*
* - Private: Private data of an I2C Hardware
*
- * High Level Functional Desecription
+ * High Level Functional Description
*
* This driver works with I2C "messages" (struct i2c_msg_s), which carry a buffer
* intended to transfer data to, or store data read from, the I2C bus.
@@ -140,7 +139,7 @@
* Interrupt mode relies on the following interrupt events:
*
* TXIS - Transmit interrupt
- * (data transmitted to bus and acknowedged)
+ * (data transmitted to bus and acknowledged)
* NACKF - Not Acknowledge Received
* (data transmitted to bus and NOT acknowledged)
* RXNE - Receive interrupt
@@ -284,9 +283,15 @@
#define MKI2C_OUTPUT(p) (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT)
-#define I2C_CR1_TXRX (I2C_CR1_RXIE | I2C_CR1_TXIE)
+#define I2C_CR1_TXRX (I2C_CR1_RXIE | I2C_CR1_TXIE)
#define I2C_CR1_ALLINTS (I2C_CR1_TXRX | I2C_CR1_TCIE | I2C_CR1_ERRIE)
+/* Unused bit in I2c_ISR used to communicate a bad state has occurred in
+ * the isr processing
+ */
+
+#define I2C_INT_BAD_STATE 0x8000000
+
/* I2C event tracing
*
* To enable tracing statements which show the details of the state machine
@@ -358,7 +363,7 @@ struct stm32_trace_s
uint32_t count; /* Interrupt count when status change */
enum stm32_intstate_e event; /* Last event that occurred with this status */
uint32_t parm; /* Parameter associated with the event */
- uint32_t time; /* First of event or first status */
+ clock_t time; /* First of event or first status */
};
/* I2C Device hardware configuration */
@@ -403,7 +408,7 @@ struct stm32_i2c_priv_s
#ifdef CONFIG_I2C_TRACE
int tndx; /* Trace array index */
- uint32_t start_time; /* Time when the trace was started */
+ clock_t start_time; /* Time when the trace was started */
/* The actual trace data */
@@ -833,9 +838,9 @@ static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv)
#else
static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv)
{
- uint32_t timeout;
- uint32_t start;
- uint32_t elapsed;
+ clock_t timeout;
+ clock_t start;
+ clock_t elapsed;
int ret;
/* Get the timeout value */
@@ -871,8 +876,8 @@ static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv)
while (priv->intstate != INTSTATE_DONE && elapsed < timeout);
- i2cinfo("intstate: %d elapsed: %d threshold: %d status: 0x%08x\n",
- priv->intstate, elapsed, timeout, priv->status);
+ i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: 0x%08x\n",
+ priv->intstate, (long)elapsed, (long)timeout, priv->status);
/* Set the interrupt state back to IDLE */
@@ -973,9 +978,9 @@ stm32_i2c_disable_reload(FAR struct stm32_i2c_priv_s *priv)
static inline void stm32_i2c_sem_waitstop(FAR struct stm32_i2c_priv_s *priv)
{
- uint32_t start;
- uint32_t elapsed;
- uint32_t timeout;
+ clock_t start;
+ clock_t elapsed;
+ clock_t timeout;
uint32_t cr;
uint32_t sr;
@@ -1055,7 +1060,8 @@ static inline void stm32_i2c_sem_init(FAR struct i2c_master_s *dev)
*/
nxsem_init(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, 0, 0);
- nxsem_set_protocol(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr, SEM_PRIO_NONE);
+ nxsem_set_protocol(&((struct stm32_i2c_inst_s *)dev)->priv->sem_isr,
+ SEM_PRIO_NONE);
#endif
}
@@ -1177,7 +1183,7 @@ static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv)
int i;
syslog(LOG_DEBUG, "Elapsed time: %d\n",
- clock_systime_ticks() - priv->start_time);
+ (int)(clock_systime_ticks() - priv->start_time));
for (i = 0; i < priv->tndx; i++)
{
@@ -1185,7 +1191,7 @@ static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv)
syslog(LOG_DEBUG,
"%2d. STATUS: %08x COUNT: %3d EVENT: %2d PARM: %08x TIME: %d\n",
i + 1, trace->status, trace->count, trace->event, trace->parm,
- trace->time - priv->start_time);
+ (int)(trace->time - priv->start_time));
}
}
#endif /* CONFIG_I2C_TRACE */
@@ -1243,24 +1249,21 @@ static void stm32_i2c_tracedump(FAR struct stm32_i2c_priv_s *priv)
static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequency)
{
- uint32_t pe;
uint8_t presc;
uint8_t scl_delay;
uint8_t sda_delay;
uint8_t scl_h_period;
uint8_t scl_l_period;
- if (frequency != priv->frequency)
- {
- /* I2C peripheral must be disabled to update clocking configuration */
+ /* I2C peripheral must be disabled to update clocking configuration.
+ * This will SW reset the device.
+ */
- pe = (stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET) & I2C_CR1_PE);
- if (pe)
- {
- stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0);
- }
+ stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0);
- /* The Sppeed and timing calculation are based on the following
+ if (frequency != priv->frequency)
+ {
+ /* The Speed and timing calculation are based on the following
* fI2CCLK = HSI and is 16Mhz
* Analog filter is on,
* Digital filter off
@@ -1309,14 +1312,12 @@ static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequ
(scl_l_period << I2C_TIMINGR_SCLL_SHIFT);
stm32_i2c_putreg32(priv, STM32_I2C_TIMINGR_OFFSET, timingr);
-
- if (pe)
- {
- stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE);
- }
-
priv->frequency = frequency;
}
+
+ /* Enable I2C peripheral */
+
+ stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE);
}
/************************************************************************************
@@ -1533,9 +1534,9 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
i2cinfo("ENTER: status = 0x%08x\n", status);
- /* Update private version of the state */
+ /* Update private version of the state assuming a good state */
- priv->status = status;
+ priv->status = status & ~I2C_INT_BAD_STATE;
/* If this is a new transmission set up the trace table accordingly */
@@ -1611,7 +1612,7 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
* 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.
+ * following the successful transmission of a byte and slave acknowledgment.
* In this state the I2C_TXDR register is ready to accept another byte for
* transmission. The TXIS bit will be cleared automatically when the next
* byte is written to I2C_TXDR.
@@ -1701,6 +1702,10 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
i2cerr("ERROR: TXIS Unsupported state detected, dcnt=%i, status 0x%08x\n",
priv->dcnt, status);
stm32_i2c_traceevent(priv, I2CEVENT_WRITE_ERROR, 0);
+
+ /* Indicate the bad state, so that on termination HW will be reset */
+
+ priv->status |= I2C_INT_BAD_STATE;
}
i2cinfo("TXIS: EXIT dcnt = %i msgc = %i status 0x%08x\n",
@@ -1789,6 +1794,7 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
/* Set signals that will terminate ISR and wake waiting thread */
+ priv->status |= I2C_INT_BAD_STATE;
priv->dcnt = -1;
priv->msgc = 0;
}
@@ -2034,7 +2040,7 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
*
* We get to this branch only if we can't handle the current state.
*
- * This should not happen in interrupt based operation.
+ * This can happen in interrupt based operation on ARLO & BUSY.
*
* This will happen during polled operation when the device is not
* in one of the supported states when polled.
@@ -2053,6 +2059,7 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
/* set condition to terminate ISR and wake waiting thread */
+ priv->status |= I2C_INT_BAD_STATE;
priv->dcnt = -1;
priv->msgc = 0;
stm32_i2c_traceevent(priv, I2CEVENT_STATE_ERROR, 0);
@@ -2084,8 +2091,6 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
priv->intstate = INTSTATE_DONE;
#else
- status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET);
-
/* Update private state to capture NACK which is used in combination
* with the astart flag to report the type of NACK received (address
* vs data) to the upper layers once we exit the ISR.
@@ -2094,12 +2099,25 @@ static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
* flag will naturally be cleared by that process.
*/
- priv->status = status;
+ status = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET);
/* Clear all interrupts */
stm32_i2c_modifyreg32(priv, STM32_I2C_ICR_OFFSET, 0, I2C_ICR_CLEARMASK);
+ /* Was a bad state detected in the processing? */
+
+ if (priv->status & I2C_INT_BAD_STATE)
+ {
+ /* SW reset device */
+
+ stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE, 0);
+ }
+
+ /* Update private status from above sans I2C_INT_BAD_STATE */
+
+ priv->status = status;
+
/* If a thread is waiting then inform it transfer is complete */
if (priv->intstate == INTSTATE_WAITING)
@@ -2184,10 +2202,6 @@ static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv)
priv->frequency = 0;
stm32_i2c_setclock(priv, 100000);
- /* Enable I2C peripheral */
-
- stm32_i2c_modifyreg32(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_PE);
-
return OK;
}
@@ -2268,7 +2282,7 @@ static int stm32_i2c_process(FAR struct i2c_master_s *dev,
stm32_i2c_tracereset(priv);
- /* Set I2C clock frequency (on change it toggles I2C_CR1_PE !) */
+ /* Set I2C clock frequency toggles I2C_CR1_PE performing a SW reset! */
stm32_i2c_setclock(priv, msgs->frequency);
@@ -2434,8 +2448,8 @@ static int stm32_i2c_process(FAR struct i2c_master_s *dev,
* wraps up the transfer with a STOP condition.
*/
- uint32_t start = clock_systime_ticks();
- uint32_t timeout = USEC2TICK(USEC_PER_SEC / priv->frequency) + 1;
+ clock_t start = clock_systime_ticks();
+ clock_t timeout = USEC2TICK(USEC_PER_SEC / priv->frequency) + 1;
status = stm32_i2c_getstatus(priv);
@@ -2685,6 +2699,12 @@ static int stm32_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain,
}
break;
+
+ default:
+
+ /* Should not get here */
+
+ break;
}
return OK;