arch/arm/src/am335x/am335x_i2c.c: AM225x I2C driver now works in both polling and interrupt modes.

This commit is contained in:
Petro Karashchenko 2019-12-12 07:39:36 -06:00 committed by Gregory Nutt
parent e5e52446ca
commit 3737aa83fa
2 changed files with 54 additions and 48 deletions

View File

@ -76,7 +76,7 @@
* Pre-processor Definitions * Pre-processor Definitions
************************************************************************************/ ************************************************************************************/
#define AM335X_I2C_PI_SYS_CLK (48000000) #define AM335X_I2C_SCLK (48000000)
/* Configuration ********************************************************************/ /* Configuration ********************************************************************/
@ -524,7 +524,7 @@ static inline int am335x_i2c_sem_waitdone(FAR struct am335x_i2c_priv_s *priv)
{ {
if ((priv->flags & I2C_M_READ) != 0) if ((priv->flags & I2C_M_READ) != 0)
{ {
regval = I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_RRDY \ regval = I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_RRDY
| I2C_IRQ_XRDY | I2C_IRQ_AERR | I2C_IRQ_BF; | I2C_IRQ_XRDY | I2C_IRQ_AERR | I2C_IRQ_BF;
am335x_i2c_putreg(priv, AM335X_I2C_IRQ_EN_SET_OFFSET, regval); am335x_i2c_putreg(priv, AM335X_I2C_IRQ_EN_SET_OFFSET, regval);
} }
@ -534,6 +534,10 @@ static inline int am335x_i2c_sem_waitdone(FAR struct am335x_i2c_priv_s *priv)
| I2C_IRQ_AERR | I2C_IRQ_BF; | I2C_IRQ_AERR | I2C_IRQ_BF;
am335x_i2c_putreg(priv, AM335X_I2C_IRQ_EN_SET_OFFSET, regval); am335x_i2c_putreg(priv, AM335X_I2C_IRQ_EN_SET_OFFSET, regval);
} }
/* Force generate bus free interrupt to start transmission */
am335x_i2c_putreg(priv, AM335X_I2C_IRQ_STAT_RAW_OFFSET, I2C_IRQ_BF);
} }
/* Enable Interrupts when slave mode */ /* Enable Interrupts when slave mode */
@ -625,11 +629,6 @@ static inline int am335x_i2c_sem_waitdone(FAR struct am335x_i2c_priv_s *priv)
timeout = CONFIG_AM335X_I2CTIMEOTICKS; timeout = CONFIG_AM335X_I2CTIMEOTICKS;
#endif #endif
/* Signal the interrupt handler that we are waiting. NOTE: Interrupts
* are currently disabled but will be temporarily re-enabled below when
* nxsem_timedwait() sleeps.
*/
priv->intstate = INTSTATE_WAITING; priv->intstate = INTSTATE_WAITING;
start = clock_systimer(); start = clock_systimer();
@ -700,7 +699,7 @@ static inline void am335x_i2c_sem_waitstop(FAR struct am335x_i2c_priv_s *priv)
/* Check for Bus Free condition */ /* Check for Bus Free condition */
regval = am335x_i2c_getreg(priv, AM335X_I2C_IRQ_STAT_RAW_OFFSET); regval = am335x_i2c_getreg(priv, AM335X_I2C_IRQ_STAT_RAW_OFFSET);
if ((regval & (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_BF)) != 0) if ((regval & I2C_IRQ_BB) == 0)
{ {
return; return;
} }
@ -893,9 +892,10 @@ static void am335x_i2c_tracedump(FAR struct am335x_i2c_priv_s *priv)
static void am335x_i2c_setclock(FAR struct am335x_i2c_priv_s *priv, static void am335x_i2c_setclock(FAR struct am335x_i2c_priv_s *priv,
uint32_t frequency) uint32_t frequency)
{ {
uint32_t src_freq = AM335X_I2C_PI_SYS_CLK; uint32_t src_freq = AM335X_I2C_SCLK;
uint32_t men; uint32_t men;
uint32_t prescale = 0; uint32_t prescale = 0;
uint32_t scl = 0;
uint32_t scl_low = 0; uint32_t scl_low = 0;
uint32_t scl_hi = 0; uint32_t scl_hi = 0;
uint32_t best_prescale = 0; uint32_t best_prescale = 0;
@ -923,35 +923,36 @@ static void am335x_i2c_setclock(FAR struct am335x_i2c_priv_s *priv,
/* I2C bus clock is Source Clock (Hz) / ((psc + 1) * (scll + 7 + sclh + 5)) */ /* I2C bus clock is Source Clock (Hz) / ((psc + 1) * (scll + 7 + sclh + 5)) */
for (prescale = 0; prescale < 256; prescale++) for (scl = 14; scl < 522; scl += 2)
{ {
for (scl_low = 0; scl_low < 256; scl_low++) for (prescale = 3; prescale < 256; prescale++)
{ {
for (scl_hi = 0; scl_hi < 256; scl_hi++) scl_low = (scl / 2) - 7;
scl_hi = (scl / 2) - 5;
computed_rate = src_freq / (prescale + 1);
computed_rate /= scl_low + 7 + scl_hi + 5;
if (frequency > computed_rate)
{ {
computed_rate = src_freq / (prescale + 1); abs_error = frequency - computed_rate;
computed_rate /= scl_low + 7 + scl_hi + 5; }
else
{
abs_error = computed_rate - frequency;
}
if (frequency > computed_rate) if (abs_error < best_error)
{ {
abs_error = frequency - computed_rate; best_prescale = prescale;
} best_scl_low = scl_low;
else best_scl_hi = scl_hi;
{ best_error = abs_error;
abs_error = computed_rate - frequency;
}
if (abs_error < best_error) if (abs_error == 0)
{ {
best_prescale = prescale; scl = 522;
best_scl_low = scl_low; break;
best_scl_hi = scl_hi;
best_error = abs_error;
if (abs_error == 0)
{
break;
}
} }
} }
} }
@ -992,7 +993,7 @@ static inline void am335x_i2c_sendstart(FAR struct am335x_i2c_priv_s *priv,
/* Generate START condition and send the address */ /* Generate START condition and send the address */
regval = am335x_i2c_getreg(priv, AM335X_I2C_CON_OFFSET) | I2C_CON_STT; regval = am335x_i2c_getreg(priv, AM335X_I2C_CON_OFFSET) | I2C_CON_STT | I2C_CON_MST;
if ((priv->flags & I2C_M_READ) != 0) if ((priv->flags & I2C_M_READ) != 0)
{ {
@ -1254,11 +1255,9 @@ static int am335x_i2c_isr_process(struct am335x_i2c_priv_s *priv)
#endif #endif
} }
#ifndef CONFIG_I2C_POLLED
/* Clear interrupt status */ /* Clear interrupt status */
am335x_i2c_putreg(priv, AM335X_I2C_IRQ_STAT_OFFSET, status); am335x_i2c_putreg(priv, AM335X_I2C_IRQ_STAT_OFFSET, status);
#endif
priv->status = status; priv->status = status;
return OK; return OK;
@ -1309,16 +1308,16 @@ static int am335x_i2c_init(FAR struct am335x_i2c_priv_s *priv)
/* Disable auto-idle mode */ /* Disable auto-idle mode */
am335x_i2c_modifyreg(priv, AM335X_I2C_SYSC_OFFSET, 0, I2C_SYSC_AUTOIDLE); am335x_i2c_modifyreg(priv, AM335X_I2C_SYSC_OFFSET, I2C_SYSC_AUTOIDLE, 0);
/* Force a frequency update */ /* Force a frequency update */
priv->frequency = 0; priv->frequency = 0;
am335x_i2c_setclock(priv, 100000); am335x_i2c_setclock(priv, 100000);
#ifndef CONFIG_I2C_POLLED
/* Attach ISRs */ /* Attach ISRs */
#ifndef CONFIG_I2C_POLLED
irq_attach(priv->config->irq, am335x_i2c_isr, priv); irq_attach(priv->config->irq, am335x_i2c_isr, priv);
up_enable_irq(priv->config->irq); up_enable_irq(priv->config->irq);
#endif #endif
@ -1327,6 +1326,10 @@ static int am335x_i2c_init(FAR struct am335x_i2c_priv_s *priv)
am335x_i2c_modifyreg(priv, AM335X_I2C_CON_OFFSET, 0, I2C_CON_EN); am335x_i2c_modifyreg(priv, AM335X_I2C_CON_OFFSET, 0, I2C_CON_EN);
/* Select free running mode */
am335x_i2c_modifyreg(priv, AM335X_I2C_SYSTEST_OFFSET, 0, I2C_SYSTEST_FREE);
/* Wait for I2C module comes out of reset */ /* Wait for I2C module comes out of reset */
while (!(am335x_i2c_getreg(priv, AM335X_I2C_SYSS_OFFSET) & I2C_SYSS_RST_DONE)) while (!(am335x_i2c_getreg(priv, AM335X_I2C_SYSS_OFFSET) & I2C_SYSS_RST_DONE))
@ -1380,7 +1383,6 @@ static int am335x_i2c_transfer(FAR struct i2c_master_s *dev,
FAR struct i2c_msg_s *msgs, int count) FAR struct i2c_msg_s *msgs, int count)
{ {
FAR struct am335x_i2c_priv_s *priv = (struct am335x_i2c_priv_s *)dev; FAR struct am335x_i2c_priv_s *priv = (struct am335x_i2c_priv_s *)dev;
uint32_t status = 0;
int ret = 0; int ret = 0;
@ -1396,6 +1398,7 @@ static int am335x_i2c_transfer(FAR struct i2c_master_s *dev,
/* Clear any pending error interrupts */ /* Clear any pending error interrupts */
am335x_i2c_putreg(priv, AM335X_I2C_IRQ_STAT_OFFSET, I2C_STS_CLEARMASK);
am335x_i2c_putreg(priv, AM335X_I2C_IRQ_EN_CLR_OFFSET, I2C_ICR_CLEARMASK); am335x_i2c_putreg(priv, AM335X_I2C_IRQ_EN_CLR_OFFSET, I2C_ICR_CLEARMASK);
/* Old transfers are done */ /* Old transfers are done */
@ -1429,47 +1432,46 @@ static int am335x_i2c_transfer(FAR struct i2c_master_s *dev,
if (am335x_i2c_sem_waitdone(priv) < 0) if (am335x_i2c_sem_waitdone(priv) < 0)
{ {
status = am335x_i2c_getreg(priv, AM335X_I2C_IRQ_STAT_RAW_OFFSET);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
i2cerr("ERROR: Timed out: IRQ_RAW: status: 0x%x\n", status); i2cerr("ERROR: Timed out: IRQ_RAW: status: 0x%x\n", priv->status);
} }
/* Check for error status conditions */ /* Check for error status conditions */
if ((status & I2C_IRQ_ERRORMASK) != 0) else if ((priv->status & I2C_IRQ_ERRORMASK) != 0)
{ {
/* I2C_IRQ_ERRORMASK is the 'OR' of the following individual bits: */ /* I2C_IRQ_ERRORMASK is the 'OR' of the following individual bits: */
if (status & I2C_IRQ_AL) if (priv->status & I2C_IRQ_AL)
{ {
/* Arbitration Lost (master mode) */ /* Arbitration Lost (master mode) */
i2cerr("Arbitration lost\n"); i2cerr("Arbitration lost\n");
ret = -EAGAIN; ret = -EAGAIN;
} }
else if (status & I2C_IRQ_NACK) else if (priv->status & I2C_IRQ_NACK)
{ {
/* Acknowledge Failure */ /* Acknowledge Failure */
i2cerr("Ack failure\n"); i2cerr("Ack failure\n");
ret = -ENXIO; ret = -ENXIO;
} }
else if (status & (I2C_IRQ_XUDF | I2C_IRQ_ROVR)) else if (priv->status & (I2C_IRQ_XUDF | I2C_IRQ_ROVR))
{ {
/* Overrun/Underrun */ /* Overrun/Underrun */
i2cerr("Overrun/Underrun status\n"); i2cerr("Overrun/Underrun status\n");
ret = -EIO; ret = -EIO;
} }
else if (status & I2C_IRQ_AERR) else if (priv->status & I2C_IRQ_AERR)
{ {
/* Access Error in reception or transmission */ /* Access Error in reception or transmission */
i2cerr("Access Error\n"); i2cerr("Access Error\n");
ret = -EPROTO; ret = -EPROTO;
} }
else if (status & I2C_IRQ_BB) else if (priv->status & I2C_IRQ_BB)
{ {
/* Bus busy Error */ /* Bus busy Error */

View File

@ -205,11 +205,15 @@
#define I2C_IRQ_RDR (1 << 13) /* Bit 13: Receive draining IRQ */ #define I2C_IRQ_RDR (1 << 13) /* Bit 13: Receive draining IRQ */
#define I2C_IRQ_XDR (1 << 14) /* Bit 14: Transmit draining IRQ */ #define I2C_IRQ_XDR (1 << 14) /* Bit 14: Transmit draining IRQ */
#define I2C_IRQ_ERRORMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_AERR | I2C_IRQ_XUDF | I2C_IRQ_ROVR | I2C_IRQ_BB) #define I2C_IRQ_ERRORMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_AERR | I2C_IRQ_XUDF | I2C_IRQ_ROVR)
#define I2C_STS_CLEARMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_ARDY | I2C_IRQ_RRDY | I2C_IRQ_XRDY \
| I2C_IRQ_GC | I2C_IRQ_STC | I2C_IRQ_AERR | I2C_IRQ_BF | I2C_IRQ_AAS \
| I2C_IRQ_XUDF | I2C_IRQ_ROVR | I2C_IRQ_BB | I2C_IRQ_RDR | I2C_IRQ_XDR)
#define I2C_ICR_CLEARMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_ARDY | I2C_IRQ_RRDY | I2C_IRQ_XRDY \ #define I2C_ICR_CLEARMASK (I2C_IRQ_AL | I2C_IRQ_NACK | I2C_IRQ_ARDY | I2C_IRQ_RRDY | I2C_IRQ_XRDY \
| I2C_IRQ_GC | I2C_IRQ_STC | I2C_IRQ_AERR | I2C_IRQ_BF | I2C_IRQ_AAS \ | I2C_IRQ_GC | I2C_IRQ_STC | I2C_IRQ_AERR | I2C_IRQ_BF | I2C_IRQ_AAS \
| I2C_IRQ_XUDF | I2C_IRQ_ROVR | I2C_IRQ_BB | I2C_IRQ_RDR | I2C_IRQ_XDR) | I2C_IRQ_XUDF | I2C_IRQ_ROVR | I2C_IRQ_RDR | I2C_IRQ_XDR)
#define I2C_WE_AL (1 << 0) /* Bit 0: Arbitration lost */ #define I2C_WE_AL (1 << 0) /* Bit 0: Arbitration lost */
#define I2C_WE_NACK (1 << 1) /* Bit 1: No acknowledgment */ #define I2C_WE_NACK (1 << 1) /* Bit 1: No acknowledgment */