mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
i2c for v7.4, part 2
This second part introduces dynamic transfer timeouts in the I2C core, already used by qcom-geni. Alongside some driver improvements and fixes, it also brings in the Rust I2C pull request. Core: - core, qcom-geni: add and use a dynamic timeout helper - acpi: fix adapter reference leak when creating I2C clients Drivers: - imx: support reversed address direction and no-start transfers - designware: queue block data reads once length is known - mt7621: skip the second data register for chunks up to 4 bytes - sh_mobile: ensure IRQs are freed before unmapping registers Rust: - Remove unsound device-to-client reference conversion -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQScDfrjQa34uOld1VLaeAVmJtMtbgUCat5h5QAKCRDaeAVmJtMt biyNAP9gA0/jhwsviVHxdXGc5MwNIA3ldVWH3DInK0Ei0zdUvwEA3LtcEdtrxWy9 ifakgLufzgUdzxv1H+h7HPimEyEqcQc= =ZZC3 -----END PGP SIGNATURE----- Merge tag 'i2c-7.4-part2' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux Pull more i2c updates from Andi Shyti: "This introduces dynamic transfer timeouts in the I2C core, already used by qcom-geni. Alongside some driver improvements and fixes, it also brings in the Rust I2C pull request. Core: - core, qcom-geni: add and use a dynamic timeout helper - acpi: fix adapter reference leak when creating I2C clients Drivers: - imx: support reversed address direction and no-start transfers - designware: queue block data reads once length is known - mt7621: skip the second data register for chunks up to 4 bytes - sh_mobile: ensure IRQs are freed before unmapping registers Rust: - Remove unsound device-to-client reference conversion" * tag 'i2c-7.4-part2' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux: i2c: sh_mobile: request IRQs after mapping registers i2c: imx: add I2C_M_REV_DIR_ADDR and I2C_M_NOSTART i2c: mt7621: fix copying uninitialized kernel stack with reduced page_len i2c: designware: queue only one read command for block reads i2c: acpi: Fix adapter reference leak in i2c_acpi_new_device_by_fwnode() i2c: qcom-geni: Add dynamic transfer timeout based on transfer length and frequency i2c: core: Add i2c_update_timeout() helper for dynamic transfer timeouts rust: i2c: remove TryFrom<&Device> implementation for &I2cClient
This commit is contained in:
commit
25e7ab89f4
|
|
@ -136,6 +136,19 @@ config I2C_SLAVE_TESTUNIT
|
|||
|
||||
endif
|
||||
|
||||
config I2C_DYNAMIC_TIMEOUT
|
||||
bool "Dynamic per-transfer timeout based on message length"
|
||||
depends on I2C
|
||||
help
|
||||
When enabled, the I2C core computes a per-transfer timeout from the
|
||||
message length and bus frequency instead of using a static 1-second
|
||||
default. A timeout explicitly configured via the I2C_TIMEOUT userspace
|
||||
interface always takes precedence over the computed value.
|
||||
|
||||
When disabled, the existing static 1-second timeout is preserved.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config I2C_DEBUG_CORE
|
||||
bool "I2C Core debugging messages"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -441,6 +441,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
if (dev->rx_outstanding >= dev->rx_fifo_depth)
|
||||
break;
|
||||
|
||||
/*
|
||||
* For block reads, queue only the length
|
||||
* byte until i2c_dw_recv_len() runs.
|
||||
*/
|
||||
if ((flags & I2C_M_RECV_LEN) &&
|
||||
buf_len < msgs[dev->msg_write_idx].len)
|
||||
break;
|
||||
|
||||
regmap_write(dev->map, DW_IC_DATA_CMD,
|
||||
cmd | 0x100);
|
||||
rx_limit--;
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -144,7 +144,6 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
u16 addr;
|
||||
int i, j, ret, len, page_len;
|
||||
u32 cmd;
|
||||
u32 data[2];
|
||||
|
||||
i2c = i2c_get_adapdata(adap);
|
||||
|
||||
|
|
@ -186,6 +185,8 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
|
||||
/* transfer data */
|
||||
for (len = pmsg->len, j = 0; len > 0; len -= 8, j += 8) {
|
||||
u32 data[2] = { };
|
||||
|
||||
page_len = (len >= 8) ? 8 : len;
|
||||
|
||||
if (pmsg->flags & I2C_M_RD) {
|
||||
|
|
@ -194,7 +195,8 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
} else {
|
||||
memcpy(data, &pmsg->buf[j], page_len);
|
||||
iowrite32(data[0], i2c->base + REG_SM0D0_REG);
|
||||
iowrite32(data[1], i2c->base + REG_SM0D1_REG);
|
||||
if (page_len > 4)
|
||||
iowrite32(data[1], i2c->base + REG_SM0D1_REG);
|
||||
cmd = SM0CTL1_WRITE;
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +206,8 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
|
||||
if (pmsg->flags & I2C_M_RD) {
|
||||
data[0] = ioread32(i2c->base + REG_SM0D0_REG);
|
||||
data[1] = ioread32(i2c->base + REG_SM0D1_REG);
|
||||
if (page_len > 4)
|
||||
data[1] = ioread32(i2c->base + REG_SM0D1_REG);
|
||||
memcpy(&pmsg->buf[j], data, page_len);
|
||||
} else {
|
||||
if (!(pmsg->flags & I2C_M_IGNORE_NAK)) {
|
||||
|
|
|
|||
|
|
@ -79,9 +79,14 @@ enum geni_i2c_err_code {
|
|||
|
||||
#define ABORT_TIMEOUT HZ
|
||||
#define CANCEL_TIMEOUT HZ
|
||||
#define XFER_TIMEOUT HZ
|
||||
#define RST_TIMEOUT HZ
|
||||
|
||||
/* 9 bits per byte (8 data + 1 ACK), 10x safety margin */
|
||||
#define I2C_TIMEOUT_SAFETY_COEFFICIENT 10
|
||||
|
||||
/* 300ms floor: budget for clock stretching; slave may hold SCL low indefinitely */
|
||||
#define I2C_TIMEOUT_MIN_USEC 300000
|
||||
|
||||
#define GENI_SE_CLK_32MHZ (32 * HZ_PER_MHZ)
|
||||
#define GENI_SE_CLK_19P2MHZ 19200000UL
|
||||
|
||||
|
|
@ -545,7 +550,10 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
}
|
||||
|
||||
cur = gi2c->cur;
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, len,
|
||||
I2C_TIMEOUT_SAFETY_COEFFICIENT,
|
||||
I2C_TIMEOUT_MIN_USEC);
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, gi2c->adap.timeout);
|
||||
if (!time_left || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err))
|
||||
geni_i2c_cancel_xfer(gi2c);
|
||||
|
||||
|
|
@ -587,7 +595,10 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
writel_relaxed(1, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
|
||||
cur = gi2c->cur;
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, len,
|
||||
I2C_TIMEOUT_SAFETY_COEFFICIENT,
|
||||
I2C_TIMEOUT_MIN_USEC);
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, gi2c->adap.timeout);
|
||||
if (!time_left || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err))
|
||||
geni_i2c_cancel_xfer(gi2c);
|
||||
|
||||
|
|
@ -665,7 +676,7 @@ static void geni_i2c_gpi_multi_desc_unmap(struct geni_i2c_dev *gi2c, struct i2c_
|
|||
* geni_i2c_gpi_multi_xfer_timeout_handler() - Handles multi message transfer timeout
|
||||
* @dev: Pointer to the corresponding dev node
|
||||
* @multi_xfer: Pointer to the geni_i2c_gpi_multi_desc_xfer
|
||||
* @transfer_timeout_msecs: Timeout value in milliseconds
|
||||
* @timeout_jiffies: Per-message completion timeout in jiffies
|
||||
* @transfer_comp: Completion object of the transfer
|
||||
*
|
||||
* This function waits for the completion of each processed transfer messages
|
||||
|
|
@ -675,18 +686,18 @@ static void geni_i2c_gpi_multi_desc_unmap(struct geni_i2c_dev *gi2c, struct i2c_
|
|||
*/
|
||||
static int geni_i2c_gpi_multi_xfer_timeout_handler(struct device *dev,
|
||||
struct geni_i2c_gpi_multi_desc_xfer *multi_xfer,
|
||||
u32 transfer_timeout_msecs,
|
||||
unsigned long timeout_jiffies,
|
||||
struct completion *transfer_comp)
|
||||
{
|
||||
int i;
|
||||
u32 time_left;
|
||||
unsigned long time_left;
|
||||
|
||||
for (i = 0; i < multi_xfer->msg_idx_cnt - 1; i++) {
|
||||
reinit_completion(transfer_comp);
|
||||
|
||||
if (multi_xfer->msg_idx_cnt != multi_xfer->irq_cnt) {
|
||||
time_left = wait_for_completion_timeout(transfer_comp,
|
||||
transfer_timeout_msecs);
|
||||
timeout_jiffies);
|
||||
if (!time_left) {
|
||||
dev_err(dev, "%s: Transfer timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
|
|
@ -810,8 +821,24 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
|
|||
dma_async_issue_pending(gi2c->tx_c);
|
||||
|
||||
if ((msg_idx == (gi2c->num_msgs - 1)) || flags & DMA_PREP_INTERRUPT) {
|
||||
size_t total_len = 0;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* All TREs except the last carry the BEI bit, so a single
|
||||
* completion interrupt fires only after the entire batch has
|
||||
* drained on the wire. The timeout budget must therefore cover
|
||||
* the combined wire time of every message in the batch.
|
||||
*/
|
||||
for (j = 0; j < gi2c->num_msgs; j++)
|
||||
total_len += msgs[j].len;
|
||||
|
||||
i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, total_len,
|
||||
I2C_TIMEOUT_SAFETY_COEFFICIENT,
|
||||
I2C_TIMEOUT_MIN_USEC);
|
||||
ret = geni_i2c_gpi_multi_xfer_timeout_handler(gi2c->se.dev, gi2c_gpi_xfer,
|
||||
XFER_TIMEOUT, &gi2c->done);
|
||||
gi2c->adap.timeout,
|
||||
&gi2c->done);
|
||||
if (ret) {
|
||||
dev_err(gi2c->se.dev,
|
||||
"I2C multi write msg transfer timeout: %d\n",
|
||||
|
|
@ -931,7 +958,10 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
|
|||
|
||||
if (!gi2c->is_tx_multi_desc_xfer) {
|
||||
dma_async_issue_pending(gi2c->tx_c);
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, msgs[i].len,
|
||||
I2C_TIMEOUT_SAFETY_COEFFICIENT,
|
||||
I2C_TIMEOUT_MIN_USEC);
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, gi2c->adap.timeout);
|
||||
if (!time_left) {
|
||||
dev_err(gi2c->se.dev, "%s:I2C timeout\n", __func__);
|
||||
gi2c->err = -ETIMEDOUT;
|
||||
|
|
|
|||
|
|
@ -880,10 +880,6 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
|||
return PTR_ERR(pd->clk);
|
||||
}
|
||||
|
||||
ret = sh_mobile_i2c_hook_irqs(dev, pd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pd->dev = &dev->dev;
|
||||
platform_set_drvdata(dev, pd);
|
||||
|
||||
|
|
@ -891,6 +887,10 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
|||
if (IS_ERR(pd->reg))
|
||||
return PTR_ERR(pd->reg);
|
||||
|
||||
ret = sh_mobile_i2c_hook_irqs(dev, pd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed);
|
||||
pd->bus_speed = (ret || !bus_speed) ? I2C_MAX_STANDARD_MODE_FREQ : bus_speed;
|
||||
pd->clks_per_count = 1;
|
||||
|
|
|
|||
|
|
@ -549,6 +549,7 @@ struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode,
|
|||
{
|
||||
struct i2c_acpi_lookup lookup;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client;
|
||||
struct acpi_device *adev;
|
||||
LIST_HEAD(resource_list);
|
||||
int ret;
|
||||
|
|
@ -576,7 +577,10 @@ struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode,
|
|||
if (!adapter)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return i2c_new_client_device(adapter, info);
|
||||
client = i2c_new_client_device(adapter, info);
|
||||
put_device(&adapter->dev);
|
||||
|
||||
return client;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_acpi_new_device_by_fwnode);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
|
|
@ -2010,6 +2011,48 @@ void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_de
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
|
||||
|
||||
#ifdef CONFIG_I2C_DYNAMIC_TIMEOUT
|
||||
/**
|
||||
* i2c_update_timeout - compute and set a dynamic transfer timeout on an adapter
|
||||
* @adap: the i2c_adapter whose timeout field will be updated
|
||||
* @bus_freq_hz: I2C bus clock frequency in Hz
|
||||
* @len: transfer length in bytes
|
||||
* @safety_coeff: multiplier applied over the theoretical wire time
|
||||
* @min_usec: minimum timeout floor in microseconds
|
||||
*
|
||||
* Computes a transfer-specific timeout from the message length and bus
|
||||
* frequency, applies a safety multiplier and a minimum floor, then stores
|
||||
* the result in adap->timeout (in jiffies). The caller supplies the policy
|
||||
* constants so they remain internal to the driver.
|
||||
*
|
||||
* A timeout explicitly configured by userspace via the I2C_TIMEOUT ioctl
|
||||
* always takes precedence; the computed value is used only when userspace
|
||||
* has not configured one.
|
||||
*/
|
||||
void i2c_update_timeout(struct i2c_adapter *adap, u32 bus_freq_hz,
|
||||
size_t len, unsigned int safety_coeff,
|
||||
unsigned int min_usec)
|
||||
{
|
||||
u64 bit_usec, total_usec;
|
||||
unsigned long jiffies_val;
|
||||
|
||||
if (adap->user_timeout > 0) {
|
||||
adap->timeout = adap->user_timeout;
|
||||
return;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(!bus_freq_hz))
|
||||
return;
|
||||
|
||||
bit_usec = mul_u64_u32_div(len * 9, USEC_PER_SEC, bus_freq_hz);
|
||||
total_usec = bit_usec * safety_coeff + min_usec;
|
||||
|
||||
jiffies_val = usecs_to_jiffies((unsigned int)min_t(u64, total_usec, UINT_MAX));
|
||||
adap->timeout = (int)min_t(unsigned long, jiffies_val, INT_MAX);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_update_timeout);
|
||||
#endif /* CONFIG_I2C_DYNAMIC_TIMEOUT */
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data))
|
||||
|
|
|
|||
|
|
@ -495,6 +495,9 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
return -EINVAL;
|
||||
|
||||
client->adapter->timeout = msecs_to_jiffies(arg * 10);
|
||||
#ifdef CONFIG_I2C_DYNAMIC_TIMEOUT
|
||||
client->adapter->user_timeout = client->adapter->timeout;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
/* NOTE: returning a fault code here could cause trouble
|
||||
|
|
|
|||
|
|
@ -757,6 +757,9 @@ struct i2c_adapter {
|
|||
struct rt_mutex mux_lock;
|
||||
|
||||
int timeout; /* in jiffies */
|
||||
#ifdef CONFIG_I2C_DYNAMIC_TIMEOUT
|
||||
int user_timeout; /* I2C_TIMEOUT ioctl value in jiffies */
|
||||
#endif
|
||||
int retries;
|
||||
struct device dev; /* the adapter device */
|
||||
unsigned long locked_flags; /* owned by the I2C core */
|
||||
|
|
@ -928,6 +931,16 @@ unsigned int i2c_adapter_depth(struct i2c_adapter *adapter);
|
|||
|
||||
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults);
|
||||
|
||||
#ifdef CONFIG_I2C_DYNAMIC_TIMEOUT
|
||||
void i2c_update_timeout(struct i2c_adapter *adap, u32 bus_freq_hz,
|
||||
size_t len, unsigned int safety_coeff,
|
||||
unsigned int min_usec);
|
||||
#else
|
||||
static inline void i2c_update_timeout(struct i2c_adapter *adap, u32 bus_freq_hz,
|
||||
size_t len, unsigned int safety_coeff,
|
||||
unsigned int min_usec) {}
|
||||
#endif
|
||||
|
||||
/* Return the functionality mask */
|
||||
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// I2C Driver abstractions.
|
||||
use crate::{
|
||||
acpi,
|
||||
container_of,
|
||||
device,
|
||||
device_id::{
|
||||
RawDeviceId,
|
||||
|
|
@ -517,26 +516,6 @@ fn as_ref(&self) -> &device::Device<Ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &I2cClient<Ctx> {
|
||||
type Error = kernel::error::Error;
|
||||
|
||||
fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
|
||||
// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
|
||||
// `struct device`.
|
||||
if unsafe { bindings::i2c_verify_client(dev.as_raw()).is_null() } {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: We've just verified that the type of `dev` equals to
|
||||
// `bindings::i2c_client_type`, hence `dev` must be embedded in a valid
|
||||
// `struct i2c_client` as guaranteed by the corresponding C code.
|
||||
let idev = unsafe { container_of!(dev.as_raw(), bindings::i2c_client, dev) };
|
||||
|
||||
// SAFETY: `idev` is a valid pointer to a `struct i2c_client`.
|
||||
Ok(unsafe { &*idev.cast() })
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `I2cClient` is always reference-counted and can be released from any thread.
|
||||
unsafe impl Send for I2cClient {}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user