2018-02-03 08:20:10

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 00/12] Major code reorganization to make all i2c transfers working

The current driver is failing in following test case
1. Handling of failure cases is not working in long run for BAM
mode. It generates error message “bam-dma-engine 7884000.dma: Cannot
free busy channel” sometimes.
2. Following I2C transfers are failing
a. Single transfer with multiple read messages
b. Single transfer with multiple read/write message with maximum
allowed length per message (65K) in BAM mode
c. Single transfer with write greater than 32 bytes in QUP v1 and
write greater than 64 bytes in QUP v2 for non-DMA mode.
3. No handling is present for Block/FIFO interrupts. Any non-error
interrupts are being treated as the transfer completion and then
polling is being done for available/free bytes in FIFO.

To fix all these issues, major code changes are required. This patch
series fixes all the above issues and makes the driver interrupt based
instead of polling based. After these changes, all the mentioned test
cases are working properly.

The code changes have been tested for QUP v1 (IPQ8064) and QUP
v2 (IPQ8074) with sample application written over i2c-dev.

Abhishek Sahu (12):
i2c: qup: fixed releasing dma without flush operation completion
i2c: qup: minor code reorganization for use_dma
i2c: qup: remove redundant variables for BAM SG count
i2c: qup: schedule EOT and FLUSH tags at the end of transfer
i2c: qup: fix the transfer length for BAM rx EOT FLUSH tags
i2c: qup: proper error handling for i2c error in BAM mode
i2c: qup: use the complete transfer length to choose DMA mode
i2c: qup: change completion timeout according to transfer length
i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
i2c: qup: send NACK for last read sub transfers
i2c: qup: reorganization of driver code to remove polling for qup v1
i2c: qup: reorganization of driver code to remove polling for qup v2

drivers/i2c/busses/i2c-qup.c | 1538 +++++++++++++++++++++++++-----------------
1 file changed, 924 insertions(+), 614 deletions(-)

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation



2018-02-03 08:01:23

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode

Currently each message length in complete transfer is being
checked for determining DMA mode and if any of the message length
is less than FIFO length then non DMA mode is being used which
will increase overhead. DMA can be used for any length and it
should be determined with complete transfer length. Now, this
patch introduces DMA threshold length and the transfer will be
done in DMA mode if the total length is greater than this
threshold length.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 6227a5c..a91fc70 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -192,6 +192,8 @@ struct qup_i2c_dev {
bool is_dma;
/* To check if the current transfer is using DMA */
bool use_dma;
+ /* The threshold length above which DMA will be used */
+ unsigned int dma_threshold;
struct dma_pool *dpool;
struct qup_i2c_tag start_tag;
struct qup_i2c_bam brx;
@@ -1294,7 +1296,8 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
int num)
{
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
- int ret, len, idx = 0;
+ int ret, idx = 0;
+ unsigned int total_len = 0;

qup->bus_err = 0;
qup->qup_err = 0;
@@ -1320,14 +1323,13 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
goto out;
}

- len = (msgs[idx].len > qup->out_fifo_sz) ||
- (msgs[idx].len > qup->in_fifo_sz);
-
- if (is_vmalloc_addr(msgs[idx].buf) || !len)
+ if (is_vmalloc_addr(msgs[idx].buf))
break;
+
+ total_len += msgs[idx].len;
}

- if (idx == num)
+ if (idx == num && total_len > qup->dma_threshold)
qup->use_dma = true;
}

@@ -1587,6 +1589,7 @@ static int qup_i2c_probe(struct platform_device *pdev)

size = QUP_INPUT_FIFO_SIZE(io_mode);
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
+ qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);

fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
hs_div = 3;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:01:29

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1

Following are the major issues in current driver code

1. The current driver simply assumes the transfer completion
whenever its gets any non-error interrupts and then simply do the
polling of available/free bytes in FIFO.
2. The block mode is not working properly since no handling in
being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ.

Because of above, i2c v1 transfers of size greater than 32 are failing
with following error message

i2c_qup 78b6000.i2c: timeout for fifo out full

To make block mode working properly and move to use the interrupts
instead of polling, major code reorganization is required. Following
are the major changes done in this patch

1. Remove the polling of TX FIFO free space and RX FIFO available
bytes and move to interrupts completely. QUP has QUP_MX_OUTPUT_DONE,
QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
interrupts to handle FIFO’s properly so check all these interrupts.
2. During write, For FIFO mode, TX FIFO can be directly written
without checking for FIFO space. For block mode, the QUP will generate
OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of available
space.
3. During read, both TX and RX FIFO will be used. TX will be used
for writing tags and RX will be used for receiving the data. In QUP,
TX and RX can operate in separate mode so configure modes accordingly.
4. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
will be generated after all the bytes have been copied in RX FIFO. For
read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
whenever it has block size of available data.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 399 ++++++++++++++++++++++++++++---------------
1 file changed, 257 insertions(+), 142 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index edea3b9..af6c21a 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -73,8 +73,11 @@
#define QUP_IN_SVC_FLAG BIT(9)
#define QUP_MX_OUTPUT_DONE BIT(10)
#define QUP_MX_INPUT_DONE BIT(11)
+#define OUT_BLOCK_WRITE_REQ BIT(12)
+#define IN_BLOCK_READ_REQ BIT(13)

/* I2C mini core related values */
+#define QUP_NO_INPUT BIT(7)
#define QUP_CLOCK_AUTO_GATE BIT(13)
#define I2C_MINI_CORE (2 << 8)
#define I2C_N_VAL 15
@@ -138,13 +141,51 @@
#define DEFAULT_CLK_FREQ 100000
#define DEFAULT_SRC_CLK 20000000

+/* MAX_OUTPUT_DONE_FLAG has been received */
+#define QUP_BLK_EVENT_TX_IRQ_DONE BIT(0)
+/* MAX_INPUT_DONE_FLAG has been received */
+#define QUP_BLK_EVENT_RX_IRQ_DONE BIT(1)
+/* All the TX bytes have been written in TX FIFO */
+#define QUP_BLK_EVENT_TX_DATA_DONE BIT(2)
+/* All the RX bytes have been read from RX FIFO */
+#define QUP_BLK_EVENT_RX_DATA_DONE BIT(3)
+
+/* All the required events to mark a QUP I2C TX transfer completed */
+#define QUP_BLK_EVENT_TX_DONE (QUP_BLK_EVENT_TX_IRQ_DONE | \
+ QUP_BLK_EVENT_TX_DATA_DONE)
+/* All the required events to mark a QUP I2C RX transfer completed */
+#define QUP_BLK_EVENT_RX_DONE (QUP_BLK_EVENT_TX_DONE | \
+ QUP_BLK_EVENT_RX_IRQ_DONE | \
+ QUP_BLK_EVENT_RX_DATA_DONE)
+
+/*
+ * count: no of blocks
+ * pos: current block number
+ * tx_tag_len: tx tag length for current block
+ * rx_tag_len: rx tag length for current block
+ * data_len: remaining data length for current message
+ * total_tx_len: total tx length including tag bytes for current QUP transfer
+ * total_rx_len: total rx length including tag bytes for current QUP transfer
+ * tx_fifo_free: number of free bytes in current QUP block write.
+ * fifo_available: number of available bytes in RX FIFO for current
+ * QUP block read
+ * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
+ * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
+ * tags: contains tx tag bytes for current QUP transfer
+ */
struct qup_i2c_block {
- int count;
- int pos;
- int tx_tag_len;
- int rx_tag_len;
- int data_len;
- u8 tags[6];
+ int count;
+ int pos;
+ int tx_tag_len;
+ int rx_tag_len;
+ int data_len;
+ int total_tx_len;
+ int total_rx_len;
+ int tx_fifo_free;
+ int fifo_available;
+ bool is_tx_blk_mode;
+ bool is_rx_blk_mode;
+ u8 tags[6];
};

struct qup_i2c_tag {
@@ -187,6 +228,7 @@ struct qup_i2c_dev {

/* To check if this is the last msg */
bool is_last;
+ bool is_qup_v1;

/* To configure when bus is in run state */
int config_run;
@@ -195,6 +237,10 @@ struct qup_i2c_dev {
bool is_dma;
/* To check if the current transfer is using DMA */
bool use_dma;
+ /* Required events to mark QUP transfer as completed */
+ u32 blk_events;
+ /* Already completed events in QUP transfer */
+ u32 cur_blk_events;
/* The threshold length above which DMA will be used */
unsigned int dma_threshold;
unsigned int max_xfer_sg_len;
@@ -205,11 +251,18 @@ struct qup_i2c_dev {
struct qup_i2c_bam btx;

struct completion xfer;
+ /* function to write data in tx fifo */
+ void (*write_tx_fifo)(struct qup_i2c_dev *qup);
+ /* function to read data from rx fifo */
+ void (*read_rx_fifo)(struct qup_i2c_dev *qup);
+ /* function to write tags in tx fifo for i2c read transfer */
+ void (*write_rx_tags)(struct qup_i2c_dev *qup);
};

static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
{
struct qup_i2c_dev *qup = dev;
+ struct qup_i2c_block *blk = &qup->blk;
u32 bus_err;
u32 qup_err;
u32 opflags;
@@ -256,12 +309,54 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
goto done;
}

- if (opflags & QUP_IN_SVC_FLAG)
- writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+ if (!qup->is_qup_v1)
+ goto done;

- if (opflags & QUP_OUT_SVC_FLAG)
+ if (opflags & QUP_OUT_SVC_FLAG) {
writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);

+ /*
+ * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG.
+ * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind
+ * QUP_OUTPUT_SERVICE_FLAG. The only reason for
+ * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is
+ * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking
+ * here QUP_OUTPUT_SERVICE_FLAG and assumes that
+ * QUP_MAX_OUTPUT_DONE_FLAG.
+ */
+ if (!blk->is_tx_blk_mode)
+ qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
+
+ if (opflags & OUT_BLOCK_WRITE_REQ) {
+ blk->tx_fifo_free += qup->out_blk_sz;
+ if (qup->msg->flags & I2C_M_RD)
+ qup->write_rx_tags(qup);
+ else
+ qup->write_tx_fifo(qup);
+ }
+ }
+
+ if (opflags & QUP_IN_SVC_FLAG) {
+ writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+ if (!blk->is_rx_blk_mode) {
+ blk->fifo_available += qup->in_fifo_sz;
+ qup->read_rx_fifo(qup);
+ } else if (opflags & IN_BLOCK_READ_REQ) {
+ blk->fifo_available += qup->in_blk_sz;
+ qup->read_rx_fifo(qup);
+ }
+ }
+
+ if (opflags & QUP_MX_OUTPUT_DONE)
+ qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
+
+ if (opflags & QUP_MX_INPUT_DONE)
+ qup->cur_blk_events |= QUP_BLK_EVENT_RX_IRQ_DONE;
+
+ if (qup->cur_blk_events != qup->blk_events)
+ return IRQ_HANDLED;
+
done:
qup->qup_err = qup_err;
qup->bus_err = bus_err;
@@ -327,6 +422,28 @@ static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
return 0;
}

+/* Check if I2C bus returns to IDLE state */
+static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
+{
+ unsigned long timeout;
+ u32 status;
+ int ret = 0;
+
+ timeout = jiffies + len * 4;
+ for (;;) {
+ status = readl(qup->base + QUP_I2C_STATUS);
+ if (!(status & I2C_STATUS_BUS_ACTIVE))
+ break;
+
+ if (time_after(jiffies, timeout))
+ ret = -ETIMEDOUT;
+
+ usleep_range(len, len * 2);
+ }
+
+ return ret;
+}
+
/**
* qup_i2c_wait_ready - wait for a give number of bytes in tx/rx path
* @qup: The qup_i2c_dev device
@@ -397,23 +514,6 @@ static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
}
}

-static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- /* Number of entries to shift out, including the start */
- int total = msg->len + 1;
-
- if (total < qup->out_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_WRITE_CNT);
- } else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_OUTPUT_CNT);
- }
-}
-
static int check_for_fifo_space(struct qup_i2c_dev *qup)
{
int ret;
@@ -446,28 +546,25 @@ static int check_for_fifo_space(struct qup_i2c_dev *qup)
return ret;
}

-static int qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
{
+ struct qup_i2c_block *blk = &qup->blk;
+ struct i2c_msg *msg = qup->msg;
u32 addr = msg->addr << 1;
u32 qup_tag;
int idx;
u32 val;
- int ret = 0;

if (qup->pos == 0) {
val = QUP_TAG_START | addr;
idx = 1;
+ blk->tx_fifo_free--;
} else {
val = 0;
idx = 0;
}

- while (qup->pos < msg->len) {
- /* Check that there's space in the FIFO for our pair */
- ret = check_for_fifo_space(qup);
- if (ret)
- return ret;
-
+ while (blk->tx_fifo_free && qup->pos < msg->len) {
if (qup->pos == msg->len - 1)
qup_tag = QUP_TAG_STOP;
else
@@ -484,11 +581,11 @@ static int qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)

qup->pos++;
idx++;
+ blk->tx_fifo_free--;
}

- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
-
- return ret;
+ if (qup->pos == msg->len)
+ qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
}

static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
@@ -1009,64 +1106,6 @@ static int qup_i2c_write_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
return ret;
}

-static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- int ret;
-
- qup->msg = msg;
- qup->pos = 0;
-
- enable_irq(qup->irq);
-
- qup_i2c_set_write_mode(qup, msg);
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto err;
-
- writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
-
- do {
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto err;
-
- ret = qup_i2c_issue_write(qup, msg);
- if (ret)
- goto err;
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto err;
-
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
- } while (qup->pos < msg->len);
-
- /* Wait for the outstanding data in the fifo to drain */
- ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
-err:
- disable_irq(qup->irq);
- qup->msg = NULL;
-
- return ret;
-}
-
-static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
-{
- if (len < qup->in_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(len, qup->base + QUP_MX_READ_CNT);
- } else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(len, qup->base + QUP_MX_INPUT_CNT);
- }
-}
-
static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
{
int tx_len = qup->blk.tx_tag_len;
@@ -1089,44 +1128,27 @@ static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
}
}

-static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- u32 addr, len, val;
-
- addr = i2c_8bit_addr_from_msg(msg);
-
- /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
- len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
-
- val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
- writel(val, qup->base + QUP_OUT_FIFO_BASE);
-}
-
-
-static int qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
{
+ struct qup_i2c_block *blk = &qup->blk;
+ struct i2c_msg *msg = qup->msg;
u32 val = 0;
int idx;
- int ret = 0;

- for (idx = 0; qup->pos < msg->len; idx++) {
+ while (blk->fifo_available && qup->pos < msg->len) {
if ((idx & 1) == 0) {
- /* Check that FIFO have data */
- ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
- SET_BIT, 4 * ONE_BYTE);
- if (ret)
- return ret;
-
/* Reading 2 words at time */
val = readl(qup->base + QUP_IN_FIFO_BASE);
-
msg->buf[qup->pos++] = val & 0xFF;
} else {
msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
}
+ idx++;
+ blk->fifo_available--;
}

- return ret;
+ if (qup->pos == msg->len)
+ qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
}

static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
@@ -1227,49 +1249,137 @@ static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
return ret;
}

-static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool is_rx)
{
+ qup->cur_blk_events = 0;
+ qup->blk_events = is_rx ? QUP_BLK_EVENT_RX_DONE : QUP_BLK_EVENT_TX_DONE;
+}
+
+static void qup_i2c_write_rx_tags_v1(struct qup_i2c_dev *qup)
+{
+ struct i2c_msg *msg = qup->msg;
+ u32 addr, len, val;
+
+ addr = i2c_8bit_addr_from_msg(msg);
+
+ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
+ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
+
+ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
+ writel(val, qup->base + QUP_OUT_FIFO_BASE);
+ qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
+}
+
+static void qup_i2c_conf_v1(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ u32 qup_config = I2C_MINI_CORE | I2C_N_VAL;
+ u32 io_mode = QUP_REPACK_EN;
+
+ blk->is_tx_blk_mode =
+ blk->total_tx_len > qup->out_fifo_sz ? true : false;
+ blk->is_rx_blk_mode =
+ blk->total_rx_len > qup->in_fifo_sz ? true : false;
+
+ if (blk->is_tx_blk_mode) {
+ io_mode |= QUP_OUTPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_WRITE_CNT);
+ writel(blk->total_tx_len, qup->base + QUP_MX_OUTPUT_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_OUTPUT_CNT);
+ writel(blk->total_tx_len, qup->base + QUP_MX_WRITE_CNT);
+ }
+
+ if (blk->total_rx_len) {
+ if (blk->is_rx_blk_mode) {
+ io_mode |= QUP_INPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_READ_CNT);
+ writel(blk->total_rx_len, qup->base + QUP_MX_INPUT_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_INPUT_CNT);
+ writel(blk->total_rx_len, qup->base + QUP_MX_READ_CNT);
+ }
+ } else {
+ qup_config |= QUP_NO_INPUT;
+ }
+
+ writel(qup_config, qup->base + QUP_CONFIG);
+ writel(io_mode, qup->base + QUP_IO_MODE);
+}
+
+static void qup_i2c_clear_blk_v1(struct qup_i2c_block *blk)
+{
+ blk->tx_fifo_free = 0;
+ blk->fifo_available = 0;
+}
+
+static int qup_i2c_conf_xfer_v1(struct qup_i2c_dev *qup, bool is_rx)
+{
+ struct qup_i2c_block *blk = &qup->blk;
int ret;

- qup->msg = msg;
- qup->pos = 0;
-
- enable_irq(qup->irq);
- qup_i2c_set_read_mode(qup, msg->len);
-
+ qup_i2c_clear_blk_v1(blk);
+ qup_i2c_conf_v1(qup);
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
if (ret)
- goto err;
+ return ret;

writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);

ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
if (ret)
- goto err;
+ return ret;

- qup_i2c_issue_read(qup, msg);
+ qup_i2c_set_blk_event(qup, is_rx);
+ reinit_completion(&qup->xfer);
+ enable_irq(qup->irq);
+ if (!blk->is_tx_blk_mode) {
+ blk->tx_fifo_free = qup->out_fifo_sz;
+
+ if (is_rx)
+ qup_i2c_write_rx_tags_v1(qup);
+ else
+ qup_i2c_write_tx_fifo_v1(qup);
+ }

ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
if (ret)
goto err;

- do {
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
+ ret = qup_i2c_wait_for_complete(qup, qup->msg);
+ if (ret)
+ goto err;

- ret = qup_i2c_read_fifo(qup, msg);
- if (ret)
- goto err;
- } while (qup->pos < msg->len);
+ ret = qup_i2c_bus_active(qup, ONE_BYTE);

err:
disable_irq(qup->irq);
- qup->msg = NULL;
-
return ret;
}

+static int qup_i2c_write_one(struct qup_i2c_dev *qup)
+{
+ struct i2c_msg *msg = qup->msg;
+ struct qup_i2c_block *blk = &qup->blk;
+
+ qup->pos = 0;
+ blk->total_tx_len = msg->len + 1;
+ blk->total_rx_len = 0;
+
+ return qup_i2c_conf_xfer_v1(qup, false);
+}
+
+static int qup_i2c_read_one(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+
+ qup->pos = 0;
+ blk->total_tx_len = 2;
+ blk->total_rx_len = qup->msg->len;
+
+ return qup_i2c_conf_xfer_v1(qup, true);
+}
+
static int qup_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
@@ -1308,10 +1418,11 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
goto out;
}

+ qup->msg = &msgs[idx];
if (msgs[idx].flags & I2C_M_RD)
- ret = qup_i2c_read_one(qup, &msgs[idx]);
+ ret = qup_i2c_read_one(qup);
else
- ret = qup_i2c_write_one(qup, &msgs[idx]);
+ ret = qup_i2c_write_one(qup);

if (ret)
break;
@@ -1489,6 +1600,10 @@ static int qup_i2c_probe(struct platform_device *pdev)
if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
qup->adap.algo = &qup_i2c_algo;
qup->adap.quirks = &qup_i2c_quirks;
+ qup->is_qup_v1 = true;
+ qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
+ qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
+ qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
} else {
qup->adap.algo = &qup_i2c_algo_v2;
ret = qup_i2c_req_dma(qup);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:02:01

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers

According to I2c specification, “If a master-receiver sends a
repeated START condition, it sends a not-acknowledge (A) just
before the repeated START condition”. QUP v2 supports sending
of NACK without stop with QUP_TAG_V2_DATARD_NACK so added the
same.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index ba717bb..edea3b9 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -113,6 +113,7 @@
#define QUP_TAG_V2_DATAWR 0x82
#define QUP_TAG_V2_DATAWR_STOP 0x83
#define QUP_TAG_V2_DATARD 0x85
+#define QUP_TAG_V2_DATARD_NACK 0x86
#define QUP_TAG_V2_DATARD_STOP 0x87

/* Status, Error flags */
@@ -609,7 +610,9 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
tags[len++] = QUP_TAG_V2_DATAWR_STOP;
} else {
if (msg->flags & I2C_M_RD)
- tags[len++] = QUP_TAG_V2_DATARD;
+ tags[len++] = qup->blk.pos == (qup->blk.count - 1) ?
+ QUP_TAG_V2_DATARD_NACK :
+ QUP_TAG_V2_DATARD;
else
tags[len++] = QUP_TAG_V2_DATAWR;
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:02:32

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode

Currently the i2c error handling in BAM mode is not working
properly in stress condition.

1. After an error, the FIFO are being written with FLUSH and
EOT tags which should not be required since already these tags
have been written in BAM descriptor itself.

2. QUP state is being moved to RESET in IRQ handler in case
of error. When QUP HW encounters an error in BAM mode then it
moves the QUP STATE to PAUSE state. In this case, I2C_FLUSH
command needs to be executed while moving to RUN_STATE by writing
to the QUP_STATE register with the I2C_FLUSH bit set to 1.

3. In Error case, sometimes, QUP generates more than one
interrupt which will trigger the complete again. After an error,
the flush operation will be scheduled after doing
reinit_completion which should be triggered by BAM IRQ callback.
If the second QUP IRQ comes during this time then it will call
the complete and the transfer function will assume the all the
BAM HW descriptors have been completed.

4. The release DMA is being called after each error which
will free the DMA tx and rx channels. The error like NACK is very
common in I2C transfer and every time this will be overhead. Now,
since the error handling is proper so this release channel can be
completely avoided.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 25 ++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 094be6a..6227a5c 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -228,9 +228,24 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
if (bus_err)
writel(bus_err, qup->base + QUP_I2C_STATUS);

+ /*
+ * Check for BAM mode and returns if already error has come for current
+ * transfer. In Error case, sometimes, QUP generates more than one
+ * interrupt.
+ */
+ if (qup->use_dma && (qup->qup_err || qup->bus_err))
+ return IRQ_HANDLED;
+
/* Reset the QUP State in case of error */
if (qup_err || bus_err) {
- writel(QUP_RESET_STATE, qup->base + QUP_STATE);
+ /*
+ * Don’t reset the QUP state in case of BAM mode. The BAM
+ * flush operation needs to be scheduled in transfer function
+ * which will clear the remaining schedule descriptors in BAM
+ * HW FIFO and generates the BAM interrupt.
+ */
+ if (!qup->use_dma)
+ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
goto done;
}

@@ -841,20 +856,12 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
goto desc_err;
}

- if (rx_buf)
- writel(QUP_BAM_INPUT_EOT,
- qup->base + QUP_OUT_FIFO_BASE);
-
- writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
-
qup_i2c_flush(qup);

/* wait for remaining interrupts to occur */
if (!wait_for_completion_timeout(&qup->xfer, HZ))
dev_err(qup->dev, "flush timed out\n");

- qup_i2c_rel_dma(qup);
-
ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
}

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:02:32

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length

Currently the completion timeout is being taken according to
maximum transfer length which is too high if SCL is operating in
high frequency. This patch calculates timeout on the basis of
one-byte transfer time and uses the same for completion timeout.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index a91fc70..6df65ea 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -130,8 +130,8 @@
#define MX_TX_RX_LEN SZ_64K
#define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)

-/* Max timeout in ms for 32k bytes */
-#define TOUT_MAX 300
+/* Min timeout for i2c transfers */
+#define TOUT_MIN 2

/* Default values. Use these if FW query fails */
#define DEFAULT_CLK_FREQ 100000
@@ -172,6 +172,7 @@ struct qup_i2c_dev {
int in_blk_sz;

unsigned long one_byte_t;
+ unsigned long xfer_timeout;
struct qup_i2c_block blk;

struct i2c_msg *msg;
@@ -845,7 +846,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
dma_async_issue_pending(qup->brx.dma);
}

- if (!wait_for_completion_timeout(&qup->xfer, TOUT_MAX * HZ)) {
+ if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) {
dev_err(qup->dev, "normal trans timed out\n");
ret = -ETIMEDOUT;
}
@@ -1601,6 +1602,8 @@ static int qup_i2c_probe(struct platform_device *pdev)
*/
one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
qup->one_byte_t = one_bit_t * 9;
+ qup->xfer_timeout = TOUT_MIN * HZ +
+ usecs_to_jiffies(MX_TX_RX_LEN * qup->one_byte_t);

dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
qup->in_blk_sz, qup->in_fifo_sz,
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:03:00

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2

Following are the major issues in current driver code

1. The current driver simply assumes the transfer completion
whenever its gets any non-error interrupts and then simply do the
polling of available/free bytes in FIFO.
2. The block mode is not working properly since no handling in
being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_READ.
3. An i2c transfer can contain multiple message and QUP v2
supports reconfiguration during run in which the mode should be same
for all the sub transfer. Currently the mode is being programmed
before every sub transfer which is functionally wrong. If one message
is less than FIFO length and other message is greater than FIFO
length, then transfers will fail.

Because of above, i2c v2 transfers of size greater than 64 are failing
with following error message

i2c_qup 78b6000.i2c: timeout for fifo out full

To make block mode working properly and move to use the interrupts
instead of polling, major code reorganization is required. Following
are the major changes done in this patch

1. Remove the polling of TX FIFO free space and RX FIFO available
bytes and move to interrupts completely. QUP has QUP_MX_OUTPUT_DONE,
QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
interrupts to handle FIFO’s properly so check all these interrupts.
2. Determine the mode for transfer before starting by checking
all the tx/rx data length in each message. The complete message can be
transferred either in DMA mode or Programmed IO by FIFO/Block mode.
in DMA mode, both tx and rx uses same mode but in PIO mode, the TX and
RX can be in different mode.
3. During write, For FIFO mode, TX FIFO can be directly written
without checking for FIFO space. For block mode, the QUP will generate
OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of available
space.
4. During read, both TX and RX FIFO will be used. TX will be used
for writing tags and RX will be used for receiving the data. In QUP,
TX and RX can operate in separate mode so configure modes accordingly.
5. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
will be generated after all the bytes have been copied in RX FIFO. For
read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
whenever it has block size of available data.
6. Split the transfer in chunk of one QUP block size(256 bytes)
and schedule each block separately. QUP v2 supports reconfiguration
during run in which QUP can transfer multiple blocks without issuing a
stop events.
7. Port the SMBus block read support for new code changes.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 917 +++++++++++++++++++++++++------------------
1 file changed, 533 insertions(+), 384 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index af6c21a..46736a1 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -141,6 +141,15 @@
#define DEFAULT_CLK_FREQ 100000
#define DEFAULT_SRC_CLK 20000000

+/*
+ * Max tags length (start, stop and maximum 2 bytes address) for each QUP
+ * data transfer
+ */
+#define QUP_MAX_TAGS_LEN 4
+/* Max data length for each DATARD tags */
+#define RECV_MAX_DATA_LEN 254
+/* TAG length for DATA READ in RX FIFO */
+#define READ_RX_TAGS_LEN 2
/* MAX_OUTPUT_DONE_FLAG has been received */
#define QUP_BLK_EVENT_TX_IRQ_DONE BIT(0)
/* MAX_INPUT_DONE_FLAG has been received */
@@ -164,11 +173,24 @@
* tx_tag_len: tx tag length for current block
* rx_tag_len: rx tag length for current block
* data_len: remaining data length for current message
+ * cur_blk_len: data length for current block
* total_tx_len: total tx length including tag bytes for current QUP transfer
* total_rx_len: total rx length including tag bytes for current QUP transfer
+ * tx_fifo_data_pos: current byte number in TX FIFO word
* tx_fifo_free: number of free bytes in current QUP block write.
+ * rx_fifo_data_pos: current byte number in RX FIFO word
* fifo_available: number of available bytes in RX FIFO for current
* QUP block read
+ * tx_fifo_data: QUP TX FIFO write works on word basis (4 bytes). New byte write
+ * to TX FIFO will be appended in this data and will be written to
+ * TX FIFO when all the 4 bytes are available.
+ * rx_fifo_data: QUP RX FIFO read works on word basis (4 bytes). This will
+ * contains the 4 bytes of RX data.
+ * cur_data: pointer to tell cur data position for current message
+ * cur_tx_tags: pointer to tell cur position in tags
+ * tx_tags_sent: all tx tag bytes have been written in FIFO word
+ * send_last_word: for tx FIFO, last word send is pending in current block
+ * rx_tags_fetched: all the rx tag bytes have been fetched from rx fifo word
* is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
* is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
* tags: contains tx tag bytes for current QUP transfer
@@ -179,10 +201,20 @@ struct qup_i2c_block {
int tx_tag_len;
int rx_tag_len;
int data_len;
+ int cur_blk_len;
int total_tx_len;
int total_rx_len;
+ int tx_fifo_data_pos;
int tx_fifo_free;
+ int rx_fifo_data_pos;
int fifo_available;
+ u32 tx_fifo_data;
+ u32 rx_fifo_data;
+ u8 *cur_data;
+ u8 *cur_tx_tags;
+ bool tx_tags_sent;
+ bool send_last_word;
+ bool rx_tags_fetched;
bool is_tx_blk_mode;
bool is_rx_blk_mode;
u8 tags[6];
@@ -214,6 +246,7 @@ struct qup_i2c_dev {
int out_blk_sz;
int in_blk_sz;

+ int blk_xfer_limit;
unsigned long one_byte_t;
unsigned long xfer_timeout;
struct qup_i2c_block blk;
@@ -228,10 +261,10 @@ struct qup_i2c_dev {

/* To check if this is the last msg */
bool is_last;
- bool is_qup_v1;
+ bool is_smbus_read;

/* To configure when bus is in run state */
- int config_run;
+ u32 config_run;

/* dma parameters */
bool is_dma;
@@ -245,6 +278,8 @@ struct qup_i2c_dev {
unsigned int dma_threshold;
unsigned int max_xfer_sg_len;
unsigned int tag_buf_pos;
+ /* The threshold length above which block mode will be used */
+ unsigned int blk_mode_threshold;
struct dma_pool *dpool;
struct qup_i2c_tag start_tag;
struct qup_i2c_bam brx;
@@ -309,9 +344,6 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
goto done;
}

- if (!qup->is_qup_v1)
- goto done;
-
if (opflags & QUP_OUT_SVC_FLAG) {
writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);

@@ -444,108 +476,6 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
return ret;
}

-/**
- * qup_i2c_wait_ready - wait for a give number of bytes in tx/rx path
- * @qup: The qup_i2c_dev device
- * @op: The bit/event to wait on
- * @val: value of the bit to wait on, 0 or 1
- * @len: The length the bytes to be transferred
- */
-static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val,
- int len)
-{
- unsigned long timeout;
- u32 opflags;
- u32 status;
- u32 shift = __ffs(op);
- int ret = 0;
-
- len *= qup->one_byte_t;
- /* timeout after a wait of twice the max time */
- timeout = jiffies + len * 4;
-
- for (;;) {
- opflags = readl(qup->base + QUP_OPERATIONAL);
- status = readl(qup->base + QUP_I2C_STATUS);
-
- if (((opflags & op) >> shift) == val) {
- if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) {
- if (!(status & I2C_STATUS_BUS_ACTIVE)) {
- ret = 0;
- goto done;
- }
- } else {
- ret = 0;
- goto done;
- }
- }
-
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto done;
- }
- usleep_range(len, len * 2);
- }
-
-done:
- if (qup->bus_err || qup->qup_err)
- ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
-
- return ret;
-}
-
-static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
- struct i2c_msg *msg)
-{
- /* Number of entries to shift out, including the tags */
- int total = msg->len + qup->blk.tx_tag_len;
-
- total |= qup->config_run;
-
- if (total < qup->out_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_WRITE_CNT);
- } else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_OUTPUT_CNT);
- }
-}
-
-static int check_for_fifo_space(struct qup_i2c_dev *qup)
-{
- int ret;
-
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto out;
-
- ret = qup_i2c_wait_ready(qup, QUP_OUT_FULL,
- RESET_BIT, 4 * ONE_BYTE);
- if (ret) {
- /* Fifo is full. Drain out the fifo */
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto out;
-
- ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY,
- RESET_BIT, 256 * ONE_BYTE);
- if (ret) {
- dev_err(qup->dev, "timeout for fifo out full");
- goto out;
- }
-
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto out;
- }
-
-out:
- return ret;
-}
-
static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
{
struct qup_i2c_block *blk = &qup->blk;
@@ -591,60 +521,17 @@ static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
struct i2c_msg *msg)
{
- memset(&qup->blk, 0, sizeof(qup->blk));
-
+ qup->blk.pos = 0;
qup->blk.data_len = msg->len;
- qup->blk.count = (msg->len + QUP_READ_LIMIT - 1) / QUP_READ_LIMIT;
-
- /* 4 bytes for first block and 2 writes for rest */
- qup->blk.tx_tag_len = 4 + (qup->blk.count - 1) * 2;
-
- /* There are 2 tag bytes that are read in to fifo for every block */
- if (msg->flags & I2C_M_RD)
- qup->blk.rx_tag_len = qup->blk.count * 2;
-}
-
-static int qup_i2c_send_data(struct qup_i2c_dev *qup, int tlen, u8 *tbuf,
- int dlen, u8 *dbuf)
-{
- u32 val = 0, idx = 0, pos = 0, i = 0, t;
- int len = tlen + dlen;
- u8 *buf = tbuf;
- int ret = 0;
-
- while (len > 0) {
- ret = check_for_fifo_space(qup);
- if (ret)
- return ret;
-
- t = (len >= 4) ? 4 : len;
-
- while (idx < t) {
- if (!i && (pos >= tlen)) {
- buf = dbuf;
- pos = 0;
- i = 1;
- }
- val |= buf[pos++] << (idx++ * 8);
- }
-
- writel(val, qup->base + QUP_OUT_FIFO_BASE);
- idx = 0;
- val = 0;
- len -= 4;
- }
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
-
- return ret;
+ qup->blk.count = DIV_ROUND_UP(msg->len, qup->blk_xfer_limit);
}

static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
{
int data_len;

- if (qup->blk.data_len > QUP_READ_LIMIT)
- data_len = QUP_READ_LIMIT;
+ if (qup->blk.data_len > qup->blk_xfer_limit)
+ data_len = qup->blk_xfer_limit;
else
data_len = qup->blk.data_len;

@@ -661,9 +548,9 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
{
int len = 0;

- if (msg->len > 1) {
+ if (qup->is_smbus_read) {
tags[len++] = QUP_TAG_V2_DATARD_STOP;
- tags[len++] = qup_i2c_get_data_len(qup) - 1;
+ tags[len++] = qup_i2c_get_data_len(qup);
} else {
tags[len++] = QUP_TAG_V2_START;
tags[len++] = addr & 0xff;
@@ -725,24 +612,6 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
return len;
}

-static int qup_i2c_issue_xfer_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- int data_len = 0, tag_len, index;
- int ret;
-
- tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg);
- index = msg->len - qup->blk.data_len;
-
- /* only tags are written for read */
- if (!(msg->flags & I2C_M_RD))
- data_len = qup_i2c_get_data_len(qup);
-
- ret = qup_i2c_send_data(qup, tag_len, qup->blk.tags,
- data_len, &msg->buf[index]);
- qup->blk.data_len -= data_len;
-
- return ret;
-}

static void qup_i2c_bam_cb(void *data)
{
@@ -809,6 +678,7 @@ static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
u32 i = 0, tlen, tx_len = 0;
u8 *tags;

+ qup->blk_xfer_limit = QUP_READ_LIMIT;
qup_i2c_set_blk_data(qup, msg);

blocks = qup->blk.count;
@@ -1057,7 +927,7 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
unsigned long left;
int ret = 0;

- left = wait_for_completion_timeout(&qup->xfer, HZ);
+ left = wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout);
if (!left) {
writel(1, qup->base + QUP_SW_RESET);
ret = -ETIMEDOUT;
@@ -1069,65 +939,6 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
return ret;
}

-static int qup_i2c_write_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- int ret = 0;
-
- qup->msg = msg;
- qup->pos = 0;
- enable_irq(qup->irq);
- qup_i2c_set_blk_data(qup, msg);
- qup_i2c_set_write_mode_v2(qup, msg);
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto err;
-
- writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
-
- do {
- ret = qup_i2c_issue_xfer_v2(qup, msg);
- if (ret)
- goto err;
-
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
-
- qup->blk.pos++;
- } while (qup->blk.pos < qup->blk.count);
-
- ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
-
-err:
- disable_irq(qup->irq);
- qup->msg = NULL;
-
- return ret;
-}
-
-static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
-{
- int tx_len = qup->blk.tx_tag_len;
-
- len += qup->blk.rx_tag_len;
- len |= qup->config_run;
- tx_len |= qup->config_run;
-
- if (len < qup->in_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(tx_len, qup->base + QUP_MX_WRITE_CNT);
- writel(len, qup->base + QUP_MX_READ_CNT);
- } else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(tx_len, qup->base + QUP_MX_OUTPUT_CNT);
- writel(len, qup->base + QUP_MX_INPUT_CNT);
- }
-}
-
static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
{
struct qup_i2c_block *blk = &qup->blk;
@@ -1151,104 +962,6 @@ static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
}

-static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
- struct i2c_msg *msg)
-{
- u32 val;
- int idx, pos = 0, ret = 0, total, msg_offset = 0;
-
- /*
- * If the message length is already read in
- * the first byte of the buffer, account for
- * that by setting the offset
- */
- if (qup_i2c_check_msg_len(msg) && (msg->len > 1))
- msg_offset = 1;
- total = qup_i2c_get_data_len(qup);
- total -= msg_offset;
-
- /* 2 extra bytes for read tags */
- while (pos < (total + 2)) {
- /* Check that FIFO have data */
- ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
- SET_BIT, 4 * ONE_BYTE);
- if (ret) {
- dev_err(qup->dev, "timeout for fifo not empty");
- return ret;
- }
- val = readl(qup->base + QUP_IN_FIFO_BASE);
-
- for (idx = 0; idx < 4; idx++, val >>= 8, pos++) {
- /* first 2 bytes are tag bytes */
- if (pos < 2)
- continue;
-
- if (pos >= (total + 2))
- goto out;
- msg->buf[qup->pos + msg_offset] = val & 0xff;
- qup->pos++;
- }
- }
-
-out:
- qup->blk.data_len -= total;
-
- return ret;
-}
-
-static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- int ret = 0;
-
- qup->msg = msg;
- qup->pos = 0;
- enable_irq(qup->irq);
- qup_i2c_set_blk_data(qup, msg);
- qup_i2c_set_read_mode_v2(qup, msg->len);
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto err;
-
- writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
-
- do {
- ret = qup_i2c_issue_xfer_v2(qup, msg);
- if (ret)
- goto err;
-
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
-
- ret = qup_i2c_read_fifo_v2(qup, msg);
- if (ret)
- goto err;
-
- qup->blk.pos++;
-
- /* Handle SMBus block read length */
- if (qup_i2c_check_msg_len(msg) && (msg->len == 1)) {
- if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) {
- ret = -EPROTO;
- goto err;
- }
- msg->len += msg->buf[0];
- qup->pos = 0;
- qup_i2c_set_blk_data(qup, msg);
- /* set tag length for block read */
- qup->blk.tx_tag_len = 2;
- qup_i2c_set_read_mode_v2(qup, msg->buf[0]);
- }
- } while (qup->blk.pos < qup->blk.count);
-
-err:
- disable_irq(qup->irq);
- qup->msg = NULL;
-
- return ret;
-}
-
static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool is_rx)
{
qup->cur_blk_events = 0;
@@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
return ret;
}

+/*
+ * Function to configure registers related with reconfiguration during run
+ * and will be done before each I2C sub transfer.
+ */
+static void qup_i2c_conf_count_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
+
+ if (blk->is_tx_blk_mode)
+ writel(qup->config_run | blk->total_tx_len,
+ qup->base + QUP_MX_OUTPUT_CNT);
+ else
+ writel(qup->config_run | blk->total_tx_len,
+ qup->base + QUP_MX_WRITE_CNT);
+
+ if (blk->total_rx_len) {
+ if (blk->is_rx_blk_mode)
+ writel(qup->config_run | blk->total_rx_len,
+ qup->base + QUP_MX_INPUT_CNT);
+ else
+ writel(qup->config_run | blk->total_rx_len,
+ qup->base + QUP_MX_READ_CNT);
+ } else {
+ qup_config |= QUP_NO_INPUT;
+ }
+
+ writel(qup_config, qup->base + QUP_CONFIG);
+}
+
+/*
+ * Function to configure registers related with transfer mode (FIFO/Block)
+ * before starting of i2c transfer and will be done only once in QUP RESET
+ * state.
+ */
+static void qup_i2c_conf_mode_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ u32 io_mode = QUP_REPACK_EN;
+
+ if (blk->is_tx_blk_mode) {
+ io_mode |= QUP_OUTPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_WRITE_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_OUTPUT_CNT);
+ }
+
+ if (blk->is_rx_blk_mode) {
+ io_mode |= QUP_INPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_READ_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_INPUT_CNT);
+ }
+
+ writel(io_mode, qup->base + QUP_IO_MODE);
+}
+
+/*
+ * Function to clear required variables before starting of any QUP v2
+ * sub transfer
+ */
+static void qup_i2c_clear_blk_v2(struct qup_i2c_block *blk)
+{
+ blk->send_last_word = false;
+ blk->tx_tags_sent = false;
+ blk->tx_fifo_data = 0;
+ blk->tx_fifo_data_pos = 0;
+ blk->tx_fifo_free = 0;
+
+ blk->rx_tags_fetched = false;
+ blk->rx_fifo_data = 0;
+ blk->rx_fifo_data_pos = 0;
+ blk->fifo_available = 0;
+}
+
+/*
+ * Function to receive data from RX FIFO for read message in QUP v2
+ * i2c transfer.
+ */
+static void qup_i2c_recv_data(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ int j;
+
+ for (j = blk->rx_fifo_data_pos;
+ blk->cur_blk_len && blk->fifo_available;
+ blk->cur_blk_len--, blk->fifo_available--) {
+ if (j == 0)
+ blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
+
+ *(blk->cur_data++) = blk->rx_fifo_data;
+ blk->rx_fifo_data >>= 8;
+
+ if (j == 3)
+ j = 0;
+ else
+ j++;
+ }
+
+ blk->rx_fifo_data_pos = j;
+}
+
+/* Function to receive tags for read message in QUP v2 i2c transfer. */
+static void qup_i2c_recv_tags(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+
+ blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
+ blk->rx_fifo_data >>= blk->rx_tag_len * 8;
+ blk->rx_fifo_data_pos = blk->rx_tag_len;
+ blk->fifo_available -= blk->rx_tag_len;
+}
+
+/*
+ * This function reads the data and tags from RX FIFO. Since in read case, the
+ * tags will be preceded by received data bytes need to be written so
+ * 1. Check if rx_tags_fetched is false i.e. the start of QUP block so receive
+ * all tag bytes and discard that.
+ * 2. Read the data from RX FIFO. When all the data bytes have been read then
+ * mark the QUP_BLK_EVENT_RX_DATA_DONE in current block event and return.
+ */
+static void qup_i2c_read_rx_fifo_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+
+ if (!blk->rx_tags_fetched) {
+ qup_i2c_recv_tags(qup);
+ blk->rx_tags_fetched = true;
+ }
+
+ qup_i2c_recv_data(qup);
+ if (!blk->cur_blk_len)
+ qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
+}
+
+/*
+ * Function to write bytes in TX FIFO for write message in QUP v2 i2c
+ * transfer. QUP TX FIFO write works on word basis (4 bytes). New byte write to
+ * TX FIFO will be appended in this data tx_fifo_data and will be written to TX
+ * FIFO when all the 4 bytes are available.
+ */
+static void
+qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsigned int *len)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ unsigned int j;
+
+ for (j = blk->tx_fifo_data_pos; *len && blk->tx_fifo_free;
+ (*len)--, blk->tx_fifo_free--) {
+ blk->tx_fifo_data |= *(*data)++ << (j * 8);
+ if (j == 3) {
+ writel(blk->tx_fifo_data,
+ qup->base + QUP_OUT_FIFO_BASE);
+ blk->tx_fifo_data = 0x0;
+ j = 0;
+ } else {
+ j++;
+ }
+ }
+
+ blk->tx_fifo_data_pos = j;
+}
+
+/* Function to transfer tags for read message in QUP v2 i2c transfer. */
+static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+
+ qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, &blk->tx_tag_len);
+ if (blk->tx_fifo_data_pos)
+ writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
+
+ qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
+}
+
+/*
+ * This function writes the data and tags in TX FIFO. Since in write case, both
+ * tags and data need to be written and QUP write tags can have maximum 256 data
+ * length, so it follows simple internal state machine to manage it.
+ * 1. Check if tx_tags_sent is false i.e. the start of QUP block so write the
+ * tags to TX FIFO.
+ * 2. Check if send_last_word is true. This will be set when last few data bytes
+ * less than 4 bytes are reamining to be written in FIFO because of no FIFO
+ * space. All this data bytes are available in tx_fifo_data so write this
+ * in FIFO and mark the tx done.
+ * 3. Write the data to TX FIFO and check for cur_blk_len. If this is non zero
+ * then more data is pending otherwise following 3 cases can be possible
+ * a. if tx_fifo_data_pos is zero that means all the data bytes in this block
+ * have been written in TX FIFO so mark the tx done.
+ * b. tx_fifo_free is zero. In this case, last few bytes (less than 4
+ * bytes) are copied to tx_fifo_data but couldn't be sent because of
+ * FIFO full so make send_last_word true.
+ * c. tx_fifo_free is non zero i.e tx FIFO is free so copy the remaining data
+ * from tx_fifo_data to tx FIFO and mark the tx done. Since,
+ * qup_i2c_write_blk_data do write in 4 bytes and FIFO space is in
+ * multiple of 4 bytes so tx_fifo_free will be always greater than or
+ * equal to 4 bytes.
+ */
+static void qup_i2c_write_tx_fifo_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+
+ if (!blk->tx_tags_sent) {
+ qup_i2c_write_blk_data(qup, &blk->cur_tx_tags,
+ &blk->tx_tag_len);
+ blk->tx_tags_sent = true;
+ }
+
+ if (blk->send_last_word)
+ goto send_last_word;
+
+ qup_i2c_write_blk_data(qup, &blk->cur_data, &blk->cur_blk_len);
+ if (!blk->cur_blk_len) {
+ if (!blk->tx_fifo_data_pos)
+ goto tx_data_done;
+
+ if (blk->tx_fifo_free)
+ goto send_last_word;
+
+ blk->send_last_word = true;
+ }
+
+ return;
+
+send_last_word:
+ writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
+tx_data_done:
+ qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
+}
+
+/*
+ * Main transfer function which will be used for reading or writing i2c data.
+ * The QUP v2 supports reconfiguration during run in which multiple i2c sub
+ * transfers can be scheduled.
+ */
+static int
+qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool is_first,
+ bool change_pause_state)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ struct i2c_msg *msg = qup->msg;
+ int ret;
+
+ /*
+ * Check if its SMBus Block read for which the top level read will be
+ * done into 2 QUP reads. One with message length 1 while other one is
+ * with actual length.
+ */
+ if (qup_i2c_check_msg_len(msg)) {
+ if (qup->is_smbus_read) {
+ /*
+ * If the message length is already read in
+ * the first byte of the buffer, account for
+ * that by setting the offset
+ */
+ blk->cur_data += 1;
+ is_first = false;
+ } else {
+ change_pause_state = false;
+ }
+ }
+
+ qup->config_run = is_first ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
+
+ qup_i2c_clear_blk_v2(blk);
+ qup_i2c_conf_count_v2(qup);
+
+ /* If it is first sub transfer, then configure i2c bus clocks */
+ if (is_first) {
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ return ret;
+
+ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ return ret;
+ }
+
+ qup_i2c_set_blk_event(qup, is_rx);
+ reinit_completion(&qup->xfer);
+ enable_irq(qup->irq);
+ /*
+ * In FIFO mode, tx FIFO can be written directly while in block mode the
+ * it will be written after getting OUT_BLOCK_WRITE_REQ interrupt
+ */
+ if (!blk->is_tx_blk_mode) {
+ blk->tx_fifo_free = qup->out_fifo_sz;
+
+ if (is_rx)
+ qup_i2c_write_rx_tags_v2(qup);
+ else
+ qup_i2c_write_tx_fifo_v2(qup);
+ }
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ ret = qup_i2c_wait_for_complete(qup, msg);
+ if (ret)
+ goto err;
+
+ /* Move to pause state for all the transfers, except last one */
+ if (change_pause_state) {
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ goto err;
+ }
+
+err:
+ disable_irq(qup->irq);
+ return ret;
+}
+
+/*
+ * Function to transfer one read/write message in i2c transfer. It splits the
+ * message into multiple of blk_xfer_limit data length blocks and schedule each
+ * QUP block individually.
+ */
+static int qup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id, bool is_rx)
+{
+ int ret = 0;
+ unsigned int data_len, i;
+ struct i2c_msg *msg = qup->msg;
+ struct qup_i2c_block *blk = &qup->blk;
+ u8 *msg_buf = msg->buf;
+
+ qup->blk_xfer_limit = is_rx ? RECV_MAX_DATA_LEN : QUP_READ_LIMIT;
+ qup_i2c_set_blk_data(qup, msg);
+
+ for (i = 0; i < blk->count; i++) {
+ data_len = qup_i2c_get_data_len(qup);
+ blk->pos = i;
+ blk->cur_tx_tags = blk->tags;
+ blk->cur_blk_len = data_len;
+ blk->tx_tag_len =
+ qup_i2c_set_tags(blk->cur_tx_tags, qup, qup->msg);
+
+ blk->cur_data = msg_buf;
+
+ if (is_rx) {
+ blk->total_tx_len = blk->tx_tag_len;
+ blk->rx_tag_len = 2;
+ blk->total_rx_len = blk->rx_tag_len + data_len;
+ } else {
+ blk->total_tx_len = blk->tx_tag_len + data_len;
+ blk->total_rx_len = 0;
+ }
+
+ ret = qup_i2c_conf_xfer_v2(qup, is_rx, !msg_id && !i,
+ !qup->is_last || i < blk->count - 1);
+ if (ret)
+ return ret;
+
+ /* Handle SMBus block read length */
+ if (qup_i2c_check_msg_len(msg) && msg->len == 1 &&
+ !qup->is_smbus_read) {
+ if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+
+ msg->len = msg->buf[0];
+ qup->is_smbus_read = true;
+ ret = qup_i2c_xfer_v2_msg(qup, msg_id, true);
+ qup->is_smbus_read = false;
+ if (ret)
+ return ret;
+
+ msg->len += 1;
+ }
+
+ msg_buf += data_len;
+ blk->data_len -= qup->blk_xfer_limit;
+ }
+
+ return ret;
+}
+
+/*
+ * QUP v2 supports 3 modes
+ * Programmed IO using FIFO mode : Less than FIFO size
+ * Programmed IO using Block mode : Greater than FIFO size
+ * DMA using BAM : Appropriate for any transactio size but the address should be
+ * DMA applicable
+ *
+ * This function determines the mode which will be used for this transfer. An
+ * i2c transfer contains multiple message. Following are the rules to determine
+ * the mode used.
+ * 1. Determine the tx and rx length for each message and maximum tx and rx
+ * length for complete transfer
+ * 2. If tx or rx length is greater than DMA threshold than use the DMA mode.
+ * 3. In FIFO or block mode, TX and RX can operate in different mode so check
+ * for maximum tx and rx length to determine mode.
+ */
+static int
+qup_i2c_determine_mode(struct qup_i2c_dev *qup, struct i2c_msg msgs[], int num)
+{
+ int idx;
+ bool no_dma = false;
+ unsigned int max_tx_len = 0, max_rx_len = 0;
+ unsigned int cur_tx_len, cur_rx_len;
+ unsigned int total_rx_len = 0, total_tx_len = 0;
+
+ /* All i2c_msgs should be transferred using either dma or cpu */
+ for (idx = 0; idx < num; idx++) {
+ if (msgs[idx].len == 0)
+ return -EINVAL;
+
+ if (msgs[idx].flags & I2C_M_RD) {
+ cur_tx_len = 0;
+ cur_rx_len = msgs[idx].len;
+ } else {
+ cur_tx_len = msgs[idx].len;
+ cur_rx_len = 0;
+ }
+
+ if (is_vmalloc_addr(msgs[idx].buf))
+ no_dma = true;
+
+ max_tx_len = max(max_tx_len, cur_tx_len);
+ max_rx_len = max(max_rx_len, cur_rx_len);
+ total_rx_len += cur_rx_len;
+ total_tx_len += cur_tx_len;
+ }
+
+ if (!no_dma && qup->is_dma &&
+ (total_tx_len > qup->dma_threshold ||
+ total_rx_len > qup->dma_threshold)) {
+ qup->use_dma = true;
+ } else {
+ qup->blk.is_tx_blk_mode =
+ max_tx_len > qup->blk_mode_threshold ? true : false;
+ qup->blk.is_rx_blk_mode =
+ max_rx_len > qup->blk_mode_threshold ? true : false;
+ }
+
+ return 0;
+}
+
static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
int ret, idx = 0;
- unsigned int total_len = 0;

qup->bus_err = 0;
qup->qup_err = 0;
@@ -1457,6 +1609,10 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
if (ret < 0)
goto out;

+ ret = qup_i2c_determine_mode(qup, msgs, num);
+ if (ret)
+ goto out;
+
writel(1, qup->base + QUP_SW_RESET);
ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
if (ret)
@@ -1466,59 +1622,35 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
writel(I2C_MINI_CORE | I2C_N_VAL_V2, qup->base + QUP_CONFIG);
writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);

- if ((qup->is_dma)) {
- /* All i2c_msgs should be transferred using either dma or cpu */
+ if (qup_i2c_poll_state_i2c_master(qup)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (qup->use_dma) {
+ reinit_completion(&qup->xfer);
+ ret = qup_i2c_bam_xfer(adap, &msgs[0], num);
+ qup->use_dma = false;
+ } else {
+ qup_i2c_conf_mode_v2(qup);
+
for (idx = 0; idx < num; idx++) {
- if (msgs[idx].len == 0) {
- ret = -EINVAL;
- goto out;
- }
+ qup->msg = &msgs[idx];
+ qup->is_last = idx == (num - 1);

- if (is_vmalloc_addr(msgs[idx].buf))
+ ret = qup_i2c_xfer_v2_msg(qup, idx,
+ !!(msgs[idx].flags & I2C_M_RD));
+ if (ret)
break;
-
- total_len += msgs[idx].len;
}
-
- if (idx == num && total_len > qup->dma_threshold)
- qup->use_dma = true;
+ qup->msg = NULL;
}

- idx = 0;
-
- do {
- if (msgs[idx].len == 0) {
- ret = -EINVAL;
- goto out;
- }
-
- if (qup_i2c_poll_state_i2c_master(qup)) {
- ret = -EIO;
- goto out;
- }
-
- qup->is_last = (idx == (num - 1));
- if (idx)
- qup->config_run = QUP_I2C_MX_CONFIG_DURING_RUN;
- else
- qup->config_run = 0;
-
- reinit_completion(&qup->xfer);
-
- if (qup->use_dma) {
- ret = qup_i2c_bam_xfer(adap, &msgs[idx], num);
- qup->use_dma = false;
- break;
- } else {
- if (msgs[idx].flags & I2C_M_RD)
- ret = qup_i2c_read_one_v2(qup, &msgs[idx]);
- else
- ret = qup_i2c_write_one_v2(qup, &msgs[idx]);
- }
- } while ((idx++ < (num - 1)) && !ret);
+ if (!ret)
+ ret = qup_i2c_bus_active(qup, ONE_BYTE);

if (!ret)
- ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
+ qup_i2c_change_state(qup, QUP_RESET_STATE);

if (ret == 0)
ret = num;
@@ -1582,6 +1714,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
u32 src_clk_freq = DEFAULT_SRC_CLK;
u32 clk_freq = DEFAULT_CLK_FREQ;
int blocks;
+ bool is_qup_v1;

qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
if (!qup)
@@ -1600,12 +1733,10 @@ static int qup_i2c_probe(struct platform_device *pdev)
if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
qup->adap.algo = &qup_i2c_algo;
qup->adap.quirks = &qup_i2c_quirks;
- qup->is_qup_v1 = true;
- qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
- qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
- qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
+ is_qup_v1 = true;
} else {
qup->adap.algo = &qup_i2c_algo_v2;
+ is_qup_v1 = false;
ret = qup_i2c_req_dma(qup);

if (ret == -EPROBE_DEFER)
@@ -1731,14 +1862,31 @@ static int qup_i2c_probe(struct platform_device *pdev)
ret = -EIO;
goto fail;
}
- qup->out_blk_sz = blk_sizes[size] / 2;
+ qup->out_blk_sz = blk_sizes[size];

size = QUP_INPUT_BLOCK_SIZE(io_mode);
if (size >= ARRAY_SIZE(blk_sizes)) {
ret = -EIO;
goto fail;
}
- qup->in_blk_sz = blk_sizes[size] / 2;
+ qup->in_blk_sz = blk_sizes[size];
+
+ if (is_qup_v1) {
+ /*
+ * in QUP v1, QUP_CONFIG uses N as 15 i.e 16 bits constitutes a
+ * single transfer but the block size is in bytes so divide the
+ * in_blk_sz and out_blk_sz by 2
+ */
+ qup->in_blk_sz /= 2;
+ qup->out_blk_sz /= 2;
+ qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
+ qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
+ qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
+ } else {
+ qup->write_tx_fifo = qup_i2c_write_tx_fifo_v2;
+ qup->read_rx_fifo = qup_i2c_read_rx_fifo_v2;
+ qup->write_rx_tags = qup_i2c_write_rx_tags_v2;
+ }

size = QUP_OUTPUT_FIFO_SIZE(io_mode);
qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
@@ -1746,6 +1894,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
size = QUP_INPUT_FIFO_SIZE(io_mode);
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);
+ qup->blk_mode_threshold = qup->dma_threshold - QUP_MAX_TAGS_LEN;

fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
hs_div = 3;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:04:13

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

The BAM mode requires buffer for start tag data and tx, rx SG
list. Currently, this is being taken for maximum transfer length
(65K). But an I2C transfer can have multiple messages and each
message can be of this maximum length so the buffer overflow will
happen in this case. Since increasing buffer length won’t be
feasible since an I2C transfer can contain any number of messages
so this patch does following changes to make i2c transfers working
for multiple messages case.

1. Calculate the required buffers for 2 maximum length messages
(65K * 2).
2. Split the descriptor formation and descriptor scheduling.
The idea is to fit as many messages in one DMA transfers for 65K
threshold value (max_xfer_sg_len). Whenever the sg_cnt is
crossing this, then schedule the BAM transfer and subsequent
transfer will again start from zero.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 199 +++++++++++++++++++++++++------------------
1 file changed, 118 insertions(+), 81 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 6df65ea..ba717bb 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -155,6 +155,7 @@ struct qup_i2c_bam {
struct qup_i2c_tag tag;
struct dma_chan *dma;
struct scatterlist *sg;
+ unsigned int sg_cnt;
};

struct qup_i2c_dev {
@@ -195,6 +196,8 @@ struct qup_i2c_dev {
bool use_dma;
/* The threshold length above which DMA will be used */
unsigned int dma_threshold;
+ unsigned int max_xfer_sg_len;
+ unsigned int tag_buf_pos;
struct dma_pool *dpool;
struct qup_i2c_tag start_tag;
struct qup_i2c_bam brx;
@@ -699,86 +702,86 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
return 0;
}

-static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
- int num)
+static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ int ret = 0, limit = QUP_READ_LIMIT;
+ u32 len = 0, blocks, rem;
+ u32 i = 0, tlen, tx_len = 0;
+ u8 *tags;
+
+ qup_i2c_set_blk_data(qup, msg);
+
+ blocks = qup->blk.count;
+ rem = msg->len - (blocks - 1) * limit;
+
+ if (msg->flags & I2C_M_RD) {
+ while (qup->blk.pos < blocks) {
+ tlen = (i == (blocks - 1)) ? rem : limit;
+ tags = &qup->start_tag.start[qup->tag_buf_pos + len];
+ len += qup_i2c_set_tags(tags, qup, msg);
+ qup->blk.data_len -= tlen;
+
+ /* scratch buf to read the start and len tags */
+ ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
+ &qup->brx.tag.start[0],
+ 2, qup, DMA_FROM_DEVICE);
+
+ if (ret)
+ return ret;
+
+ ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
+ &msg->buf[limit * i],
+ tlen, qup,
+ DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+
+ i++;
+ qup->blk.pos = i;
+ }
+ ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
+ &qup->start_tag.start[qup->tag_buf_pos],
+ len, qup, DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+
+ qup->tag_buf_pos += len;
+ } else {
+ while (qup->blk.pos < blocks) {
+ tlen = (i == (blocks - 1)) ? rem : limit;
+ tags = &qup->start_tag.start[qup->tag_buf_pos + tx_len];
+ len = qup_i2c_set_tags(tags, qup, msg);
+ qup->blk.data_len -= tlen;
+
+ ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
+ tags, len,
+ qup, DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+
+ tx_len += len;
+ ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
+ &msg->buf[limit * i],
+ tlen, qup, DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+ i++;
+ qup->blk.pos = i;
+ }
+
+ qup->tag_buf_pos += tx_len;
+ }
+
+ return 0;
+}
+
+static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
{
struct dma_async_tx_descriptor *txd, *rxd = NULL;
- int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
+ int ret = 0;
dma_cookie_t cookie_rx, cookie_tx;
- u32 len, blocks, rem;
- u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
- u8 *tags;
-
- while (idx < num) {
- tx_len = 0, len = 0, i = 0;
-
- qup->is_last = (idx == (num - 1));
-
- qup_i2c_set_blk_data(qup, msg);
-
- blocks = qup->blk.count;
- rem = msg->len - (blocks - 1) * limit;
-
- if (msg->flags & I2C_M_RD) {
- while (qup->blk.pos < blocks) {
- tlen = (i == (blocks - 1)) ? rem : limit;
- tags = &qup->start_tag.start[off + len];
- len += qup_i2c_set_tags(tags, qup, msg);
- qup->blk.data_len -= tlen;
-
- /* scratch buf to read the start and len tags */
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
- &qup->brx.tag.start[0],
- 2, qup, DMA_FROM_DEVICE);
-
- if (ret)
- return ret;
-
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
- &msg->buf[limit * i],
- tlen, qup,
- DMA_FROM_DEVICE);
- if (ret)
- return ret;
-
- i++;
- qup->blk.pos = i;
- }
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- &qup->start_tag.start[off],
- len, qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
-
- off += len;
- } else {
- while (qup->blk.pos < blocks) {
- tlen = (i == (blocks - 1)) ? rem : limit;
- tags = &qup->start_tag.start[off + tx_len];
- len = qup_i2c_set_tags(tags, qup, msg);
- qup->blk.data_len -= tlen;
-
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- tags, len,
- qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
-
- tx_len += len;
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- &msg->buf[limit * i],
- tlen, qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
- i++;
- qup->blk.pos = i;
- }
- off += tx_len;
-
- }
- idx++;
- msg++;
- }
+ u32 len = 0;
+ u32 tx_buf = qup->btx.sg_cnt, rx_buf = qup->brx.sg_cnt;

/* schedule the EOT and FLUSH I2C tags */
len = 1;
@@ -878,11 +881,19 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
return ret;
}

+static void qup_i2c_bam_clear_tag_buffers(struct qup_i2c_dev *qup)
+{
+ qup->btx.sg_cnt = 0;
+ qup->brx.sg_cnt = 0;
+ qup->tag_buf_pos = 0;
+}
+
static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
int ret = 0;
+ int idx = 0;

enable_irq(qup->irq);
ret = qup_i2c_req_dma(qup);
@@ -905,9 +916,34 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
goto out;

writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+ qup_i2c_bam_clear_tag_buffers(qup);
+
+ for (idx = 0; idx < num; idx++) {
+ qup->msg = msg + idx;
+ qup->is_last = idx == (num - 1);
+
+ ret = qup_i2c_bam_make_desc(qup, qup->msg);
+ if (ret)
+ break;
+
+ /*
+ * Make DMA descriptor and schedule the BAM transfer if its
+ * already crossed the maximum length. Since the memory for all
+ * tags buffers have been taken for 2 maximum possible
+ * transfers length so it will never cross the buffer actual
+ * length.
+ */
+ if (qup->btx.sg_cnt > qup->max_xfer_sg_len ||
+ qup->brx.sg_cnt > qup->max_xfer_sg_len ||
+ qup->is_last) {
+ ret = qup_i2c_bam_schedule_desc(qup);
+ if (ret)
+ break;
+
+ qup_i2c_bam_clear_tag_buffers(qup);
+ }
+ }

- qup->msg = msg;
- ret = qup_i2c_bam_do_xfer(qup, qup->msg, num);
out:
disable_irq(qup->irq);

@@ -1459,7 +1495,8 @@ static int qup_i2c_probe(struct platform_device *pdev)
else if (ret != 0)
goto nodma;

- blocks = (MX_BLOCKS << 1) + 1;
+ qup->max_xfer_sg_len = (MX_BLOCKS << 1);
+ blocks = 2 * qup->max_xfer_sg_len + 1;
qup->btx.sg = devm_kzalloc(&pdev->dev,
sizeof(*qup->btx.sg) * blocks,
GFP_KERNEL);
@@ -1603,7 +1640,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
qup->one_byte_t = one_bit_t * 9;
qup->xfer_timeout = TOUT_MIN * HZ +
- usecs_to_jiffies(MX_TX_RX_LEN * qup->one_byte_t);
+ usecs_to_jiffies(2 * MX_TX_RX_LEN * qup->one_byte_t);

dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
qup->in_blk_sz, qup->in_fifo_sz,
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:04:22

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count

The rx_nents and tx_nents are redundant. rx_buf and tx_buf can
be used for total number of SG entries.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 26 ++++++++++----------------
1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index c68f433..bb83a2967 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -692,7 +692,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
struct dma_async_tx_descriptor *txd, *rxd = NULL;
int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
dma_cookie_t cookie_rx, cookie_tx;
- u32 rx_nents = 0, tx_nents = 0, len, blocks, rem;
+ u32 len, blocks, rem;
u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
u8 *tags;

@@ -707,9 +707,6 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
rem = msg->len - (blocks - 1) * limit;

if (msg->flags & I2C_M_RD) {
- rx_nents += (blocks * 2) + 1;
- tx_nents += 1;
-
while (qup->blk.pos < blocks) {
tlen = (i == (blocks - 1)) ? rem : limit;
tags = &qup->start_tag.start[off + len];
@@ -748,8 +745,6 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
if (ret)
return ret;
} else {
- tx_nents += (blocks * 2);
-
while (qup->blk.pos < blocks) {
tlen = (i == (blocks - 1)) ? rem : limit;
tags = &qup->start_tag.start[off + tx_len];
@@ -775,7 +770,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,

if (idx == (num - 1)) {
len = 1;
- if (rx_nents) {
+ if (rx_buf) {
qup->btx.tag.start[0] =
QUP_BAM_INPUT_EOT;
len++;
@@ -787,14 +782,13 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
len, qup, DMA_TO_DEVICE);
if (ret)
return ret;
- tx_nents += 1;
}
}
idx++;
msg++;
}

- txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_nents,
+ txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_buf,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_PREP_FENCE);
if (!txd) {
@@ -803,7 +797,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
goto desc_err;
}

- if (!rx_nents) {
+ if (!rx_buf) {
txd->callback = qup_i2c_bam_cb;
txd->callback_param = qup;
}
@@ -816,9 +810,9 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,

dma_async_issue_pending(qup->btx.dma);

- if (rx_nents) {
+ if (rx_buf) {
rxd = dmaengine_prep_slave_sg(qup->brx.dma, qup->brx.sg,
- rx_nents, DMA_DEV_TO_MEM,
+ rx_buf, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!rxd) {
dev_err(qup->dev, "failed to get rx desc\n");
@@ -853,7 +847,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
goto desc_err;
}

- if (rx_nents)
+ if (rx_buf)
writel(QUP_BAM_INPUT_EOT,
qup->base + QUP_OUT_FIFO_BASE);

@@ -871,10 +865,10 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
}

desc_err:
- dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE);
+ dma_unmap_sg(qup->dev, qup->btx.sg, tx_buf, DMA_TO_DEVICE);

- if (rx_nents)
- dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents,
+ if (rx_buf)
+ dma_unmap_sg(qup->dev, qup->brx.sg, rx_buf,
DMA_FROM_DEVICE);

return ret;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:20:18

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 02/12] i2c: qup: minor code reorganization for use_dma

1. Assigns use_dma in qup_dev structure itself which will
help in subsequent patches to determine the mode in IRQ handler.
2. Does minor code reorganization for loops to reduce the
unnecessary comparison and assignment.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 9faa26c41a..c68f433 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -190,6 +190,8 @@ struct qup_i2c_dev {

/* dma parameters */
bool is_dma;
+ /* To check if the current transfer is using DMA */
+ bool use_dma;
struct dma_pool *dpool;
struct qup_i2c_tag start_tag;
struct qup_i2c_bam brx;
@@ -1297,7 +1299,7 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
int num)
{
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
- int ret, len, idx = 0, use_dma = 0;
+ int ret, len, idx = 0;

qup->bus_err = 0;
qup->qup_err = 0;
@@ -1326,13 +1328,12 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
len = (msgs[idx].len > qup->out_fifo_sz) ||
(msgs[idx].len > qup->in_fifo_sz);

- if ((!is_vmalloc_addr(msgs[idx].buf)) && len) {
- use_dma = 1;
- } else {
- use_dma = 0;
+ if (is_vmalloc_addr(msgs[idx].buf) || !len)
break;
- }
}
+
+ if (idx == num)
+ qup->use_dma = true;
}

idx = 0;
@@ -1356,15 +1357,17 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,

reinit_completion(&qup->xfer);

- if (use_dma) {
+ if (qup->use_dma) {
ret = qup_i2c_bam_xfer(adap, &msgs[idx], num);
+ qup->use_dma = false;
+ break;
} else {
if (msgs[idx].flags & I2C_M_RD)
ret = qup_i2c_read_one_v2(qup, &msgs[idx]);
else
ret = qup_i2c_write_one_v2(qup, &msgs[idx]);
}
- } while ((idx++ < (num - 1)) && !use_dma && !ret);
+ } while ((idx++ < (num - 1)) && !ret);

if (!ret)
ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:20:26

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion

The QUP BSLP BAM generates the following error sometimes if the
current I2C DMA transfer fails and the flush operation has been
scheduled

“bam-dma-engine 7884000.dma: Cannot free busy channel”

If any I2C error comes during BAM DMA transfer, then the QUP I2C
interrupt will be generated and the flush operation will be
carried out to make i2c consume all scheduled DMA transfer.
Currently, the same completion structure is being used for BAM
transfer which has already completed without reinit. It will make
flush operation wait_for_completion_timeout completed immediately
and will proceed for freeing the DMA resources where the
descriptors are still in process.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 08f8e01..9faa26c41a 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2014, Sony Mobile Communications AB.
*
*
@@ -844,6 +844,8 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
}

if (ret || qup->bus_err || qup->qup_err) {
+ reinit_completion(&qup->xfer);
+
if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
dev_err(qup->dev, "change to run state timed out");
goto desc_err;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:20:29

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer

A single BAM transfer can have multiple read and write messages.
The EOT and FLUSH tags should be scheduled at the end of BAM HW
descriptors. Since the READ and WRITE can be present in any order
so for some of the cases, these tags are not being written
correctly.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 54 ++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 30 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index bb83a2967..6357aff 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -560,7 +560,7 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
}

static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
- struct i2c_msg *msg, int is_dma)
+ struct i2c_msg *msg)
{
u16 addr = i2c_8bit_addr_from_msg(msg);
int len = 0;
@@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
else
tags[len++] = data_len;

- if ((msg->flags & I2C_M_RD) && last && is_dma) {
- tags[len++] = QUP_BAM_INPUT_EOT;
- tags[len++] = QUP_BAM_FLUSH_STOP;
- }
-
return len;
}

@@ -614,7 +609,7 @@ static int qup_i2c_issue_xfer_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
int data_len = 0, tag_len, index;
int ret;

- tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg, 0);
+ tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg);
index = msg->len - qup->blk.data_len;

/* only tags are written for read */
@@ -710,7 +705,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
while (qup->blk.pos < blocks) {
tlen = (i == (blocks - 1)) ? rem : limit;
tags = &qup->start_tag.start[off + len];
- len += qup_i2c_set_tags(tags, qup, msg, 1);
+ len += qup_i2c_set_tags(tags, qup, msg);
qup->blk.data_len -= tlen;

/* scratch buf to read the start and len tags */
@@ -738,17 +733,11 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
return ret;

off += len;
- /* scratch buf to read the BAM EOT and FLUSH tags */
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
- &qup->brx.tag.start[0],
- 2, qup, DMA_FROM_DEVICE);
- if (ret)
- return ret;
} else {
while (qup->blk.pos < blocks) {
tlen = (i == (blocks - 1)) ? rem : limit;
tags = &qup->start_tag.start[off + tx_len];
- len = qup_i2c_set_tags(tags, qup, msg, 1);
+ len = qup_i2c_set_tags(tags, qup, msg);
qup->blk.data_len -= tlen;

ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
@@ -768,26 +757,31 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
}
off += tx_len;

- if (idx == (num - 1)) {
- len = 1;
- if (rx_buf) {
- qup->btx.tag.start[0] =
- QUP_BAM_INPUT_EOT;
- len++;
- }
- qup->btx.tag.start[len - 1] =
- QUP_BAM_FLUSH_STOP;
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- &qup->btx.tag.start[0],
- len, qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
- }
}
idx++;
msg++;
}

+ /* schedule the EOT and FLUSH I2C tags */
+ len = 1;
+ if (rx_buf) {
+ qup->btx.tag.start[0] = QUP_BAM_INPUT_EOT;
+ len++;
+
+ /* scratch buf to read the BAM EOT and FLUSH tags */
+ ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
+ &qup->brx.tag.start[0],
+ 2, qup, DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+ }
+
+ qup->btx.tag.start[len - 1] = QUP_BAM_FLUSH_STOP;
+ ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], &qup->btx.tag.start[0],
+ len, qup, DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+
txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_buf,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_PREP_FENCE);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-03 08:20:37

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH 05/12] i2c: qup: fix the transfer length for BAM rx EOT FLUSH tags

In case of FLUSH operation, BAM copies INPUT EOT FLUSH (0x94)
instead of normal EOT (0x93) tag in input data stream when an
input EOT tag is received during flush operation. So only one tag
will be written instead of 2 separate tags.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/i2c/busses/i2c-qup.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 6357aff..094be6a 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -768,10 +768,10 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
qup->btx.tag.start[0] = QUP_BAM_INPUT_EOT;
len++;

- /* scratch buf to read the BAM EOT and FLUSH tags */
+ /* scratch buf to read the BAM EOT FLUSH tags */
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
&qup->brx.tag.start[0],
- 2, qup, DMA_FROM_DEVICE);
+ 1, qup, DMA_FROM_DEVICE);
if (ret)
return ret;
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2018-02-05 23:06:39

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1

Hi Abhishek,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on wsa/i2c/for-next]
[also build test WARNING on v4.15 next-20180205]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Abhishek-Sahu/Major-code-reorganization-to-make-all-i2c-transfers-working/20180206-035527
base: https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: arm64-defconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm64

Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings

All warnings (new ones prefixed by >>):

drivers/i2c/busses/i2c-qup.c: In function 'qup_i2c_read_rx_fifo_v1':
>> drivers/i2c/busses/i2c-qup.c:1139:12: warning: 'idx' may be used uninitialized in this function [-Wmaybe-uninitialized]
if ((idx & 1) == 0) {
~~~~~^~~~

vim +/idx +1139 drivers/i2c/busses/i2c-qup.c

191424bb Sricharan R 2016-01-19 1130
3a487e36 Abhishek Sahu 2018-02-03 1131 static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
10c5a842 Bjorn Andersson 2014-03-13 1132 {
3a487e36 Abhishek Sahu 2018-02-03 1133 struct qup_i2c_block *blk = &qup->blk;
3a487e36 Abhishek Sahu 2018-02-03 1134 struct i2c_msg *msg = qup->msg;
10c5a842 Bjorn Andersson 2014-03-13 1135 u32 val = 0;
10c5a842 Bjorn Andersson 2014-03-13 1136 int idx;
10c5a842 Bjorn Andersson 2014-03-13 1137
3a487e36 Abhishek Sahu 2018-02-03 1138 while (blk->fifo_available && qup->pos < msg->len) {
10c5a842 Bjorn Andersson 2014-03-13 @1139 if ((idx & 1) == 0) {
10c5a842 Bjorn Andersson 2014-03-13 1140 /* Reading 2 words at time */
10c5a842 Bjorn Andersson 2014-03-13 1141 val = readl(qup->base + QUP_IN_FIFO_BASE);
10c5a842 Bjorn Andersson 2014-03-13 1142 msg->buf[qup->pos++] = val & 0xFF;
10c5a842 Bjorn Andersson 2014-03-13 1143 } else {
10c5a842 Bjorn Andersson 2014-03-13 1144 msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
10c5a842 Bjorn Andersson 2014-03-13 1145 }
3a487e36 Abhishek Sahu 2018-02-03 1146 idx++;
3a487e36 Abhishek Sahu 2018-02-03 1147 blk->fifo_available--;
10c5a842 Bjorn Andersson 2014-03-13 1148 }
c4f0c5fb Sricharan R 2016-01-19 1149
3a487e36 Abhishek Sahu 2018-02-03 1150 if (qup->pos == msg->len)
3a487e36 Abhishek Sahu 2018-02-03 1151 qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
10c5a842 Bjorn Andersson 2014-03-13 1152 }
10c5a842 Bjorn Andersson 2014-03-13 1153

:::::: The code at line 1139 was first introduced by commit
:::::: 10c5a8425968f8a43b7039ce6261367fc992289f i2c: qup: New bus driver for the Qualcomm QUP I2C controller

:::::: TO: Bjorn Andersson <[email protected]>
:::::: CC: Wolfram Sang <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (3.29 kB)
.config.gz (36.62 kB)
Download all attachments

2018-02-08 14:04:34

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> The QUP BSLP BAM generates the following error sometimes if the
> current I2C DMA transfer fails and the flush operation has been
> scheduled
>
> “bam-dma-engine 7884000.dma: Cannot free busy channel”
>
> If any I2C error comes during BAM DMA transfer, then the QUP I2C
> interrupt will be generated and the flush operation will be
> carried out to make i2c consume all scheduled DMA transfer.
> Currently, the same completion structure is being used for BAM
> transfer which has already completed without reinit. It will make
> flush operation wait_for_completion_timeout completed immediately
> and will proceed for freeing the DMA resources where the
> descriptors are still in process.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 08f8e01..9faa26c41a 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.

Not sure, if this was an intended change. But given that you are fixing this,
you can change the header to the new SPDX one.

> * Copyright (c) 2014, Sony Mobile Communications AB.
> *
> *
> @@ -844,6 +844,8 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> }
>
> if (ret || qup->bus_err || qup->qup_err) {
> + reinit_completion(&qup->xfer);
> +
> if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
> dev_err(qup->dev, "change to run state timed out");
> goto desc_err;
>

Except for the above nit,
Acked-by: Sricharan R <[email protected]>

Regards,
Sricharan

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-09 02:18:00

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> The rx_nents and tx_nents are redundant. rx_buf and tx_buf can
> be used for total number of SG entries.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 26 ++++++++++----------------
> 1 file changed, 10 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index c68f433..bb83a2967 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -692,7 +692,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> struct dma_async_tx_descriptor *txd, *rxd = NULL;
> int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
> dma_cookie_t cookie_rx, cookie_tx;
> - u32 rx_nents = 0, tx_nents = 0, len, blocks, rem;
> + u32 len, blocks, rem;
> u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
> u8 *tags;
>

This is correct. Just a nit, may be rx/tx_buf can be changed to
rx/tx_count to make it more clear.

Regards,
Sricharan

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-15 14:32:33

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> A single BAM transfer can have multiple read and write messages.
> The EOT and FLUSH tags should be scheduled at the end of BAM HW
> descriptors. Since the READ and WRITE can be present in any order
> so for some of the cases, these tags are not being written
> correctly.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 54 ++++++++++++++++++++------------------------
> 1 file changed, 24 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index bb83a2967..6357aff 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -560,7 +560,7 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
> }
>
> static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> - struct i2c_msg *msg, int is_dma)
> + struct i2c_msg *msg)
> {
> u16 addr = i2c_8bit_addr_from_msg(msg);
> int len = 0;
> @@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> else
> tags[len++] = data_len;
>
> - if ((msg->flags & I2C_M_RD) && last && is_dma) {
> - tags[len++] = QUP_BAM_INPUT_EOT;
> - tags[len++] = QUP_BAM_FLUSH_STOP;
> - }
> -
> return len;
> }
>
> @@ -614,7 +609,7 @@ static int qup_i2c_issue_xfer_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> int data_len = 0, tag_len, index;
> int ret;
>
> - tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg, 0);
> + tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg);
> index = msg->len - qup->blk.data_len;
>
> /* only tags are written for read */
> @@ -710,7 +705,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> while (qup->blk.pos < blocks) {
> tlen = (i == (blocks - 1)) ? rem : limit;
> tags = &qup->start_tag.start[off + len];
> - len += qup_i2c_set_tags(tags, qup, msg, 1);
> + len += qup_i2c_set_tags(tags, qup, msg);
> qup->blk.data_len -= tlen;
>
> /* scratch buf to read the start and len tags */
> @@ -738,17 +733,11 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> return ret;
>
> off += len;
> - /* scratch buf to read the BAM EOT and FLUSH tags */
> - ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
> - &qup->brx.tag.start[0],
> - 2, qup, DMA_FROM_DEVICE);
> - if (ret)
> - return ret;
> } else {
> while (qup->blk.pos < blocks) {
> tlen = (i == (blocks - 1)) ? rem : limit;
> tags = &qup->start_tag.start[off + tx_len];
> - len = qup_i2c_set_tags(tags, qup, msg, 1);
> + len = qup_i2c_set_tags(tags, qup, msg);
> qup->blk.data_len -= tlen;
>
> ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> @@ -768,26 +757,31 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> }
> off += tx_len;
>
> - if (idx == (num - 1)) {
> - len = 1;
> - if (rx_buf) {
> - qup->btx.tag.start[0] =
> - QUP_BAM_INPUT_EOT;
> - len++;
> - }
> - qup->btx.tag.start[len - 1] =
> - QUP_BAM_FLUSH_STOP;
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - &qup->btx.tag.start[0],
> - len, qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> - }
> }
> idx++;
> msg++;
> }

While here, can you please split above the if, else in to two separate functions ?
and probably one more function for handling the NACK case down below. The function
size at the moment is too big.

>
> + /* schedule the EOT and FLUSH I2C tags */
> + len = 1;
> + if (rx_buf) {
> + qup->btx.tag.start[0] = QUP_BAM_INPUT_EOT;
> + len++;
> +
> + /* scratch buf to read the BAM EOT and FLUSH tags */
> + ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
> + &qup->brx.tag.start[0],
> + 2, qup, DMA_FROM_DEVICE);
> + if (ret)
> + return ret;
> + }
> +
> + qup->btx.tag.start[len - 1] = QUP_BAM_FLUSH_STOP;
> + ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], &qup->btx.tag.start[0],
> + len, qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> +

May be you can change the commit to make it explicit to say what is being
fixed, like "current code is broken when there is sequence of wr,rd,wr" like that.
Agree on this fix otherwise.

Regards,
Sricharan

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 18:45:26

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode



On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> Currently each message length in complete transfer is being
> checked for determining DMA mode and if any of the message length
> is less than FIFO length then non DMA mode is being used which
> will increase overhead. DMA can be used for any length and it
> should be determined with complete transfer length. Now, this
> patch introduces DMA threshold length and the transfer will be
> done in DMA mode if the total length is greater than this
> threshold length.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 15 +++++++++------
> 1 file changed, 9 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 6227a5c..a91fc70 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -192,6 +192,8 @@ struct qup_i2c_dev {
> bool is_dma;
> /* To check if the current transfer is using DMA */
> bool use_dma;
> + /* The threshold length above which DMA will be used */
> + unsigned int dma_threshold;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -1294,7 +1296,8 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> - int ret, len, idx = 0;
> + int ret, idx = 0;
> + unsigned int total_len = 0;
>
> qup->bus_err = 0;
> qup->qup_err = 0;
> @@ -1320,14 +1323,13 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> goto out;
> }
>
> - len = (msgs[idx].len > qup->out_fifo_sz) ||
> - (msgs[idx].len > qup->in_fifo_sz);
> -
> - if (is_vmalloc_addr(msgs[idx].buf) || !len)
> + if (is_vmalloc_addr(msgs[idx].buf))
> break;
> +
> + total_len += msgs[idx].len;
> }
>
> - if (idx == num)
> + if (idx == num && total_len > qup->dma_threshold)
> qup->use_dma = true;
> }
>
> @@ -1587,6 +1589,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
>
> size = QUP_INPUT_FIFO_SIZE(io_mode);
> qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
> + qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);

The patch is fine. small nit, you can avoid this global dma_threshold. Instead
have it locally in qup_i2c_xfer_v2 itself.

Regards,
Sricharan


--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 18:45:45

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length



On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> Currently the completion timeout is being taken according to
> maximum transfer length which is too high if SCL is operating in
> high frequency. This patch calculates timeout on the basis of
> one-byte transfer time and uses the same for completion timeout.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index a91fc70..6df65ea 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -130,8 +130,8 @@
> #define MX_TX_RX_LEN SZ_64K
> #define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)
>
> -/* Max timeout in ms for 32k bytes */
> -#define TOUT_MAX 300
> +/* Min timeout for i2c transfers */
> +#define TOUT_MIN 2
>

may be you can mention, why is this 2 ?

Regards,
Sricharan


> /* Default values. Use these if FW query fails */
> #define DEFAULT_CLK_FREQ 100000
> @@ -172,6 +172,7 @@ struct qup_i2c_dev {
> int in_blk_sz;
>
> unsigned long one_byte_t;
> + unsigned long xfer_timeout;
> struct qup_i2c_block blk;
>
> struct i2c_msg *msg;
> @@ -845,7 +846,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> dma_async_issue_pending(qup->brx.dma);
> }
>
> - if (!wait_for_completion_timeout(&qup->xfer, TOUT_MAX * HZ)) {
> + if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) {
> dev_err(qup->dev, "normal trans timed out\n");
> ret = -ETIMEDOUT;
> }
> @@ -1601,6 +1602,8 @@ static int qup_i2c_probe(struct platform_device *pdev)
> */
> one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
> qup->one_byte_t = one_bit_t * 9;
> + qup->xfer_timeout = TOUT_MIN * HZ +
> + usecs_to_jiffies(MX_TX_RX_LEN * qup->one_byte_t);
>
> dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
> qup->in_blk_sz, qup->in_fifo_sz,
>

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 18:45:49

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode



On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> Currently the i2c error handling in BAM mode is not working
> properly in stress condition.
>
> 1. After an error, the FIFO are being written with FLUSH and
> EOT tags which should not be required since already these tags
> have been written in BAM descriptor itself.
>
> 2. QUP state is being moved to RESET in IRQ handler in case
> of error. When QUP HW encounters an error in BAM mode then it
> moves the QUP STATE to PAUSE state. In this case, I2C_FLUSH
> command needs to be executed while moving to RUN_STATE by writing
> to the QUP_STATE register with the I2C_FLUSH bit set to 1.
>
> 3. In Error case, sometimes, QUP generates more than one
> interrupt which will trigger the complete again. After an error,
> the flush operation will be scheduled after doing
> reinit_completion which should be triggered by BAM IRQ callback.
> If the second QUP IRQ comes during this time then it will call
> the complete and the transfer function will assume the all the
> BAM HW descriptors have been completed.
>
> 4. The release DMA is being called after each error which
> will free the DMA tx and rx channels. The error like NACK is very
> common in I2C transfer and every time this will be overhead. Now,
> since the error handling is proper so this release channel can be
> completely avoided.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 25 ++++++++++++++++---------
> 1 file changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 094be6a..6227a5c 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -228,9 +228,24 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
> if (bus_err)
> writel(bus_err, qup->base + QUP_I2C_STATUS);
>
> + /*
> + * Check for BAM mode and returns if already error has come for current
> + * transfer. In Error case, sometimes, QUP generates more than one
> + * interrupt.
> + */
> + if (qup->use_dma && (qup->qup_err || qup->bus_err))
> + return IRQ_HANDLED;
> +
> /* Reset the QUP State in case of error */
> if (qup_err || bus_err) {
> - writel(QUP_RESET_STATE, qup->base + QUP_STATE);
> + /*
> + * Don’t reset the QUP state in case of BAM mode. The BAM
> + * flush operation needs to be scheduled in transfer function
> + * which will clear the remaining schedule descriptors in BAM
> + * HW FIFO and generates the BAM interrupt.
> + */
> + if (!qup->use_dma)
> + writel(QUP_RESET_STATE, qup->base + QUP_STATE);
> goto done;
> }
>
> @@ -841,20 +856,12 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> goto desc_err;
> }
>
> - if (rx_buf)
> - writel(QUP_BAM_INPUT_EOT,
> - qup->base + QUP_OUT_FIFO_BASE);
> -
> - writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
> -
> qup_i2c_flush(qup);
>
> /* wait for remaining interrupts to occur */
> if (!wait_for_completion_timeout(&qup->xfer, HZ))
> dev_err(qup->dev, "flush timed out\n");
>
> - qup_i2c_rel_dma(qup);
> -
> ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
> }
>
>

Reviewed-by: Sricharan R <[email protected]>

Regards,
Sricharan

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 18:49:29

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> Following are the major issues in current driver code
>
> 1. The current driver simply assumes the transfer completion
> whenever its gets any non-error interrupts and then simply do the
> polling of available/free bytes in FIFO.
> 2. The block mode is not working properly since no handling in
> being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ.
>
> Because of above, i2c v1 transfers of size greater than 32 are failing
> with following error message
>
> i2c_qup 78b6000.i2c: timeout for fifo out full
>
> To make block mode working properly and move to use the interrupts
> instead of polling, major code reorganization is required. Following
> are the major changes done in this patch
>
> 1. Remove the polling of TX FIFO free space and RX FIFO available
> bytes and move to interrupts completely. QUP has QUP_MX_OUTPUT_DONE,
> QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
> interrupts to handle FIFO’s properly so check all these interrupts.
> 2. During write, For FIFO mode, TX FIFO can be directly written
> without checking for FIFO space. For block mode, the QUP will generate
> OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of available
> space.
> 3. During read, both TX and RX FIFO will be used. TX will be used
> for writing tags and RX will be used for receiving the data. In QUP,
> TX and RX can operate in separate mode so configure modes accordingly.
> 4. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
> will be generated after all the bytes have been copied in RX FIFO. For
> read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
> whenever it has block size of available data.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 399 ++++++++++++++++++++++++++++---------------
> 1 file changed, 257 insertions(+), 142 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index edea3b9..af6c21a 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -73,8 +73,11 @@
> #define QUP_IN_SVC_FLAG BIT(9)
> #define QUP_MX_OUTPUT_DONE BIT(10)
> #define QUP_MX_INPUT_DONE BIT(11)
> +#define OUT_BLOCK_WRITE_REQ BIT(12)
> +#define IN_BLOCK_READ_REQ BIT(13)
>
> /* I2C mini core related values */
> +#define QUP_NO_INPUT BIT(7)
> #define QUP_CLOCK_AUTO_GATE BIT(13)
> #define I2C_MINI_CORE (2 << 8)
> #define I2C_N_VAL 15
> @@ -138,13 +141,51 @@
> #define DEFAULT_CLK_FREQ 100000
> #define DEFAULT_SRC_CLK 20000000
>
> +/* MAX_OUTPUT_DONE_FLAG has been received */
> +#define QUP_BLK_EVENT_TX_IRQ_DONE BIT(0)
> +/* MAX_INPUT_DONE_FLAG has been received */
> +#define QUP_BLK_EVENT_RX_IRQ_DONE BIT(1)
> +/* All the TX bytes have been written in TX FIFO */
> +#define QUP_BLK_EVENT_TX_DATA_DONE BIT(2)
> +/* All the RX bytes have been read from RX FIFO */
> +#define QUP_BLK_EVENT_RX_DATA_DONE BIT(3)
> +
> +/* All the required events to mark a QUP I2C TX transfer completed */
> +#define QUP_BLK_EVENT_TX_DONE (QUP_BLK_EVENT_TX_IRQ_DONE | \
> + QUP_BLK_EVENT_TX_DATA_DONE)
> +/* All the required events to mark a QUP I2C RX transfer completed */
> +#define QUP_BLK_EVENT_RX_DONE (QUP_BLK_EVENT_TX_DONE | \
> + QUP_BLK_EVENT_RX_IRQ_DONE | \
> + QUP_BLK_EVENT_RX_DATA_DONE)
> +
> +/*
> + * count: no of blocks
> + * pos: current block number
> + * tx_tag_len: tx tag length for current block
> + * rx_tag_len: rx tag length for current block
> + * data_len: remaining data length for current message
> + * total_tx_len: total tx length including tag bytes for current QUP transfer
> + * total_rx_len: total rx length including tag bytes for current QUP transfer
> + * tx_fifo_free: number of free bytes in current QUP block write.
> + * fifo_available: number of available bytes in RX FIFO for current
> + * QUP block read
> + * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
> + * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
> + * tags: contains tx tag bytes for current QUP transfer
> + */
> struct qup_i2c_block {
> - int count;
> - int pos;
> - int tx_tag_len;
> - int rx_tag_len;
> - int data_len;
> - u8 tags[6];
> + int count;
> + int pos;
> + int tx_tag_len;
> + int rx_tag_len;
> + int data_len;
> + int total_tx_len;
> + int total_rx_len;
> + int tx_fifo_free;
> + int fifo_available;
> + bool is_tx_blk_mode;
> + bool is_rx_blk_mode;
> + u8 tags[6];
> };
>
> struct qup_i2c_tag {
> @@ -187,6 +228,7 @@ struct qup_i2c_dev {
>
> /* To check if this is the last msg */
> bool is_last;
> + bool is_qup_v1;
>
> /* To configure when bus is in run state */
> int config_run;
> @@ -195,6 +237,10 @@ struct qup_i2c_dev {
> bool is_dma;
> /* To check if the current transfer is using DMA */
> bool use_dma;
> + /* Required events to mark QUP transfer as completed */
> + u32 blk_events;
> + /* Already completed events in QUP transfer */
> + u32 cur_blk_events;
> /* The threshold length above which DMA will be used */
> unsigned int dma_threshold;
> unsigned int max_xfer_sg_len;
> @@ -205,11 +251,18 @@ struct qup_i2c_dev {
> struct qup_i2c_bam btx;
>
> struct completion xfer;
> + /* function to write data in tx fifo */
> + void (*write_tx_fifo)(struct qup_i2c_dev *qup);
> + /* function to read data from rx fifo */
> + void (*read_rx_fifo)(struct qup_i2c_dev *qup);
> + /* function to write tags in tx fifo for i2c read transfer */
> + void (*write_rx_tags)(struct qup_i2c_dev *qup);
> };
>
> static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
> {
> struct qup_i2c_dev *qup = dev;
> + struct qup_i2c_block *blk = &qup->blk;
> u32 bus_err;
> u32 qup_err;
> u32 opflags;
> @@ -256,12 +309,54 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
> goto done;
> }
>
> - if (opflags & QUP_IN_SVC_FLAG)
> - writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> + if (!qup->is_qup_v1)
> + goto done;
>
> - if (opflags & QUP_OUT_SVC_FLAG)
> + if (opflags & QUP_OUT_SVC_FLAG) {
> writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>
> + /*
> + * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG.
> + * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind
> + * QUP_OUTPUT_SERVICE_FLAG. The only reason for
> + * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is
> + * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking
> + * here QUP_OUTPUT_SERVICE_FLAG and assumes that
> + * QUP_MAX_OUTPUT_DONE_FLAG.
> + */
> + if (!blk->is_tx_blk_mode)
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
> +
> + if (opflags & OUT_BLOCK_WRITE_REQ) {
> + blk->tx_fifo_free += qup->out_blk_sz;
> + if (qup->msg->flags & I2C_M_RD)
> + qup->write_rx_tags(qup);
> + else
> + qup->write_tx_fifo(qup);
> + }
> + }
> +
> + if (opflags & QUP_IN_SVC_FLAG) {
> + writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
> +
> + if (!blk->is_rx_blk_mode) {
> + blk->fifo_available += qup->in_fifo_sz;
> + qup->read_rx_fifo(qup);
> + } else if (opflags & IN_BLOCK_READ_REQ) {
> + blk->fifo_available += qup->in_blk_sz;
> + qup->read_rx_fifo(qup);
> + }
> + }
> +
> + if (opflags & QUP_MX_OUTPUT_DONE)
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
> +
> + if (opflags & QUP_MX_INPUT_DONE)
> + qup->cur_blk_events |= QUP_BLK_EVENT_RX_IRQ_DONE;
> +
> + if (qup->cur_blk_events != qup->blk_events)
> + return IRQ_HANDLED;

Is it correct that if we do a complete in above case, i mean
for TX -> based on QUP_MX_OUTPUT_DONE and for RX -> based on
QUP_MX_INPUT_DONE, would that simplify things by getting rid of
QUP_BLK_EVENT_RX/TX_DONE and QUP_BLK_EVENT_RX/TX_IRQ_DONE
altogether ?

Regards,
Sricharan


> +
> done:
> qup->qup_err = qup_err;
> qup->bus_err = bus_err;
> @@ -327,6 +422,28 @@ static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
> return 0;
> }
>
> +/* Check if I2C bus returns to IDLE state */
> +static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
> +{
> + unsigned long timeout;
> + u32 status;
> + int ret = 0;
> +
> + timeout = jiffies + len * 4;
> + for (;;) {
> + status = readl(qup->base + QUP_I2C_STATUS);
> + if (!(status & I2C_STATUS_BUS_ACTIVE))
> + break;
> +
> + if (time_after(jiffies, timeout))
> + ret = -ETIMEDOUT;
> +
> + usleep_range(len, len * 2);
> + }
> +
> + return ret;
> +}
> +
> /**
> * qup_i2c_wait_ready - wait for a give number of bytes in tx/rx path
> * @qup: The qup_i2c_dev device
> @@ -397,23 +514,6 @@ static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
> }
> }
>
> -static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - /* Number of entries to shift out, including the start */
> - int total = msg->len + 1;
> -
> - if (total < qup->out_fifo_sz) {
> - /* FIFO mode */
> - writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
> - writel(total, qup->base + QUP_MX_WRITE_CNT);
> - } else {
> - /* BLOCK mode (transfer data on chunks) */
> - writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
> - qup->base + QUP_IO_MODE);
> - writel(total, qup->base + QUP_MX_OUTPUT_CNT);
> - }
> -}
> -
> static int check_for_fifo_space(struct qup_i2c_dev *qup)
> {
> int ret;
> @@ -446,28 +546,25 @@ static int check_for_fifo_space(struct qup_i2c_dev *qup)
> return ret;
> }
>
> -static int qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> +static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
> {
> + struct qup_i2c_block *blk = &qup->blk;
> + struct i2c_msg *msg = qup->msg;
> u32 addr = msg->addr << 1;
> u32 qup_tag;
> int idx;
> u32 val;
> - int ret = 0;
>
> if (qup->pos == 0) {
> val = QUP_TAG_START | addr;
> idx = 1;
> + blk->tx_fifo_free--;
> } else {
> val = 0;
> idx = 0;
> }
>
> - while (qup->pos < msg->len) {
> - /* Check that there's space in the FIFO for our pair */
> - ret = check_for_fifo_space(qup);
> - if (ret)
> - return ret;
> -
> + while (blk->tx_fifo_free && qup->pos < msg->len) {
> if (qup->pos == msg->len - 1)
> qup_tag = QUP_TAG_STOP;
> else
> @@ -484,11 +581,11 @@ static int qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
>
> qup->pos++;
> idx++;
> + blk->tx_fifo_free--;
> }
>
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> -
> - return ret;
> + if (qup->pos == msg->len)
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
> }
>
> static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
> @@ -1009,64 +1106,6 @@ static int qup_i2c_write_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> return ret;
> }
>
> -static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int ret;
> -
> - qup->msg = msg;
> - qup->pos = 0;
> -
> - enable_irq(qup->irq);
> -
> - qup_i2c_set_write_mode(qup, msg);
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto err;
> -
> - writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> -
> - do {
> - ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_issue_write(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_wait_for_complete(qup, msg);
> - if (ret)
> - goto err;
> - } while (qup->pos < msg->len);
> -
> - /* Wait for the outstanding data in the fifo to drain */
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
> -err:
> - disable_irq(qup->irq);
> - qup->msg = NULL;
> -
> - return ret;
> -}
> -
> -static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
> -{
> - if (len < qup->in_fifo_sz) {
> - /* FIFO mode */
> - writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
> - writel(len, qup->base + QUP_MX_READ_CNT);
> - } else {
> - /* BLOCK mode (transfer data on chunks) */
> - writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
> - qup->base + QUP_IO_MODE);
> - writel(len, qup->base + QUP_MX_INPUT_CNT);
> - }
> -}
> -
> static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
> {
> int tx_len = qup->blk.tx_tag_len;
> @@ -1089,44 +1128,27 @@ static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
> }
> }
>
> -static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - u32 addr, len, val;
> -
> - addr = i2c_8bit_addr_from_msg(msg);
> -
> - /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
> - len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
> -
> - val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
> - writel(val, qup->base + QUP_OUT_FIFO_BASE);
> -}
> -
> -
> -static int qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> +static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
> {
> + struct qup_i2c_block *blk = &qup->blk;
> + struct i2c_msg *msg = qup->msg;
> u32 val = 0;
> int idx;
> - int ret = 0;
>
> - for (idx = 0; qup->pos < msg->len; idx++) {
> + while (blk->fifo_available && qup->pos < msg->len) {
> if ((idx & 1) == 0) {
> - /* Check that FIFO have data */
> - ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
> - SET_BIT, 4 * ONE_BYTE);
> - if (ret)
> - return ret;
> -
> /* Reading 2 words at time */
> val = readl(qup->base + QUP_IN_FIFO_BASE);
> -
> msg->buf[qup->pos++] = val & 0xFF;
> } else {
> msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
> }
> + idx++;
> + blk->fifo_available--;
> }
>
> - return ret;
> + if (qup->pos == msg->len)
> + qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
> }
>
> static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
> @@ -1227,49 +1249,137 @@ static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> return ret;
> }
>
> -static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> +static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool is_rx)
> {
> + qup->cur_blk_events = 0;
> + qup->blk_events = is_rx ? QUP_BLK_EVENT_RX_DONE : QUP_BLK_EVENT_TX_DONE;
> +}
> +
> +static void qup_i2c_write_rx_tags_v1(struct qup_i2c_dev *qup)
> +{
> + struct i2c_msg *msg = qup->msg;
> + u32 addr, len, val;
> +
> + addr = i2c_8bit_addr_from_msg(msg);
> +
> + /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
> + len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
> +
> + val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
> + writel(val, qup->base + QUP_OUT_FIFO_BASE);
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
> +}
> +
> +static void qup_i2c_conf_v1(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + u32 qup_config = I2C_MINI_CORE | I2C_N_VAL;
> + u32 io_mode = QUP_REPACK_EN;
> +
> + blk->is_tx_blk_mode =
> + blk->total_tx_len > qup->out_fifo_sz ? true : false;
> + blk->is_rx_blk_mode =
> + blk->total_rx_len > qup->in_fifo_sz ? true : false;
> +
> + if (blk->is_tx_blk_mode) {
> + io_mode |= QUP_OUTPUT_BLK_MODE;
> + writel(0, qup->base + QUP_MX_WRITE_CNT);
> + writel(blk->total_tx_len, qup->base + QUP_MX_OUTPUT_CNT);
> + } else {
> + writel(0, qup->base + QUP_MX_OUTPUT_CNT);
> + writel(blk->total_tx_len, qup->base + QUP_MX_WRITE_CNT);
> + }
> +
> + if (blk->total_rx_len) {
> + if (blk->is_rx_blk_mode) {
> + io_mode |= QUP_INPUT_BLK_MODE;
> + writel(0, qup->base + QUP_MX_READ_CNT);
> + writel(blk->total_rx_len, qup->base + QUP_MX_INPUT_CNT);
> + } else {
> + writel(0, qup->base + QUP_MX_INPUT_CNT);
> + writel(blk->total_rx_len, qup->base + QUP_MX_READ_CNT);
> + }
> + } else {
> + qup_config |= QUP_NO_INPUT;
> + }
> +
> + writel(qup_config, qup->base + QUP_CONFIG);
> + writel(io_mode, qup->base + QUP_IO_MODE);
> +}
> +
> +static void qup_i2c_clear_blk_v1(struct qup_i2c_block *blk)
> +{
> + blk->tx_fifo_free = 0;
> + blk->fifo_available = 0;
> +}
> +
> +static int qup_i2c_conf_xfer_v1(struct qup_i2c_dev *qup, bool is_rx)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> int ret;
>
> - qup->msg = msg;
> - qup->pos = 0;
> -
> - enable_irq(qup->irq);
> - qup_i2c_set_read_mode(qup, msg->len);
> -
> + qup_i2c_clear_blk_v1(blk);
> + qup_i2c_conf_v1(qup);
> ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> if (ret)
> - goto err;
> + return ret;
>
> writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
>
> ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> if (ret)
> - goto err;
> + return ret;
>
> - qup_i2c_issue_read(qup, msg);
> + qup_i2c_set_blk_event(qup, is_rx);
> + reinit_completion(&qup->xfer);
> + enable_irq(qup->irq);
> + if (!blk->is_tx_blk_mode) {
> + blk->tx_fifo_free = qup->out_fifo_sz;
> +
> + if (is_rx)
> + qup_i2c_write_rx_tags_v1(qup);
> + else
> + qup_i2c_write_tx_fifo_v1(qup);
> + }
>
> ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> if (ret)
> goto err;
>
> - do {
> - ret = qup_i2c_wait_for_complete(qup, msg);
> - if (ret)
> - goto err;
> + ret = qup_i2c_wait_for_complete(qup, qup->msg);
> + if (ret)
> + goto err;
>
> - ret = qup_i2c_read_fifo(qup, msg);
> - if (ret)
> - goto err;
> - } while (qup->pos < msg->len);
> + ret = qup_i2c_bus_active(qup, ONE_BYTE);
>
> err:
> disable_irq(qup->irq);
> - qup->msg = NULL;
> -
> return ret;
> }
>
> +static int qup_i2c_write_one(struct qup_i2c_dev *qup)
> +{
> + struct i2c_msg *msg = qup->msg;
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + qup->pos = 0;
> + blk->total_tx_len = msg->len + 1;
> + blk->total_rx_len = 0;
> +
> + return qup_i2c_conf_xfer_v1(qup, false);
> +}
> +
> +static int qup_i2c_read_one(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + qup->pos = 0;
> + blk->total_tx_len = 2;
> + blk->total_rx_len = qup->msg->len;
> +
> + return qup_i2c_conf_xfer_v1(qup, true);
> +}
> +
> static int qup_i2c_xfer(struct i2c_adapter *adap,
> struct i2c_msg msgs[],
> int num)
> @@ -1308,10 +1418,11 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
> goto out;
> }
>
> + qup->msg = &msgs[idx];
> if (msgs[idx].flags & I2C_M_RD)
> - ret = qup_i2c_read_one(qup, &msgs[idx]);
> + ret = qup_i2c_read_one(qup);
> else
> - ret = qup_i2c_write_one(qup, &msgs[idx]);
> + ret = qup_i2c_write_one(qup);
>
> if (ret)
> break;
> @@ -1489,6 +1600,10 @@ static int qup_i2c_probe(struct platform_device *pdev)
> if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
> qup->adap.algo = &qup_i2c_algo;
> qup->adap.quirks = &qup_i2c_quirks;
> + qup->is_qup_v1 = true;
> + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
> + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
> + qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
> } else {
> qup->adap.algo = &qup_i2c_algo_v2;
> ret = qup_i2c_req_dma(qup);
>

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 19:03:02

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> Following are the major issues in current driver code
>
> 1. The current driver simply assumes the transfer completion
> whenever its gets any non-error interrupts and then simply do the
> polling of available/free bytes in FIFO.
> 2. The block mode is not working properly since no handling in
> being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_READ.
> 3. An i2c transfer can contain multiple message and QUP v2
> supports reconfiguration during run in which the mode should be same
> for all the sub transfer. Currently the mode is being programmed
> before every sub transfer which is functionally wrong. If one message
> is less than FIFO length and other message is greater than FIFO length,
then transfers will fail.
>
> Because of above, i2c v2 transfers of size greater than 64 are failing
> with following error message
>
> i2c_qup 78b6000.i2c: timeout for fifo out full
>
> To make block mode working properly and move to use the interrupts
> instead of polling, major code reorganization is required. Following
> are the major changes done in this patch
>
> 1. Remove the polling of TX FIFO free space and RX FIFO available
> bytes and move to interrupts completely. QUP has QUP_MX_OUTPUT_DONE,
> QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
> interrupts to handle FIFO’s properly so check all these interrupts.
> 2. Determine the mode for transfer before starting by checking
> all the tx/rx data length in each message. The complete message can be
> transferred either in DMA mode or Programmed IO by FIFO/Block mode.
> in DMA mode, both tx and rx uses same mode but in PIO mode, the TX and
> RX can be in different mode.
> 3. During write, For FIFO mode, TX FIFO can be directly written
> without checking for FIFO space. For block mode, the QUP will generate
> OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of available
> space.
> 4. During read, both TX and RX FIFO will be used. TX will be used
> for writing tags and RX will be used for receiving the data. In QUP,
> TX and RX can operate in separate mode so configure modes accordingly.
> 5. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
> will be generated after all the bytes have been copied in RX FIFO. For
> read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
> whenever it has block size of available data.
> 6. Split the transfer in chunk of one QUP block size(256 bytes)
> and schedule each block separately. QUP v2 supports reconfiguration
> during run in which QUP can transfer multiple blocks without issuing a
> stop events.
> 7. Port the SMBus block read support for new code changes.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 917 +++++++++++++++++++++++++------------------
> 1 file changed, 533 insertions(+), 384 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index af6c21a..46736a1 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -141,6 +141,15 @@
> #define DEFAULT_CLK_FREQ 100000
> #define DEFAULT_SRC_CLK 20000000
>
> +/*
> + * Max tags length (start, stop and maximum 2 bytes address) for each QUP
> + * data transfer
> + */
> +#define QUP_MAX_TAGS_LEN 4
> +/* Max data length for each DATARD tags */
> +#define RECV_MAX_DATA_LEN 254
> +/* TAG length for DATA READ in RX FIFO */
> +#define READ_RX_TAGS_LEN 2
> /* MAX_OUTPUT_DONE_FLAG has been received */
> #define QUP_BLK_EVENT_TX_IRQ_DONE BIT(0)
> /* MAX_INPUT_DONE_FLAG has been received */
> @@ -164,11 +173,24 @@
> * tx_tag_len: tx tag length for current block
> * rx_tag_len: rx tag length for current block
> * data_len: remaining data length for current message
> + * cur_blk_len: data length for current block
> * total_tx_len: total tx length including tag bytes for current QUP transfer
> * total_rx_len: total rx length including tag bytes for current QUP transfer
> + * tx_fifo_data_pos: current byte number in TX FIFO word
> * tx_fifo_free: number of free bytes in current QUP block write.
> + * rx_fifo_data_pos: current byte number in RX FIFO word
> * fifo_available: number of available bytes in RX FIFO for current
> * QUP block read
> + * tx_fifo_data: QUP TX FIFO write works on word basis (4 bytes). New byte write
> + * to TX FIFO will be appended in this data and will be written to
> + * TX FIFO when all the 4 bytes are available.
> + * rx_fifo_data: QUP RX FIFO read works on word basis (4 bytes). This will
> + * contains the 4 bytes of RX data.
> + * cur_data: pointer to tell cur data position for current message
> + * cur_tx_tags: pointer to tell cur position in tags
> + * tx_tags_sent: all tx tag bytes have been written in FIFO word
> + * send_last_word: for tx FIFO, last word send is pending in current block
> + * rx_tags_fetched: all the rx tag bytes have been fetched from rx fifo word
> * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
> * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
> * tags: contains tx tag bytes for current QUP transfer
> @@ -179,10 +201,20 @@ struct qup_i2c_block {
> int tx_tag_len;
> int rx_tag_len;
> int data_len;
> + int cur_blk_len;
> int total_tx_len;
> int total_rx_len;
> + int tx_fifo_data_pos;
> int tx_fifo_free;
> + int rx_fifo_data_pos;
> int fifo_available;
> + u32 tx_fifo_data;
> + u32 rx_fifo_data;
> + u8 *cur_data;
> + u8 *cur_tx_tags;
> + bool tx_tags_sent;
> + bool send_last_word;
> + bool rx_tags_fetched;
> bool is_tx_blk_mode;
> bool is_rx_blk_mode;
> u8 tags[6];
> @@ -214,6 +246,7 @@ struct qup_i2c_dev {
> int out_blk_sz;
> int in_blk_sz;
>
> + int blk_xfer_limit;
> unsigned long one_byte_t;
> unsigned long xfer_timeout;
> struct qup_i2c_block blk;
> @@ -228,10 +261,10 @@ struct qup_i2c_dev {
>
> /* To check if this is the last msg */
> bool is_last;
> - bool is_qup_v1;
> + bool is_smbus_read;
>
> /* To configure when bus is in run state */
> - int config_run;
> + u32 config_run;
>
> /* dma parameters */
> bool is_dma;
> @@ -245,6 +278,8 @@ struct qup_i2c_dev {
> unsigned int dma_threshold;
> unsigned int max_xfer_sg_len;
> unsigned int tag_buf_pos;
> + /* The threshold length above which block mode will be used */
> + unsigned int blk_mode_threshold;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -309,9 +344,6 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
> goto done;
> }
>
> - if (!qup->is_qup_v1)
> - goto done;
> -
> if (opflags & QUP_OUT_SVC_FLAG) {
> writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>
> @@ -444,108 +476,6 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
> return ret;
> }
>
> -/**
> - * qup_i2c_wait_ready - wait for a give number of bytes in tx/rx path
> - * @qup: The qup_i2c_dev device
> - * @op: The bit/event to wait on
> - * @val: value of the bit to wait on, 0 or 1
> - * @len: The length the bytes to be transferred
> - */
> -static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val,
> - int len)
> -{
> - unsigned long timeout;
> - u32 opflags;
> - u32 status;
> - u32 shift = __ffs(op);
> - int ret = 0;
> -
> - len *= qup->one_byte_t;
> - /* timeout after a wait of twice the max time */
> - timeout = jiffies + len * 4;
> -
> - for (;;) {
> - opflags = readl(qup->base + QUP_OPERATIONAL);
> - status = readl(qup->base + QUP_I2C_STATUS);
> -
> - if (((opflags & op) >> shift) == val) {
> - if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) {
> - if (!(status & I2C_STATUS_BUS_ACTIVE)) {
> - ret = 0;
> - goto done;
> - }
> - } else {
> - ret = 0;
> - goto done;
> - }
> - }
> -
> - if (time_after(jiffies, timeout)) {
> - ret = -ETIMEDOUT;
> - goto done;
> - }
> - usleep_range(len, len * 2);
> - }
> -
> -done:
> - if (qup->bus_err || qup->qup_err)
> - ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
> -
> - return ret;
> -}
> -
> -static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
> - struct i2c_msg *msg)
> -{
> - /* Number of entries to shift out, including the tags */
> - int total = msg->len + qup->blk.tx_tag_len;
> -
> - total |= qup->config_run;
> -
> - if (total < qup->out_fifo_sz) {
> - /* FIFO mode */
> - writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
> - writel(total, qup->base + QUP_MX_WRITE_CNT);
> - } else {
> - /* BLOCK mode (transfer data on chunks) */
> - writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
> - qup->base + QUP_IO_MODE);
> - writel(total, qup->base + QUP_MX_OUTPUT_CNT);
> - }
> -}
> -
> -static int check_for_fifo_space(struct qup_i2c_dev *qup)
> -{
> - int ret;
> -
> - ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> - if (ret)
> - goto out;
> -
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_FULL,
> - RESET_BIT, 4 * ONE_BYTE);
> - if (ret) {
> - /* Fifo is full. Drain out the fifo */
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto out;
> -
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY,
> - RESET_BIT, 256 * ONE_BYTE);
> - if (ret) {
> - dev_err(qup->dev, "timeout for fifo out full");
> - goto out;
> - }
> -
> - ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> - if (ret)
> - goto out;
> - }
> -
> -out:
> - return ret;
> -}
> -
> static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
> {
> struct qup_i2c_block *blk = &qup->blk;
> @@ -591,60 +521,17 @@ static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
> static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
> struct i2c_msg *msg)
> {
> - memset(&qup->blk, 0, sizeof(qup->blk));
> -
> + qup->blk.pos = 0;
> qup->blk.data_len = msg->len;
> - qup->blk.count = (msg->len + QUP_READ_LIMIT - 1) / QUP_READ_LIMIT;
> -
> - /* 4 bytes for first block and 2 writes for rest */
> - qup->blk.tx_tag_len = 4 + (qup->blk.count - 1) * 2;
> -
> - /* There are 2 tag bytes that are read in to fifo for every block */
> - if (msg->flags & I2C_M_RD)
> - qup->blk.rx_tag_len = qup->blk.count * 2;
> -}
> -
> -static int qup_i2c_send_data(struct qup_i2c_dev *qup, int tlen, u8 *tbuf,
> - int dlen, u8 *dbuf)
> -{
> - u32 val = 0, idx = 0, pos = 0, i = 0, t;
> - int len = tlen + dlen;
> - u8 *buf = tbuf;
> - int ret = 0;
> -
> - while (len > 0) {
> - ret = check_for_fifo_space(qup);
> - if (ret)
> - return ret;
> -
> - t = (len >= 4) ? 4 : len;
> -
> - while (idx < t) {
> - if (!i && (pos >= tlen)) {
> - buf = dbuf;
> - pos = 0;
> - i = 1;
> - }
> - val |= buf[pos++] << (idx++ * 8);
> - }
> -
> - writel(val, qup->base + QUP_OUT_FIFO_BASE);
> - idx = 0;
> - val = 0;
> - len -= 4;
> - }
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> -
> - return ret;
> + qup->blk.count = DIV_ROUND_UP(msg->len, qup->blk_xfer_limit);
> }
>
> static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
> {
> int data_len;
>
> - if (qup->blk.data_len > QUP_READ_LIMIT)
> - data_len = QUP_READ_LIMIT;
> + if (qup->blk.data_len > qup->blk_xfer_limit)
> + data_len = qup->blk_xfer_limit;
> else
> data_len = qup->blk.data_len;
>
> @@ -661,9 +548,9 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
> {
> int len = 0;
>
> - if (msg->len > 1) {
> + if (qup->is_smbus_read) {
> tags[len++] = QUP_TAG_V2_DATARD_STOP;
> - tags[len++] = qup_i2c_get_data_len(qup) - 1;
> + tags[len++] = qup_i2c_get_data_len(qup);
> } else {
> tags[len++] = QUP_TAG_V2_START;
> tags[len++] = addr & 0xff;
> @@ -725,24 +612,6 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> return len;
> }
>
> -static int qup_i2c_issue_xfer_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int data_len = 0, tag_len, index;
> - int ret;
> -
> - tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg);
> - index = msg->len - qup->blk.data_len;
> -
> - /* only tags are written for read */
> - if (!(msg->flags & I2C_M_RD))
> - data_len = qup_i2c_get_data_len(qup);
> -
> - ret = qup_i2c_send_data(qup, tag_len, qup->blk.tags,
> - data_len, &msg->buf[index]);
> - qup->blk.data_len -= data_len;
> -
> - return ret;
> -}
>
> static void qup_i2c_bam_cb(void *data)
> {
> @@ -809,6 +678,7 @@ static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> u32 i = 0, tlen, tx_len = 0;
> u8 *tags;
>
> + qup->blk_xfer_limit = QUP_READ_LIMIT;
> qup_i2c_set_blk_data(qup, msg);
>
> blocks = qup->blk.count;
> @@ -1057,7 +927,7 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
> unsigned long left;
> int ret = 0;
>
> - left = wait_for_completion_timeout(&qup->xfer, HZ);
> + left = wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout);
> if (!left) {
> writel(1, qup->base + QUP_SW_RESET);
> ret = -ETIMEDOUT;
> @@ -1069,65 +939,6 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
> return ret;
> }
>
> -static int qup_i2c_write_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int ret = 0;
> -
> - qup->msg = msg;
> - qup->pos = 0;
> - enable_irq(qup->irq);
> - qup_i2c_set_blk_data(qup, msg);
> - qup_i2c_set_write_mode_v2(qup, msg);
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto err;
> -
> - writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> -
> - do {
> - ret = qup_i2c_issue_xfer_v2(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_wait_for_complete(qup, msg);
> - if (ret)
> - goto err;
> -
> - qup->blk.pos++;
> - } while (qup->blk.pos < qup->blk.count);
> -
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
> -
> -err:
> - disable_irq(qup->irq);
> - qup->msg = NULL;
> -
> - return ret;
> -}
> -
> -static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
> -{
> - int tx_len = qup->blk.tx_tag_len;
> -
> - len += qup->blk.rx_tag_len;
> - len |= qup->config_run;
> - tx_len |= qup->config_run;
> -
> - if (len < qup->in_fifo_sz) {
> - /* FIFO mode */
> - writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
> - writel(tx_len, qup->base + QUP_MX_WRITE_CNT);
> - writel(len, qup->base + QUP_MX_READ_CNT);
> - } else {
> - /* BLOCK mode (transfer data on chunks) */
> - writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
> - qup->base + QUP_IO_MODE);
> - writel(tx_len, qup->base + QUP_MX_OUTPUT_CNT);
> - writel(len, qup->base + QUP_MX_INPUT_CNT);
> - }
> -}
> -
> static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
> {
> struct qup_i2c_block *blk = &qup->blk;
> @@ -1151,104 +962,6 @@ static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
> qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
> }
>
> -static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
> - struct i2c_msg *msg)
> -{
> - u32 val;
> - int idx, pos = 0, ret = 0, total, msg_offset = 0;
> -
> - /*
> - * If the message length is already read in
> - * the first byte of the buffer, account for
> - * that by setting the offset
> - */
> - if (qup_i2c_check_msg_len(msg) && (msg->len > 1))
> - msg_offset = 1;
> - total = qup_i2c_get_data_len(qup);
> - total -= msg_offset;
> -
> - /* 2 extra bytes for read tags */
> - while (pos < (total + 2)) {
> - /* Check that FIFO have data */
> - ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
> - SET_BIT, 4 * ONE_BYTE);
> - if (ret) {
> - dev_err(qup->dev, "timeout for fifo not empty");
> - return ret;
> - }
> - val = readl(qup->base + QUP_IN_FIFO_BASE);
> -
> - for (idx = 0; idx < 4; idx++, val >>= 8, pos++) {
> - /* first 2 bytes are tag bytes */
> - if (pos < 2)
> - continue;
> -
> - if (pos >= (total + 2))
> - goto out;
> - msg->buf[qup->pos + msg_offset] = val & 0xff;
> - qup->pos++;
> - }
> - }
> -
> -out:
> - qup->blk.data_len -= total;
> -
> - return ret;
> -}
> -
> -static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int ret = 0;
> -
> - qup->msg = msg;
> - qup->pos = 0;
> - enable_irq(qup->irq);
> - qup_i2c_set_blk_data(qup, msg);
> - qup_i2c_set_read_mode_v2(qup, msg->len);
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto err;
> -
> - writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> -
> - do {
> - ret = qup_i2c_issue_xfer_v2(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_wait_for_complete(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_read_fifo_v2(qup, msg);
> - if (ret)
> - goto err;
> -
> - qup->blk.pos++;
> -
> - /* Handle SMBus block read length */
> - if (qup_i2c_check_msg_len(msg) && (msg->len == 1)) {
> - if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) {
> - ret = -EPROTO;
> - goto err;
> - }
> - msg->len += msg->buf[0];
> - qup->pos = 0;
> - qup_i2c_set_blk_data(qup, msg);
> - /* set tag length for block read */
> - qup->blk.tx_tag_len = 2;
> - qup_i2c_set_read_mode_v2(qup, msg->buf[0]);
> - }
> - } while (qup->blk.pos < qup->blk.count);
> -
> -err:
> - disable_irq(qup->irq);
> - qup->msg = NULL;
> -
> - return ret;
> -}
> -
> static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool is_rx)
> {
> qup->cur_blk_events = 0;
> @@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
> return ret;
> }
>

Since this is used for both FIFO and BLK mode, might be good to call it
qup_i2c_set_event. Same in rest of the places and macros as well.
But if this is going to be removed altogether, then great.

> +/*
> + * Function to configure registers related with reconfiguration during run
> + * and will be done before each I2C sub transfer.
> + */
> +static void qup_i2c_conf_count_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
> +
> + if (blk->is_tx_blk_mode)
> + writel(qup->config_run | blk->total_tx_len,
> + qup->base + QUP_MX_OUTPUT_CNT);
> + else
> + writel(qup->config_run | blk->total_tx_len,
> + qup->base + QUP_MX_WRITE_CNT);
> +
> + if (blk->total_rx_len) {
> + if (blk->is_rx_blk_mode)
> + writel(qup->config_run | blk->total_rx_len,
> + qup->base + QUP_MX_INPUT_CNT);
> + else
> + writel(qup->config_run | blk->total_rx_len,
> + qup->base + QUP_MX_READ_CNT);
> + } else {
> + qup_config |= QUP_NO_INPUT;
> + }
> +
> + writel(qup_config, qup->base + QUP_CONFIG);
> +}
> +
> +/*
> + * Function to configure registers related with transfer mode (FIFO/Block)
> + * before starting of i2c transfer and will be done only once in QUP RESET
> + * state.
> + */
> +static void qup_i2c_conf_mode_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + u32 io_mode = QUP_REPACK_EN;
> +
> + if (blk->is_tx_blk_mode) {
> + io_mode |= QUP_OUTPUT_BLK_MODE;
> + writel(0, qup->base + QUP_MX_WRITE_CNT);
> + } else {
> + writel(0, qup->base + QUP_MX_OUTPUT_CNT);
> + }
> +
> + if (blk->is_rx_blk_mode) {
> + io_mode |= QUP_INPUT_BLK_MODE;
> + writel(0, qup->base + QUP_MX_READ_CNT);
> + } else {
> + writel(0, qup->base + QUP_MX_INPUT_CNT);
> + }
> +
> + writel(io_mode, qup->base + QUP_IO_MODE);
> +}
> +
> +/*
> + * Function to clear required variables before starting of any QUP v2
> + * sub transfer
> + */
> +static void qup_i2c_clear_blk_v2(struct qup_i2c_block *blk)
> +{
> + blk->send_last_word = false;
> + blk->tx_tags_sent = false;
> + blk->tx_fifo_data = 0;
> + blk->tx_fifo_data_pos = 0;
> + blk->tx_fifo_free = 0;
> +
> + blk->rx_tags_fetched = false;
> + blk->rx_fifo_data = 0;
> + blk->rx_fifo_data_pos = 0;
> + blk->fifo_available = 0;
> +}
> +
> +/*
> + * Function to receive data from RX FIFO for read message in QUP v2
> + * i2c transfer.
> + */
> +static void qup_i2c_recv_data(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + int j;
> +
> + for (j = blk->rx_fifo_data_pos;
> + blk->cur_blk_len && blk->fifo_available;
> + blk->cur_blk_len--, blk->fifo_available--) {
> + if (j == 0)
> + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
> +
> + *(blk->cur_data++) = blk->rx_fifo_data;
> + blk->rx_fifo_data >>= 8;
> +
> + if (j == 3)
> + j = 0;
> + else
> + j++;
> + }
> +
> + blk->rx_fifo_data_pos = j;
> +}
> +
> +/* Function to receive tags for read message in QUP v2 i2c transfer. */
> +static void qup_i2c_recv_tags(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
> + blk->rx_fifo_data >>= blk->rx_tag_len * 8;

why cant it be simply ignored ?

> + blk->rx_fifo_data_pos = blk->rx_tag_len;
> + blk->fifo_available -= blk->rx_tag_len;
> +}
> +
> +/*
> + * This function reads the data and tags from RX FIFO. Since in read case, the
> + * tags will be preceded by received data bytes need to be written so
> + * 1. Check if rx_tags_fetched is false i.e. the start of QUP block so receive
> + * all tag bytes and discard that.
> + * 2. Read the data from RX FIFO. When all the data bytes have been read then
> + * mark the QUP_BLK_EVENT_RX_DATA_DONE in current block event and return.
> + */
> +static void qup_i2c_read_rx_fifo_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + if (!blk->rx_tags_fetched) {
> + qup_i2c_recv_tags(qup);
> + blk->rx_tags_fetched = true;
> + }
> +
> + qup_i2c_recv_data(qup);
> + if (!blk->cur_blk_len)
> + qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
> +}
> +
> +/*
> + * Function to write bytes in TX FIFO for write message in QUP v2 i2c
> + * transfer. QUP TX FIFO write works on word basis (4 bytes). New byte write to
> + * TX FIFO will be appended in this data tx_fifo_data and will be written to TX
> + * FIFO when all the 4 bytes are available.
> + */
> +static void
> +qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsigned int *len)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + unsigned int j;
> +
> + for (j = blk->tx_fifo_data_pos; *len && blk->tx_fifo_free;
> + (*len)--, blk->tx_fifo_free--) {
> + blk->tx_fifo_data |= *(*data)++ << (j * 8);
> + if (j == 3) {
> + writel(blk->tx_fifo_data,
> + qup->base + QUP_OUT_FIFO_BASE);
> + blk->tx_fifo_data = 0x0;
> + j = 0;
> + } else {
> + j++;
> + }
> + }
> +
> + blk->tx_fifo_data_pos = j;
> +}
> +
> +/* Function to transfer tags for read message in QUP v2 i2c transfer. */
> +static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, &blk->tx_tag_len);
> + if (blk->tx_fifo_data_pos)
> + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
> +
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
> +}
> +
> +/*
> + * This function writes the data and tags in TX FIFO. Since in write case, both
> + * tags and data need to be written and QUP write tags can have maximum 256 data
> + * length, so it follows simple internal state machine to manage it.
> + * 1. Check if tx_tags_sent is false i.e. the start of QUP block so write the
> + * tags to TX FIFO.
> + * 2. Check if send_last_word is true. This will be set when last few data bytes
> + * less than 4 bytes are reamining to be written in FIFO because of no FIFO
> + * space. All this data bytes are available in tx_fifo_data so write this
> + * in FIFO and mark the tx done.
> + * 3. Write the data to TX FIFO and check for cur_blk_len. If this is non zero
> + * then more data is pending otherwise following 3 cases can be possible
> + * a. if tx_fifo_data_pos is zero that means all the data bytes in this block
> + * have been written in TX FIFO so mark the tx done.
> + * b. tx_fifo_free is zero. In this case, last few bytes (less than 4
> + * bytes) are copied to tx_fifo_data but couldn't be sent because of
> + * FIFO full so make send_last_word true.
> + * c. tx_fifo_free is non zero i.e tx FIFO is free so copy the remaining data
> + * from tx_fifo_data to tx FIFO and mark the tx done. Since,
> + * qup_i2c_write_blk_data do write in 4 bytes and FIFO space is in
> + * multiple of 4 bytes so tx_fifo_free will be always greater than or
> + * equal to 4 bytes.

Comments b and c should be c and b as per the code below

> + */
> +static void qup_i2c_write_tx_fifo_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + if (!blk->tx_tags_sent) {
> + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags,
> + &blk->tx_tag_len);
> + blk->tx_tags_sent = true;
> + }
> +
> + if (blk->send_last_word)
> + goto send_last_word;
> +
> + qup_i2c_write_blk_data(qup, &blk->cur_data, &blk->cur_blk_len);

ok, do understand, why is cur_blk_len zero and we still have pending bytes to be written ?

> + if (!blk->cur_blk_len) {
> + if (!blk->tx_fifo_data_pos)
> + goto tx_data_done;
> +
> + if (blk->tx_fifo_free)
> + goto send_last_word;
> +
> + blk->send_last_word = true;
> + }
> +
> + return;
> +
> +send_last_word:
> + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
> +tx_data_done:
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;

Yes, as commented on the previous patch, if we can get rid of this 4 flags
for completion in block/fifo mode, it would simply things a bit.

> +}
> +
> +/*
> + * Main transfer function which will be used for reading or writing i2c data.
> + * The QUP v2 supports reconfiguration during run in which multiple i2c sub
> + * transfers can be scheduled.
> + */
> +static int
> +qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool is_first,
> + bool change_pause_state)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + struct i2c_msg *msg = qup->msg;
> + int ret;
> +
> + /*
> + * Check if its SMBus Block read for which the top level read will be
> + * done into 2 QUP reads. One with message length 1 while other one is
> + * with actual length.
> + */
> + if (qup_i2c_check_msg_len(msg)) {
> + if (qup->is_smbus_read) {
> + /*
> + * If the message length is already read in
> + * the first byte of the buffer, account for
> + * that by setting the offset
> + */
> + blk->cur_data += 1;
> + is_first = false;
> + } else {
> + change_pause_state = false;
> + }
> + }
> +
> + qup->config_run = is_first ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
> +
> + qup_i2c_clear_blk_v2(blk);
> + qup_i2c_conf_count_v2(qup);
> +
> + /* If it is first sub transfer, then configure i2c bus clocks */
> + if (is_first) {
> + ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> + if (ret)
> + return ret;
> +
> + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> +
> + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> + if (ret)
> + return ret;
> + }
> +
> + qup_i2c_set_blk_event(qup, is_rx);

hmm, if the completion is changed as per the just INPUT/OUTPUT done
then this blk event tracking can be removed.

> + reinit_completion(&qup->xfer);
> + enable_irq(qup->irq);
> + /*
> + * In FIFO mode, tx FIFO can be written directly while in block mode the
> + * it will be written after getting OUT_BLOCK_WRITE_REQ interrupt
> + */
> + if (!blk->is_tx_blk_mode) {
> + blk->tx_fifo_free = qup->out_fifo_sz;
> +
> + if (is_rx)
> + qup_i2c_write_rx_tags_v2(qup);
> + else
> + qup_i2c_write_tx_fifo_v2(qup);
> + }
> +
> + ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> + if (ret)
> + goto err;
> +
> + ret = qup_i2c_wait_for_complete(qup, msg);
> + if (ret)
> + goto err;
> +
> + /* Move to pause state for all the transfers, except last one */
> + if (change_pause_state) {
> + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> + if (ret)
> + goto err;
> + }
> +
> +err:
> + disable_irq(qup->irq);
> + return ret;
> +}
> +
> +/*
> + * Function to transfer one read/write message in i2c transfer. It splits the
> + * message into multiple of blk_xfer_limit data length blocks and schedule each
> + * QUP block individually.
> + */
> +static int qup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id, bool is_rx)
> +{
> + int ret = 0;
> + unsigned int data_len, i;
> + struct i2c_msg *msg = qup->msg;
> + struct qup_i2c_block *blk = &qup->blk;
> + u8 *msg_buf = msg->buf;
> +
> + qup->blk_xfer_limit = is_rx ? RECV_MAX_DATA_LEN : QUP_READ_LIMIT;
> + qup_i2c_set_blk_data(qup, msg);
> +
> + for (i = 0; i < blk->count; i++) {
> + data_len = qup_i2c_get_data_len(qup);
> + blk->pos = i;
> + blk->cur_tx_tags = blk->tags;
> + blk->cur_blk_len = data_len;
> + blk->tx_tag_len =
> + qup_i2c_set_tags(blk->cur_tx_tags, qup, qup->msg);
> +
> + blk->cur_data = msg_buf;
> +
> + if (is_rx) {
> + blk->total_tx_len = blk->tx_tag_len;
> + blk->rx_tag_len = 2;
> + blk->total_rx_len = blk->rx_tag_len + data_len;
> + } else {
> + blk->total_tx_len = blk->tx_tag_len + data_len;
> + blk->total_rx_len = 0;
> + }
> +
> + ret = qup_i2c_conf_xfer_v2(qup, is_rx, !msg_id && !i,
> + !qup->is_last || i < blk->count - 1);
> + if (ret)
> + return ret;
> +
> + /* Handle SMBus block read length */
> + if (qup_i2c_check_msg_len(msg) && msg->len == 1 &&
> + !qup->is_smbus_read) {
> + if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX)
> + return -EPROTO;
> +
> + msg->len = msg->buf[0];
> + qup->is_smbus_read = true;
> + ret = qup_i2c_xfer_v2_msg(qup, msg_id, true);
> + qup->is_smbus_read = false;
> + if (ret)
> + return ret;
> +
> + msg->len += 1;
> + }
> +
> + msg_buf += data_len;
> + blk->data_len -= qup->blk_xfer_limit;
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * QUP v2 supports 3 modes
> + * Programmed IO using FIFO mode : Less than FIFO size
> + * Programmed IO using Block mode : Greater than FIFO size
> + * DMA using BAM : Appropriate for any transactio size but the address should be
> + * DMA applicable
> + *

s/transactio/transaction

> + * This function determines the mode which will be used for this transfer. An
> + * i2c transfer contains multiple message. Following are the rules to determine
> + * the mode used.
> + * 1. Determine the tx and rx length for each message and maximum tx and rx
> + * length for complete transfer
> + * 2. If tx or rx length is greater than DMA threshold than use the DMA mode.
> + * 3. In FIFO or block mode, TX and RX can operate in different mode so check
> + * for maximum tx and rx length to determine mode.
> + */
> +static int
> +qup_i2c_determine_mode(struct qup_i2c_dev *qup, struct i2c_msg msgs[], int num)

qup_i2c_determine_mode_v2

> +{
> + int idx;
> + bool no_dma = false;
> + unsigned int max_tx_len = 0, max_rx_len = 0;
> + unsigned int cur_tx_len, cur_rx_len;
> + unsigned int total_rx_len = 0, total_tx_len = 0;
> +
> + /* All i2c_msgs should be transferred using either dma or cpu */
> + for (idx = 0; idx < num; idx++) {
> + if (msgs[idx].len == 0)
> + return -EINVAL;
> +
> + if (msgs[idx].flags & I2C_M_RD) {
> + cur_tx_len = 0;
> + cur_rx_len = msgs[idx].len;
> + } else {
> + cur_tx_len = msgs[idx].len;
> + cur_rx_len = 0;
> + }
> +
> + if (is_vmalloc_addr(msgs[idx].buf))
> + no_dma = true;
> +
> + max_tx_len = max(max_tx_len, cur_tx_len);
> + max_rx_len = max(max_rx_len, cur_rx_len);
> + total_rx_len += cur_rx_len;
> + total_tx_len += cur_tx_len;
> + }

why is tag length for each block not being considered here ?

> +
> + if (!no_dma && qup->is_dma &&

why do we need is_dma and use_dma ?
Now that you have removed the need for is_dma in rest of places,
better to get rid of that fully.

> + (total_tx_len > qup->dma_threshold ||
> + total_rx_len > qup->dma_threshold)) {
> + qup->use_dma = true;
> + } else {
> + qup->blk.is_tx_blk_mode =
> + max_tx_len > qup->blk_mode_threshold ? true : false;
> + qup->blk.is_rx_blk_mode =
> + max_rx_len > qup->blk_mode_threshold ? true : false;
> + }
> +
> + return 0;
> +}
> +
> static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> struct i2c_msg msgs[],
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> int ret, idx = 0;
> - unsigned int total_len = 0;
>
> qup->bus_err = 0;
> qup->qup_err = 0;
> @@ -1457,6 +1609,10 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> if (ret < 0)
> goto out;
>
> + ret = qup_i2c_determine_mode(qup, msgs, num);
> + if (ret)
> + goto out;
> +
> writel(1, qup->base + QUP_SW_RESET);
> ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
> if (ret)
> @@ -1466,59 +1622,35 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> writel(I2C_MINI_CORE | I2C_N_VAL_V2, qup->base + QUP_CONFIG);
> writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
>
> - if ((qup->is_dma)) {
> - /* All i2c_msgs should be transferred using either dma or cpu */
> + if (qup_i2c_poll_state_i2c_master(qup)) {
> + ret = -EIO;
> + goto out;
> + }
> +
> + if (qup->use_dma) {
> + reinit_completion(&qup->xfer);
> + ret = qup_i2c_bam_xfer(adap, &msgs[0], num);
> + qup->use_dma = false;
> + } else {
> + qup_i2c_conf_mode_v2(qup);
> +
> for (idx = 0; idx < num; idx++) {
> - if (msgs[idx].len == 0) {
> - ret = -EINVAL;
> - goto out;
> - }
> + qup->msg = &msgs[idx];
> + qup->is_last = idx == (num - 1);
>
> - if (is_vmalloc_addr(msgs[idx].buf))
> + ret = qup_i2c_xfer_v2_msg(qup, idx,
> + !!(msgs[idx].flags & I2C_M_RD));

why !!() is required here ?

> + if (ret)
> break;
> -
> - total_len += msgs[idx].len;
> }
> -
> - if (idx == num && total_len > qup->dma_threshold)
> - qup->use_dma = true;
> + qup->msg = NULL;
> }
>
> - idx = 0;
> -
> - do {
> - if (msgs[idx].len == 0) {
> - ret = -EINVAL;
> - goto out;
> - }
> -
> - if (qup_i2c_poll_state_i2c_master(qup)) {
> - ret = -EIO;
> - goto out;
> - }
> -
> - qup->is_last = (idx == (num - 1));
> - if (idx)
> - qup->config_run = QUP_I2C_MX_CONFIG_DURING_RUN;
> - else
> - qup->config_run = 0;
> -
> - reinit_completion(&qup->xfer);
> -
> - if (qup->use_dma) {
> - ret = qup_i2c_bam_xfer(adap, &msgs[idx], num);
> - qup->use_dma = false;
> - break;
> - } else {
> - if (msgs[idx].flags & I2C_M_RD)
> - ret = qup_i2c_read_one_v2(qup, &msgs[idx]);
> - else
> - ret = qup_i2c_write_one_v2(qup, &msgs[idx]);
> - }
> - } while ((idx++ < (num - 1)) && !ret);
> + if (!ret)
> + ret = qup_i2c_bus_active(qup, ONE_BYTE);
>
> if (!ret)
> - ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
> + qup_i2c_change_state(qup, QUP_RESET_STATE);
>
> if (ret == 0)
> ret = num;
> @@ -1582,6 +1714,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
> u32 src_clk_freq = DEFAULT_SRC_CLK;
> u32 clk_freq = DEFAULT_CLK_FREQ;
> int blocks;
> + bool is_qup_v1;
>
> qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
> if (!qup)
> @@ -1600,12 +1733,10 @@ static int qup_i2c_probe(struct platform_device *pdev)
> if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
> qup->adap.algo = &qup_i2c_algo;
> qup->adap.quirks = &qup_i2c_quirks;
> - qup->is_qup_v1 = true;
> - qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
> - qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
> - qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
> + is_qup_v1 = true;
> } else {
> qup->adap.algo = &qup_i2c_algo_v2;
> + is_qup_v1 = false;
> ret = qup_i2c_req_dma(qup);
>
> if (ret == -EPROBE_DEFER)
> @@ -1731,14 +1862,31 @@ static int qup_i2c_probe(struct platform_device *pdev)
> ret = -EIO;
> goto fail;
> }
> - qup->out_blk_sz = blk_sizes[size] / 2;
> + qup->out_blk_sz = blk_sizes[size];
>
> size = QUP_INPUT_BLOCK_SIZE(io_mode);
> if (size >= ARRAY_SIZE(blk_sizes)) {
> ret = -EIO;
> goto fail;
> }
> - qup->in_blk_sz = blk_sizes[size] / 2;
> + qup->in_blk_sz = blk_sizes[size];
> +
> + if (is_qup_v1) {
> + /*
> + * in QUP v1, QUP_CONFIG uses N as 15 i.e 16 bits constitutes a
> + * single transfer but the block size is in bytes so divide the
> + * in_blk_sz and out_blk_sz by 2
> + */
> + qup->in_blk_sz /= 2;
> + qup->out_blk_sz /= 2;
> + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
> + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
> + qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
> + } else {
> + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v2;
> + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v2;
> + qup->write_rx_tags = qup_i2c_write_rx_tags_v2;
> + }
>
> size = QUP_OUTPUT_FIFO_SIZE(io_mode);
> qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
> @@ -1746,6 +1894,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
> size = QUP_INPUT_FIFO_SIZE(io_mode);
> qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
> qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);
> + qup->blk_mode_threshold = qup->dma_threshold - QUP_MAX_TAGS_LEN;
>
> fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
> hs_div = 3;
>

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 19:35:01

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> The BAM mode requires buffer for start tag data and tx, rx SG
> list. Currently, this is being taken for maximum transfer length
> (65K). But an I2C transfer can have multiple messages and each
> message can be of this maximum length so the buffer overflow will
> happen in this case. Since increasing buffer length won’t be
> feasible since an I2C transfer can contain any number of messages
> so this patch does following changes to make i2c transfers working
> for multiple messages case.
>
> 1. Calculate the required buffers for 2 maximum length messages
> (65K * 2).
> 2. Split the descriptor formation and descriptor scheduling.
> The idea is to fit as many messages in one DMA transfers for 65K
> threshold value (max_xfer_sg_len). Whenever the sg_cnt is
> crossing this, then schedule the BAM transfer and subsequent
> transfer will again start from zero.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 199 +++++++++++++++++++++++++------------------
> 1 file changed, 118 insertions(+), 81 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 6df65ea..ba717bb 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -155,6 +155,7 @@ struct qup_i2c_bam {
> struct qup_i2c_tag tag;
> struct dma_chan *dma;
> struct scatterlist *sg;
> + unsigned int sg_cnt;
> };
>
> struct qup_i2c_dev {
> @@ -195,6 +196,8 @@ struct qup_i2c_dev {
> bool use_dma;
> /* The threshold length above which DMA will be used */
> unsigned int dma_threshold;
> + unsigned int max_xfer_sg_len;
> + unsigned int tag_buf_pos;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -699,86 +702,86 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
> return 0;
> }
>
> -static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> - int num)
> +static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> +{
> + int ret = 0, limit = QUP_READ_LIMIT;
> + u32 len = 0, blocks, rem;
> + u32 i = 0, tlen, tx_len = 0;
> + u8 *tags;
> +
> + qup_i2c_set_blk_data(qup, msg);
> +
> + blocks = qup->blk.count;
> + rem = msg->len - (blocks - 1) * limit;
> +
> + if (msg->flags & I2C_M_RD) {
> + while (qup->blk.pos < blocks) {
> + tlen = (i == (blocks - 1)) ? rem : limit;
> + tags = &qup->start_tag.start[qup->tag_buf_pos + len];
> + len += qup_i2c_set_tags(tags, qup, msg);
> + qup->blk.data_len -= tlen;
> +
> + /* scratch buf to read the start and len tags */
> + ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
> + &qup->brx.tag.start[0],
> + 2, qup, DMA_FROM_DEVICE);
> +
> + if (ret)
> + return ret;
> +
> + ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
> + &msg->buf[limit * i],
> + tlen, qup,
> + DMA_FROM_DEVICE);
> + if (ret)
> + return ret;
> +
> + i++;
> + qup->blk.pos = i;
> + }
> + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
> + &qup->start_tag.start[qup->tag_buf_pos],
> + len, qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> +
> + qup->tag_buf_pos += len;
> + } else {
> + while (qup->blk.pos < blocks) {
> + tlen = (i == (blocks - 1)) ? rem : limit;
> + tags = &qup->start_tag.start[qup->tag_buf_pos + tx_len];
> + len = qup_i2c_set_tags(tags, qup, msg);
> + qup->blk.data_len -= tlen;
> +
> + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
> + tags, len,
> + qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> +
> + tx_len += len;
> + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
> + &msg->buf[limit * i],
> + tlen, qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> + i++;
> + qup->blk.pos = i;
> + }
> +
> + qup->tag_buf_pos += tx_len;
> + }
> +
> + return 0;
> +}
> +
> +static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
> {
> struct dma_async_tx_descriptor *txd, *rxd = NULL;
> - int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
> + int ret = 0;
> dma_cookie_t cookie_rx, cookie_tx;
> - u32 len, blocks, rem;
> - u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
> - u8 *tags;
> -
> - while (idx < num) {
> - tx_len = 0, len = 0, i = 0;
> -
> - qup->is_last = (idx == (num - 1));
> -
> - qup_i2c_set_blk_data(qup, msg);
> -
> - blocks = qup->blk.count;
> - rem = msg->len - (blocks - 1) * limit;
> -
> - if (msg->flags & I2C_M_RD) {
> - while (qup->blk.pos < blocks) {
> - tlen = (i == (blocks - 1)) ? rem : limit;
> - tags = &qup->start_tag.start[off + len];
> - len += qup_i2c_set_tags(tags, qup, msg);
> - qup->blk.data_len -= tlen;
> -
> - /* scratch buf to read the start and len tags */
> - ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
> - &qup->brx.tag.start[0],
> - 2, qup, DMA_FROM_DEVICE);
> -
> - if (ret)
> - return ret;
> -
> - ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
> - &msg->buf[limit * i],
> - tlen, qup,
> - DMA_FROM_DEVICE);
> - if (ret)
> - return ret;
> -
> - i++;
> - qup->blk.pos = i;
> - }
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - &qup->start_tag.start[off],
> - len, qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> -
> - off += len;
> - } else {
> - while (qup->blk.pos < blocks) {
> - tlen = (i == (blocks - 1)) ? rem : limit;
> - tags = &qup->start_tag.start[off + tx_len];
> - len = qup_i2c_set_tags(tags, qup, msg);
> - qup->blk.data_len -= tlen;
> -
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - tags, len,
> - qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> -
> - tx_len += len;
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - &msg->buf[limit * i],
> - tlen, qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> - i++;
> - qup->blk.pos = i;
> - }
> - off += tx_len;
> -
> - }
> - idx++;
> - msg++;
> - }
> + u32 len = 0;
> + u32 tx_buf = qup->btx.sg_cnt, rx_buf = qup->brx.sg_cnt;
>
> /* schedule the EOT and FLUSH I2C tags */
> len = 1;
> @@ -878,11 +881,19 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> return ret;
> }
>
> +static void qup_i2c_bam_clear_tag_buffers(struct qup_i2c_dev *qup)
> +{
> + qup->btx.sg_cnt = 0;
> + qup->brx.sg_cnt = 0;
> + qup->tag_buf_pos = 0;
> +}
> +
> static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> int ret = 0;
> + int idx = 0;
>
> enable_irq(qup->irq);
> ret = qup_i2c_req_dma(qup);
> @@ -905,9 +916,34 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
> goto out;
>
> writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> + qup_i2c_bam_clear_tag_buffers(qup);
> +
> + for (idx = 0; idx < num; idx++) {
> + qup->msg = msg + idx;
> + qup->is_last = idx == (num - 1);
> +
> + ret = qup_i2c_bam_make_desc(qup, qup->msg);
> + if (ret)
> + break;
> +
> + /*
> + * Make DMA descriptor and schedule the BAM transfer if its
> + * already crossed the maximum length. Since the memory for all
> + * tags buffers have been taken for 2 maximum possible
> + * transfers length so it will never cross the buffer actual
> + * length.
> + */
> + if (qup->btx.sg_cnt > qup->max_xfer_sg_len ||
> + qup->brx.sg_cnt > qup->max_xfer_sg_len ||
> + qup->is_last) {
> + ret = qup_i2c_bam_schedule_desc(qup);
> + if (ret)
> + break;
> +
> + qup_i2c_bam_clear_tag_buffers(qup);
> + }
> + }
>

hmm, is this because of only stress tests or was there any device which
was using i2c for multiple messages exceeding 64k bytes ?

Infact we are trying to club two separate messages together across 64k
boundaries. Not sure if its really correct. So either we club all messages
fully or club only up to the length that would cover the whole message < 64K
and send the remaining whole messages in next transfer.

Regards,
Sricharan


> - qup->msg = msg;
> - ret = qup_i2c_bam_do_xfer(qup, qup->msg, num);
> out:
> disable_irq(qup->irq);
>
> @@ -1459,7 +1495,8 @@ static int qup_i2c_probe(struct platform_device *pdev)
> else if (ret != 0)
> goto nodma;
>
> - blocks = (MX_BLOCKS << 1) + 1;
> + qup->max_xfer_sg_len = (MX_BLOCKS << 1);
> + blocks = 2 * qup->max_xfer_sg_len + 1;
> qup->btx.sg = devm_kzalloc(&pdev->dev,
> sizeof(*qup->btx.sg) * blocks,
> GFP_KERNEL);
> @@ -1603,7 +1640,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
> one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
> qup->one_byte_t = one_bit_t * 9;
> qup->xfer_timeout = TOUT_MIN * HZ +
> - usecs_to_jiffies(MX_TX_RX_LEN * qup->one_byte_t);
> + usecs_to_jiffies(2 * MX_TX_RX_LEN * qup->one_byte_t);
>
> dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
> qup->in_blk_sz, qup->in_fifo_sz,
>

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-16 19:35:47

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers

Hi Abhishek,

On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> According to I2c specification, “If a master-receiver sends a
> repeated START condition, it sends a not-acknowledge (A) just
> before the repeated START condition”. QUP v2 supports sending
> of NACK without stop with QUP_TAG_V2_DATARD_NACK so added the
> same.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index ba717bb..edea3b9 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -113,6 +113,7 @@
> #define QUP_TAG_V2_DATAWR 0x82
> #define QUP_TAG_V2_DATAWR_STOP 0x83
> #define QUP_TAG_V2_DATARD 0x85
> +#define QUP_TAG_V2_DATARD_NACK 0x86
> #define QUP_TAG_V2_DATARD_STOP 0x87
>
> /* Status, Error flags */
> @@ -609,7 +610,9 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> tags[len++] = QUP_TAG_V2_DATAWR_STOP;
> } else {
> if (msg->flags & I2C_M_RD)
> - tags[len++] = QUP_TAG_V2_DATARD;
> + tags[len++] = qup->blk.pos == (qup->blk.count - 1) ?
> + QUP_TAG_V2_DATARD_NACK :
> + QUP_TAG_V2_DATARD;
> else
> tags[len++] = QUP_TAG_V2_DATAWR;

good one. Thanks .

Regards,
Sricharan

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-19 10:25:26

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion

On 2018-02-08 19:33, Sricharan R wrote:
> Hi Abhishek,
>
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> The QUP BSLP BAM generates the following error sometimes if the
>> current I2C DMA transfer fails and the flush operation has been
>> scheduled
>>
>> “bam-dma-engine 7884000.dma: Cannot free busy channel”
>>
>> If any I2C error comes during BAM DMA transfer, then the QUP I2C
>> interrupt will be generated and the flush operation will be
>> carried out to make i2c consume all scheduled DMA transfer.
>> Currently, the same completion structure is being used for BAM
>> transfer which has already completed without reinit. It will make
>> flush operation wait_for_completion_timeout completed immediately
>> and will proceed for freeing the DMA resources where the
>> descriptors are still in process.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 4 +++-
>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index 08f8e01..9faa26c41a 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -1,5 +1,5 @@
>> /*
>> - * Copyright (c) 2009-2013, The Linux Foundation. All rights
>> reserved.
>> + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All
>> rights reserved.
>
> Not sure, if this was an intended change. But given that you are fixing
> this,

Thanks Sricharan.
Since this is the first patch, so I updated the year.

> you can change the header to the new SPDX one.
>

Yes that would be better.
I will fix that also.

Thanks,
Abhishek

>> * Copyright (c) 2014, Sony Mobile Communications AB.
>> *
>> *
>> @@ -844,6 +844,8 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev
>> *qup, struct i2c_msg *msg,
>> }
>>
>> if (ret || qup->bus_err || qup->qup_err) {
>> + reinit_completion(&qup->xfer);
>> +
>> if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
>> dev_err(qup->dev, "change to run state timed out");
>> goto desc_err;
>>
>
> Except for the above nit,
> Acked-by: Sricharan R <[email protected]>
>
> Regards,
> Sricharan

2018-02-19 10:27:32

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count

On 2018-02-09 07:46, Sricharan R wrote:
> Hi Abhishek,
>
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> The rx_nents and tx_nents are redundant. rx_buf and tx_buf can
>> be used for total number of SG entries.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 26 ++++++++++----------------
>> 1 file changed, 10 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index c68f433..bb83a2967 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -692,7 +692,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev
>> *qup, struct i2c_msg *msg,
>> struct dma_async_tx_descriptor *txd, *rxd = NULL;
>> int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
>> dma_cookie_t cookie_rx, cookie_tx;
>> - u32 rx_nents = 0, tx_nents = 0, len, blocks, rem;
>> + u32 len, blocks, rem;
>> u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
>> u8 *tags;
>>
>
> This is correct. Just a nit, may be rx/tx_buf can be changed to
> rx/tx_count to make it more clear.
>

Yes, rx/tx_count will be more meaningful. rx/tx_buf gives the
impression that it is uchar buffer. I will change that.

Thanks,
Abhishek

2018-02-19 10:35:48

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer

On 2018-02-15 20:01, Sricharan R wrote:
> Hi Abhishek,
>
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> A single BAM transfer can have multiple read and write messages.
>> The EOT and FLUSH tags should be scheduled at the end of BAM HW
>> descriptors. Since the READ and WRITE can be present in any order
>> so for some of the cases, these tags are not being written
>> correctly.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 54
>> ++++++++++++++++++++------------------------
>> 1 file changed, 24 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index bb83a2967..6357aff 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -560,7 +560,7 @@ static int qup_i2c_set_tags_smb(u16 addr, u8
>> *tags, struct qup_i2c_dev *qup,
>> }
>>
>> static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
>> - struct i2c_msg *msg, int is_dma)
>> + struct i2c_msg *msg)
>> {
>> u16 addr = i2c_8bit_addr_from_msg(msg);
>> int len = 0;
>> @@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, struct
>> qup_i2c_dev *qup,
>> else
>> tags[len++] = data_len;
>>
>> - if ((msg->flags & I2C_M_RD) && last && is_dma) {
>> - tags[len++] = QUP_BAM_INPUT_EOT;
>> - tags[len++] = QUP_BAM_FLUSH_STOP;
>> - }
>> -
>> return len;
>> }
>>
>> @@ -614,7 +609,7 @@ static int qup_i2c_issue_xfer_v2(struct
>> qup_i2c_dev *qup, struct i2c_msg *msg)
>> int data_len = 0, tag_len, index;
>> int ret;
>>
>> - tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg, 0);
>> + tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg);
>> index = msg->len - qup->blk.data_len;
>>
>> /* only tags are written for read */
>> @@ -710,7 +705,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev
>> *qup, struct i2c_msg *msg,
>> while (qup->blk.pos < blocks) {
>> tlen = (i == (blocks - 1)) ? rem : limit;
>> tags = &qup->start_tag.start[off + len];
>> - len += qup_i2c_set_tags(tags, qup, msg, 1);
>> + len += qup_i2c_set_tags(tags, qup, msg);
>> qup->blk.data_len -= tlen;
>>
>> /* scratch buf to read the start and len tags */
>> @@ -738,17 +733,11 @@ static int qup_i2c_bam_do_xfer(struct
>> qup_i2c_dev *qup, struct i2c_msg *msg,
>> return ret;
>>
>> off += len;
>> - /* scratch buf to read the BAM EOT and FLUSH tags */
>> - ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
>> - &qup->brx.tag.start[0],
>> - 2, qup, DMA_FROM_DEVICE);
>> - if (ret)
>> - return ret;
>> } else {
>> while (qup->blk.pos < blocks) {
>> tlen = (i == (blocks - 1)) ? rem : limit;
>> tags = &qup->start_tag.start[off + tx_len];
>> - len = qup_i2c_set_tags(tags, qup, msg, 1);
>> + len = qup_i2c_set_tags(tags, qup, msg);
>> qup->blk.data_len -= tlen;
>>
>> ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
>> @@ -768,26 +757,31 @@ static int qup_i2c_bam_do_xfer(struct
>> qup_i2c_dev *qup, struct i2c_msg *msg,
>> }
>> off += tx_len;
>>
>> - if (idx == (num - 1)) {
>> - len = 1;
>> - if (rx_buf) {
>> - qup->btx.tag.start[0] =
>> - QUP_BAM_INPUT_EOT;
>> - len++;
>> - }
>> - qup->btx.tag.start[len - 1] =
>> - QUP_BAM_FLUSH_STOP;
>> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
>> - &qup->btx.tag.start[0],
>> - len, qup, DMA_TO_DEVICE);
>> - if (ret)
>> - return ret;
>> - }
>> }
>> idx++;
>> msg++;
>> }
>
> While here, can you please split above the if, else in to two
> separate functions ?
> and probably one more function for handling the NACK case down below.
> The function
> size at the moment is too big.
>

Sure. Already I split this function into 2 functions in
[PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum
xfer len
which is making function size smaller. I will check if I can split in
more
functions.

>>
>> + /* schedule the EOT and FLUSH I2C tags */
>> + len = 1;
>> + if (rx_buf) {
>> + qup->btx.tag.start[0] = QUP_BAM_INPUT_EOT;
>> + len++;
>> +
>> + /* scratch buf to read the BAM EOT and FLUSH tags */
>> + ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
>> + &qup->brx.tag.start[0],
>> + 2, qup, DMA_FROM_DEVICE);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + qup->btx.tag.start[len - 1] = QUP_BAM_FLUSH_STOP;
>> + ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], &qup->btx.tag.start[0],
>> + len, qup, DMA_TO_DEVICE);
>> + if (ret)
>> + return ret;
>> +
>
> May be you can change the commit to make it explicit to say what is
> being
> fixed, like "current code is broken when there is sequence of
> wr,rd,wr" like that.
> Agree on this fix otherwise.
>

Thanks. I will update the commit message to include the failure
case which will give clear idea.

Thanks,
Abhishek

2018-02-19 10:50:50

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode

On 2018-02-16 10:05, Sricharan R wrote:
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> Currently each message length in complete transfer is being
>> checked for determining DMA mode and if any of the message length
>> is less than FIFO length then non DMA mode is being used which
>> will increase overhead. DMA can be used for any length and it
>> should be determined with complete transfer length. Now, this
>> patch introduces DMA threshold length and the transfer will be
>> done in DMA mode if the total length is greater than this
>> threshold length.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 15 +++++++++------
>> 1 file changed, 9 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index 6227a5c..a91fc70 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -192,6 +192,8 @@ struct qup_i2c_dev {
>> bool is_dma;
>> /* To check if the current transfer is using DMA */
>> bool use_dma;
>> + /* The threshold length above which DMA will be used */
>> + unsigned int dma_threshold;
>> struct dma_pool *dpool;
>> struct qup_i2c_tag start_tag;
>> struct qup_i2c_bam brx;
>> @@ -1294,7 +1296,8 @@ static int qup_i2c_xfer_v2(struct i2c_adapter
>> *adap,
>> int num)
>> {
>> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
>> - int ret, len, idx = 0;
>> + int ret, idx = 0;
>> + unsigned int total_len = 0;
>>
>> qup->bus_err = 0;
>> qup->qup_err = 0;
>> @@ -1320,14 +1323,13 @@ static int qup_i2c_xfer_v2(struct i2c_adapter
>> *adap,
>> goto out;
>> }
>>
>> - len = (msgs[idx].len > qup->out_fifo_sz) ||
>> - (msgs[idx].len > qup->in_fifo_sz);
>> -
>> - if (is_vmalloc_addr(msgs[idx].buf) || !len)
>> + if (is_vmalloc_addr(msgs[idx].buf))
>> break;
>> +
>> + total_len += msgs[idx].len;
>> }
>>
>> - if (idx == num)
>> + if (idx == num && total_len > qup->dma_threshold)
>> qup->use_dma = true;
>> }
>>
>> @@ -1587,6 +1589,7 @@ static int qup_i2c_probe(struct platform_device
>> *pdev)
>>
>> size = QUP_INPUT_FIFO_SIZE(io_mode);
>> qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
>> + qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);
>
> The patch is fine. small nit, you can avoid this global
> dma_threshold. Instead
> have it locally in qup_i2c_xfer_v2 itself.

Thanks Sricharan.

The idea for introducing global dma_threshold was to give option
for controlling the behavior from one place. If someone wants to
change the length above which dma will be used, then It can be
controlled from once place in the probe function itself.

Also, min(qup->out_fifo_sz, qup->in_fifo_sz) requires comparison
and adding in qup_i2c_xfer_v2 will make this happen everytime.

Regards,
Abhishek




2018-02-19 10:57:15

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length

On 2018-02-16 10:18, Sricharan R wrote:
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> Currently the completion timeout is being taken according to
>> maximum transfer length which is too high if SCL is operating in
>> high frequency. This patch calculates timeout on the basis of
>> one-byte transfer time and uses the same for completion timeout.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 9 ++++++---
>> 1 file changed, 6 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index a91fc70..6df65ea 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -130,8 +130,8 @@
>> #define MX_TX_RX_LEN SZ_64K
>> #define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)
>>
>> -/* Max timeout in ms for 32k bytes */
>> -#define TOUT_MAX 300
>> +/* Min timeout for i2c transfers */
>> +#define TOUT_MIN 2
>>
>
> may be you can mention, why is this 2 ?
>

This 2 seconds is timeout which I am adding on the top of maximum
xfer time calculated from bus speed to compensate the interrupt
latency and other factors. It will make xfer timeout minimum as
2 seconds.

I will update the comment to explain it in more detail.

Thanks,
Abhishek


2018-02-19 11:25:13

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

On 2018-02-16 10:51, Sricharan R wrote:
> Hi Abhishek,
>
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> The BAM mode requires buffer for start tag data and tx, rx SG
>> list. Currently, this is being taken for maximum transfer length
>> (65K). But an I2C transfer can have multiple messages and each
>> message can be of this maximum length so the buffer overflow will
>> happen in this case. Since increasing buffer length won’t be
>> feasible since an I2C transfer can contain any number of messages
>> so this patch does following changes to make i2c transfers working
>> for multiple messages case.
>>
>> 1. Calculate the required buffers for 2 maximum length messages
>> (65K * 2).
>> 2. Split the descriptor formation and descriptor scheduling.
>> The idea is to fit as many messages in one DMA transfers for 65K
>> threshold value (max_xfer_sg_len). Whenever the sg_cnt is
>> crossing this, then schedule the BAM transfer and subsequent
>> transfer will again start from zero.

<snip>

>> +static void qup_i2c_bam_clear_tag_buffers(struct qup_i2c_dev *qup)
>> +{
>> + qup->btx.sg_cnt = 0;
>> + qup->brx.sg_cnt = 0;
>> + qup->tag_buf_pos = 0;
>> +}
>> +
>> static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg
>> *msg,
>> int num)
>> {
>> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
>> int ret = 0;
>> + int idx = 0;
>>
>> enable_irq(qup->irq);
>> ret = qup_i2c_req_dma(qup);
>> @@ -905,9 +916,34 @@ static int qup_i2c_bam_xfer(struct i2c_adapter
>> *adap, struct i2c_msg *msg,
>> goto out;
>>
>> writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
>> + qup_i2c_bam_clear_tag_buffers(qup);
>> +
>> + for (idx = 0; idx < num; idx++) {
>> + qup->msg = msg + idx;
>> + qup->is_last = idx == (num - 1);
>> +
>> + ret = qup_i2c_bam_make_desc(qup, qup->msg);
>> + if (ret)
>> + break;
>> +
>> + /*
>> + * Make DMA descriptor and schedule the BAM transfer if its
>> + * already crossed the maximum length. Since the memory for all
>> + * tags buffers have been taken for 2 maximum possible
>> + * transfers length so it will never cross the buffer actual
>> + * length.
>> + */
>> + if (qup->btx.sg_cnt > qup->max_xfer_sg_len ||
>> + qup->brx.sg_cnt > qup->max_xfer_sg_len ||
>> + qup->is_last) {
>> + ret = qup_i2c_bam_schedule_desc(qup);
>> + if (ret)
>> + break;
>> +
>> + qup_i2c_bam_clear_tag_buffers(qup);
>> + }
>> + }
>>
>
> hmm, is this because of only stress tests or was there any device
> which
> was using i2c for multiple messages exceeding 64k bytes ?

Its mainly part of stress test but we have test slave devices which
supports the multiple messages exceeding 64k bytes. Also, in I2C EEPROM
we can send the multiple messages exceeding 64k bytes. It will roll
over
to starting address after its capacity.

>
> Infact we are trying to club two separate messages together across
> 64k
> boundaries. Not sure if its really correct. So either we club all
> messages
> fully or club only up to the length that would cover the whole
> message < 64K
> and send the remaining whole messages in next transfer.
>

The QUP DMA can be used for any transfer length. It supports greater
than
64k also in one go. Only restriction is descriptors memory. clubing all
messages won't be feasible since there is no restriction on the number
of
messages due to which we can't determine the required descriptors size.

whole message < 64K will require more code changes since we need to
calculate
the number of required descriptors in advance. Again in descriptor
formation,
the number of required descriptors will be calculated and filled. To
make the
code less complicated, I have taken the memory for 128K xfer length
which
will make the current code working without any major code changes.

Thanks,
Abhishek



2018-02-19 13:22:40

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1

On 2018-02-16 13:14, Sricharan R wrote:
> Hi Abhishek,
>
> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>> Following are the major issues in current driver code
>>
>> 1. The current driver simply assumes the transfer completion
>> whenever its gets any non-error interrupts and then simply do the
>> polling of available/free bytes in FIFO.
>> 2. The block mode is not working properly since no handling in
>> being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ.
>>
>> Because of above, i2c v1 transfers of size greater than 32 are failing
>> with following error message
>>
>> i2c_qup 78b6000.i2c: timeout for fifo out full
>>
>> To make block mode working properly and move to use the interrupts
>> instead of polling, major code reorganization is required. Following
>> are the major changes done in this patch
>>
>> 1. Remove the polling of TX FIFO free space and RX FIFO available
>> bytes and move to interrupts completely. QUP has
>> QUP_MX_OUTPUT_DONE,
>> QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
>> interrupts to handle FIFO’s properly so check all these interrupts.
>> 2. During write, For FIFO mode, TX FIFO can be directly written
>> without checking for FIFO space. For block mode, the QUP will
>> generate
>> OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of
>> available
>> space.
>> 3. During read, both TX and RX FIFO will be used. TX will be used
>> for writing tags and RX will be used for receiving the data. In
>> QUP,
>> TX and RX can operate in separate mode so configure modes
>> accordingly.
>> 4. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
>> will be generated after all the bytes have been copied in RX FIFO.
>> For
>> read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
>> whenever it has block size of available data.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 399
>> ++++++++++++++++++++++++++++---------------
>> 1 file changed, 257 insertions(+), 142 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index edea3b9..af6c21a 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -73,8 +73,11 @@
>> #define QUP_IN_SVC_FLAG BIT(9)
>> #define QUP_MX_OUTPUT_DONE BIT(10)
>> #define QUP_MX_INPUT_DONE BIT(11)
>> +#define OUT_BLOCK_WRITE_REQ BIT(12)
>> +#define IN_BLOCK_READ_REQ BIT(13)
>>
>> /* I2C mini core related values */
>> +#define QUP_NO_INPUT BIT(7)
>> #define QUP_CLOCK_AUTO_GATE BIT(13)
>> #define I2C_MINI_CORE (2 << 8)
>> #define I2C_N_VAL 15
>> @@ -138,13 +141,51 @@
>> #define DEFAULT_CLK_FREQ 100000
>> #define DEFAULT_SRC_CLK 20000000
>>
>> +/* MAX_OUTPUT_DONE_FLAG has been received */
>> +#define QUP_BLK_EVENT_TX_IRQ_DONE BIT(0)
>> +/* MAX_INPUT_DONE_FLAG has been received */
>> +#define QUP_BLK_EVENT_RX_IRQ_DONE BIT(1)
>> +/* All the TX bytes have been written in TX FIFO */
>> +#define QUP_BLK_EVENT_TX_DATA_DONE BIT(2)
>> +/* All the RX bytes have been read from RX FIFO */
>> +#define QUP_BLK_EVENT_RX_DATA_DONE BIT(3)
>> +
>> +/* All the required events to mark a QUP I2C TX transfer completed */
>> +#define QUP_BLK_EVENT_TX_DONE (QUP_BLK_EVENT_TX_IRQ_DONE | \
>> + QUP_BLK_EVENT_TX_DATA_DONE)
>> +/* All the required events to mark a QUP I2C RX transfer completed */
>> +#define QUP_BLK_EVENT_RX_DONE (QUP_BLK_EVENT_TX_DONE | \
>> + QUP_BLK_EVENT_RX_IRQ_DONE | \
>> + QUP_BLK_EVENT_RX_DATA_DONE)
>> +
>> +/*
>> + * count: no of blocks
>> + * pos: current block number
>> + * tx_tag_len: tx tag length for current block
>> + * rx_tag_len: rx tag length for current block
>> + * data_len: remaining data length for current message
>> + * total_tx_len: total tx length including tag bytes for current QUP
>> transfer
>> + * total_rx_len: total rx length including tag bytes for current QUP
>> transfer
>> + * tx_fifo_free: number of free bytes in current QUP block write.
>> + * fifo_available: number of available bytes in RX FIFO for current
>> + * QUP block read
>> + * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non
>> BAM xfer.
>> + * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non
>> BAM xfer.
>> + * tags: contains tx tag bytes for current QUP transfer
>> + */
>> struct qup_i2c_block {
>> - int count;
>> - int pos;
>> - int tx_tag_len;
>> - int rx_tag_len;
>> - int data_len;
>> - u8 tags[6];
>> + int count;
>> + int pos;
>> + int tx_tag_len;
>> + int rx_tag_len;
>> + int data_len;
>> + int total_tx_len;
>> + int total_rx_len;
>> + int tx_fifo_free;
>> + int fifo_available;
>> + bool is_tx_blk_mode;
>> + bool is_rx_blk_mode;
>> + u8 tags[6];
>> };
>>
>> struct qup_i2c_tag {
>> @@ -187,6 +228,7 @@ struct qup_i2c_dev {
>>
>> /* To check if this is the last msg */
>> bool is_last;
>> + bool is_qup_v1;
>>
>> /* To configure when bus is in run state */
>> int config_run;
>> @@ -195,6 +237,10 @@ struct qup_i2c_dev {
>> bool is_dma;
>> /* To check if the current transfer is using DMA */
>> bool use_dma;
>> + /* Required events to mark QUP transfer as completed */
>> + u32 blk_events;
>> + /* Already completed events in QUP transfer */
>> + u32 cur_blk_events;
>> /* The threshold length above which DMA will be used */
>> unsigned int dma_threshold;
>> unsigned int max_xfer_sg_len;
>> @@ -205,11 +251,18 @@ struct qup_i2c_dev {
>> struct qup_i2c_bam btx;
>>
>> struct completion xfer;
>> + /* function to write data in tx fifo */
>> + void (*write_tx_fifo)(struct qup_i2c_dev *qup);
>> + /* function to read data from rx fifo */
>> + void (*read_rx_fifo)(struct qup_i2c_dev *qup);
>> + /* function to write tags in tx fifo for i2c read transfer */
>> + void (*write_rx_tags)(struct qup_i2c_dev *qup);
>> };
>>
>> static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
>> {
>> struct qup_i2c_dev *qup = dev;
>> + struct qup_i2c_block *blk = &qup->blk;
>> u32 bus_err;
>> u32 qup_err;
>> u32 opflags;
>> @@ -256,12 +309,54 @@ static irqreturn_t qup_i2c_interrupt(int irq,
>> void *dev)
>> goto done;
>> }
>>
>> - if (opflags & QUP_IN_SVC_FLAG)
>> - writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>> + if (!qup->is_qup_v1)
>> + goto done;
>>
>> - if (opflags & QUP_OUT_SVC_FLAG)
>> + if (opflags & QUP_OUT_SVC_FLAG) {
>> writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>>
>> + /*
>> + * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG.
>> + * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind
>> + * QUP_OUTPUT_SERVICE_FLAG. The only reason for
>> + * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is
>> + * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking
>> + * here QUP_OUTPUT_SERVICE_FLAG and assumes that
>> + * QUP_MAX_OUTPUT_DONE_FLAG.
>> + */
>> + if (!blk->is_tx_blk_mode)
>> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
>> +
>> + if (opflags & OUT_BLOCK_WRITE_REQ) {
>> + blk->tx_fifo_free += qup->out_blk_sz;
>> + if (qup->msg->flags & I2C_M_RD)
>> + qup->write_rx_tags(qup);
>> + else
>> + qup->write_tx_fifo(qup);
>> + }
>> + }
>> +
>> + if (opflags & QUP_IN_SVC_FLAG) {
>> + writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>> +
>> + if (!blk->is_rx_blk_mode) {
>> + blk->fifo_available += qup->in_fifo_sz;
>> + qup->read_rx_fifo(qup);
>> + } else if (opflags & IN_BLOCK_READ_REQ) {
>> + blk->fifo_available += qup->in_blk_sz;
>> + qup->read_rx_fifo(qup);
>> + }
>> + }
>> +
>> + if (opflags & QUP_MX_OUTPUT_DONE)
>> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
>> +
>> + if (opflags & QUP_MX_INPUT_DONE)
>> + qup->cur_blk_events |= QUP_BLK_EVENT_RX_IRQ_DONE;
>> +
>> + if (qup->cur_blk_events != qup->blk_events)
>> + return IRQ_HANDLED;
>
> Is it correct that if we do a complete in above case, i mean
> for TX -> based on QUP_MX_OUTPUT_DONE and for RX -> based on
> QUP_MX_INPUT_DONE, would that simplify things by getting rid of
> QUP_BLK_EVENT_RX/TX_DONE and QUP_BLK_EVENT_RX/TX_IRQ_DONE
> altogether ?

We can get rid of QUP_BLK_EVENT_TX_DONE.
For RX, the QUP_MX_INPUT_DONE will be triggered when QUP copies all
the data in FIFO from external i2c slave. So if 64 bytes read has been
scheduled then following is the behaviour

IRQ with QUP_MX_INPUT_DONE and IN_BLOCK_READ_REQ -> read 16 bytes
IRQ with IN_BLOCK_READ_REQ -> read next 16 bytes
IRQ with IN_BLOCK_READ_REQ -> read next 16 bytes
IRQ with IN_BLOCK_READ_REQ -> read last 16 bytes

So for RX, we can't trigger complete by checking QUP_BLK_EVENT_RX_DONE
alone.
We need to track the number of bytes read from FIFO. Instead of putting
this check, I am taking one extra event bit QUP_BLK_EVENT_RX_DONE which
will be set when all the bytes has been read.

I am not sure if checking QUP_MX_INPUT_DONE will always work since
there may be some case, when for small transfers the QUP_MX_INPUT_DONE
will come before QUP_MX_OUTPUT_DONE so checking for both will work
always.

Thanks,
Abhishek

2018-02-19 14:10:04

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2

<snip>

>> static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool
>> is_rx)
>> {
>> qup->cur_blk_events = 0;
>> @@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter
>> *adap,
>> return ret;
>> }
>>
>
> Since this is used for both FIFO and BLK mode, might be good to call
> it
> qup_i2c_set_event. Same in rest of the places and macros as well.
> But if this is going to be removed altogether, then great.
>

Sure. I will rename this and others.
I will check for removing but as replied in Patch 11. We need to track
for
3 things in RX case. For TX case, OUTPUT_DONE should be sufficient.

>> +/*
>> + * Function to configure registers related with reconfiguration
>> during run
>> + * and will be done before each I2C sub transfer.
>> + */
>> +static void qup_i2c_conf_count_v2(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> + u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
>> +
>> + if (blk->is_tx_blk_mode)
>> + writel(qup->config_run | blk->total_tx_len,
>> + qup->base + QUP_MX_OUTPUT_CNT);
>> + else
>> + writel(qup->config_run | blk->total_tx_len,
>> + qup->base + QUP_MX_WRITE_CNT);
>> +
>> + if (blk->total_rx_len) {
>> + if (blk->is_rx_blk_mode)
>> + writel(qup->config_run | blk->total_rx_len,
>> + qup->base + QUP_MX_INPUT_CNT);
>> + else
>> + writel(qup->config_run | blk->total_rx_len,
>> + qup->base + QUP_MX_READ_CNT);
>> + } else {
>> + qup_config |= QUP_NO_INPUT;
>> + }
>> +
>> + writel(qup_config, qup->base + QUP_CONFIG);
>> +}
>> +
>> +/*
>> + * Function to configure registers related with transfer mode
>> (FIFO/Block)
>> + * before starting of i2c transfer and will be done only once in QUP
>> RESET
>> + * state.
>> + */
>> +static void qup_i2c_conf_mode_v2(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> + u32 io_mode = QUP_REPACK_EN;
>> +
>> + if (blk->is_tx_blk_mode) {
>> + io_mode |= QUP_OUTPUT_BLK_MODE;
>> + writel(0, qup->base + QUP_MX_WRITE_CNT);
>> + } else {
>> + writel(0, qup->base + QUP_MX_OUTPUT_CNT);
>> + }
>> +
>> + if (blk->is_rx_blk_mode) {
>> + io_mode |= QUP_INPUT_BLK_MODE;
>> + writel(0, qup->base + QUP_MX_READ_CNT);
>> + } else {
>> + writel(0, qup->base + QUP_MX_INPUT_CNT);
>> + }
>> +
>> + writel(io_mode, qup->base + QUP_IO_MODE);
>> +}
>> +
>> +/*
>> + * Function to clear required variables before starting of any QUP v2
>> + * sub transfer
>> + */
>> +static void qup_i2c_clear_blk_v2(struct qup_i2c_block *blk)
>> +{
>> + blk->send_last_word = false;
>> + blk->tx_tags_sent = false;
>> + blk->tx_fifo_data = 0;
>> + blk->tx_fifo_data_pos = 0;
>> + blk->tx_fifo_free = 0;
>> +
>> + blk->rx_tags_fetched = false;
>> + blk->rx_fifo_data = 0;
>> + blk->rx_fifo_data_pos = 0;
>> + blk->fifo_available = 0;
>> +}
>> +
>> +/*
>> + * Function to receive data from RX FIFO for read message in QUP v2
>> + * i2c transfer.
>> + */
>> +static void qup_i2c_recv_data(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> + int j;
>> +
>> + for (j = blk->rx_fifo_data_pos;
>> + blk->cur_blk_len && blk->fifo_available;
>> + blk->cur_blk_len--, blk->fifo_available--) {
>> + if (j == 0)
>> + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
>> +
>> + *(blk->cur_data++) = blk->rx_fifo_data;
>> + blk->rx_fifo_data >>= 8;
>> +
>> + if (j == 3)
>> + j = 0;
>> + else
>> + j++;
>> + }
>> +
>> + blk->rx_fifo_data_pos = j;
>> +}
>> +
>> +/* Function to receive tags for read message in QUP v2 i2c transfer.
>> */
>> +static void qup_i2c_recv_tags(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> +
>> + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
>> + blk->rx_fifo_data >>= blk->rx_tag_len * 8;
>
> why cant it be simply ignored ?
>

We need to read the data from FIFO and increment the rx_fifo_data_pos
and decrement the fifo_available. The tag bytes are being ignored.

>> + blk->rx_fifo_data_pos = blk->rx_tag_len;
>> + blk->fifo_available -= blk->rx_tag_len;
>> +}
>> +
>> +/*
>> + * This function reads the data and tags from RX FIFO. Since in read
>> case, the
>> + * tags will be preceded by received data bytes need to be written so
>> + * 1. Check if rx_tags_fetched is false i.e. the start of QUP block
>> so receive
>> + * all tag bytes and discard that.
>> + * 2. Read the data from RX FIFO. When all the data bytes have been
>> read then
>> + * mark the QUP_BLK_EVENT_RX_DATA_DONE in current block event and
>> return.
>> + */
>> +static void qup_i2c_read_rx_fifo_v2(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> +
>> + if (!blk->rx_tags_fetched) {
>> + qup_i2c_recv_tags(qup);
>> + blk->rx_tags_fetched = true;
>> + }
>> +
>> + qup_i2c_recv_data(qup);
>> + if (!blk->cur_blk_len)
>> + qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
>> +}
>> +
>> +/*
>> + * Function to write bytes in TX FIFO for write message in QUP v2 i2c
>> + * transfer. QUP TX FIFO write works on word basis (4 bytes). New
>> byte write to
>> + * TX FIFO will be appended in this data tx_fifo_data and will be
>> written to TX
>> + * FIFO when all the 4 bytes are available.
>> + */
>> +static void
>> +qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsigned
>> int *len)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> + unsigned int j;
>> +
>> + for (j = blk->tx_fifo_data_pos; *len && blk->tx_fifo_free;
>> + (*len)--, blk->tx_fifo_free--) {
>> + blk->tx_fifo_data |= *(*data)++ << (j * 8);
>> + if (j == 3) {
>> + writel(blk->tx_fifo_data,
>> + qup->base + QUP_OUT_FIFO_BASE);
>> + blk->tx_fifo_data = 0x0;
>> + j = 0;
>> + } else {
>> + j++;
>> + }
>> + }
>> +
>> + blk->tx_fifo_data_pos = j;
>> +}
>> +
>> +/* Function to transfer tags for read message in QUP v2 i2c transfer.
>> */
>> +static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> +
>> + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, &blk->tx_tag_len);
>> + if (blk->tx_fifo_data_pos)
>> + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
>> +
>> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
>> +}
>> +
>> +/*
>> + * This function writes the data and tags in TX FIFO. Since in write
>> case, both
>> + * tags and data need to be written and QUP write tags can have
>> maximum 256 data
>> + * length, so it follows simple internal state machine to manage it.
>> + * 1. Check if tx_tags_sent is false i.e. the start of QUP block so
>> write the
>> + * tags to TX FIFO.
>> + * 2. Check if send_last_word is true. This will be set when last few
>> data bytes
>> + * less than 4 bytes are reamining to be written in FIFO because
>> of no FIFO
>> + * space. All this data bytes are available in tx_fifo_data so
>> write this
>> + * in FIFO and mark the tx done.
>> + * 3. Write the data to TX FIFO and check for cur_blk_len. If this is
>> non zero
>> + * then more data is pending otherwise following 3 cases can be
>> possible
>> + * a. if tx_fifo_data_pos is zero that means all the data bytes in
>> this block
>> + * have been written in TX FIFO so mark the tx done.
>> + * b. tx_fifo_free is zero. In this case, last few bytes (less
>> than 4
>> + * bytes) are copied to tx_fifo_data but couldn't be sent
>> because of
>> + * FIFO full so make send_last_word true.
>> + * c. tx_fifo_free is non zero i.e tx FIFO is free so copy the
>> remaining data
>> + * from tx_fifo_data to tx FIFO and mark the tx done. Since,
>> + * qup_i2c_write_blk_data do write in 4 bytes and FIFO space is
>> in
>> + * multiple of 4 bytes so tx_fifo_free will be always greater
>> than or
>> + * equal to 4 bytes.
>
> Comments b and c should be c and b as per the code below
>

I Will fix that.

>> + */
>> +static void qup_i2c_write_tx_fifo_v2(struct qup_i2c_dev *qup)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> +
>> + if (!blk->tx_tags_sent) {
>> + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags,
>> + &blk->tx_tag_len);
>> + blk->tx_tags_sent = true;
>> + }
>> +
>> + if (blk->send_last_word)
>> + goto send_last_word;
>> +
>> + qup_i2c_write_blk_data(qup, &blk->cur_data, &blk->cur_blk_len);
>
> ok, do understand, why is cur_blk_len zero and we still have pending
> bytes to be written ?
>

qup_i2c_write_blk_data will return if we don't have space
in TX FIFO or these are last remaining bytes (less than 4).
qup_i2c_write_blk_data will write to TX FIFO when the tx_fifo_data_pos
is 3 i.e all 4 bytes.

The following check is taking care of this case.
If we have space available in TX FIFO then copy the data from
tx_fifo_data
otherwise mark send_last_word true so that when space will be
available then this function will just sent those remaining bytes.


>> + if (!blk->cur_blk_len) {
>> + if (!blk->tx_fifo_data_pos)
>> + goto tx_data_done;
>> +
>> + if (blk->tx_fifo_free)
>> + goto send_last_word;
>> +
>> + blk->send_last_word = true;
>> + }
>> +
>> + return;
>> +
>> +send_last_word:
>> + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
>> +tx_data_done:
>> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
>
> Yes, as commented on the previous patch, if we can get rid of this 4
> flags
> for completion in block/fifo mode, it would simply things a bit.
>

It seems QUP_BLK_EVENT_TX_DATA_DONE can be removed.

>> +}
>> +
>> +/*
>> + * Main transfer function which will be used for reading or writing
>> i2c data.
>> + * The QUP v2 supports reconfiguration during run in which multiple
>> i2c sub
>> + * transfers can be scheduled.
>> + */
>> +static int
>> +qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool
>> is_first,
>> + bool change_pause_state)
>> +{
>> + struct qup_i2c_block *blk = &qup->blk;
>> + struct i2c_msg *msg = qup->msg;
>> + int ret;
>> +
>> + /*
>> + * Check if its SMBus Block read for which the top level read will
>> be
>> + * done into 2 QUP reads. One with message length 1 while other one
>> is
>> + * with actual length.
>> + */
>> + if (qup_i2c_check_msg_len(msg)) {
>> + if (qup->is_smbus_read) {
>> + /*
>> + * If the message length is already read in
>> + * the first byte of the buffer, account for
>> + * that by setting the offset
>> + */
>> + blk->cur_data += 1;
>> + is_first = false;
>> + } else {
>> + change_pause_state = false;
>> + }
>> + }
>> +
>> + qup->config_run = is_first ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
>> +
>> + qup_i2c_clear_blk_v2(blk);
>> + qup_i2c_conf_count_v2(qup);
>> +
>> + /* If it is first sub transfer, then configure i2c bus clocks */
>> + if (is_first) {
>> + ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
>> + if (ret)
>> + return ret;
>> +
>> + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
>> +
>> + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + qup_i2c_set_blk_event(qup, is_rx);
>
> hmm, if the completion is changed as per the just INPUT/OUTPUT done
> then this blk event tracking can be removed.
>

I will check for RX case. If adding condition for I2C_M_RD and
remaining
bytes to be read is zero makes code more readable then I will change
and remove this function.

>> + reinit_completion(&qup->xfer);
>> + enable_irq(qup->irq);
>> + /*
>> + * In FIFO mode, tx FIFO can be written directly while in block mode
>> the
>> + * it will be written after getting OUT_BLOCK_WRITE_REQ interrupt
>> + */
>> + if (!blk->is_tx_blk_mode) {
>> + blk->tx_fifo_free = qup->out_fifo_sz;
>> +
>> + if (is_rx)
>> + qup_i2c_write_rx_tags_v2(qup);
>> + else
>> + qup_i2c_write_tx_fifo_v2(qup);
>> + }
>> +
>> + ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
>> + if (ret)
>> + goto err;
>> +
>> + ret = qup_i2c_wait_for_complete(qup, msg);
>> + if (ret)
>> + goto err;
>> +
>> + /* Move to pause state for all the transfers, except last one */
>> + if (change_pause_state) {
>> + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
>> + if (ret)
>> + goto err;
>> + }
>> +
>> +err:
>> + disable_irq(qup->irq);
>> + return ret;
>> +}
>> +
>> +/*
>> + * Function to transfer one read/write message in i2c transfer. It
>> splits the
>> + * message into multiple of blk_xfer_limit data length blocks and
>> schedule each
>> + * QUP block individually.
>> + */
>> +static int qup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id,
>> bool is_rx)
>> +{
>> + int ret = 0;
>> + unsigned int data_len, i;
>> + struct i2c_msg *msg = qup->msg;
>> + struct qup_i2c_block *blk = &qup->blk;
>> + u8 *msg_buf = msg->buf;
>> +
>> + qup->blk_xfer_limit = is_rx ? RECV_MAX_DATA_LEN : QUP_READ_LIMIT;
>> + qup_i2c_set_blk_data(qup, msg);
>> +
>> + for (i = 0; i < blk->count; i++) {
>> + data_len = qup_i2c_get_data_len(qup);
>> + blk->pos = i;
>> + blk->cur_tx_tags = blk->tags;
>> + blk->cur_blk_len = data_len;
>> + blk->tx_tag_len =
>> + qup_i2c_set_tags(blk->cur_tx_tags, qup, qup->msg);
>> +
>> + blk->cur_data = msg_buf;
>> +
>> + if (is_rx) {
>> + blk->total_tx_len = blk->tx_tag_len;
>> + blk->rx_tag_len = 2;
>> + blk->total_rx_len = blk->rx_tag_len + data_len;
>> + } else {
>> + blk->total_tx_len = blk->tx_tag_len + data_len;
>> + blk->total_rx_len = 0;
>> + }
>> +
>> + ret = qup_i2c_conf_xfer_v2(qup, is_rx, !msg_id && !i,
>> + !qup->is_last || i < blk->count - 1);
>> + if (ret)
>> + return ret;
>> +
>> + /* Handle SMBus block read length */
>> + if (qup_i2c_check_msg_len(msg) && msg->len == 1 &&
>> + !qup->is_smbus_read) {
>> + if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX)
>> + return -EPROTO;
>> +
>> + msg->len = msg->buf[0];
>> + qup->is_smbus_read = true;
>> + ret = qup_i2c_xfer_v2_msg(qup, msg_id, true);
>> + qup->is_smbus_read = false;
>> + if (ret)
>> + return ret;
>> +
>> + msg->len += 1;
>> + }
>> +
>> + msg_buf += data_len;
>> + blk->data_len -= qup->blk_xfer_limit;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +/*
>> + * QUP v2 supports 3 modes
>> + * Programmed IO using FIFO mode : Less than FIFO size
>> + * Programmed IO using Block mode : Greater than FIFO size
>> + * DMA using BAM : Appropriate for any transactio size but the
>> address should be
>> + * DMA applicable
>> + *
>
> s/transactio/transaction
>

I Will fix this and qup_i2c_determine_mode.

>> + * This function determines the mode which will be used for this
>> transfer. An
>> + * i2c transfer contains multiple message. Following are the rules to
>> determine
>> + * the mode used.
>> + * 1. Determine the tx and rx length for each message and maximum tx
>> and rx
>> + * length for complete transfer
>> + * 2. If tx or rx length is greater than DMA threshold than use the
>> DMA mode.
>> + * 3. In FIFO or block mode, TX and RX can operate in different mode
>> so check
>> + * for maximum tx and rx length to determine mode.
>> + */
>> +static int
>> +qup_i2c_determine_mode(struct qup_i2c_dev *qup, struct i2c_msg
>> msgs[], int num)
>
> qup_i2c_determine_mode_v2
>
>> +{
>> + int idx;
>> + bool no_dma = false;
>> + unsigned int max_tx_len = 0, max_rx_len = 0;
>> + unsigned int cur_tx_len, cur_rx_len;
>> + unsigned int total_rx_len = 0, total_tx_len = 0;
>> +
>> + /* All i2c_msgs should be transferred using either dma or cpu */
>> + for (idx = 0; idx < num; idx++) {
>> + if (msgs[idx].len == 0)
>> + return -EINVAL;
>> +
>> + if (msgs[idx].flags & I2C_M_RD) {
>> + cur_tx_len = 0;
>> + cur_rx_len = msgs[idx].len;
>> + } else {
>> + cur_tx_len = msgs[idx].len;
>> + cur_rx_len = 0;
>> + }
>> +
>> + if (is_vmalloc_addr(msgs[idx].buf))
>> + no_dma = true;
>> +
>> + max_tx_len = max(max_tx_len, cur_tx_len);
>> + max_rx_len = max(max_rx_len, cur_rx_len);
>> + total_rx_len += cur_rx_len;
>> + total_tx_len += cur_tx_len;
>> + }
>
> why is tag length for each block not being considered here ?

That is to avoid unnecessary calculation.
The role of this function is to just determine mode.
DMA and block mode will work for any xfer length.
but FIFO won't work for > 64.

The max tag length already we have considered in blk_mode_threshold.
in None of the case, it will give FIFO mode for xfer greater than
64 bytes.

>
>> +
>> + if (!no_dma && qup->is_dma &&
>
> why do we need is_dma and use_dma ?
> Now that you have removed the need for is_dma in rest of places,
> better to get rid of that fully.
>

Both are doing separate things.
use_dma will be false if any msgs[idx].buf is vmalloc address.
qup->is_dma will be false if the dma channels are not defined
in dtsi or in case of some DMA channel allocation failure.


>> + (total_tx_len > qup->dma_threshold ||
>> + total_rx_len > qup->dma_threshold)) {
>> + qup->use_dma = true;
>> + } else {
>> + qup->blk.is_tx_blk_mode =
>> + max_tx_len > qup->blk_mode_threshold ? true : false;
>> + qup->blk.is_rx_blk_mode =
>> + max_rx_len > qup->blk_mode_threshold ? true : false;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
>> struct i2c_msg msgs[],
>> int num)
>> {
>> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
>> int ret, idx = 0;
>> - unsigned int total_len = 0;
>>
>> qup->bus_err = 0;
>> qup->qup_err = 0;
>> @@ -1457,6 +1609,10 @@ static int qup_i2c_xfer_v2(struct i2c_adapter
>> *adap,
>> if (ret < 0)
>> goto out;
>>
>> + ret = qup_i2c_determine_mode(qup, msgs, num);
>> + if (ret)
>> + goto out;
>> +
>> writel(1, qup->base + QUP_SW_RESET);
>> ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
>> if (ret)
>> @@ -1466,59 +1622,35 @@ static int qup_i2c_xfer_v2(struct i2c_adapter
>> *adap,
>> writel(I2C_MINI_CORE | I2C_N_VAL_V2, qup->base + QUP_CONFIG);
>> writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
>>
>> - if ((qup->is_dma)) {
>> - /* All i2c_msgs should be transferred using either dma or cpu */
>> + if (qup_i2c_poll_state_i2c_master(qup)) {
>> + ret = -EIO;
>> + goto out;
>> + }
>> +
>> + if (qup->use_dma) {
>> + reinit_completion(&qup->xfer);
>> + ret = qup_i2c_bam_xfer(adap, &msgs[0], num);
>> + qup->use_dma = false;
>> + } else {
>> + qup_i2c_conf_mode_v2(qup);
>> +
>> for (idx = 0; idx < num; idx++) {
>> - if (msgs[idx].len == 0) {
>> - ret = -EINVAL;
>> - goto out;
>> - }
>> + qup->msg = &msgs[idx];
>> + qup->is_last = idx == (num - 1);
>>
>> - if (is_vmalloc_addr(msgs[idx].buf))
>> + ret = qup_i2c_xfer_v2_msg(qup, idx,
>> + !!(msgs[idx].flags & I2C_M_RD));
>
> why !!() is required here ?
>

I made it to change for boolean.
Same thing, we are doing in other places inside i2c base driver.

Regards,
Abhishek

2018-02-20 04:33:10

by Sricharan R

[permalink] [raw]
Subject: Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1

Hi Abhishek,

On 2/19/2018 6:51 PM, Abhishek Sahu wrote:
> On 2018-02-16 13:14, Sricharan R wrote:
>> Hi Abhishek,
>>
>> On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
>>> Following are the major issues in current driver code
>>>
>>> 1. The current driver simply assumes the transfer completion
>>>    whenever its gets any non-error interrupts and then simply do the
>>>    polling of available/free bytes in FIFO.
>>> 2. The block mode is not working properly since no handling in
>>>    being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ.
>>>
>>> Because of above, i2c v1 transfers of size greater than 32 are failing
>>> with following error message
>>>
>>>     i2c_qup 78b6000.i2c: timeout for fifo out full
>>>
>>> To make block mode working properly and move to use the interrupts
>>> instead of polling, major code reorganization is required. Following
>>> are the major changes done in this patch
>>>
>>> 1. Remove the polling of TX FIFO free space and RX FIFO available
>>>    bytes and move to interrupts completely. QUP has QUP_MX_OUTPUT_DONE,
>>>    QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
>>>    interrupts to handle FIFO’s properly so check all these interrupts.
>>> 2. During write, For FIFO mode, TX FIFO can be directly written
>>>    without checking for FIFO space. For block mode, the QUP will generate
>>>    OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of available
>>>    space.
>>> 3. During read, both TX and RX FIFO will be used. TX will be used
>>>    for writing tags and RX will be used for receiving the data. In QUP,
>>>    TX and RX can operate in separate mode so configure modes accordingly.
>>> 4. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
>>>    will be generated after all the bytes have been copied in RX FIFO. For
>>>    read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
>>>    whenever it has block size of available data.
>>>
>>> Signed-off-by: Abhishek Sahu <[email protected]>
>>> ---
>>>  drivers/i2c/busses/i2c-qup.c | 399 ++++++++++++++++++++++++++++---------------
>>>  1 file changed, 257 insertions(+), 142 deletions(-)
>>>
>>> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
>>> index edea3b9..af6c21a 100644
>>> --- a/drivers/i2c/busses/i2c-qup.c
>>> +++ b/drivers/i2c/busses/i2c-qup.c
>>> @@ -73,8 +73,11 @@
>>>  #define QUP_IN_SVC_FLAG        BIT(9)
>>>  #define QUP_MX_OUTPUT_DONE    BIT(10)
>>>  #define QUP_MX_INPUT_DONE    BIT(11)
>>> +#define OUT_BLOCK_WRITE_REQ    BIT(12)
>>> +#define IN_BLOCK_READ_REQ    BIT(13)
>>>
>>>  /* I2C mini core related values */
>>> +#define QUP_NO_INPUT        BIT(7)
>>>  #define QUP_CLOCK_AUTO_GATE    BIT(13)
>>>  #define I2C_MINI_CORE        (2 << 8)
>>>  #define I2C_N_VAL        15
>>> @@ -138,13 +141,51 @@
>>>  #define DEFAULT_CLK_FREQ 100000
>>>  #define DEFAULT_SRC_CLK 20000000
>>>
>>> +/* MAX_OUTPUT_DONE_FLAG has been received */
>>> +#define QUP_BLK_EVENT_TX_IRQ_DONE        BIT(0)
>>> +/* MAX_INPUT_DONE_FLAG has been received */
>>> +#define QUP_BLK_EVENT_RX_IRQ_DONE        BIT(1)
>>> +/* All the TX bytes have been written in TX FIFO */
>>> +#define QUP_BLK_EVENT_TX_DATA_DONE        BIT(2)
>>> +/* All the RX bytes have been read from RX FIFO */
>>> +#define QUP_BLK_EVENT_RX_DATA_DONE        BIT(3)
>>> +
>>> +/* All the required events to mark a QUP I2C TX transfer completed */
>>> +#define QUP_BLK_EVENT_TX_DONE        (QUP_BLK_EVENT_TX_IRQ_DONE | \
>>> +                     QUP_BLK_EVENT_TX_DATA_DONE)
>>> +/* All the required events to mark a QUP I2C RX transfer completed */
>>> +#define QUP_BLK_EVENT_RX_DONE        (QUP_BLK_EVENT_TX_DONE | \
>>> +                     QUP_BLK_EVENT_RX_IRQ_DONE | \
>>> +                     QUP_BLK_EVENT_RX_DATA_DONE)
>>> +
>>> +/*
>>> + * count: no of blocks
>>> + * pos: current block number
>>> + * tx_tag_len: tx tag length for current block
>>> + * rx_tag_len: rx tag length for current block
>>> + * data_len: remaining data length for current message
>>> + * total_tx_len: total tx length including tag bytes for current QUP transfer
>>> + * total_rx_len: total rx length including tag bytes for current QUP transfer
>>> + * tx_fifo_free: number of free bytes in current QUP block write.
>>> + * fifo_available: number of available bytes in RX FIFO for current
>>> + *           QUP block read
>>> + * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
>>> + * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
>>> + * tags: contains tx tag bytes for current QUP transfer
>>> + */
>>>  struct qup_i2c_block {
>>> -    int    count;
>>> -    int    pos;
>>> -    int    tx_tag_len;
>>> -    int    rx_tag_len;
>>> -    int    data_len;
>>> -    u8    tags[6];
>>> +    int        count;
>>> +    int        pos;
>>> +    int        tx_tag_len;
>>> +    int        rx_tag_len;
>>> +    int        data_len;
>>> +    int        total_tx_len;
>>> +    int        total_rx_len;
>>> +    int        tx_fifo_free;
>>> +    int        fifo_available;
>>> +    bool        is_tx_blk_mode;
>>> +    bool        is_rx_blk_mode;
>>> +    u8        tags[6];
>>>  };
>>>
>>>  struct qup_i2c_tag {
>>> @@ -187,6 +228,7 @@ struct qup_i2c_dev {
>>>
>>>      /* To check if this is the last msg */
>>>      bool            is_last;
>>> +    bool            is_qup_v1;
>>>
>>>      /* To configure when bus is in run state */
>>>      int            config_run;
>>> @@ -195,6 +237,10 @@ struct qup_i2c_dev {
>>>      bool            is_dma;
>>>      /* To check if the current transfer is using DMA */
>>>      bool            use_dma;
>>> +    /* Required events to mark QUP transfer as completed */
>>> +    u32            blk_events;
>>> +    /* Already completed events in QUP transfer */
>>> +    u32            cur_blk_events;
>>>      /* The threshold length above which DMA will be used */
>>>      unsigned int        dma_threshold;
>>>      unsigned int        max_xfer_sg_len;
>>> @@ -205,11 +251,18 @@ struct qup_i2c_dev {
>>>      struct            qup_i2c_bam btx;
>>>
>>>      struct completion    xfer;
>>> +    /* function to write data in tx fifo */
>>> +    void (*write_tx_fifo)(struct qup_i2c_dev *qup);
>>> +    /* function to read data from rx fifo */
>>> +    void (*read_rx_fifo)(struct qup_i2c_dev *qup);
>>> +    /* function to write tags in tx fifo for i2c read transfer */
>>> +    void (*write_rx_tags)(struct qup_i2c_dev *qup);
>>>  };
>>>
>>>  static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
>>>  {
>>>      struct qup_i2c_dev *qup = dev;
>>> +    struct qup_i2c_block *blk = &qup->blk;
>>>      u32 bus_err;
>>>      u32 qup_err;
>>>      u32 opflags;
>>> @@ -256,12 +309,54 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
>>>          goto done;
>>>      }
>>>
>>> -    if (opflags & QUP_IN_SVC_FLAG)
>>> -        writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>>> +    if (!qup->is_qup_v1)
>>> +        goto done;
>>>
>>> -    if (opflags & QUP_OUT_SVC_FLAG)
>>> +    if (opflags & QUP_OUT_SVC_FLAG) {
>>>          writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>>>
>>> +        /*
>>> +         * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG.
>>> +         * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind
>>> +         * QUP_OUTPUT_SERVICE_FLAG. The only reason for
>>> +         * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is
>>> +         * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking
>>> +         * here QUP_OUTPUT_SERVICE_FLAG and assumes that
>>> +         * QUP_MAX_OUTPUT_DONE_FLAG.
>>> +         */
>>> +        if (!blk->is_tx_blk_mode)
>>> +            qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
>>> +
>>> +        if (opflags & OUT_BLOCK_WRITE_REQ) {
>>> +            blk->tx_fifo_free += qup->out_blk_sz;
>>> +            if (qup->msg->flags & I2C_M_RD)
>>> +                qup->write_rx_tags(qup);
>>> +            else
>>> +                qup->write_tx_fifo(qup);
>>> +        }
>>> +    }
>>> +
>>> +    if (opflags & QUP_IN_SVC_FLAG) {
>>> +        writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>>> +
>>> +        if (!blk->is_rx_blk_mode) {
>>> +            blk->fifo_available += qup->in_fifo_sz;
>>> +            qup->read_rx_fifo(qup);
>>> +        } else if (opflags & IN_BLOCK_READ_REQ) {
>>> +            blk->fifo_available += qup->in_blk_sz;
>>> +            qup->read_rx_fifo(qup);
>>> +        }
>>> +    }
>>> +
>>> +    if (opflags & QUP_MX_OUTPUT_DONE)
>>> +        qup->cur_blk_events |= QUP_BLK_EVENT_TX_IRQ_DONE;
>>> +
>>> +    if (opflags & QUP_MX_INPUT_DONE)
>>> +        qup->cur_blk_events |= QUP_BLK_EVENT_RX_IRQ_DONE;
>>> +
>>> +    if (qup->cur_blk_events != qup->blk_events)
>>> +        return IRQ_HANDLED;
>>
>>   Is it correct that if we do a complete in above case, i mean
>>   for TX -> based on QUP_MX_OUTPUT_DONE and for RX -> based on
>>   QUP_MX_INPUT_DONE, would that simplify things by getting rid of
>>   QUP_BLK_EVENT_RX/TX_DONE and QUP_BLK_EVENT_RX/TX_IRQ_DONE
>>   altogether ?
>
>  We can get rid of QUP_BLK_EVENT_TX_DONE.
>  For RX, the QUP_MX_INPUT_DONE will be triggered when QUP copies all
>  the data in FIFO from external i2c slave. So if 64 bytes read has been
>  scheduled then  following is the behaviour
>
>  IRQ with QUP_MX_INPUT_DONE and IN_BLOCK_READ_REQ -> read 16 bytes
>  IRQ with IN_BLOCK_READ_REQ -> read next 16 bytes
>  IRQ with IN_BLOCK_READ_REQ  -> read next 16 bytes
>  IRQ with IN_BLOCK_READ_REQ -> read last 16 bytes
>
>  So for RX, we can't trigger complete by checking QUP_BLK_EVENT_RX_DONE alone.
>  We need to track the number of bytes read from FIFO. Instead of putting
>  this check, I am taking one extra event bit QUP_BLK_EVENT_RX_DONE which
>  will be set when all the bytes has been read.
>
>  I am not sure if checking QUP_MX_INPUT_DONE will always work since
>  there may be some case, when for small transfers the QUP_MX_INPUT_DONE
>  will come before QUP_MX_OUTPUT_DONE so checking for both will work
>  always.

Looking in to the code and the above case,
RX -> complete when the required len bytes are read from FIFO in to the msg buffer.
TX -> complete just when QUP_MX_OUTPUT_DONE is set.

Tf this helps of getting rid of 3 of the above 4 flags tracking and all your stress/testing
continues to work then fine.

Regards,
Sricharan

--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2018-02-27 21:47:46

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion

Tested on Centriq 2400

Reviewed-by: Austin Christ <[email protected]>

On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> The QUP BSLP BAM generates the following error sometimes if the
> current I2C DMA transfer fails and the flush operation has been
> scheduled
>
> “bam-dma-engine 7884000.dma: Cannot free busy channel”
>
> If any I2C error comes during BAM DMA transfer, then the QUP I2C
> interrupt will be generated and the flush operation will be
> carried out to make i2c consume all scheduled DMA transfer.
> Currently, the same completion structure is being used for BAM
> transfer which has already completed without reinit. It will make
> flush operation wait_for_completion_timeout completed immediately
> and will proceed for freeing the DMA resources where the
> descriptors are still in process.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 08f8e01..9faa26c41a 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.
> * Copyright (c) 2014, Sony Mobile Communications AB.
> *
> *
> @@ -844,6 +844,8 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> }
>
> if (ret || qup->bus_err || qup->qup_err) {
> + reinit_completion(&qup->xfer);
> +
> if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
> dev_err(qup->dev, "change to run state timed out");
> goto desc_err;
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 21:49:48

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 02/12] i2c: qup: minor code reorganization for use_dma

Tested on Centriq 2400

Reviewed-by: Austin Christ <[email protected]>

On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> 1. Assigns use_dma in qup_dev structure itself which will
> help in subsequent patches to determine the mode in IRQ handler.
> 2. Does minor code reorganization for loops to reduce the
> unnecessary comparison and assignment.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 19 +++++++++++--------
> 1 file changed, 11 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 9faa26c41a..c68f433 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -190,6 +190,8 @@ struct qup_i2c_dev {
>
> /* dma parameters */
> bool is_dma;
> + /* To check if the current transfer is using DMA */
> + bool use_dma;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -1297,7 +1299,7 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> - int ret, len, idx = 0, use_dma = 0;
> + int ret, len, idx = 0;
>
> qup->bus_err = 0;
> qup->qup_err = 0;
> @@ -1326,13 +1328,12 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> len = (msgs[idx].len > qup->out_fifo_sz) ||
> (msgs[idx].len > qup->in_fifo_sz);
>
> - if ((!is_vmalloc_addr(msgs[idx].buf)) && len) {
> - use_dma = 1;
> - } else {
> - use_dma = 0;
> + if (is_vmalloc_addr(msgs[idx].buf) || !len)
> break;
> - }
> }
> +
> + if (idx == num)
> + qup->use_dma = true;
> }
>
> idx = 0;
> @@ -1356,15 +1357,17 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
>
> reinit_completion(&qup->xfer);
>
> - if (use_dma) {
> + if (qup->use_dma) {
> ret = qup_i2c_bam_xfer(adap, &msgs[idx], num);
> + qup->use_dma = false;
> + break;
> } else {
> if (msgs[idx].flags & I2C_M_RD)
> ret = qup_i2c_read_one_v2(qup, &msgs[idx]);
> else
> ret = qup_i2c_write_one_v2(qup, &msgs[idx]);
> }
> - } while ((idx++ < (num - 1)) && !use_dma && !ret);
> + } while ((idx++ < (num - 1)) && !ret);
>
> if (!ret)
> ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 21:52:56

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count

I agree with Sricharan's comments about naming, otherwise looks good.

Tested on Centriq 2400

Reviewed-by: Austin Christ <[email protected]>

On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> The rx_nents and tx_nents are redundant. rx_buf and tx_buf can
> be used for total number of SG entries.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 26 ++++++++++----------------
> 1 file changed, 10 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index c68f433..bb83a2967 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -692,7 +692,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> struct dma_async_tx_descriptor *txd, *rxd = NULL;
> int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
> dma_cookie_t cookie_rx, cookie_tx;
> - u32 rx_nents = 0, tx_nents = 0, len, blocks, rem;
> + u32 len, blocks, rem;
> u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
> u8 *tags;
>
> @@ -707,9 +707,6 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> rem = msg->len - (blocks - 1) * limit;
>
> if (msg->flags & I2C_M_RD) {
> - rx_nents += (blocks * 2) + 1;
> - tx_nents += 1;
> -
> while (qup->blk.pos < blocks) {
> tlen = (i == (blocks - 1)) ? rem : limit;
> tags = &qup->start_tag.start[off + len];
> @@ -748,8 +745,6 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> if (ret)
> return ret;
> } else {
> - tx_nents += (blocks * 2);
> -
> while (qup->blk.pos < blocks) {
> tlen = (i == (blocks - 1)) ? rem : limit;
> tags = &qup->start_tag.start[off + tx_len];
> @@ -775,7 +770,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
>
> if (idx == (num - 1)) {
> len = 1;
> - if (rx_nents) {
> + if (rx_buf) {
> qup->btx.tag.start[0] =
> QUP_BAM_INPUT_EOT;
> len++;
> @@ -787,14 +782,13 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> len, qup, DMA_TO_DEVICE);
> if (ret)
> return ret;
> - tx_nents += 1;
> }
> }
> idx++;
> msg++;
> }
>
> - txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_nents,
> + txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_buf,
> DMA_MEM_TO_DEV,
> DMA_PREP_INTERRUPT | DMA_PREP_FENCE);
> if (!txd) {
> @@ -803,7 +797,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> goto desc_err;
> }
>
> - if (!rx_nents) {
> + if (!rx_buf) {
> txd->callback = qup_i2c_bam_cb;
> txd->callback_param = qup;
> }
> @@ -816,9 +810,9 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
>
> dma_async_issue_pending(qup->btx.dma);
>
> - if (rx_nents) {
> + if (rx_buf) {
> rxd = dmaengine_prep_slave_sg(qup->brx.dma, qup->brx.sg,
> - rx_nents, DMA_DEV_TO_MEM,
> + rx_buf, DMA_DEV_TO_MEM,
> DMA_PREP_INTERRUPT);
> if (!rxd) {
> dev_err(qup->dev, "failed to get rx desc\n");
> @@ -853,7 +847,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> goto desc_err;
> }
>
> - if (rx_nents)
> + if (rx_buf)
> writel(QUP_BAM_INPUT_EOT,
> qup->base + QUP_OUT_FIFO_BASE);
>
> @@ -871,10 +865,10 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> }
>
> desc_err:
> - dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE);
> + dma_unmap_sg(qup->dev, qup->btx.sg, tx_buf, DMA_TO_DEVICE);
>
> - if (rx_nents)
> - dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents,
> + if (rx_buf)
> + dma_unmap_sg(qup->dev, qup->brx.sg, rx_buf,
> DMA_FROM_DEVICE);
>
> return ret;
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 22:01:32

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode

Tested on Centriq 2400

Reviewed-by: Austin Christ <[email protected]>

On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> Currently the i2c error handling in BAM mode is not working
> properly in stress condition.
>
> 1. After an error, the FIFO are being written with FLUSH and
> EOT tags which should not be required since already these tags
> have been written in BAM descriptor itself.
>
> 2. QUP state is being moved to RESET in IRQ handler in case
> of error. When QUP HW encounters an error in BAM mode then it
> moves the QUP STATE to PAUSE state. In this case, I2C_FLUSH
> command needs to be executed while moving to RUN_STATE by writing
> to the QUP_STATE register with the I2C_FLUSH bit set to 1.
>
> 3. In Error case, sometimes, QUP generates more than one
> interrupt which will trigger the complete again. After an error,
> the flush operation will be scheduled after doing
> reinit_completion which should be triggered by BAM IRQ callback.
> If the second QUP IRQ comes during this time then it will call
> the complete and the transfer function will assume the all the
> BAM HW descriptors have been completed.
>
> 4. The release DMA is being called after each error which
> will free the DMA tx and rx channels. The error like NACK is very
> common in I2C transfer and every time this will be overhead. Now,
> since the error handling is proper so this release channel can be
> completely avoided.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 25 ++++++++++++++++---------
> 1 file changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 094be6a..6227a5c 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -228,9 +228,24 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
> if (bus_err)
> writel(bus_err, qup->base + QUP_I2C_STATUS);
>
> + /*
> + * Check for BAM mode and returns if already error has come for current
> + * transfer. In Error case, sometimes, QUP generates more than one
> + * interrupt.
> + */
> + if (qup->use_dma && (qup->qup_err || qup->bus_err))
> + return IRQ_HANDLED;
> +
> /* Reset the QUP State in case of error */
> if (qup_err || bus_err) {
> - writel(QUP_RESET_STATE, qup->base + QUP_STATE);
> + /*
> + * Don’t reset the QUP state in case of BAM mode. The BAM
> + * flush operation needs to be scheduled in transfer function
> + * which will clear the remaining schedule descriptors in BAM
> + * HW FIFO and generates the BAM interrupt.
> + */
> + if (!qup->use_dma)
> + writel(QUP_RESET_STATE, qup->base + QUP_STATE);
> goto done;
> }
>
> @@ -841,20 +856,12 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> goto desc_err;
> }
>
> - if (rx_buf)
> - writel(QUP_BAM_INPUT_EOT,
> - qup->base + QUP_OUT_FIFO_BASE);
> -
> - writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
> -
> qup_i2c_flush(qup);
>
> /* wait for remaining interrupts to occur */
> if (!wait_for_completion_timeout(&qup->xfer, HZ))
> dev_err(qup->dev, "flush timed out\n");
>
> - qup_i2c_rel_dma(qup);
> -
> ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
> }
>
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 22:02:19

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode

Tested on Centriq 2400

Reviewed-by: Austin Christ <[email protected]>

On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> Currently each message length in complete transfer is being
> checked for determining DMA mode and if any of the message length
> is less than FIFO length then non DMA mode is being used which
> will increase overhead. DMA can be used for any length and it
> should be determined with complete transfer length. Now, this
> patch introduces DMA threshold length and the transfer will be
> done in DMA mode if the total length is greater than this
> threshold length.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 15 +++++++++------
> 1 file changed, 9 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 6227a5c..a91fc70 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -192,6 +192,8 @@ struct qup_i2c_dev {
> bool is_dma;
> /* To check if the current transfer is using DMA */
> bool use_dma;
> + /* The threshold length above which DMA will be used */
> + unsigned int dma_threshold;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -1294,7 +1296,8 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> - int ret, len, idx = 0;
> + int ret, idx = 0;
> + unsigned int total_len = 0;
>
> qup->bus_err = 0;
> qup->qup_err = 0;
> @@ -1320,14 +1323,13 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> goto out;
> }
>
> - len = (msgs[idx].len > qup->out_fifo_sz) ||
> - (msgs[idx].len > qup->in_fifo_sz);
> -
> - if (is_vmalloc_addr(msgs[idx].buf) || !len)
> + if (is_vmalloc_addr(msgs[idx].buf))
> break;
> +
> + total_len += msgs[idx].len;
> }
>
> - if (idx == num)
> + if (idx == num && total_len > qup->dma_threshold)
> qup->use_dma = true;
> }
>
> @@ -1587,6 +1589,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
>
> size = QUP_INPUT_FIFO_SIZE(io_mode);
> qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
> + qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);
>
> fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
> hs_div = 3;
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 22:08:42

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers

Tested on Centriq 2400

Reviewed-by: Austin Christ <[email protected]>

On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> According to I2c specification, “If a master-receiver sends a
> repeated START condition, it sends a not-acknowledge (A) just
> before the repeated START condition”. QUP v2 supports sending
> of NACK without stop with QUP_TAG_V2_DATARD_NACK so added the
> same.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index ba717bb..edea3b9 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -113,6 +113,7 @@
> #define QUP_TAG_V2_DATAWR 0x82
> #define QUP_TAG_V2_DATAWR_STOP 0x83
> #define QUP_TAG_V2_DATARD 0x85
> +#define QUP_TAG_V2_DATARD_NACK 0x86
> #define QUP_TAG_V2_DATARD_STOP 0x87
>
> /* Status, Error flags */
> @@ -609,7 +610,9 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> tags[len++] = QUP_TAG_V2_DATAWR_STOP;
> } else {
> if (msg->flags & I2C_M_RD)
> - tags[len++] = QUP_TAG_V2_DATARD;
> + tags[len++] = qup->blk.pos == (qup->blk.count - 1) ?
> + QUP_TAG_V2_DATARD_NACK :
> + QUP_TAG_V2_DATARD;
> else
> tags[len++] = QUP_TAG_V2_DATAWR;
> }
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 22:09:27

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

Hey Abhishek,


On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> The BAM mode requires buffer for start tag data and tx, rx SG
> list. Currently, this is being taken for maximum transfer length
> (65K). But an I2C transfer can have multiple messages and each
> message can be of this maximum length so the buffer overflow will
> happen in this case. Since increasing buffer length won’t be
> feasible since an I2C transfer can contain any number of messages
> so this patch does following changes to make i2c transfers working
> for multiple messages case.
>
> 1. Calculate the required buffers for 2 maximum length messages
> (65K * 2).
> 2. Split the descriptor formation and descriptor scheduling.
> The idea is to fit as many messages in one DMA transfers for 65K
> threshold value (max_xfer_sg_len). Whenever the sg_cnt is
> crossing this, then schedule the BAM transfer and subsequent
> transfer will again start from zero.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 199 +++++++++++++++++++++++++------------------
> 1 file changed, 118 insertions(+), 81 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index 6df65ea..ba717bb 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -155,6 +155,7 @@ struct qup_i2c_bam {
> struct qup_i2c_tag tag;
> struct dma_chan *dma;
> struct scatterlist *sg;
> + unsigned int sg_cnt;
> };
>
> struct qup_i2c_dev {
> @@ -195,6 +196,8 @@ struct qup_i2c_dev {
> bool use_dma;
> /* The threshold length above which DMA will be used */
> unsigned int dma_threshold;
> + unsigned int max_xfer_sg_len;
> + unsigned int tag_buf_pos;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -699,86 +702,86 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
> return 0;
> }
>
> -static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> - int num)
> +static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> +{
> + int ret = 0, limit = QUP_READ_LIMIT;
> + u32 len = 0, blocks, rem;
> + u32 i = 0, tlen, tx_len = 0;
> + u8 *tags;
> +
> + qup_i2c_set_blk_data(qup, msg);
> +
> + blocks = qup->blk.count;
> + rem = msg->len - (blocks - 1) * limit;
> +
> + if (msg->flags & I2C_M_RD) {
> + while (qup->blk.pos < blocks) {
> + tlen = (i == (blocks - 1)) ? rem : limit;
> + tags = &qup->start_tag.start[qup->tag_buf_pos + len];
> + len += qup_i2c_set_tags(tags, qup, msg);
> + qup->blk.data_len -= tlen;
> +
> + /* scratch buf to read the start and len tags */
> + ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
> + &qup->brx.tag.start[0],
> + 2, qup, DMA_FROM_DEVICE);
> +
> + if (ret)
> + return ret;
> +
> + ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
> + &msg->buf[limit * i],
> + tlen, qup,
> + DMA_FROM_DEVICE);
> + if (ret)
> + return ret;
> +
> + i++;
> + qup->blk.pos = i;
> + }
> + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
> + &qup->start_tag.start[qup->tag_buf_pos],
> + len, qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> +
> + qup->tag_buf_pos += len;
> + } else {
> + while (qup->blk.pos < blocks) {
> + tlen = (i == (blocks - 1)) ? rem : limit;
> + tags = &qup->start_tag.start[qup->tag_buf_pos + tx_len];
> + len = qup_i2c_set_tags(tags, qup, msg);
> + qup->blk.data_len -= tlen;
> +
> + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
> + tags, len,
> + qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> +
> + tx_len += len;
> + ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
> + &msg->buf[limit * i],
> + tlen, qup, DMA_TO_DEVICE);
> + if (ret)
> + return ret;
> + i++;
> + qup->blk.pos = i;
> + }
> +
> + qup->tag_buf_pos += tx_len;
> + }
> +
> + return 0;
> +}
> +
> +static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
> {
> struct dma_async_tx_descriptor *txd, *rxd = NULL;
> - int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
> + int ret = 0;
> dma_cookie_t cookie_rx, cookie_tx;
> - u32 len, blocks, rem;
> - u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
> - u8 *tags;
> -
> - while (idx < num) {
> - tx_len = 0, len = 0, i = 0;
> -
> - qup->is_last = (idx == (num - 1));
> -
> - qup_i2c_set_blk_data(qup, msg);
> -
> - blocks = qup->blk.count;
> - rem = msg->len - (blocks - 1) * limit;
> -
> - if (msg->flags & I2C_M_RD) {
> - while (qup->blk.pos < blocks) {
> - tlen = (i == (blocks - 1)) ? rem : limit;
> - tags = &qup->start_tag.start[off + len];
> - len += qup_i2c_set_tags(tags, qup, msg);
> - qup->blk.data_len -= tlen;
> -
> - /* scratch buf to read the start and len tags */
> - ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
> - &qup->brx.tag.start[0],
> - 2, qup, DMA_FROM_DEVICE);
> -
> - if (ret)
> - return ret;
> -
> - ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
> - &msg->buf[limit * i],
> - tlen, qup,
> - DMA_FROM_DEVICE);
> - if (ret)
> - return ret;
> -
> - i++;
> - qup->blk.pos = i;
> - }
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - &qup->start_tag.start[off],
> - len, qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> -
> - off += len;
> - } else {
> - while (qup->blk.pos < blocks) {
> - tlen = (i == (blocks - 1)) ? rem : limit;
> - tags = &qup->start_tag.start[off + tx_len];
> - len = qup_i2c_set_tags(tags, qup, msg);
> - qup->blk.data_len -= tlen;
> -
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - tags, len,
> - qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> -
> - tx_len += len;
> - ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
> - &msg->buf[limit * i],
> - tlen, qup, DMA_TO_DEVICE);
> - if (ret)
> - return ret;
> - i++;
> - qup->blk.pos = i;
> - }
> - off += tx_len;
> -
> - }
> - idx++;
> - msg++;
> - }
> + u32 len = 0;
> + u32 tx_buf = qup->btx.sg_cnt, rx_buf = qup->brx.sg_cnt;
>
> /* schedule the EOT and FLUSH I2C tags */
> len = 1;
> @@ -878,11 +881,19 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> return ret;
> }
>
> +static void qup_i2c_bam_clear_tag_buffers(struct qup_i2c_dev *qup)
> +{
> + qup->btx.sg_cnt = 0;
> + qup->brx.sg_cnt = 0;
> + qup->tag_buf_pos = 0;
> +}
> +
> static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> int ret = 0;
> + int idx = 0;
>
> enable_irq(qup->irq);
> ret = qup_i2c_req_dma(qup);
> @@ -905,9 +916,34 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
> goto out;
>
> writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> + qup_i2c_bam_clear_tag_buffers(qup);
> +
> + for (idx = 0; idx < num; idx++) {
> + qup->msg = msg + idx;
> + qup->is_last = idx == (num - 1);
> +
> + ret = qup_i2c_bam_make_desc(qup, qup->msg);
> + if (ret)
> + break;
> +
> + /*
> + * Make DMA descriptor and schedule the BAM transfer if its
> + * already crossed the maximum length. Since the memory for all
> + * tags buffers have been taken for 2 maximum possible
> + * transfers length so it will never cross the buffer actual
> + * length.
> + */
> + if (qup->btx.sg_cnt > qup->max_xfer_sg_len ||
> + qup->brx.sg_cnt > qup->max_xfer_sg_len ||
> + qup->is_last) {
> + ret = qup_i2c_bam_schedule_desc(qup);
> + if (ret)
> + break;
> +
> + qup_i2c_bam_clear_tag_buffers(qup);
> + }
> + }
>
> - qup->msg = msg;
> - ret = qup_i2c_bam_do_xfer(qup, qup->msg, num);
> out:
> disable_irq(qup->irq);
>
> @@ -1459,7 +1495,8 @@ static int qup_i2c_probe(struct platform_device *pdev)
> else if (ret != 0)
> goto nodma;
>
> - blocks = (MX_BLOCKS << 1) + 1;
> + qup->max_xfer_sg_len = (MX_BLOCKS << 1);
> + blocks = 2 * qup->max_xfer_sg_len + 1;
> qup->btx.sg = devm_kzalloc(&pdev->dev,
> sizeof(*qup->btx.sg) * blocks,
> GFP_KERNEL);
> @@ -1603,7 +1640,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
> one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
> qup->one_byte_t = one_bit_t * 9;
> qup->xfer_timeout = TOUT_MIN * HZ +
> - usecs_to_jiffies(MX_TX_RX_LEN * qup->one_byte_t);
> + usecs_to_jiffies(2 * MX_TX_RX_LEN * qup->one_byte_t);

Maybe it would make sense to add a comment here explaining why the magic
number 2 has been added.
>
> dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
> qup->in_blk_sz, qup->in_fifo_sz,
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-02-27 22:26:34

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion

On Sat, Feb 03, 2018 at 01:28:06PM +0530, Abhishek Sahu wrote:
> The QUP BSLP BAM generates the following error sometimes if the
> current I2C DMA transfer fails and the flush operation has been
> scheduled
>
> “bam-dma-engine 7884000.dma: Cannot free busy channel”
>
> If any I2C error comes during BAM DMA transfer, then the QUP I2C
> interrupt will be generated and the flush operation will be
> carried out to make i2c consume all scheduled DMA transfer.
> Currently, the same completion structure is being used for BAM
> transfer which has already completed without reinit. It will make
> flush operation wait_for_completion_timeout completed immediately
> and will proceed for freeing the DMA resources where the
> descriptors are still in process.
>
> Signed-off-by: Abhishek Sahu <[email protected]>

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 22:27:28

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 02/12] i2c: qup: minor code reorganization for use_dma

On Sat, Feb 03, 2018 at 01:28:07PM +0530, Abhishek Sahu wrote:
> 1. Assigns use_dma in qup_dev structure itself which will
> help in subsequent patches to determine the mode in IRQ handler.
> 2. Does minor code reorganization for loops to reduce the
> unnecessary comparison and assignment.
>
> Signed-off-by: Abhishek Sahu <[email protected]>

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 22:31:35

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count

On Sat, Feb 03, 2018 at 01:28:08PM +0530, Abhishek Sahu wrote:
> The rx_nents and tx_nents are redundant. rx_buf and tx_buf can
> be used for total number of SG entries.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---

Naming conventions aside,

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 22:38:35

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer

On Sat, Feb 03, 2018 at 01:28:09PM +0530, Abhishek Sahu wrote:
> A single BAM transfer can have multiple read and write messages.
> The EOT and FLUSH tags should be scheduled at the end of BAM HW
> descriptors. Since the READ and WRITE can be present in any order
> so for some of the cases, these tags are not being written
> correctly.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 54 ++++++++++++++++++++------------------------
> 1 file changed, 24 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index bb83a2967..6357aff 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -560,7 +560,7 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
> }
>
> static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> - struct i2c_msg *msg, int is_dma)
> + struct i2c_msg *msg)
> {
> u16 addr = i2c_8bit_addr_from_msg(msg);
> int len = 0;
> @@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> else
> tags[len++] = data_len;
>
> - if ((msg->flags & I2C_M_RD) && last && is_dma) {
> - tags[len++] = QUP_BAM_INPUT_EOT;
> - tags[len++] = QUP_BAM_FLUSH_STOP;
> - }
> -

So lets say you have multiple read and 1 write message. These changes will send
a EOT/FLUSH for all reads. I think the intent here was that the last read
message (not the last message) would have the EOT+FLUSH. Can there be an issue
with sending EOT/FLUSH for all reads? And how does this mesh up with the BAM
signaling?


Regards,

Andy

2018-02-27 22:39:22

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 05/12] i2c: qup: fix the transfer length for BAM rx EOT FLUSH tags

On Sat, Feb 03, 2018 at 01:28:10PM +0530, Abhishek Sahu wrote:
> In case of FLUSH operation, BAM copies INPUT EOT FLUSH (0x94)
> instead of normal EOT (0x93) tag in input data stream when an
> input EOT tag is received during flush operation. So only one tag
> will be written instead of 2 separate tags.
>
> Signed-off-by: Abhishek Sahu <[email protected]>

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 23:00:04

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode

On Sat, Feb 03, 2018 at 01:28:11PM +0530, Abhishek Sahu wrote:

<snip>

> @@ -841,20 +856,12 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
> goto desc_err;
> }
>
> - if (rx_buf)
> - writel(QUP_BAM_INPUT_EOT,
> - qup->base + QUP_OUT_FIFO_BASE);
> -
> - writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
> -
> qup_i2c_flush(qup);
>
> /* wait for remaining interrupts to occur */
> if (!wait_for_completion_timeout(&qup->xfer, HZ))
> dev_err(qup->dev, "flush timed out\n");
>
> - qup_i2c_rel_dma(qup);
> -

So this really only works due to the previous patch that adds the flush/eot tags
to all of the read messages. If the answer to the previous question is that
only the last read message gets the eot/flush, then this code needs to remain in
place. Otherwise, it's fine.


Andy

2018-02-27 23:01:31

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode

On Sat, Feb 03, 2018 at 01:28:12PM +0530, Abhishek Sahu wrote:
> Currently each message length in complete transfer is being
> checked for determining DMA mode and if any of the message length
> is less than FIFO length then non DMA mode is being used which
> will increase overhead. DMA can be used for any length and it
> should be determined with complete transfer length. Now, this
> patch introduces DMA threshold length and the transfer will be
> done in DMA mode if the total length is greater than this
> threshold length.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---

I agree with Sricharan's nit.

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 23:06:44

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length

On Mon, Feb 19, 2018 at 04:26:18PM +0530, Abhishek Sahu wrote:
> On 2018-02-16 10:18, Sricharan R wrote:
> >On 2/3/2018 1:28 PM, Abhishek Sahu wrote:
> >>Currently the completion timeout is being taken according to
> >>maximum transfer length which is too high if SCL is operating in
> >>high frequency. This patch calculates timeout on the basis of
> >>one-byte transfer time and uses the same for completion timeout.
> >>
> >>Signed-off-by: Abhishek Sahu <[email protected]>
> >>---
> >> drivers/i2c/busses/i2c-qup.c | 9 ++++++---
> >> 1 file changed, 6 insertions(+), 3 deletions(-)
> >>
> >>diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> >>index a91fc70..6df65ea 100644
> >>--- a/drivers/i2c/busses/i2c-qup.c
> >>+++ b/drivers/i2c/busses/i2c-qup.c
> >>@@ -130,8 +130,8 @@
> >> #define MX_TX_RX_LEN SZ_64K
> >> #define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)
> >>
> >>-/* Max timeout in ms for 32k bytes */
> >>-#define TOUT_MAX 300
> >>+/* Min timeout for i2c transfers */
> >>+#define TOUT_MIN 2
> >>
> >
> > may be you can mention, why is this 2 ?
> >
>
> This 2 seconds is timeout which I am adding on the top of maximum
> xfer time calculated from bus speed to compensate the interrupt
> latency and other factors. It will make xfer timeout minimum as
> 2 seconds.
>
> I will update the comment to explain it in more detail.

Once you do that add:

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 23:17:38

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

On Sat, Feb 03, 2018 at 01:28:14PM +0530, Abhishek Sahu wrote:
> The BAM mode requires buffer for start tag data and tx, rx SG
> list. Currently, this is being taken for maximum transfer length
> (65K). But an I2C transfer can have multiple messages and each
> message can be of this maximum length so the buffer overflow will
> happen in this case. Since increasing buffer length won’t be
> feasible since an I2C transfer can contain any number of messages
> so this patch does following changes to make i2c transfers working
> for multiple messages case.
>
> 1. Calculate the required buffers for 2 maximum length messages
> (65K * 2).
> 2. Split the descriptor formation and descriptor scheduling.
> The idea is to fit as many messages in one DMA transfers for 65K
> threshold value (max_xfer_sg_len). Whenever the sg_cnt is
> crossing this, then schedule the BAM transfer and subsequent
> transfer will again start from zero.
>
> Signed-off-by: Abhishek Sahu <[email protected]>

I'm ok with this patch. I find the idea of a > 64k size message to be something
that usually wouldn't be encountered, but... with some eeproms and maybe TPMs
perhaps this could happen?

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 23:20:07

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers

On Sat, Feb 03, 2018 at 01:28:15PM +0530, Abhishek Sahu wrote:
> According to I2c specification, “If a master-receiver sends a
> repeated START condition, it sends a not-acknowledge (A) just
> before the repeated START condition”. QUP v2 supports sending
> of NACK without stop with QUP_TAG_V2_DATARD_NACK so added the
> same.
>
> Signed-off-by: Abhishek Sahu <[email protected]>

Reviewed-by: Andy Gross <[email protected]>

2018-02-27 23:26:09

by Christ, Austin

[permalink] [raw]
Subject: Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2



On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
> Following are the major issues in current driver code
>
> 1. The current driver simply assumes the transfer completion
> whenever its gets any non-error interrupts and then simply do the
> polling of available/free bytes in FIFO.
> 2. The block mode is not working properly since no handling in
> being done for OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_READ.
> 3. An i2c transfer can contain multiple message and QUP v2
> supports reconfiguration during run in which the mode should be same
> for all the sub transfer. Currently the mode is being programmed
> before every sub transfer which is functionally wrong. If one message
> is less than FIFO length and other message is greater than FIFO
> length, then transfers will fail.
>
> Because of above, i2c v2 transfers of size greater than 64 are failing
> with following error message
>
> i2c_qup 78b6000.i2c: timeout for fifo out full
>
> To make block mode working properly and move to use the interrupts
> instead of polling, major code reorganization is required. Following
> are the major changes done in this patch
>
> 1. Remove the polling of TX FIFO free space and RX FIFO available
> bytes and move to interrupts completely. QUP has QUP_MX_OUTPUT_DONE,
> QUP_MX_INPUT_DONE, OUT_BLOCK_WRITE_REQ and IN_BLOCK_READ_REQ
> interrupts to handle FIFO’s properly so check all these interrupts.
> 2. Determine the mode for transfer before starting by checking
> all the tx/rx data length in each message. The complete message can be
> transferred either in DMA mode or Programmed IO by FIFO/Block mode.
> in DMA mode, both tx and rx uses same mode but in PIO mode, the TX and
> RX can be in different mode.
> 3. During write, For FIFO mode, TX FIFO can be directly written
> without checking for FIFO space. For block mode, the QUP will generate
> OUT_BLOCK_WRITE_REQ interrupt whenever it has block size of available
> space.
> 4. During read, both TX and RX FIFO will be used. TX will be used
> for writing tags and RX will be used for receiving the data. In QUP,
> TX and RX can operate in separate mode so configure modes accordingly.
> 5. For read FIFO mode, wait for QUP_MX_INPUT_DONE interrupt which
> will be generated after all the bytes have been copied in RX FIFO. For
> read Block mode, QUP will generate IN_BLOCK_READ_REQ interrupts
> whenever it has block size of available data.
> 6. Split the transfer in chunk of one QUP block size(256 bytes)
> and schedule each block separately. QUP v2 supports reconfiguration
> during run in which QUP can transfer multiple blocks without issuing a
> stop events.
> 7. Port the SMBus block read support for new code changes.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
> drivers/i2c/busses/i2c-qup.c | 917 +++++++++++++++++++++++++------------------
> 1 file changed, 533 insertions(+), 384 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
> index af6c21a..46736a1 100644
> --- a/drivers/i2c/busses/i2c-qup.c
> +++ b/drivers/i2c/busses/i2c-qup.c
> @@ -141,6 +141,15 @@
> #define DEFAULT_CLK_FREQ 100000
> #define DEFAULT_SRC_CLK 20000000
>
> +/*
> + * Max tags length (start, stop and maximum 2 bytes address) for each QUP
> + * data transfer
> + */
> +#define QUP_MAX_TAGS_LEN 4
> +/* Max data length for each DATARD tags */
> +#define RECV_MAX_DATA_LEN 254
> +/* TAG length for DATA READ in RX FIFO */
> +#define READ_RX_TAGS_LEN 2
> /* MAX_OUTPUT_DONE_FLAG has been received */
> #define QUP_BLK_EVENT_TX_IRQ_DONE BIT(0)
> /* MAX_INPUT_DONE_FLAG has been received */
> @@ -164,11 +173,24 @@
> * tx_tag_len: tx tag length for current block
> * rx_tag_len: rx tag length for current block
> * data_len: remaining data length for current message
> + * cur_blk_len: data length for current block
> * total_tx_len: total tx length including tag bytes for current QUP transfer
> * total_rx_len: total rx length including tag bytes for current QUP transfer
> + * tx_fifo_data_pos: current byte number in TX FIFO word
> * tx_fifo_free: number of free bytes in current QUP block write.
> + * rx_fifo_data_pos: current byte number in RX FIFO word
> * fifo_available: number of available bytes in RX FIFO for current
> * QUP block read
> + * tx_fifo_data: QUP TX FIFO write works on word basis (4 bytes). New byte write
> + * to TX FIFO will be appended in this data and will be written to
> + * TX FIFO when all the 4 bytes are available.
> + * rx_fifo_data: QUP RX FIFO read works on word basis (4 bytes). This will
> + * contains the 4 bytes of RX data.
> + * cur_data: pointer to tell cur data position for current message
> + * cur_tx_tags: pointer to tell cur position in tags
> + * tx_tags_sent: all tx tag bytes have been written in FIFO word
> + * send_last_word: for tx FIFO, last word send is pending in current block
> + * rx_tags_fetched: all the rx tag bytes have been fetched from rx fifo word
> * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
> * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
> * tags: contains tx tag bytes for current QUP transfer
> @@ -179,10 +201,20 @@ struct qup_i2c_block {
> int tx_tag_len;
> int rx_tag_len;
> int data_len;
> + int cur_blk_len;
> int total_tx_len;
> int total_rx_len;
> + int tx_fifo_data_pos;
> int tx_fifo_free;
> + int rx_fifo_data_pos;
> int fifo_available;
> + u32 tx_fifo_data;
> + u32 rx_fifo_data;
> + u8 *cur_data;
> + u8 *cur_tx_tags;
> + bool tx_tags_sent;
> + bool send_last_word;
> + bool rx_tags_fetched;
> bool is_tx_blk_mode;
> bool is_rx_blk_mode;
> u8 tags[6];
> @@ -214,6 +246,7 @@ struct qup_i2c_dev {
> int out_blk_sz;
> int in_blk_sz;
>
> + int blk_xfer_limit;
> unsigned long one_byte_t;
> unsigned long xfer_timeout;
> struct qup_i2c_block blk;
> @@ -228,10 +261,10 @@ struct qup_i2c_dev {
>
> /* To check if this is the last msg */
> bool is_last;
> - bool is_qup_v1;
> + bool is_smbus_read;
>
> /* To configure when bus is in run state */
> - int config_run;
> + u32 config_run;
>
> /* dma parameters */
> bool is_dma;
> @@ -245,6 +278,8 @@ struct qup_i2c_dev {
> unsigned int dma_threshold;
> unsigned int max_xfer_sg_len;
> unsigned int tag_buf_pos;
> + /* The threshold length above which block mode will be used */
> + unsigned int blk_mode_threshold;
> struct dma_pool *dpool;
> struct qup_i2c_tag start_tag;
> struct qup_i2c_bam brx;
> @@ -309,9 +344,6 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
> goto done;
> }
>
> - if (!qup->is_qup_v1)
> - goto done;
> -
> if (opflags & QUP_OUT_SVC_FLAG) {
> writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
>
> @@ -444,108 +476,6 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
> return ret;
> }
>
> -/**
> - * qup_i2c_wait_ready - wait for a give number of bytes in tx/rx path
> - * @qup: The qup_i2c_dev device
> - * @op: The bit/event to wait on
> - * @val: value of the bit to wait on, 0 or 1
> - * @len: The length the bytes to be transferred
> - */
> -static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val,
> - int len)
> -{
> - unsigned long timeout;
> - u32 opflags;
> - u32 status;
> - u32 shift = __ffs(op);
> - int ret = 0;
> -
> - len *= qup->one_byte_t;
> - /* timeout after a wait of twice the max time */
> - timeout = jiffies + len * 4;
> -
> - for (;;) {
> - opflags = readl(qup->base + QUP_OPERATIONAL);
> - status = readl(qup->base + QUP_I2C_STATUS);
> -
> - if (((opflags & op) >> shift) == val) {
> - if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) {
> - if (!(status & I2C_STATUS_BUS_ACTIVE)) {
> - ret = 0;
> - goto done;
> - }
> - } else {
> - ret = 0;
> - goto done;
> - }
> - }
> -
> - if (time_after(jiffies, timeout)) {
> - ret = -ETIMEDOUT;
> - goto done;
> - }
> - usleep_range(len, len * 2);
> - }
> -
> -done:
> - if (qup->bus_err || qup->qup_err)
> - ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
> -
> - return ret;
> -}
> -
> -static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
> - struct i2c_msg *msg)
> -{
> - /* Number of entries to shift out, including the tags */
> - int total = msg->len + qup->blk.tx_tag_len;
> -
> - total |= qup->config_run;
> -
> - if (total < qup->out_fifo_sz) {
> - /* FIFO mode */
> - writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
> - writel(total, qup->base + QUP_MX_WRITE_CNT);
> - } else {
> - /* BLOCK mode (transfer data on chunks) */
> - writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
> - qup->base + QUP_IO_MODE);
> - writel(total, qup->base + QUP_MX_OUTPUT_CNT);
> - }
> -}
> -
> -static int check_for_fifo_space(struct qup_i2c_dev *qup)
> -{
> - int ret;
> -
> - ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> - if (ret)
> - goto out;
> -
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_FULL,
> - RESET_BIT, 4 * ONE_BYTE);
> - if (ret) {
> - /* Fifo is full. Drain out the fifo */
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto out;
> -
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY,
> - RESET_BIT, 256 * ONE_BYTE);
> - if (ret) {
> - dev_err(qup->dev, "timeout for fifo out full");
> - goto out;
> - }
> -
> - ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> - if (ret)
> - goto out;
> - }
> -
> -out:
> - return ret;
> -}
> -
> static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
> {
> struct qup_i2c_block *blk = &qup->blk;
> @@ -591,60 +521,17 @@ static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
> static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
> struct i2c_msg *msg)
> {
> - memset(&qup->blk, 0, sizeof(qup->blk));
> -
> + qup->blk.pos = 0;
> qup->blk.data_len = msg->len;
> - qup->blk.count = (msg->len + QUP_READ_LIMIT - 1) / QUP_READ_LIMIT;
> -
> - /* 4 bytes for first block and 2 writes for rest */
> - qup->blk.tx_tag_len = 4 + (qup->blk.count - 1) * 2;
> -
> - /* There are 2 tag bytes that are read in to fifo for every block */
> - if (msg->flags & I2C_M_RD)
> - qup->blk.rx_tag_len = qup->blk.count * 2;
> -}
> -
> -static int qup_i2c_send_data(struct qup_i2c_dev *qup, int tlen, u8 *tbuf,
> - int dlen, u8 *dbuf)
> -{
> - u32 val = 0, idx = 0, pos = 0, i = 0, t;
> - int len = tlen + dlen;
> - u8 *buf = tbuf;
> - int ret = 0;
> -
> - while (len > 0) {
> - ret = check_for_fifo_space(qup);
> - if (ret)
> - return ret;
> -
> - t = (len >= 4) ? 4 : len;
> -
> - while (idx < t) {
> - if (!i && (pos >= tlen)) {
> - buf = dbuf;
> - pos = 0;
> - i = 1;
> - }
> - val |= buf[pos++] << (idx++ * 8);
> - }
> -
> - writel(val, qup->base + QUP_OUT_FIFO_BASE);
> - idx = 0;
> - val = 0;
> - len -= 4;
> - }
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> -
> - return ret;
> + qup->blk.count = DIV_ROUND_UP(msg->len, qup->blk_xfer_limit);
> }
>
> static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
> {
> int data_len;
>
> - if (qup->blk.data_len > QUP_READ_LIMIT)
> - data_len = QUP_READ_LIMIT;
> + if (qup->blk.data_len > qup->blk_xfer_limit)
> + data_len = qup->blk_xfer_limit;
> else
> data_len = qup->blk.data_len;
>
> @@ -661,9 +548,9 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
> {
> int len = 0;
>
> - if (msg->len > 1) {
> + if (qup->is_smbus_read) {
> tags[len++] = QUP_TAG_V2_DATARD_STOP;
> - tags[len++] = qup_i2c_get_data_len(qup) - 1;
> + tags[len++] = qup_i2c_get_data_len(qup);
> } else {
> tags[len++] = QUP_TAG_V2_START;
> tags[len++] = addr & 0xff;
> @@ -725,24 +612,6 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
> return len;
> }
>
> -static int qup_i2c_issue_xfer_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int data_len = 0, tag_len, index;
> - int ret;
> -
> - tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg);
> - index = msg->len - qup->blk.data_len;
> -
> - /* only tags are written for read */
> - if (!(msg->flags & I2C_M_RD))
> - data_len = qup_i2c_get_data_len(qup);
> -
> - ret = qup_i2c_send_data(qup, tag_len, qup->blk.tags,
> - data_len, &msg->buf[index]);
> - qup->blk.data_len -= data_len;
> -
> - return ret;
> -}
>
> static void qup_i2c_bam_cb(void *data)
> {
> @@ -809,6 +678,7 @@ static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> u32 i = 0, tlen, tx_len = 0;
> u8 *tags;
>
> + qup->blk_xfer_limit = QUP_READ_LIMIT;
> qup_i2c_set_blk_data(qup, msg);
>
> blocks = qup->blk.count;
> @@ -1057,7 +927,7 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
> unsigned long left;
> int ret = 0;
>
> - left = wait_for_completion_timeout(&qup->xfer, HZ);
> + left = wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout);
> if (!left) {
> writel(1, qup->base + QUP_SW_RESET);
> ret = -ETIMEDOUT;
> @@ -1069,65 +939,6 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
> return ret;
> }
>
> -static int qup_i2c_write_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int ret = 0;
> -
> - qup->msg = msg;
> - qup->pos = 0;
> - enable_irq(qup->irq);
> - qup_i2c_set_blk_data(qup, msg);
> - qup_i2c_set_write_mode_v2(qup, msg);
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto err;
> -
> - writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> -
> - do {
> - ret = qup_i2c_issue_xfer_v2(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_wait_for_complete(qup, msg);
> - if (ret)
> - goto err;
> -
> - qup->blk.pos++;
> - } while (qup->blk.pos < qup->blk.count);
> -
> - ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
> -
> -err:
> - disable_irq(qup->irq);
> - qup->msg = NULL;
> -
> - return ret;
> -}
> -
> -static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
> -{
> - int tx_len = qup->blk.tx_tag_len;
> -
> - len += qup->blk.rx_tag_len;
> - len |= qup->config_run;
> - tx_len |= qup->config_run;
> -
> - if (len < qup->in_fifo_sz) {
> - /* FIFO mode */
> - writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
> - writel(tx_len, qup->base + QUP_MX_WRITE_CNT);
> - writel(len, qup->base + QUP_MX_READ_CNT);
> - } else {
> - /* BLOCK mode (transfer data on chunks) */
> - writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
> - qup->base + QUP_IO_MODE);
> - writel(tx_len, qup->base + QUP_MX_OUTPUT_CNT);
> - writel(len, qup->base + QUP_MX_INPUT_CNT);
> - }
> -}
> -
> static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
> {
> struct qup_i2c_block *blk = &qup->blk;
> @@ -1151,104 +962,6 @@ static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
> qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
> }
>
> -static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
> - struct i2c_msg *msg)
> -{
> - u32 val;
> - int idx, pos = 0, ret = 0, total, msg_offset = 0;
> -
> - /*
> - * If the message length is already read in
> - * the first byte of the buffer, account for
> - * that by setting the offset
> - */
> - if (qup_i2c_check_msg_len(msg) && (msg->len > 1))
> - msg_offset = 1;
> - total = qup_i2c_get_data_len(qup);
> - total -= msg_offset;
> -
> - /* 2 extra bytes for read tags */
> - while (pos < (total + 2)) {
> - /* Check that FIFO have data */
> - ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
> - SET_BIT, 4 * ONE_BYTE);
> - if (ret) {
> - dev_err(qup->dev, "timeout for fifo not empty");
> - return ret;
> - }
> - val = readl(qup->base + QUP_IN_FIFO_BASE);
> -
> - for (idx = 0; idx < 4; idx++, val >>= 8, pos++) {
> - /* first 2 bytes are tag bytes */
> - if (pos < 2)
> - continue;
> -
> - if (pos >= (total + 2))
> - goto out;
> - msg->buf[qup->pos + msg_offset] = val & 0xff;
> - qup->pos++;
> - }
> - }
> -
> -out:
> - qup->blk.data_len -= total;
> -
> - return ret;
> -}
> -
> -static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
> -{
> - int ret = 0;
> -
> - qup->msg = msg;
> - qup->pos = 0;
> - enable_irq(qup->irq);
> - qup_i2c_set_blk_data(qup, msg);
> - qup_i2c_set_read_mode_v2(qup, msg->len);
> -
> - ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> - if (ret)
> - goto err;
> -
> - writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> -
> - do {
> - ret = qup_i2c_issue_xfer_v2(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_wait_for_complete(qup, msg);
> - if (ret)
> - goto err;
> -
> - ret = qup_i2c_read_fifo_v2(qup, msg);
> - if (ret)
> - goto err;
> -
> - qup->blk.pos++;
> -
> - /* Handle SMBus block read length */
> - if (qup_i2c_check_msg_len(msg) && (msg->len == 1)) {
> - if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) {
> - ret = -EPROTO;
> - goto err;
> - }
> - msg->len += msg->buf[0];
> - qup->pos = 0;
> - qup_i2c_set_blk_data(qup, msg);
> - /* set tag length for block read */
> - qup->blk.tx_tag_len = 2;
> - qup_i2c_set_read_mode_v2(qup, msg->buf[0]);
> - }
> - } while (qup->blk.pos < qup->blk.count);
> -
> -err:
> - disable_irq(qup->irq);
> - qup->msg = NULL;
> -
> - return ret;
> -}
> -
> static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool is_rx)
> {
> qup->cur_blk_events = 0;
> @@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
> return ret;
> }
>
> +/*
> + * Function to configure registers related with reconfiguration during run
> + * and will be done before each I2C sub transfer.
> + */
Consider changing comment style to remove the word "Function" and state
the operation in simple present tense.

"Function to configure ..." would be "Configure ..."

Same comment for all comments of this style below.

> +static void qup_i2c_conf_count_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
> +
> + if (blk->is_tx_blk_mode)
> + writel(qup->config_run | blk->total_tx_len,
> + qup->base + QUP_MX_OUTPUT_CNT);
> + else
> + writel(qup->config_run | blk->total_tx_len,
> + qup->base + QUP_MX_WRITE_CNT);
> +
> + if (blk->total_rx_len) {
> + if (blk->is_rx_blk_mode)
> + writel(qup->config_run | blk->total_rx_len,
> + qup->base + QUP_MX_INPUT_CNT);
> + else
> + writel(qup->config_run | blk->total_rx_len,
> + qup->base + QUP_MX_READ_CNT);
> + } else {
> + qup_config |= QUP_NO_INPUT;
> + }
> +
> + writel(qup_config, qup->base + QUP_CONFIG);
> +}
> +
> +/*
> + * Function to configure registers related with transfer mode (FIFO/Block)
> + * before starting of i2c transfer and will be done only once in QUP RESET
> + * state.
> + */
> +static void qup_i2c_conf_mode_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + u32 io_mode = QUP_REPACK_EN;
> +
> + if (blk->is_tx_blk_mode) {
> + io_mode |= QUP_OUTPUT_BLK_MODE;
> + writel(0, qup->base + QUP_MX_WRITE_CNT);
> + } else {
> + writel(0, qup->base + QUP_MX_OUTPUT_CNT);
> + }
> +
> + if (blk->is_rx_blk_mode) {
> + io_mode |= QUP_INPUT_BLK_MODE;
> + writel(0, qup->base + QUP_MX_READ_CNT);
> + } else {
> + writel(0, qup->base + QUP_MX_INPUT_CNT);
> + }
> +
> + writel(io_mode, qup->base + QUP_IO_MODE);
> +}
> +
> +/*
> + * Function to clear required variables before starting of any QUP v2
> + * sub transfer
> + */
> +static void qup_i2c_clear_blk_v2(struct qup_i2c_block *blk)
> +{
> + blk->send_last_word = false;
> + blk->tx_tags_sent = false;
> + blk->tx_fifo_data = 0;
> + blk->tx_fifo_data_pos = 0;
> + blk->tx_fifo_free = 0;
> +
> + blk->rx_tags_fetched = false;
> + blk->rx_fifo_data = 0;
> + blk->rx_fifo_data_pos = 0;
> + blk->fifo_available = 0;
> +}
> +
> +/*
> + * Function to receive data from RX FIFO for read message in QUP v2
> + * i2c transfer.
> + */
> +static void qup_i2c_recv_data(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + int j;
> +
> + for (j = blk->rx_fifo_data_pos;
> + blk->cur_blk_len && blk->fifo_available;
> + blk->cur_blk_len--, blk->fifo_available--) {
> + if (j == 0)
> + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
> +
> + *(blk->cur_data++) = blk->rx_fifo_data;
> + blk->rx_fifo_data >>= 8;
> +
> + if (j == 3)
> + j = 0;
> + else
> + j++;
> + }
> +
> + blk->rx_fifo_data_pos = j;
> +}
> +
> +/* Function to receive tags for read message in QUP v2 i2c transfer. */
> +static void qup_i2c_recv_tags(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
> + blk->rx_fifo_data >>= blk->rx_tag_len * 8;
> + blk->rx_fifo_data_pos = blk->rx_tag_len;
> + blk->fifo_available -= blk->rx_tag_len;
> +}
> +
> +/*
> + * This function reads the data and tags from RX FIFO. Since in read case, the
> + * tags will be preceded by received data bytes need to be written so
> + * 1. Check if rx_tags_fetched is false i.e. the start of QUP block so receive
> + * all tag bytes and discard that.
put i.e. statement into () to make the comment more readable
> + * 2. Read the data from RX FIFO. When all the data bytes have been read then
> + * mark the QUP_BLK_EVENT_RX_DATA_DONE in current block event and return.
> + */
> +static void qup_i2c_read_rx_fifo_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + if (!blk->rx_tags_fetched) {
> + qup_i2c_recv_tags(qup);
> + blk->rx_tags_fetched = true;
> + }
> +
> + qup_i2c_recv_data(qup);
> + if (!blk->cur_blk_len)
> + qup->cur_blk_events |= QUP_BLK_EVENT_RX_DATA_DONE;
> +}
> +
> +/*
> + * Function to write bytes in TX FIFO for write message in QUP v2 i2c
> + * transfer. QUP TX FIFO write works on word basis (4 bytes). New byte write to
> + * TX FIFO will be appended in this data tx_fifo_data and will be written to TX
> + * FIFO when all the 4 bytes are available.
> + */
> +static void
> +qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsigned int *len)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + unsigned int j;
> +
> + for (j = blk->tx_fifo_data_pos; *len && blk->tx_fifo_free;
> + (*len)--, blk->tx_fifo_free--) {
> + blk->tx_fifo_data |= *(*data)++ << (j * 8);
> + if (j == 3) {
> + writel(blk->tx_fifo_data,
> + qup->base + QUP_OUT_FIFO_BASE);
> + blk->tx_fifo_data = 0x0;
> + j = 0;
> + } else {
> + j++;
> + }
> + }
> +
> + blk->tx_fifo_data_pos = j;
> +}
> +
> +/* Function to transfer tags for read message in QUP v2 i2c transfer. */
> +static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, &blk->tx_tag_len);
> + if (blk->tx_fifo_data_pos)
> + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
> +
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
> +}
> +
> +/*
> + * This function writes the data and tags in TX FIFO. Since in write case, both
> + * tags and data need to be written and QUP write tags can have maximum 256 data
> + * length, so it follows simple internal state machine to manage it.
> + * 1. Check if tx_tags_sent is false i.e. the start of QUP block so write the
> + * tags to TX FIFO.
put i.e. the start of QUP block in () for clarity
> + * 2. Check if send_last_word is true. This will be set when last few data bytes
> + * less than 4 bytes are reamining to be written in FIFO because of no FIFO
> + * space. All this data bytes are available in tx_fifo_data so write this
> + * in FIFO and mark the tx done.
> + * 3. Write the data to TX FIFO and check for cur_blk_len. If this is non zero
> + * then more data is pending otherwise following 3 cases can be possible
> + * a. if tx_fifo_data_pos is zero that means all the data bytes in this block
> + * have been written in TX FIFO so mark the tx done.
> + * b. tx_fifo_free is zero. In this case, last few bytes (less than 4
> + * bytes) are copied to tx_fifo_data but couldn't be sent because of
> + * FIFO full so make send_last_word true.
> + * c. tx_fifo_free is non zero i.e tx FIFO is free so copy the remaining data
> + * from tx_fifo_data to tx FIFO and mark the tx done. Since,
> + * qup_i2c_write_blk_data do write in 4 bytes and FIFO space is in
> + * multiple of 4 bytes so tx_fifo_free will be always greater than or
> + * equal to 4 bytes.
> + */
> +static void qup_i2c_write_tx_fifo_v2(struct qup_i2c_dev *qup)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> +
> + if (!blk->tx_tags_sent) {
> + qup_i2c_write_blk_data(qup, &blk->cur_tx_tags,
> + &blk->tx_tag_len);
> + blk->tx_tags_sent = true;
> + }
> +
> + if (blk->send_last_word)
> + goto send_last_word;
> +
> + qup_i2c_write_blk_data(qup, &blk->cur_data, &blk->cur_blk_len);
> + if (!blk->cur_blk_len) {
> + if (!blk->tx_fifo_data_pos)
> + goto tx_data_done;
> +
> + if (blk->tx_fifo_free)
> + goto send_last_word;
> +
> + blk->send_last_word = true;
> + }
> +
> + return;
> +
> +send_last_word:
> + writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
> +tx_data_done:
> + qup->cur_blk_events |= QUP_BLK_EVENT_TX_DATA_DONE;
> +}
> +
> +/*
> + * Main transfer function which will be used for reading or writing i2c data.
> + * The QUP v2 supports reconfiguration during run in which multiple i2c sub
> + * transfers can be scheduled.
> + */
> +static int
> +qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool is_first,
> + bool change_pause_state)
> +{
> + struct qup_i2c_block *blk = &qup->blk;
> + struct i2c_msg *msg = qup->msg;
> + int ret;
> +
> + /*
> + * Check if its SMBus Block read for which the top level read will be
> + * done into 2 QUP reads. One with message length 1 while other one is
> + * with actual length.
> + */
> + if (qup_i2c_check_msg_len(msg)) {
> + if (qup->is_smbus_read) {
> + /*
> + * If the message length is already read in
> + * the first byte of the buffer, account for
> + * that by setting the offset
> + */
> + blk->cur_data += 1;
> + is_first = false;
> + } else {
> + change_pause_state = false;
> + }
> + }
> +
> + qup->config_run = is_first ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
> +
> + qup_i2c_clear_blk_v2(blk);
> + qup_i2c_conf_count_v2(qup);
> +
> + /* If it is first sub transfer, then configure i2c bus clocks */
> + if (is_first) {
> + ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> + if (ret)
> + return ret;
> +
> + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
> +
> + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> + if (ret)
> + return ret;
> + }
> +
> + qup_i2c_set_blk_event(qup, is_rx);
> + reinit_completion(&qup->xfer);
> + enable_irq(qup->irq);
> + /*
> + * In FIFO mode, tx FIFO can be written directly while in block mode the
> + * it will be written after getting OUT_BLOCK_WRITE_REQ interrupt
> + */
> + if (!blk->is_tx_blk_mode) {
> + blk->tx_fifo_free = qup->out_fifo_sz;
> +
> + if (is_rx)
> + qup_i2c_write_rx_tags_v2(qup);
> + else
> + qup_i2c_write_tx_fifo_v2(qup);
> + }
> +
> + ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
> + if (ret)
> + goto err;
> +
> + ret = qup_i2c_wait_for_complete(qup, msg);
> + if (ret)
> + goto err;
> +
> + /* Move to pause state for all the transfers, except last one */
> + if (change_pause_state) {
> + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
> + if (ret)
> + goto err;
> + }
> +
> +err:
> + disable_irq(qup->irq);
> + return ret;
> +}
> +
> +/*
> + * Function to transfer one read/write message in i2c transfer. It splits the
> + * message into multiple of blk_xfer_limit data length blocks and schedule each
> + * QUP block individually.
> + */
> +static int qup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id, bool is_rx)
> +{
> + int ret = 0;
> + unsigned int data_len, i;
> + struct i2c_msg *msg = qup->msg;
> + struct qup_i2c_block *blk = &qup->blk;
> + u8 *msg_buf = msg->buf;
> +
> + qup->blk_xfer_limit = is_rx ? RECV_MAX_DATA_LEN : QUP_READ_LIMIT;
> + qup_i2c_set_blk_data(qup, msg);
> +
> + for (i = 0; i < blk->count; i++) {
> + data_len = qup_i2c_get_data_len(qup);
> + blk->pos = i;
> + blk->cur_tx_tags = blk->tags;
> + blk->cur_blk_len = data_len;
> + blk->tx_tag_len =
> + qup_i2c_set_tags(blk->cur_tx_tags, qup, qup->msg);
> +
> + blk->cur_data = msg_buf;
> +
> + if (is_rx) {
> + blk->total_tx_len = blk->tx_tag_len;
> + blk->rx_tag_len = 2;
> + blk->total_rx_len = blk->rx_tag_len + data_len;
> + } else {
> + blk->total_tx_len = blk->tx_tag_len + data_len;
> + blk->total_rx_len = 0;
> + }
> +
> + ret = qup_i2c_conf_xfer_v2(qup, is_rx, !msg_id && !i,
> + !qup->is_last || i < blk->count - 1);
> + if (ret)
> + return ret;
> +
> + /* Handle SMBus block read length */
> + if (qup_i2c_check_msg_len(msg) && msg->len == 1 &&
> + !qup->is_smbus_read) {
> + if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX)
> + return -EPROTO;
> +
> + msg->len = msg->buf[0];
> + qup->is_smbus_read = true;
> + ret = qup_i2c_xfer_v2_msg(qup, msg_id, true);
> + qup->is_smbus_read = false;
> + if (ret)
> + return ret;
> +
> + msg->len += 1;
> + }
> +
> + msg_buf += data_len;
> + blk->data_len -= qup->blk_xfer_limit;
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * QUP v2 supports 3 modes
> + * Programmed IO using FIFO mode : Less than FIFO size
> + * Programmed IO using Block mode : Greater than FIFO size
> + * DMA using BAM : Appropriate for any transactio size but the address should be
> + * DMA applicable
> + *
> + * This function determines the mode which will be used for this transfer. An
> + * i2c transfer contains multiple message. Following are the rules to determine
> + * the mode used.
> + * 1. Determine the tx and rx length for each message and maximum tx and rx
> + * length for complete transfer
> + * 2. If tx or rx length is greater than DMA threshold than use the DMA mode.
> + * 3. In FIFO or block mode, TX and RX can operate in different mode so check
> + * for maximum tx and rx length to determine mode.
> + */
> +static int
> +qup_i2c_determine_mode(struct qup_i2c_dev *qup, struct i2c_msg msgs[], int num)
> +{
> + int idx;
> + bool no_dma = false;
> + unsigned int max_tx_len = 0, max_rx_len = 0;
> + unsigned int cur_tx_len, cur_rx_len;
> + unsigned int total_rx_len = 0, total_tx_len = 0;
> +
> + /* All i2c_msgs should be transferred using either dma or cpu */
> + for (idx = 0; idx < num; idx++) {
> + if (msgs[idx].len == 0)
> + return -EINVAL;
> +
> + if (msgs[idx].flags & I2C_M_RD) {
> + cur_tx_len = 0;
> + cur_rx_len = msgs[idx].len;
> + } else {
> + cur_tx_len = msgs[idx].len;
> + cur_rx_len = 0;
> + }
> +
> + if (is_vmalloc_addr(msgs[idx].buf))
> + no_dma = true;
> +
> + max_tx_len = max(max_tx_len, cur_tx_len);
> + max_rx_len = max(max_rx_len, cur_rx_len);
> + total_rx_len += cur_rx_len;
> + total_tx_len += cur_tx_len;
> + }
> +
> + if (!no_dma && qup->is_dma &&
> + (total_tx_len > qup->dma_threshold ||
> + total_rx_len > qup->dma_threshold)) {
> + qup->use_dma = true;
> + } else {
> + qup->blk.is_tx_blk_mode =
> + max_tx_len > qup->blk_mode_threshold ? true : false;
> + qup->blk.is_rx_blk_mode =
> + max_rx_len > qup->blk_mode_threshold ? true : false;
> + }
> +
> + return 0;
> +}
> +
> static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> struct i2c_msg msgs[],
> int num)
> {
> struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
> int ret, idx = 0;
> - unsigned int total_len = 0;
>
> qup->bus_err = 0;
> qup->qup_err = 0;
> @@ -1457,6 +1609,10 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> if (ret < 0)
> goto out;
>
> + ret = qup_i2c_determine_mode(qup, msgs, num);
> + if (ret)
> + goto out;
> +
> writel(1, qup->base + QUP_SW_RESET);
> ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
> if (ret)
> @@ -1466,59 +1622,35 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
> writel(I2C_MINI_CORE | I2C_N_VAL_V2, qup->base + QUP_CONFIG);
> writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
>
> - if ((qup->is_dma)) {
> - /* All i2c_msgs should be transferred using either dma or cpu */
> + if (qup_i2c_poll_state_i2c_master(qup)) {
> + ret = -EIO;
> + goto out;
> + }
> +
> + if (qup->use_dma) {
> + reinit_completion(&qup->xfer);
> + ret = qup_i2c_bam_xfer(adap, &msgs[0], num);
> + qup->use_dma = false;
> + } else {
> + qup_i2c_conf_mode_v2(qup);
> +
> for (idx = 0; idx < num; idx++) {
> - if (msgs[idx].len == 0) {
> - ret = -EINVAL;
> - goto out;
> - }
> + qup->msg = &msgs[idx];
> + qup->is_last = idx == (num - 1);
>
> - if (is_vmalloc_addr(msgs[idx].buf))
> + ret = qup_i2c_xfer_v2_msg(qup, idx,
> + !!(msgs[idx].flags & I2C_M_RD));
> + if (ret)
> break;
> -
> - total_len += msgs[idx].len;
> }
> -
> - if (idx == num && total_len > qup->dma_threshold)
> - qup->use_dma = true;
> + qup->msg = NULL;
> }
>
> - idx = 0;
> -
> - do {
> - if (msgs[idx].len == 0) {
> - ret = -EINVAL;
> - goto out;
> - }
> -
> - if (qup_i2c_poll_state_i2c_master(qup)) {
> - ret = -EIO;
> - goto out;
> - }
> -
> - qup->is_last = (idx == (num - 1));
> - if (idx)
> - qup->config_run = QUP_I2C_MX_CONFIG_DURING_RUN;
> - else
> - qup->config_run = 0;
> -
> - reinit_completion(&qup->xfer);
> -
> - if (qup->use_dma) {
> - ret = qup_i2c_bam_xfer(adap, &msgs[idx], num);
> - qup->use_dma = false;
> - break;
> - } else {
> - if (msgs[idx].flags & I2C_M_RD)
> - ret = qup_i2c_read_one_v2(qup, &msgs[idx]);
> - else
> - ret = qup_i2c_write_one_v2(qup, &msgs[idx]);
> - }
> - } while ((idx++ < (num - 1)) && !ret);
> + if (!ret)
> + ret = qup_i2c_bus_active(qup, ONE_BYTE);
>
> if (!ret)
> - ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
> + qup_i2c_change_state(qup, QUP_RESET_STATE);
>
> if (ret == 0)
> ret = num;
> @@ -1582,6 +1714,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
> u32 src_clk_freq = DEFAULT_SRC_CLK;
> u32 clk_freq = DEFAULT_CLK_FREQ;
> int blocks;
> + bool is_qup_v1;
>
> qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
> if (!qup)
> @@ -1600,12 +1733,10 @@ static int qup_i2c_probe(struct platform_device *pdev)
> if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
> qup->adap.algo = &qup_i2c_algo;
> qup->adap.quirks = &qup_i2c_quirks;
> - qup->is_qup_v1 = true;
> - qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
> - qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
> - qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
> + is_qup_v1 = true;
> } else {
> qup->adap.algo = &qup_i2c_algo_v2;
> + is_qup_v1 = false;
> ret = qup_i2c_req_dma(qup);
>
> if (ret == -EPROBE_DEFER)
> @@ -1731,14 +1862,31 @@ static int qup_i2c_probe(struct platform_device *pdev)
> ret = -EIO;
> goto fail;
> }
> - qup->out_blk_sz = blk_sizes[size] / 2;
> + qup->out_blk_sz = blk_sizes[size];
>
> size = QUP_INPUT_BLOCK_SIZE(io_mode);
> if (size >= ARRAY_SIZE(blk_sizes)) {
> ret = -EIO;
> goto fail;
> }
> - qup->in_blk_sz = blk_sizes[size] / 2;
> + qup->in_blk_sz = blk_sizes[size];
> +
> + if (is_qup_v1) {
> + /*
> + * in QUP v1, QUP_CONFIG uses N as 15 i.e 16 bits constitutes a
> + * single transfer but the block size is in bytes so divide the
> + * in_blk_sz and out_blk_sz by 2
> + */
> + qup->in_blk_sz /= 2;
> + qup->out_blk_sz /= 2;
> + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
> + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
> + qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
> + } else {
> + qup->write_tx_fifo = qup_i2c_write_tx_fifo_v2;
> + qup->read_rx_fifo = qup_i2c_read_rx_fifo_v2;
> + qup->write_rx_tags = qup_i2c_write_rx_tags_v2;
> + }
>
> size = QUP_OUTPUT_FIFO_SIZE(io_mode);
> qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
> @@ -1746,6 +1894,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
> size = QUP_INPUT_FIFO_SIZE(io_mode);
> qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
> qup->dma_threshold = min(qup->out_fifo_sz, qup->in_fifo_sz);
> + qup->blk_mode_threshold = qup->dma_threshold - QUP_MAX_TAGS_LEN;
>
> fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
> hs_div = 3;
>

--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

2018-03-08 13:42:34

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer

On 2018-02-28 04:06, Andy Gross wrote:
> On Sat, Feb 03, 2018 at 01:28:09PM +0530, Abhishek Sahu wrote:
>> A single BAM transfer can have multiple read and write messages.
>> The EOT and FLUSH tags should be scheduled at the end of BAM HW
>> descriptors. Since the READ and WRITE can be present in any order
>> so for some of the cases, these tags are not being written
>> correctly.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>> ---
>> drivers/i2c/busses/i2c-qup.c | 54
>> ++++++++++++++++++++------------------------
>> 1 file changed, 24 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-qup.c
>> b/drivers/i2c/busses/i2c-qup.c
>> index bb83a2967..6357aff 100644
>> --- a/drivers/i2c/busses/i2c-qup.c
>> +++ b/drivers/i2c/busses/i2c-qup.c
>> @@ -560,7 +560,7 @@ static int qup_i2c_set_tags_smb(u16 addr, u8
>> *tags, struct qup_i2c_dev *qup,
>> }
>>
>> static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
>> - struct i2c_msg *msg, int is_dma)
>> + struct i2c_msg *msg)
>> {
>> u16 addr = i2c_8bit_addr_from_msg(msg);
>> int len = 0;
>> @@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, struct
>> qup_i2c_dev *qup,
>> else
>> tags[len++] = data_len;
>>
>> - if ((msg->flags & I2C_M_RD) && last && is_dma) {
>> - tags[len++] = QUP_BAM_INPUT_EOT;
>> - tags[len++] = QUP_BAM_FLUSH_STOP;
>> - }
>> -
>
> So lets say you have multiple read and 1 write message. These changes
> will send
> a EOT/FLUSH for all reads. I think the intent here was that the last
> read
> message (not the last message) would have the EOT+FLUSH. Can there be
> an issue
> with sending EOT/FLUSH for all reads? And how does this mesh up with
> the BAM
> signaling?
>

Thanks Andy and Austin for reviewing these patches.

The role of FLUSH and EOT tag is to flush already scheduled descriptors
in HW. EOT is required only when descriptors are scheduled in RX FIFO.
If all the messages are WRITE, then only FLUSH tag will be used.

Let’s take following example

READ, READ, READ, READ

Currently EOT and FLUSH tags are being written after each READ. If we
get the NACK for first READ itself, then flush will be triggered. It
will look for first FLUSH tag in TX FIFO and will stop there so only
descriptors for first READ will be flushed. We need to clear all
scheduled descriptors to generate the completion.

Now this patch is scheduling FLUSH and EOT only once after all the
descriptors. So, flush will clear all the scheduled descriptors and
BAM will generate the completion interrupt. For multiple READ and
single WRITE also, this will work fine.

I tested with
- single xfer with multiple read messages
- single xfer with multiple write messages
- single xfer with multiple alternate read and write messages

for non-connected address in forceful DMA mode which will generate
the NACK for first byte itself.

Thanks,
Abhishek



2018-03-12 12:29:23

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

On 2018-02-28 04:45, Andy Gross wrote:
> On Sat, Feb 03, 2018 at 01:28:14PM +0530, Abhishek Sahu wrote:
>> The BAM mode requires buffer for start tag data and tx, rx SG
>> list. Currently, this is being taken for maximum transfer length
>> (65K). But an I2C transfer can have multiple messages and each
>> message can be of this maximum length so the buffer overflow will
>> happen in this case. Since increasing buffer length won’t be
>> feasible since an I2C transfer can contain any number of messages
>> so this patch does following changes to make i2c transfers working
>> for multiple messages case.
>>
>> 1. Calculate the required buffers for 2 maximum length messages
>> (65K * 2).
>> 2. Split the descriptor formation and descriptor scheduling.
>> The idea is to fit as many messages in one DMA transfers for 65K
>> threshold value (max_xfer_sg_len). Whenever the sg_cnt is
>> crossing this, then schedule the BAM transfer and subsequent
>> transfer will again start from zero.
>>
>> Signed-off-by: Abhishek Sahu <[email protected]>
>
> I'm ok with this patch. I find the idea of a > 64k size message to be
> something
> that usually wouldn't be encountered, but... with some eeproms and
> maybe TPMs
> perhaps this could happen?
>
> Reviewed-by: Andy Gross <[email protected]>

Thanks Andy for reviewing this patch.

There are EEPROM available with 1MB size like AT24CM01 in which we can
read
complete flash (128 KB) in single go by one transfer with 2 read
messages of
64KB.

Thanks,
Abhishek

2018-03-12 12:36:00

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode

On 2018-02-28 04:28, Andy Gross wrote:
> On Sat, Feb 03, 2018 at 01:28:11PM +0530, Abhishek Sahu wrote:
>
> <snip>
>
>> @@ -841,20 +856,12 @@ static int qup_i2c_bam_do_xfer(struct
>> qup_i2c_dev *qup, struct i2c_msg *msg,
>> goto desc_err;
>> }
>>
>> - if (rx_buf)
>> - writel(QUP_BAM_INPUT_EOT,
>> - qup->base + QUP_OUT_FIFO_BASE);
>> -
>> - writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
>> -
>> qup_i2c_flush(qup);
>>
>> /* wait for remaining interrupts to occur */
>> if (!wait_for_completion_timeout(&qup->xfer, HZ))
>> dev_err(qup->dev, "flush timed out\n");
>>
>> - qup_i2c_rel_dma(qup);
>> -
>
> So this really only works due to the previous patch that adds the
> flush/eot tags
> to all of the read messages. If the answer to the previous question is
> that
> only the last read message gets the eot/flush, then this code needs to
> remain in
> place. Otherwise, it's fine.
>
>
> Andy

Thanks Andy,

We need to schedule EOT/FLUSH after last message.
For following transfer

READ, READ, WRITE (FLUSH + EOT tags after that)

In this case, FLUSH will clear all the descriptors till WRITE and
trigger TX
completion. EOT will be copied in RX FIFO and trigger RX completion.

Thanks,
Abhishek



2018-03-12 13:56:39

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len

On 2018-02-28 03:36, Christ, Austin wrote:
> Hey Abhishek,
>
>
> On 2/3/2018 12:58 AM, Abhishek Sahu wrote:
>> The BAM mode requires buffer for start tag data and tx, rx SG
>> list. Currently, this is being taken for maximum transfer length
>> (65K). But an I2C transfer can have multiple messages and each
>> message can be of this maximum length so the buffer overflow will
>> happen in this case. Since increasing buffer length won’t be
>> feasible since an I2C transfer can contain any number of messages
>> so this patch does following changes to make i2c transfers working
>> for multiple messages case.
>>
>> 1. Calculate the required buffers for 2 maximum length messages
>> (65K * 2).
>> 2. Split the descriptor formation and descriptor scheduling.
>> The idea is to fit as many messages in one DMA transfers for 65K
>> threshold value (max_xfer_sg_len). Whenever the sg_cnt is
>> crossing this, then schedule the BAM transfer and subsequent
>> transfer will again start from zero.
>>

<snip>

>> @@ -1603,7 +1640,7 @@ static int qup_i2c_probe(struct platform_device
>> *pdev)
>> one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
>> qup->one_byte_t = one_bit_t * 9;
>> qup->xfer_timeout = TOUT_MIN * HZ +
>> - usecs_to_jiffies(MX_TX_RX_LEN * qup->one_byte_t);
>> + usecs_to_jiffies(2 * MX_TX_RX_LEN * qup->one_byte_t);
>
> Maybe it would make sense to add a comment here explaining why the
> magic number 2 has been added.

Thanks Austin for reviewing the patches.

Now in v2, I have used the new macro MX_DMA_TX_RX_LEN which will make
this multiplication by 2 more clear. This 2 is for allocating memory
by taking 2 maximum length messages.

https://lkml.org/lkml/2018/3/12/423

Thanks,
Abhishek

2018-03-12 14:00:03

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2

<snip>

>> static void qup_i2c_set_blk_event(struct qup_i2c_dev *qup, bool
>> is_rx)
>> {
>> qup->cur_blk_events = 0;
>> @@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter
>> *adap,
>> return ret;
>> }
>> +/*
>> + * Function to configure registers related with reconfiguration
>> during run
>> + * and will be done before each I2C sub transfer.
>> + */
> Consider changing comment style to remove the word "Function" and
> state the operation in simple present tense.
>
> "Function to configure ..." would be "Configure ..."
>
> Same comment for all comments of this style below.
>


Thanks Austin.

I have done the changes for comments styles in v2

https://lkml.org/lkml/2018/3/12/421

Regards,
Abhishek