Tiva I2C: Add register-level debug capability

This commit is contained in:
Gregory Nutt 2014-12-11 09:34:03 -06:00
parent 6a98255aa0
commit 218967b80e
2 changed files with 169 additions and 33 deletions

View File

@ -355,6 +355,14 @@ config TIVA_I2C_TIMEOTICKS
depends on !TIVA_I2C_DYNTIMEO
endif # !TIVA_I2C_DYNTIMEO
config TIVA_I2C_REGDEBUG
bool "Register level debug"
default n
depends on DEBUG
---help---
Enables extremely detailed register access debug output.
endmenu # I2C Configuration
endif # TIVA_I2C

View File

@ -52,6 +52,7 @@
#include <stdbool.h>
#include <semaphore.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
@ -105,6 +106,7 @@
# define CONFIG_TIVA_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_TIVA_I2C_TIMEOTICKS)
#endif
/* GPIO pins ************************************************************************/
/* Macros to convert a I2C pin to a GPIO output */
#define I2C_INPUT (GPIO_FUNC_INPUT)
@ -124,6 +126,10 @@
# define i2cvdbg(x...)
#endif
#ifndef CONFIG_DEBUG
# undef CONFIG_TIVA_I2C_REGDEBUG
#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.
@ -166,6 +172,7 @@ enum tiva_trace_e
I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */
I2CEVENT_SPURIOUS, /* Spurious interrupt received, param = msgc */
I2CEVENT_NEXTMSG, /* Starting next message, param = msgc */
I2CEVENT_TIMEOUT, /* Software detectected timeout, param = RIS */
I2CEVENT_DONE /* All messages transferred, param = intstate */
};
@ -216,9 +223,18 @@ struct tiva_i2c_priv_s
uint16_t flags; /* Current message flags */
uint32_t status; /* MCS register at the end of the transfer */
/* I2C trace support */
#ifdef CONFIG_TIVA_I2C_REGDEBUG
/* Register level debug */
bool wrlast; /* Last was a write */
uintptr_t addrlast; /* Last address */
uint32_t vallast; /* Last value */
int ntimes; /* Number of times */
#endif
#ifdef CONFIG_I2C_TRACE
/* I2C trace support */
int tndx; /* Trace array index */
int tcount; /* Number of events with this status */
uint32_t ttime; /* Time when the trace was started */
@ -246,13 +262,18 @@ struct tiva_i2c_inst_s
* Private Function Prototypes
************************************************************************************/
#ifdef CONFIG_TIVA_I2C_REGDEBUG
static bool tiva_i2c_checkreg(struct tiva_i2c_priv_s *priv, bool wr,
uint32_t regval, uintptr_t regaddr);
static uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv, unsigned int offset);
static void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv, unsigned int offset,
uint32_t value);
#else
static inline uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv,
unsigned int offset);
static inline void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv,
unsigned int offset, uint32_t value);
static inline void tiva_i2c_modifyreg(struct tiva_i2c_priv_s *priv,
uint8_t offset, uint16_t clearbits,
uint16_t setbits);
#endif
static inline void tiva_i2c_sem_wait(struct i2c_dev_s *dev);
#ifdef CONFIG_TIVA_I2C_DYNTIMEO
@ -463,6 +484,60 @@ static const struct i2c_ops_s tiva_i2c_ops =
* Private Functions
************************************************************************************/
/************************************************************************************
* Name: sam_checkreg
*
* Description:
* Check if the current register access is a duplicate of the preceding.
*
* Input Parameters:
* regval - The value to be written
* regaddr - The address of the register to write to
*
* Returned Value:
* true: This is the first register access of this type.
* flase: This is the same as the preceding register access.
*
************************************************************************************/
#ifdef CONFIG_TIVA_I2C_REGDEBUG
static bool tiva_i2c_checkreg(struct tiva_i2c_priv_s *priv, bool wr,
uint32_t regval, uintptr_t regaddr)
{
if (wr == priv->wrlast && /* Same kind of access? */
regval == priv->vallast && /* Same value? */
regaddr == priv->addrlast) /* Same address? */
{
/* Yes, then just keep a count of the number of times we did this. */
priv->ntimes++;
return false;
}
else
{
/* Did we do the previous operation more than once? */
if (priv->ntimes > 0)
{
/* Yes... show how many times we did it */
lldbg("...[Repeats %d times]...\n", priv->ntimes);
}
/* Save information about the new access */
priv->wrlast = wr;
priv->vallast = regval;
priv->addrlast = regaddr;
priv->ntimes = 0;
}
/* Return true if this is the first time that we have done this operation */
return true;
}
#endif
/************************************************************************************
* Name: tiva_i2c_getreg
*
@ -471,11 +546,26 @@ static const struct i2c_ops_s tiva_i2c_ops =
*
************************************************************************************/
#ifdef CONFIG_TIVA_I2C_REGDEBUG
static uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv, unsigned int offset)
{
uintptr_t regaddr = priv->config->base + offset;
uint32_t regval = getreg32(regaddr);
if (tiva_i2c_checkreg(priv, false, regval, regaddr))
{
lldbg("%08x->%08x\n", regaddr, regval);
}
return regval;
}
#else
static inline uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv,
unsigned int offset)
{
return getreg32(priv->config->base + offset);
}
#endif
/************************************************************************************
* Name: tiva_i2c_putreg
@ -485,26 +575,26 @@ static inline uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv,
*
************************************************************************************/
#ifdef CONFIG_TIVA_I2C_REGDEBUG
static void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv, unsigned int offset,
uint32_t regval)
{
uintptr_t regaddr = priv->config->base + offset;
if (tiva_i2c_checkreg(priv, true, regval, regaddr))
{
lldbg("%08x<-%08x\n", regaddr, regval);
}
putreg32(regval, regaddr);
}
#else
static inline void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv,
unsigned int offset, uint32_t regval)
{
putreg32(regval, priv->config->base + offset);
}
/************************************************************************************
* Name: tiva_i2c_modifyreg
*
* Description:
* Modify a 16-bit register value by offset
*
************************************************************************************/
static inline void tiva_i2c_modifyreg(struct tiva_i2c_priv_s *priv,
uint8_t offset, uint16_t clearbits,
uint16_t setbits)
{
modifyreg16(priv->config->base + offset, clearbits, setbits);
}
#endif
/************************************************************************************
* Name: tiva_i2c_sem_wait
@ -623,6 +713,8 @@ static inline int tiva_i2c_sem_waitdone(struct tiva_i2c_priv_s *priv)
* NOTE that we try again if we are awakened by a signal (EINTR).
*/
tiva_i2c_traceevent(priv, I2CEVENT_TIMEOUT,
tiva_i2c_getreg(priv, TIVA_I2CM_RIS_OFFSET));
break;
}
}
@ -750,7 +842,7 @@ static inline void tiva_i2c_sem_destroy(struct i2c_dev_s *dev)
}
/************************************************************************************
* Name: tiva_i2c_trace*
* Name: tiva_i2c_trace
*
* Description:
* I2C trace instrumentation
@ -796,7 +888,7 @@ static void tiva_i2c_tracenew(struct tiva_i2c_priv_s *priv, uint32_t status)
if (priv->tndx >= (CONFIG_I2C_NTRACE-1))
{
i2cdbg("Trace table overflow\n");
i2cdbg("ERROR: Trace table overflow\n");
return;
}
@ -845,7 +937,7 @@ static void tiva_i2c_traceevent(struct tiva_i2c_priv_s *priv,
if (priv->tndx >= (CONFIG_I2C_NTRACE-1))
{
i2cdbg("Trace table overflow\n");
i2cdbg("ERROR: Trace table overflow\n");
return;
}
@ -1376,12 +1468,22 @@ static int tiva_i2c_initialize(struct tiva_i2c_priv_s *priv)
uint32_t regval;
int ret;
i2cvdbg("I2C%d:\n", priv->config->devno);
/* Enable clocking to the I2C peripheral */
#ifdef TIVA_SYSCON_RCGCI2C
modifyreg32(TIVA_SYSCON_RCGCI2C, 0, SYSCON_RCGCI2C(priv->config->devno));
i2cvdbg("I2C%d: RCGI2C[%08x]=%08lx\n",
priv->config->devno, TIVA_SYSCON_RCGCI2C,
(unsigned long)getreg32(TIVA_SYSCON_RCGCI2C));
#else
modifyreg32(TIVA_SYSCON_RCGC1, 0, priv->rcgbit);
i2cvdbg("I2C%d: RCGC1[%08x]=%08lx\n",
priv->config->devno, TIVA_SYSCON_RCGC1,
(unsigned long)getreg32(TIVA_SYSCON_RCGC1));
#endif
/* Configure pins */
@ -1432,6 +1534,8 @@ static int tiva_i2c_uninitialize(struct tiva_i2c_priv_s *priv)
{
uint32_t regval;
i2cvdbg("I2C%d:\n", priv->config->devno);
/* Disable I2C */
regval = tiva_i2c_getreg(priv, TIVA_I2CM_CR_OFFSET);
@ -1474,6 +1578,8 @@ static uint32_t tiva_i2c_setclock(struct tiva_i2c_priv_s *priv, uint32_t frequen
uint32_t regval;
uint32_t tmp;
i2cvdbg("I2C%d: frequency: %lu\n", priv->config->devno, (unsigned long)frequency);
/* Calculate the clock divider that results in the highest frequency that
* is than or equal to the desired speed.
*/
@ -1517,6 +1623,8 @@ static uint32_t tiva_i2c_setfrequency(struct i2c_dev_s *dev, uint32_t frequency)
DEBUGASSERT(inst && inst->priv);
priv = inst->priv;
i2cvdbg("I2C%d: frequency: %lu\n", inst->priv->config->devno, (unsigned long)frequency);
/* Get exclusive access to the I2C device */
tiva_i2c_sem_wait(dev);
@ -1540,6 +1648,10 @@ static uint32_t tiva_i2c_setfrequency(struct i2c_dev_s *dev, uint32_t frequency)
static int tiva_i2c_setaddress(struct i2c_dev_s *dev, int addr, int nbits)
{
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d: addr: %02x nbits=%d\n", inst->priv->config->devno, addr, nbits);
tiva_i2c_sem_wait(dev);
inst->address = addr;
@ -1558,7 +1670,7 @@ static int tiva_i2c_setaddress(struct i2c_dev_s *dev, int addr, int nbits)
************************************************************************************/
static int tiva_i2c_process(struct i2c_dev_s *dev, struct i2c_msg_s *msgs,
int count)
int count)
{
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
struct tiva_i2c_priv_s *priv = inst->priv;
@ -1566,6 +1678,8 @@ static int tiva_i2c_process(struct i2c_dev_s *dev, struct i2c_msg_s *msgs,
ASSERT(count);
i2cvdbg("I2C%d: count=%d\n", priv->config->devno, count);
/* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't
* overwrite stale data.
*/
@ -1676,9 +1790,6 @@ static int tiva_i2c_process(struct i2c_dev_s *dev, struct i2c_msg_s *msgs,
static int tiva_i2c_write(struct i2c_dev_s *dev, const uint8_t *buffer, int buflen)
{
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
tiva_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */
struct i2c_msg_s msgv =
{
.addr = inst->address,
@ -1687,6 +1798,10 @@ static int tiva_i2c_write(struct i2c_dev_s *dev, const uint8_t *buffer, int bufl
.length = buflen
};
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d: buflen=%d\n", inst->priv->config->devno, buflen);
tiva_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */
return tiva_i2c_process(dev, &msgv, 1);
}
@ -1701,9 +1816,6 @@ static int tiva_i2c_write(struct i2c_dev_s *dev, const uint8_t *buffer, int bufl
int tiva_i2c_read(struct i2c_dev_s *dev, uint8_t *buffer, int buflen)
{
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
tiva_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */
struct i2c_msg_s msgv =
{
.addr = inst->address,
@ -1712,6 +1824,10 @@ int tiva_i2c_read(struct i2c_dev_s *dev, uint8_t *buffer, int buflen)
.length = buflen
};
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d: buflen=%d\n", inst->priv->config->devno, buflen);
tiva_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */
return tiva_i2c_process(dev, &msgv, 1);
}
@ -1729,8 +1845,6 @@ static int tiva_i2c_writeread(struct i2c_dev_s *dev,
uint8_t *buffer, int buflen)
{
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
tiva_i2c_sem_wait(dev); /* Ensure that address or flags don't change meanwhile */
struct i2c_msg_s msgv[2] =
{
{
@ -1747,6 +1861,10 @@ static int tiva_i2c_writeread(struct i2c_dev_s *dev,
}
};
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d: wbuflen=%d buflen=%d\n", inst->priv->config->devno, wbuflen, buflen);
tiva_i2c_sem_wait(dev); /* Ensure that address or flags don't change meanwhile */
return tiva_i2c_process(dev, msgv, 2);
}
#endif
@ -1763,6 +1881,12 @@ static int tiva_i2c_writeread(struct i2c_dev_s *dev,
static int tiva_i2c_transfer(struct i2c_dev_s *dev, struct i2c_msg_s *msgs,
int count)
{
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d: count=%d\n", inst->priv->config->devno, count);
UNUSED(inst);
tiva_i2c_sem_wait(dev); /* Ensure that address or flags don't change meanwhile */
return tiva_i2c_process(dev, msgs, count);
}
@ -1787,6 +1911,8 @@ struct i2c_dev_s *up_i2cinitialize(int port)
const struct tiva_i2c_config_s *config; /* Constant configuration */
int irqs;
i2cvdbg("I2C%d: port=%d\n", port, port);
/* Get I2C private structure */
switch (port)
@ -1879,7 +2005,8 @@ int up_i2cuninitialize(struct i2c_dev_s *dev)
struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev;
int irqs;
ASSERT(dev);
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d:\n", inst->priv->config->devno);
/* Decrement reference count and check for underflow */
@ -1930,7 +2057,8 @@ int up_i2creset(struct i2c_dev_s *dev)
uint32_t sda_gpio;
int ret = ERROR;
ASSERT(dev);
DEBUGASSERT(inst && inst->priv && inst->priv->config);
i2cvdbg("I2C%d:\n", inst->priv->config->devno);
/* Get I2C private structure */