Commit 434f14e7 authored by Cyrille Pitchen's avatar Cyrille Pitchen Committed by Wolfram Sang

i2c: at91: fix support of the "alternative command" feature

The "alternative command" feature was introduced with sama5d2 SoCs.

Its purpose is to let the hardware i2c controller automatically send the
STOP condition on the i2c bus at the end of a data transfer.
Without this feature, the i2c driver has to write the 'STOP' bit into the
Control Register so the hardware i2c controller is triggered to send the
STOP condition on the bus.

Using the "alternative command" feature requires to set the transfer data
length into the 8bit DATAL field of the Alternative Command Register.
Hence only data transfers up to 255 bytes can take advantage of the
"alternative command" feature. For greater data transfer sizes, the driver
should use the previous implementation, when the "alternative command"
support was not implemented yet.
Signed-off-by: default avatarCyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 97ccd4af
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */ #define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */ #define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
#define AUTOSUSPEND_TIMEOUT 2000 #define AUTOSUSPEND_TIMEOUT 2000
#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
/* AT91 TWI register definitions */ /* AT91 TWI register definitions */
#define AT91_TWI_CR 0x0000 /* Control Register */ #define AT91_TWI_CR 0x0000 /* Control Register */
...@@ -141,6 +142,7 @@ struct at91_twi_dev { ...@@ -141,6 +142,7 @@ struct at91_twi_dev {
unsigned twi_cwgr_reg; unsigned twi_cwgr_reg;
struct at91_twi_pdata *pdata; struct at91_twi_pdata *pdata;
bool use_dma; bool use_dma;
bool use_alt_cmd;
bool recv_len_abort; bool recv_len_abort;
u32 fifo_size; u32 fifo_size;
struct at91_twi_dma dma; struct at91_twi_dma dma;
...@@ -269,7 +271,7 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev) ...@@ -269,7 +271,7 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
/* send stop when last byte has been written */ /* send stop when last byte has been written */
if (--dev->buf_len == 0) if (--dev->buf_len == 0)
if (!dev->pdata->has_alt_cmd) if (!dev->use_alt_cmd)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len); dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len);
...@@ -292,7 +294,7 @@ static void at91_twi_write_data_dma_callback(void *data) ...@@ -292,7 +294,7 @@ static void at91_twi_write_data_dma_callback(void *data)
* we just have to enable TXCOMP one. * we just have to enable TXCOMP one.
*/ */
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP);
if (!dev->pdata->has_alt_cmd) if (!dev->use_alt_cmd)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
} }
...@@ -410,7 +412,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev) ...@@ -410,7 +412,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
} }
/* send stop if second but last byte has been read */ /* send stop if second but last byte has been read */
if (!dev->pdata->has_alt_cmd && dev->buf_len == 1) if (!dev->use_alt_cmd && dev->buf_len == 1)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len); dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
...@@ -426,7 +428,7 @@ static void at91_twi_read_data_dma_callback(void *data) ...@@ -426,7 +428,7 @@ static void at91_twi_read_data_dma_callback(void *data)
dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]),
dev->buf_len, DMA_FROM_DEVICE); dev->buf_len, DMA_FROM_DEVICE);
if (!dev->pdata->has_alt_cmd) { if (!dev->use_alt_cmd) {
/* The last two bytes have to be read without using dma */ /* The last two bytes have to be read without using dma */
dev->buf += dev->buf_len - 2; dev->buf += dev->buf_len - 2;
dev->buf_len = 2; dev->buf_len = 2;
...@@ -443,7 +445,7 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev) ...@@ -443,7 +445,7 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev)
struct dma_chan *chan_rx = dma->chan_rx; struct dma_chan *chan_rx = dma->chan_rx;
size_t buf_len; size_t buf_len;
buf_len = (dev->pdata->has_alt_cmd) ? dev->buf_len : dev->buf_len - 2; buf_len = (dev->use_alt_cmd) ? dev->buf_len : dev->buf_len - 2;
dma->direction = DMA_FROM_DEVICE; dma->direction = DMA_FROM_DEVICE;
/* Keep in mind that we won't use dma to read the last two bytes */ /* Keep in mind that we won't use dma to read the last two bytes */
...@@ -651,7 +653,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ...@@ -651,7 +653,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
unsigned start_flags = AT91_TWI_START; unsigned start_flags = AT91_TWI_START;
/* if only one byte is to be read, immediately stop transfer */ /* if only one byte is to be read, immediately stop transfer */
if (!has_alt_cmd && dev->buf_len <= 1 && if (!dev->use_alt_cmd && dev->buf_len <= 1 &&
!(dev->msg->flags & I2C_M_RECV_LEN)) !(dev->msg->flags & I2C_M_RECV_LEN))
start_flags |= AT91_TWI_STOP; start_flags |= AT91_TWI_STOP;
at91_twi_write(dev, AT91_TWI_CR, start_flags); at91_twi_write(dev, AT91_TWI_CR, start_flags);
...@@ -745,7 +747,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) ...@@ -745,7 +747,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
int ret; int ret;
unsigned int_addr_flag = 0; unsigned int_addr_flag = 0;
struct i2c_msg *m_start = msg; struct i2c_msg *m_start = msg;
bool is_read, use_alt_cmd = false; bool is_read;
dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num); dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
...@@ -768,14 +770,16 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) ...@@ -768,14 +770,16 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
at91_twi_write(dev, AT91_TWI_IADR, internal_address); at91_twi_write(dev, AT91_TWI_IADR, internal_address);
} }
dev->use_alt_cmd = false;
is_read = (m_start->flags & I2C_M_RD); is_read = (m_start->flags & I2C_M_RD);
if (dev->pdata->has_alt_cmd) { if (dev->pdata->has_alt_cmd) {
if (m_start->len > 0) { if (m_start->len > 0 &&
m_start->len < AT91_I2C_MAX_ALT_CMD_DATA_SIZE) {
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMEN);
at91_twi_write(dev, AT91_TWI_ACR, at91_twi_write(dev, AT91_TWI_ACR,
AT91_TWI_ACR_DATAL(m_start->len) | AT91_TWI_ACR_DATAL(m_start->len) |
((is_read) ? AT91_TWI_ACR_DIR : 0)); ((is_read) ? AT91_TWI_ACR_DIR : 0));
use_alt_cmd = true; dev->use_alt_cmd = true;
} else { } else {
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMDIS); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMDIS);
} }
...@@ -784,7 +788,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) ...@@ -784,7 +788,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
at91_twi_write(dev, AT91_TWI_MMR, at91_twi_write(dev, AT91_TWI_MMR,
(m_start->addr << 16) | (m_start->addr << 16) |
int_addr_flag | int_addr_flag |
((!use_alt_cmd && is_read) ? AT91_TWI_MREAD : 0)); ((!dev->use_alt_cmd && is_read) ? AT91_TWI_MREAD : 0));
dev->buf_len = m_start->len; dev->buf_len = m_start->len;
dev->buf = m_start->buf; dev->buf = m_start->buf;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment