summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/i2c/busses/i2c-at91-master.c8
-rw-r--r--drivers/i2c/busses/i2c-xiic.c61
2 files changed, 53 insertions, 16 deletions
diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c
index 68238cc8a..e0c3de5e8 100644
--- a/drivers/i2c/busses/i2c-at91-master.c
+++ b/drivers/i2c/busses/i2c-at91-master.c
@@ -921,8 +921,14 @@ int at91_twi_probe_master(struct platform_device *pdev,
at91_calc_twi_clock(dev);
rc = at91_init_twi_recovery_info(pdev, dev);
- if (rc == -EPROBE_DEFER)
+ if (rc == -EPROBE_DEFER) {
+ /*
+ * The DMA channels were already set up above, release them
+ * before deferring the probe so they are not leaked.
+ */
+ at91_twi_dma_release(dev);
return rc;
+ }
dev->adapter.algo = &at91_twi_algorithm;
dev->adapter.quirks = &at91_twi_quirks;
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index 3e7735e1d..5e397a7e6 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -73,6 +73,9 @@ enum i2c_scl_freq {
* @prev_msg_tx: Previous message is Tx
* @quirks: To hold platform specific bug info
* @smbus_block_read: Flag to handle block read
+ * @smbus_actual_len: Valid byte count (length + payload + optional PEC) of a
+ * padded SMBus block read. msg->len is trimmed to this on completion.
+ * Zero when no trimming is needed.
* @input_clk: Input clock to I2C controller
* @i2c_clk: I2C SCL frequency
* @atomic: Mode of transfer
@@ -98,6 +101,7 @@ struct xiic_i2c {
bool prev_msg_tx;
u32 quirks;
bool smbus_block_read;
+ unsigned int smbus_actual_len;
unsigned long input_clk;
unsigned int i2c_clk;
bool atomic;
@@ -539,30 +543,38 @@ static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c)
/* Check if received length is valid */
if (rxmsg_len <= I2C_SMBUS_BLOCK_MAX) {
+ unsigned int pec_len = i2c->rx_msg->len - 1;
+
/* Set Receive fifo depth */
- if (rxmsg_len > IIC_RX_FIFO_DEPTH) {
+ if (rxmsg_len + pec_len > IIC_RX_FIFO_DEPTH) {
/*
- * When Rx msg len greater than or equal to Rx fifo capacity
- * Receive fifo depth should set to Rx fifo capacity minus 1
+ * Trailing bytes (payload plus any PEC) exceed Rx FIFO
+ * capacity, so drain in chunks. This also keeps the
+ * else branch below from pushing rfd_set past the
+ * 4-bit XIIC_RFD_REG_OFFSET field.
*/
rfd_set = IIC_RX_FIFO_DEPTH - 1;
- i2c->rx_msg->len = rxmsg_len + 1;
- } else if ((rxmsg_len == 1) ||
- (rxmsg_len == 0)) {
+ i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
+ } else if (1 + rxmsg_len + pec_len < SMBUS_BLOCK_READ_MIN_LEN) {
/*
- * Minimum of 3 bytes required to exit cleanly. 1 byte
- * already received, Second byte is being received. Have
- * to set NACK in read_rx before receiving the last byte
+ * The HW needs SMBUS_BLOCK_READ_MIN_LEN bytes on the
+ * bus to exit cleanly: by the time the ISR reads the
+ * length byte the second byte is already being clocked
+ * in, too late to NACK. Pad the drain target and record
+ * the real length, trimmed back on completion so the
+ * PEC check sees the right byte.
*/
rfd_set = 0;
i2c->rx_msg->len = SMBUS_BLOCK_READ_MIN_LEN;
+ i2c->smbus_actual_len = 1 + rxmsg_len + pec_len;
} else {
/*
- * When Rx msg len less than Rx fifo capacity
- * Receive fifo depth should set to Rx msg len minus 2
+ * All trailing bytes fit in the Rx FIFO. Defer RX_FULL
+ * until every one of them is buffered, so the drain
+ * takes xiic_read_rx()'s bytes_rem == 0 path.
*/
- rfd_set = rxmsg_len - 2;
- i2c->rx_msg->len = rxmsg_len + 1;
+ rfd_set = rxmsg_len + pec_len - 1;
+ i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
}
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
@@ -575,6 +587,16 @@ static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c)
dev_err(i2c->adap.dev.parent, "smbus_block_read Invalid msg length\n");
}
+/*
+ * Undo the setup-time padding of a short SMBus block read before rx_msg is
+ * cleared, so the PEC check sees the right byte.
+ */
+static void xiic_smbus_trim_len(struct xiic_i2c *i2c)
+{
+ if (i2c->rx_msg && i2c->smbus_actual_len)
+ i2c->rx_msg->len = i2c->smbus_actual_len;
+}
+
static void xiic_read_rx(struct xiic_i2c *i2c)
{
u8 bytes_in_fifo, cr = 0, bytes_to_read = 0;
@@ -797,6 +819,8 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
xiic_read_rx(i2c);
if (xiic_rx_space(i2c) == 0) {
+ xiic_smbus_trim_len(i2c);
+
/* this is the last part of the message */
i2c->rx_msg = NULL;
@@ -865,8 +889,11 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
if (i2c->tx_msg && i2c->smbus_block_read) {
i2c->smbus_block_read = false;
- /* Set requested message len=1 to indicate STATE_DONE */
- i2c->tx_msg->len = 1;
+ /*
+ * Drive xiic_tx_space() to 0 to signal STATE_DONE
+ * without truncating the rx_msg length.
+ */
+ i2c->tx_pos = i2c->tx_msg->len;
}
if (!i2c->tx_msg)
@@ -938,6 +965,7 @@ static void xiic_recv_atomic(struct xiic_i2c *i2c)
return;
}
+ xiic_smbus_trim_len(i2c);
i2c->rx_msg = NULL;
xiic_irq_clr_en(i2c, XIIC_INTR_TX_ERROR_MASK);
@@ -955,6 +983,9 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
u8 cr = 0, rfd_set = 0;
struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg;
+ /* A stale value from an aborted block read would truncate this msg. */
+ i2c->smbus_actual_len = 0;
+
if (!i2c->atomic)
dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),