diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-09-11 17:52:23 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-09-11 17:52:23 -0600 |
commit | 739500903c4800c98dc43e26f09eaa2e8789813a (patch) | |
tree | 7f13a361599cba04d48d3ddfff21741a702e64fa | |
parent | 038431ed2be1c38bac7ca3a061075d66c5e4b7a3 (diff) | |
download | nuttx-739500903c4800c98dc43e26f09eaa2e8789813a.tar.gz nuttx-739500903c4800c98dc43e26f09eaa2e8789813a.tar.bz2 nuttx-739500903c4800c98dc43e26f09eaa2e8789813a.zip |
SAMA5 TWI: Add support for I2C readwrite and transfer methods
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_twi.c | 348 |
1 files changed, 280 insertions, 68 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_twi.c b/nuttx/arch/arm/src/sama5/sam_twi.c index 4103cb77a..c2d5ce9aa 100644 --- a/nuttx/arch/arm/src/sama5/sam_twi.c +++ b/nuttx/arch/arm/src/sama5/sam_twi.c @@ -137,9 +137,12 @@ struct twi_dev_s { struct i2c_dev_s dev; /* Generic I2C device */ - struct i2c_msg_s msg; /* A single message for legacy read/write */ + struct i2c_msg_s *msg; /* Message list */ uintptr_t base; /* Base address of registers */ uint16_t irq; /* IRQ number for this device */ + uint16_t address; /* Slave address */ + uint16_t flags; /* Transfer flags */ + uint8_t msgc; /* Number of message in the message list */ uint8_t twi; /* TWI peripheral number (for debug output) */ sem_t exclsem; /* Only one thread can access at a time */ @@ -194,6 +197,10 @@ static int twi2_interrupt(int irq, FAR void *context); #endif static void twi_timeout(int argc, uint32_t arg, ...); +static int twi_startread(struct twi_dev_s *priv, struct i2c_msg_s *msg); +static int twi_startwrite(struct twi_dev_s *priv, struct i2c_msg_s *msg); +static int twi_startmessage(struct twi_dev_s *priv, struct i2c_msg_s *msg); + /* I2C device operations */ static uint32_t twi_setfrequency(FAR struct i2c_dev_s *dev, @@ -203,8 +210,8 @@ static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int buflen); static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen); #ifdef CONFIG_I2C_WRITEREAD -static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *buffer, - int buflen, uint8_t *rbuffer, int buflen); +static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *wbuffer, + int wbuflen, uint8_t *rbuffer, int rbuflen); #endif #ifdef CONFIG_I2C_TRANSFER static int twi_transfer(FAR struct i2c_dev_s *dev, @@ -433,10 +440,12 @@ static int twi_wait(struct twi_dev_s *priv) static int twi_interrupt(struct twi_dev_s *priv) { + struct i2c_msg_s *msg; uint32_t sr; uint32_t imr; uint32_t pending; uint32_t regval; + int ret; /* Retrieve masked interrupt status */ @@ -448,14 +457,15 @@ static int twi_interrupt(struct twi_dev_s *priv) /* Byte received */ + msg = priv->msg; if ((pending & TWI_INT_RXRDY) == TWI_INT_RXRDY) { - priv->msg.buffer[priv->xfrd] = twi_getreg(priv, SAM_TWI_RHR_OFFSET); + msg->buffer[priv->xfrd] = twi_getreg(priv, SAM_TWI_RHR_OFFSET); priv->xfrd++; /* Check for transfer complete */ - if (priv->xfrd >= priv->msg.length) + if (priv->xfrd >= msg->length) { /* The transfer is complete. Disable the RXRDY interrupt and * enable the TXCOMP interrupt @@ -467,7 +477,7 @@ static int twi_interrupt(struct twi_dev_s *priv) /* Not yet complete, but will the next be the last byte? */ - else if (priv->xfrd == (priv->msg.length - 1)) + else if (priv->xfrd == (msg->length - 1)) { /* Yes, set the stop signal */ @@ -481,7 +491,7 @@ static int twi_interrupt(struct twi_dev_s *priv) { /* Transfer finished? */ - if (priv->xfrd >= priv->msg.length) + if (priv->xfrd >= msg->length) { /* The transfer is complete. Disable the TXRDY interrupt and * enable the TXCOMP interrupt @@ -501,7 +511,7 @@ static int twi_interrupt(struct twi_dev_s *priv) else { - twi_putreg(priv, SAM_TWI_THR_OFFSET, priv->msg.buffer[priv->xfrd]); + twi_putreg(priv, SAM_TWI_THR_OFFSET, msg->buffer[priv->xfrd]); priv->xfrd++; } } @@ -511,11 +521,34 @@ static int twi_interrupt(struct twi_dev_s *priv) else if ((pending & TWI_INT_TXCOMP) == TWI_INT_TXCOMP) { twi_putreg(priv, SAM_TWI_IDR_OFFSET, TWI_INT_TXCOMP); - priv->result = OK; + ret = OK; + + /* Is there another messasge to send? */ - /* Wake up the waiting thread */ + if (priv->msgc > 1) + { + /* Yes... start the next message */ + + priv->msg++; + priv->msgc--; + ret = twi_startmessage(priv, priv->msg); + if (ret < 0) + { + /* Wake up the thread with the error indication */ + + priv->result = ret; + twi_givesem(&priv->waitsem); + } + } + else + { + /* No.. we made it to the end of the message list with no errors. + * Wake up the waiting thread with a success indication. + */ - twi_givesem(&priv->waitsem); + priv->result = OK; + twi_givesem(&priv->waitsem); + } } return OK; @@ -563,6 +596,123 @@ static void twi_timeout(int argc, uint32_t arg, ...) } /******************************************************************************* + * Name: twi_startread + * + * Description: + * Start the next read message + * + *******************************************************************************/ + +static int twi_startread(struct twi_dev_s *priv, struct i2c_msg_s *msg) +{ + irqstate_t flags; + int ret; + + /* This function might be called from the interrupt level */ + + flags = irqsave(); + + /* Setup for the transfer */ + + priv->result = -EBUSY; + priv->xfrd = 0; + + /* Set STOP signal if only one byte is sent*/ + + if (msg->length == 1) + { + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_STOP); + } + + /* Set slave address and number of internal address bytes. */ + + twi_putreg(priv, SAM_TWI_MMR_OFFSET, 0); + twi_putreg(priv, SAM_TWI_MMR_OFFSET, TWI_MMR_IADRSZ_NONE | TWI_MMR_MREAD | TWI_MMR_DADR(msg->addr)); + + /* Set internal address bytes */ + + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + + /* Enable read interrupt and send the START condition */ + + twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_RXRDY); + + if ((msg->flags & I2C_M_NORESTART) == 0) + { + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_START); + } + + ret = twi_wait(priv); + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: twi_startwrite + * + * Description: + * Start the next write message + * + *******************************************************************************/ + +static int twi_startwrite(struct twi_dev_s *priv, struct i2c_msg_s *msg) +{ + irqstate_t flags; + int ret; + + /* This function might be called from the interrupt level */ + + flags = irqsave(); + + /* Setup for the transfer */ + + priv->result = -EBUSY; + priv->xfrd = 0; + + /* Set slave address and number of internal address bytes. */ + + twi_putreg(priv, SAM_TWI_MMR_OFFSET, 0); + twi_putreg(priv, SAM_TWI_MMR_OFFSET, TWI_MMR_IADRSZ_NONE | TWI_MMR_DADR(msg->addr)); + + /* Set internal address bytes. */ + + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + + /* Write first byte to send.*/ + + twi_putreg(priv, SAM_TWI_THR_OFFSET, msg->buffer[0]); + + /* Enable write interrupt */ + + twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_TXRDY); + ret = twi_wait(priv); + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: twi_startmessage + * + * Description: + * Start the next write message + * + *******************************************************************************/ + +static int twi_startmessage(struct twi_dev_s *priv, struct i2c_msg_s *msg) +{ + if ((msg->flags & I2C_M_READ) == 0) + { + return twi_startread(priv, msg); + } + else + { + return twi_startwrite(priv, msg); + } +} + +/******************************************************************************* * I2C device operations *******************************************************************************/ @@ -613,8 +763,8 @@ static int twi_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) /* Set the correctly shifted, 7-bit address */ - priv->msg.addr = addr << 1; - priv->msg.flags = 0 ; + priv->address = addr; + priv->flags = (nbits == 10) ? I2C_M_TEN : 0; twi_givesem(&priv->exclsem); return OK; @@ -632,9 +782,16 @@ static int twi_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int buflen) { struct twi_dev_s *priv = (struct twi_dev_s *) dev; - uint8_t devaddr; int ret; + struct i2c_msg_s msg = + { + .addr = priv->address, + .flags = priv->flags, + .buffer = (uint8_t *)buffer, + .length = buflen + }; + i2cvdbg("TWI%d buflen: %d\n", priv->twi, buflen); DEBUGASSERT(dev != NULL); @@ -642,33 +799,23 @@ static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int bufle twi_takesem(&priv->exclsem); - priv->msg.addr &= ~0x01; - priv->msg.buffer = (uint8_t*)buffer; - priv->msg.length = buflen; - priv->result = -EBUSY; - priv->xfrd = 0; + /* Initiate the wrte */ - /* Set slave address and number of internal address bytes. */ - - twi_putreg(priv, SAM_TWI_MMR_OFFSET, 0); + priv->msg = &msg; + priv->msgc = 1; - devaddr = priv->msg.addr >> 1; - twi_putreg(priv, SAM_TWI_MMR_OFFSET, TWI_MMR_IADRSZ_NONE | TWI_MMR_DADR(devaddr)); - - /* Set internal address bytes. */ - - twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); - twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); - - /* Write first byte to send.*/ - - twi_putreg(priv, SAM_TWI_THR_OFFSET, *buffer); + ret = twi_startwrite(priv, &msg); + if (ret < 0) + { + i2cdbg("ERROR: twi_startwrite failed: %d\n", ret); + goto errout; + } - /* Enable write interrupt */ + /* And wait for the wrtie to complete */ - twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_TXRDY); ret = twi_wait(priv); +errout: twi_givesem(&priv->exclsem); return ret; } @@ -684,49 +831,41 @@ static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int bufle static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen) { - struct twi_dev_s *priv = (struct twi_dev_s *) dev; - uint8_t devaddr; + struct twi_dev_s *priv = (struct twi_dev_s *)dev; int ret; - i2cvdbg(TWI%d "buflen: %d\n", priv->twi, buflen); + struct i2c_msg_s msg = + { + .addr = priv->address, + .flags = priv->flags | I2C_M_READ, + .buffer = buffer, + .length = buflen + }; + DEBUGASSERT(dev != NULL); + i2cvdbg(TWI%d "buflen: %d\n", priv->twi, buflen); /* Get exclusive access to the device */ twi_takesem(&priv->exclsem); - priv->msg.addr |= 0x01; - priv->msg.buffer = buffer; - priv->msg.length = buflen; - priv->result = -EBUSY; - priv->xfrd = 0; + /* Initiate the read */ - /* Set STOP signal if only one byte is sent*/ + priv->msg = &msg; + priv->msgc = 1; - if (buflen == 1) + ret = twi_startread(priv, &msg); + if (ret < 0) { - twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_STOP); + i2cdbg("ERROR: twi_startread failed: %d\n", ret); + goto errout; } - /* Set slave address and number of internal address bytes. */ - - twi_putreg(priv, SAM_TWI_MMR_OFFSET, 0); - - devaddr = priv->msg.addr >> 1; - twi_putreg(priv, SAM_TWI_MMR_OFFSET, TWI_MMR_IADRSZ_NONE | TWI_MMR_MREAD | TWI_MMR_DADR(devaddr)); - - /* Set internal address bytes */ - - twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); - twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); - - /* Enable read interrupt and send the START codnition */ - - twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_RXRDY); - twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_START); + /* And wait for the read to complete */ ret = twi_wait(priv); +errout: twi_givesem(&priv->exclsem); return ret; } @@ -739,11 +878,54 @@ static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen) *******************************************************************************/ #ifdef CONFIG_I2C_WRITEREAD -static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *buffer, - int buflen, uint8_t *rbuffer, int buflen) +static int twi_writeread(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer, + int wbuflen, uint8_t *rbuffer, int rbuflen) { -#error Not implemented - return -ENOSYS; + struct twi_dev_s *priv = (struct twi_dev_s *)dev; + int ret; + + struct i2c_msg_s msgv[2] = + { + { + .addr = priv->address, + .flags = priv->flags, + .buffer = (uint8_t *)wbuffer, /* Override const */ + .length = wbuflen + }, + { + .addr = priv->address, + .flags = priv->flags | ((rbuflen > 0) ? I2C_M_READ : I2C_M_NORESTART), + .buffer = rbuffer, + .length = (rbuflen < 0) ? -rbuflen : rbuflen + } + }; + + DEBUGASSERT(dev != NULL); + i2cvdbg(TWI%d "wbuflen: %d rbuflen: %d\n", priv->twi, wbuflen, rbuflen); + + /* Get exclusive access to the device */ + + twi_takesem(&priv->exclsem); + + /* Initiate the read */ + + priv->msg = msgv; + priv->msgc = 2; + + ret = twi_startread(priv, msgv); + if (ret < 0) + { + i2cdbg("ERROR: twi_startread failed: %d\n", ret); + goto errout; + } + + /* And wait for the read to complete */ + + ret = twi_wait(priv); + +errout: + twi_givesem(&priv->exclsem); + return ret; } #endif @@ -791,8 +973,35 @@ static int twi_registercallback(FAR struct i2c_dev_s *dev, static int twi_transfer(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int count) { -#error Not implemented - return -ENOSYS; + struct twi_dev_s *priv = (struct twi_dev_s *)dev; + int ret; + + DEBUGASSERT(dev != NULL); + i2cvdbg(TWI%d "count: %d\n", priv->twi, count); + + /* Get exclusive access to the device */ + + twi_takesem(&priv->exclsem); + + /* Initiate the message transfer */ + + priv->msg = msgs; + priv->msgc = count; + + ret = twi_startmessage(priv, msgs); + if (ret < 0) + { + i2cdbg("ERROR: twi_startread failed: %d\n", ret); + goto errout; + } + + /* And wait for the read to complete */ + + ret = twi_wait(priv); + +errout: + twi_givesem(&priv->exclsem); + return ret; } #endif @@ -1012,6 +1221,9 @@ struct i2c_dev_s *up_i2cinitialize(int bus) /* Initialize the device structure */ priv->dev.ops = &g_twiops; + priv->address = 0; + priv->flags = 0; + sem_init(&priv->exclsem, 0, 1); sem_init(&priv->waitsem, 0, 0); |