mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
i2c: imx: add I2C_M_REV_DIR_ADDR and I2C_M_NOSTART
The Realtek RTL8366SE SMI read frame per: S Addr Rd [A] reg[7:0] [A] reg[15:8] [A] [data[7:0]] A [data[15:8]] NA P Linux support it using I2C_M_REV_DIR_ADDR on a write message that inverts the transmitted R/W bit, and I2C_M_NOSTART on the following read message that continues the frame without re-addressing. This NXP i2c-imx is missing such support, so on an LX2160A the Realtek switch could not be used. Signed-off-by: Vincent Jardin <vjardin@free.fr> Acked-by: Carlos Song <carlos.song@nxp.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Andi Shyti <andi.shyti@kernel.org> Link: https://patch.msgid.link/20261023-for-upstream-i2c-imx-lx2160-reverse-v3-1-a07b249df381@free.fr
This commit is contained in:
parent
b7fd977fec
commit
c2867bb0fa
|
|
@ -1000,16 +1000,10 @@ static inline int i2c_imx_isr_write(struct imx_i2c_struct *i2c_imx)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static inline int i2c_imx_isr_read(struct imx_i2c_struct *i2c_imx)
|
||||
static inline void i2c_imx_setup_read(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
int result;
|
||||
unsigned int temp;
|
||||
|
||||
result = i2c_imx_isr_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* setup bus to read data */
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp &= ~I2CR_MTX;
|
||||
if ((i2c_imx->msg->len - 1) || (i2c_imx->msg->flags & I2C_M_RECV_LEN))
|
||||
|
|
@ -1017,6 +1011,18 @@ static inline int i2c_imx_isr_read(struct imx_i2c_struct *i2c_imx)
|
|||
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
||||
}
|
||||
|
||||
static inline int i2c_imx_isr_read(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = i2c_imx_isr_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* setup bus to read data */
|
||||
i2c_imx_setup_read(i2c_imx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1172,6 +1178,16 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
|||
return i2c_imx_master_isr(i2c_imx, status);
|
||||
}
|
||||
|
||||
static u8 i2c_imx_addr_byte(struct i2c_msg *msg)
|
||||
{
|
||||
u8 addr = i2c_8bit_addr_from_msg(msg);
|
||||
|
||||
if (msg->flags & I2C_M_REV_DIR_ADDR)
|
||||
addr ^= 1;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||
struct i2c_msg *msgs)
|
||||
{
|
||||
|
|
@ -1200,7 +1216,7 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
|||
* Write slave address.
|
||||
* The first byte must be transmitted by the CPU.
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
imx_i2c_write_reg(i2c_imx_addr_byte(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
time_left = wait_for_completion_timeout(
|
||||
&i2c_imx->dma->cmd_complete,
|
||||
msecs_to_jiffies(DMA_TIMEOUT));
|
||||
|
|
@ -1242,14 +1258,20 @@ static int i2c_imx_prepare_read(struct imx_i2c_struct *i2c_imx,
|
|||
int result;
|
||||
unsigned int temp = 0;
|
||||
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, !use_dma);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
/*
|
||||
* I2C_M_NOSTART continues a frame that is already open, so there is
|
||||
* no address phase: go straight to turning the bus around.
|
||||
*/
|
||||
if (!(msgs->flags & I2C_M_NOSTART)) {
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_imx_addr_byte(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, !use_dma);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
|
||||
|
||||
|
|
@ -1371,16 +1393,18 @@ static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx,
|
|||
int i, result;
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
|
||||
__func__, i2c_8bit_addr_from_msg(msgs));
|
||||
__func__, i2c_imx_addr_byte(msgs));
|
||||
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, true);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
if (!(msgs->flags & I2C_M_NOSTART)) {
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_imx_addr_byte(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, true);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__);
|
||||
|
||||
/* write data */
|
||||
|
|
@ -1402,7 +1426,7 @@ static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx,
|
|||
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
||||
{
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
|
||||
__func__, i2c_8bit_addr_from_msg(msgs));
|
||||
__func__, i2c_imx_addr_byte(msgs));
|
||||
|
||||
i2c_imx->state = IMX_I2C_STATE_WRITE;
|
||||
i2c_imx->msg = msgs;
|
||||
|
|
@ -1411,8 +1435,16 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
|||
/*
|
||||
* By writing the device address we start the state machine in the ISR.
|
||||
* The ISR will report when it is done or when it fails.
|
||||
*
|
||||
* I2C_M_NOSTART continues a frame that is already open and so has no
|
||||
* address byte: push the first data byte instead. That raises the same
|
||||
* interrupt and the ISR carries on from the second byte.
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
if (msgs->flags & I2C_M_NOSTART)
|
||||
imx_i2c_write_reg(msgs->buf[i2c_imx->msg_buf_idx++], i2c_imx,
|
||||
IMX_I2C_I2DR);
|
||||
else
|
||||
imx_i2c_write_reg(i2c_imx_addr_byte(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
wait_event_timeout(i2c_imx->queue,
|
||||
i2c_imx->state == IMX_I2C_STATE_DONE ||
|
||||
i2c_imx->state == IMX_I2C_STATE_FAILED,
|
||||
|
|
@ -1529,22 +1561,29 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
|||
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> write slave address: addr=0x%x\n",
|
||||
__func__, i2c_8bit_addr_from_msg(msgs));
|
||||
__func__, i2c_imx_addr_byte(msgs));
|
||||
|
||||
i2c_imx->is_lastmsg = is_lastmsg;
|
||||
|
||||
if (block_data)
|
||||
i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA;
|
||||
else
|
||||
i2c_imx->state = IMX_I2C_STATE_READ;
|
||||
i2c_imx->msg = msgs;
|
||||
i2c_imx->msg_buf_idx = 0;
|
||||
|
||||
/*
|
||||
* By writing the device address we start the state machine in the ISR.
|
||||
* The ISR will report when it is done or when it fails.
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
if (msgs->flags & I2C_M_NOSTART) {
|
||||
i2c_imx->state = block_data ? IMX_I2C_STATE_READ_BLOCK_DATA_LEN
|
||||
: IMX_I2C_STATE_READ_CONTINUE;
|
||||
i2c_imx_setup_read(i2c_imx);
|
||||
} else {
|
||||
if (block_data)
|
||||
i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA;
|
||||
else
|
||||
i2c_imx->state = IMX_I2C_STATE_READ;
|
||||
|
||||
/*
|
||||
* By writing the device address we start the state machine in the ISR.
|
||||
* The ISR will report when it is done or when it fails.
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_imx_addr_byte(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
}
|
||||
wait_event_timeout(i2c_imx->queue,
|
||||
i2c_imx->state == IMX_I2C_STATE_DONE ||
|
||||
i2c_imx->state == IMX_I2C_STATE_FAILED,
|
||||
|
|
@ -1574,6 +1613,37 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define I2C_IMX_UNSUPPORTED_PROTOCOL_MANGLING_FLAGS \
|
||||
(I2C_M_IGNORE_NAK | I2C_M_NO_RD_ACK | I2C_M_STOP)
|
||||
|
||||
static int i2c_imx_check_msgs(struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
/* Reject rather than silently transfer */
|
||||
if (msgs[i].flags & I2C_IMX_UNSUPPORTED_PROTOCOL_MANGLING_FLAGS)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (msgs[i].flags & I2C_M_NOSTART) {
|
||||
/*
|
||||
* NOSTART continues an already open frame. The first
|
||||
* message has nothing to continue from,
|
||||
* a 0 length continuation leaves the controller with
|
||||
* no byte to clock.
|
||||
*/
|
||||
if (i == 0 || msgs[i].len == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((msgs[i].flags & I2C_M_RD) &&
|
||||
(msgs[i - 1].flags & I2C_M_RD))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num, bool atomic)
|
||||
{
|
||||
|
|
@ -1583,6 +1653,10 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
|||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
||||
int use_dma = 0;
|
||||
|
||||
result = i2c_imx_check_msgs(msgs, num);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* Start I2C transfer */
|
||||
result = i2c_imx_start(i2c_imx, atomic);
|
||||
if (result) {
|
||||
|
|
@ -1604,7 +1678,7 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
|||
if (i == num - 1)
|
||||
is_lastmsg = true;
|
||||
|
||||
if (i) {
|
||||
if (i && !(msgs[i].flags & I2C_M_NOSTART)) {
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> repeated start\n", __func__);
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
|
|
@ -1636,7 +1710,8 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
|||
#endif
|
||||
|
||||
use_dma = i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
|
||||
msgs[i].flags & I2C_M_DMA_SAFE;
|
||||
msgs[i].flags & I2C_M_DMA_SAFE &&
|
||||
!(msgs[i].flags & I2C_M_NOSTART);
|
||||
if (msgs[i].flags & I2C_M_RD) {
|
||||
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||
|
||||
|
|
@ -1730,7 +1805,9 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
|
|||
static u32 i2c_imx_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA
|
||||
| I2C_FUNC_NOSTART
|
||||
| I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm i2c_imx_algo = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user