diff --git a/arch/xtensa/src/esp32/esp32_i2c.c b/arch/xtensa/src/esp32/esp32_i2c.c index 224a64c0abb..a991c5581a2 100644 --- a/arch/xtensa/src/esp32/esp32_i2c.c +++ b/arch/xtensa/src/esp32/esp32_i2c.c @@ -83,6 +83,22 @@ I2C_ARBITRATION_LOST_INT_ENA | \ I2C_RXFIFO_OVF_INT_ENA) +/* I2C event trace logic. + * NOTE: trace uses the internal, non-standard, low-level debug interface + * syslog() but does not require that any other debug is enabled. + */ + +#ifndef CONFIG_I2C_TRACE +# define esp32_i2c_tracereset(p) +# define esp32_i2c_tracenew(p,s) +# define esp32_i2c_traceevent(p,e,a,s) +# define esp32_i2c_tracedump(p) +#endif + +#ifndef CONFIG_I2C_NTRACE +# define CONFIG_I2C_NTRACE 32 +#endif + /* I2C state */ enum esp32_i2cstate_e @@ -104,6 +120,34 @@ enum i2c_opmode_e I2C_CMD_END /* I2C end command */ }; +#ifdef CONFIG_I2C_TRACE + +/* Trace events */ + +enum esp32_trace_e +{ + I2CEVENT_NONE = 0, /* No events have occurred with this status */ + I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = addr */ + I2CEVENT_SENDBYTE, /* Send byte, param = bytes */ + I2CEVENT_RCVMODEEN, /* Receive mode enabled, param = 0 */ + I2CEVENT_RCVBYTE, /* Read more dta, param = bytes */ + I2CEVENT_STOP, /* Last byte sten, send stop, param = length */ + I2CEVENT_ERROR /* Error occurred, param = 0 */ +}; + +/* Trace data */ + +struct esp32_trace_s +{ + uint32_t status; /* I2C 32-bit SR status */ + uint32_t count; /* Interrupt count when status change */ + enum esp32_i2cstate_e event; /* Last event that occurred with this status */ + uint32_t parm; /* Parameter associated with the event */ + clock_t time; /* First of event or first status */ +}; + +#endif /* CONFIG_I2C_TRACE */ + /* I2C Device hardware configuration */ struct esp32_i2c_config_s @@ -158,6 +202,17 @@ struct esp32_i2c_priv_s bool ready_read; /* If I2C has can read date */ uint32_t clk_freq; /* Current I2C Clock frequency */ + + /* I2C trace support */ + +#ifdef CONFIG_I2C_TRACE + int tndx; /* Trace array index */ + clock_t start_time; /* Time when the trace was started */ + + /* The actual trace data */ + + struct esp32_trace_s trace[CONFIG_I2C_NTRACE]; +#endif }; /**************************************************************************** @@ -175,6 +230,17 @@ static int esp32_i2c_transfer(FAR struct i2c_master_s *dev, static int esp32_i2c_reset(FAR struct i2c_master_s *dev); #endif +#ifdef CONFIG_I2C_TRACE +static void esp32_i2c_tracereset(struct esp32_i2c_priv_s *priv); +static void esp32_i2c_tracenew(struct esp32_i2c_priv_s *priv, + uint32_t status); +static void esp32_i2c_traceevent(struct esp32_i2c_priv_s *priv, + enum esp32_trace_e event, + uint32_t parm, + uint32_t status); +static void esp32_i2c_tracedump(struct esp32_i2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ + /**************************************************************************** * Private Data ****************************************************************************/ @@ -253,6 +319,21 @@ static struct esp32_i2c_priv_s esp32_i2c1_priv = }; #endif /* CONFIG_ESP32_I2C1 */ +/* Trace events strings */ + +#ifdef CONFIG_I2C_TRACE +static const char *g_trace_names[] = +{ + "NONE ", + "SENDADDR ", + "SENDBYTE ", + "RCVMODEEN ", + "RCVBYTE ", + "STOP ", + "ERROR " +}; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -701,6 +782,13 @@ static int esp32_i2c_transfer(FAR struct i2c_master_s *dev, esp32_i2c_init_clock(priv, msgs[i].frequency); + /* Reset I2C trace logic */ + + esp32_i2c_tracereset(priv); + + esp32_i2c_traceevent(priv, I2CEVENT_SENDADDR, msgs[i].addr, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); + esp32_i2c_sendstart(priv); if (esp32_i2c_sem_waitdone(priv) < 0) @@ -723,6 +811,10 @@ static int esp32_i2c_transfer(FAR struct i2c_master_s *dev, } } + /* Dump the trace result */ + + esp32_i2c_tracedump(priv); + esp32_i2c_sem_post(priv); return ret; @@ -767,6 +859,185 @@ static int esp32_i2c_reset(FAR struct i2c_master_s *dev) } #endif +/**************************************************************************** + * Name: esp32_i2c_traceclear + * + * Description: + * Set I2C trace fields to default value. + * + * Parameters: + * priv - Pointer to the internal driver state structure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void esp32_i2c_traceclear(struct esp32_i2c_priv_s *priv) +{ + struct esp32_trace_s *trace = &priv->trace[priv->tndx]; + + trace->status = 0; + trace->count = 0; + trace->event = I2CEVENT_NONE; + trace->parm = 0; + trace->time = 0; +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: esp32_i2c_tracereset + * + * Description: + * Reset the trace info for a new data collection. + * + * Parameters: + * priv - Pointer to the internal driver state structure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void esp32_i2c_tracereset(struct esp32_i2c_priv_s *priv) +{ + priv->tndx = 0; + priv->start_time = clock_systime_ticks(); + esp32_i2c_traceclear(priv); +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: esp32_i2c_tracenew + * + * Description: + * Create a new trace entry. + * + * Parameters: + * priv - Pointer to the internal driver state structure. + * status - Current value of I2C status register. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void esp32_i2c_tracenew(struct esp32_i2c_priv_s *priv, + uint32_t status) +{ + struct esp32_trace_s *trace = &priv->trace[priv->tndx]; + + /* Check if the current entry is already initialized or if its status had + * already changed + */ + + if (trace->count == 0 || status != trace->status) + { + /* Check whether the status changed */ + + if (trace->count != 0) + { + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + trace = &priv->trace[priv->tndx]; + } + + /* Initialize the new trace entry */ + + esp32_i2c_traceclear(priv); + trace->status = status; + trace->count = 1; + trace->time = clock_systime_ticks(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: esp32_i2c_traceevent + * + * Description: + * Record a new trace event. + * + * Parameters: + * priv - Pointer to the internal driver state structure. + * event - Event to be recorded on the trace. + * parm - Parameter associated with the event. + * status - Current value of I2C status register. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void esp32_i2c_traceevent(struct esp32_i2c_priv_s *priv, + enum esp32_trace_e event, + uint32_t parm, + uint32_t status) +{ + /* Check for new trace setup */ + + esp32_i2c_tracenew(priv, status); + + if (event != I2CEVENT_NONE) + { + struct esp32_trace_s *trace = &priv->trace[priv->tndx]; + + /* Initialize the new trace entry */ + + trace->event = event; + trace->parm = parm; + + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + esp32_i2c_traceclear(priv); + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: esp32_i2c_tracedump + * + * Description: + * Dump the trace results. + * + * Parameters: + * priv - Pointer to the internal driver state structure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void esp32_i2c_tracedump(struct esp32_i2c_priv_s *priv) +{ + struct esp32_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %" PRIu32 "\n", + (clock_systime_ticks() - priv->start_time)); + + for (i = 0; i < priv->tndx; i++) + { + trace = &priv->trace[i]; + syslog(LOG_DEBUG, + "%2d. STATUS: %08" PRIx32 " COUNT: %3" PRIu32 " EVENT: %s(%2d)" + " PARM: %08" PRIx32 " TIME: %" PRIu32 "\n", + i + 1, trace->status, trace->count, g_trace_names[trace->event], + trace->event, trace->parm, trace->time - priv->start_time); + } +} +#endif /* CONFIG_I2C_TRACE */ + /**************************************************************************** * Name: esp32_i2c_irq * @@ -787,6 +1058,8 @@ static int esp32_i2c_irq(int cpuint, void *context, FAR void *arg) if (I2C_INT_ERR_EN_BITS & status) { priv->error = status & I2C_INT_ERR_EN_BITS; + esp32_i2c_traceevent(priv, I2CEVENT_ERROR, priv->error, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); esp32_i2c_set_reg(priv, I2C_INT_ENA_OFFSET, 0); nxsem_post(&priv->sem_isr); } @@ -798,6 +1071,8 @@ static int esp32_i2c_irq(int cpuint, void *context, FAR void *arg) { if (priv->ready_read) { + esp32_i2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->bytes, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); esp32_i2c_recvdata(priv); priv->ready_read = 0; @@ -805,12 +1080,16 @@ static int esp32_i2c_irq(int cpuint, void *context, FAR void *arg) if (priv->bytes == msg->length) { + esp32_i2c_traceevent(priv, I2CEVENT_STOP, msg->length, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); esp32_i2c_sendstop(priv); priv->i2cstate = I2CSTATE_FINISH; } else { + esp32_i2c_traceevent(priv, I2CEVENT_RCVMODEEN, 0, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); esp32_i2c_startrecv(priv); priv->ready_read = 1; @@ -818,6 +1097,8 @@ static int esp32_i2c_irq(int cpuint, void *context, FAR void *arg) } else { + esp32_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->bytes, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); esp32_i2c_senddata(priv); if (priv->bytes == msg->length) @@ -828,6 +1109,8 @@ static int esp32_i2c_irq(int cpuint, void *context, FAR void *arg) } else if (priv->i2cstate == I2CSTATE_STOP) { + esp32_i2c_traceevent(priv, I2CEVENT_STOP, msg->length, + esp32_i2c_get_reg(priv, I2C_SR_OFFSET)); esp32_i2c_sendstop(priv); priv->i2cstate = I2CSTATE_FINISH;