ipq40xx: backport I2C QUP driver changes from 4.17
Backport below changes for I2C QUP driver from v4.17: 0668bc44a426 i2c: qup: fix copyrights and update to SPDX identifier 7239872fb340 i2c: qup: fixed releasing dma without flush operation completion eb422b539c1f i2c: qup: minor code reorganization for use_dma 6d5f37f166bb i2c: qup: remove redundant variables for BAM SG count c5adc0fa63a9 i2c: qup: schedule EOT and FLUSH tags at the end of transfer 7e6c35fe602d i2c: qup: fix the transfer length for BAM RX EOT FLUSH tags 3f450d3eea14 i2c: qup: proper error handling for i2c error in BAM mode 08f15963bc75 i2c: qup: use the complete transfer length to choose DMA mode ecb6e1e5f435 i2c: qup: change completion timeout according to transfer length 6f2f0f6465ac i2c: qup: fix buffer overflow for multiple msg of maximum xfer len f7714b4e451b i2c: qup: send NACK for last read sub transfers fbfab1ab0658 i2c: qup: reorganization of driver code to remove polling for qup v1 7545c7dba169 i2c: qup: reorganization of driver code to remove polling for qup v2 This fixes various I2C issues observed on AP120C-AC board equipped with Atmel/Microchip AT97SC3205T TPM module. Tested-by: Christian Lamparter <chunkeey@gmail.com> Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
This commit is contained in:
parent
ff8a8074b2
commit
24de7c29e5
@ -0,0 +1,36 @@
|
||||
From 0668bc44a42672626666e4f66aa1f2c22528e8a5 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:50 +0530
|
||||
Subject: [PATCH 01/13] i2c: qup: fix copyrights and update to SPDX identifier
|
||||
|
||||
The file has been updated from 2016 to 2018 so fixed the
|
||||
copyright years.
|
||||
|
||||
Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 13 ++-----------
|
||||
1 file changed, 2 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -1,17 +1,8 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
- * 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.
|
||||
*
|
||||
- *
|
||||
- * This program is free software; you can redistribute it and/or modify
|
||||
- * it under the terms of the GNU General Public License version 2 and
|
||||
- * only version 2 as published by the Free Software Foundation.
|
||||
- *
|
||||
- * This program is distributed in the hope that it will be useful,
|
||||
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- * GNU General Public License for more details.
|
||||
- *
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
@ -0,0 +1,44 @@
|
||||
From 7239872fb3400b21a8f5547257f9f86455867bd6 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:51 +0530
|
||||
Subject: [PATCH 02/13] i2c: qup: fixed releasing dma without flush operation
|
||||
completion
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
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 <absahu@codeaurora.org>
|
||||
Acked-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Reviewed-by: Austin Christ <austinwc@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -835,6 +835,8 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
}
|
||||
|
||||
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;
|
@ -0,0 +1,76 @@
|
||||
From eb422b539c1f39faf576826b54be93e84d9cb32a Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:52 +0530
|
||||
Subject: [PATCH 03/13] 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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Austin Christ <austinwc@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 19 +++++++++++--------
|
||||
1 file changed, 11 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -181,6 +181,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;
|
||||
@@ -1288,7 +1290,7 @@ static int qup_i2c_xfer_v2(struct i2c_ad
|
||||
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;
|
||||
@@ -1317,13 +1319,12 @@ static int qup_i2c_xfer_v2(struct i2c_ad
|
||||
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;
|
||||
@@ -1347,15 +1348,17 @@ static int qup_i2c_xfer_v2(struct i2c_ad
|
||||
|
||||
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);
|
@ -0,0 +1,174 @@
|
||||
From 6d5f37f166bb07b04b4d42e9d1f5427b7931cd3c Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:53 +0530
|
||||
Subject: [PATCH 04/13] 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. Since rx_buf and tx_buf
|
||||
give the impression that it is buffer instead of count so rename
|
||||
it to tx_cnt and rx_cnt for giving it more meaningful variable
|
||||
name.
|
||||
|
||||
Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Reviewed-by: Austin Christ <austinwc@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 42 ++++++++++++++++--------------------
|
||||
1 file changed, 18 insertions(+), 24 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -683,8 +683,8 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
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 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
|
||||
+ u32 len, blocks, rem;
|
||||
+ u32 i, tlen, tx_len, tx_cnt = 0, rx_cnt = 0, off = 0;
|
||||
u8 *tags;
|
||||
|
||||
while (idx < num) {
|
||||
@@ -698,9 +698,6 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
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];
|
||||
@@ -708,14 +705,14 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
qup->blk.data_len -= tlen;
|
||||
|
||||
/* scratch buf to read the start and len tags */
|
||||
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
||||
+ ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++],
|
||||
&qup->brx.tag.start[0],
|
||||
2, qup, DMA_FROM_DEVICE);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
||||
+ ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++],
|
||||
&msg->buf[limit * i],
|
||||
tlen, qup,
|
||||
DMA_FROM_DEVICE);
|
||||
@@ -725,7 +722,7 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
i++;
|
||||
qup->blk.pos = i;
|
||||
}
|
||||
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||
+ ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++],
|
||||
&qup->start_tag.start[off],
|
||||
len, qup, DMA_TO_DEVICE);
|
||||
if (ret)
|
||||
@@ -733,28 +730,26 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
|
||||
off += len;
|
||||
/* scratch buf to read the BAM EOT and FLUSH tags */
|
||||
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
||||
+ ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++],
|
||||
&qup->brx.tag.start[0],
|
||||
2, qup, DMA_FROM_DEVICE);
|
||||
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];
|
||||
len = qup_i2c_set_tags(tags, qup, msg, 1);
|
||||
qup->blk.data_len -= tlen;
|
||||
|
||||
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||
+ ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++],
|
||||
tags, len,
|
||||
qup, DMA_TO_DEVICE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tx_len += len;
|
||||
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||
+ ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++],
|
||||
&msg->buf[limit * i],
|
||||
tlen, qup, DMA_TO_DEVICE);
|
||||
if (ret)
|
||||
@@ -766,26 +761,25 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
|
||||
if (idx == (num - 1)) {
|
||||
len = 1;
|
||||
- if (rx_nents) {
|
||||
+ if (rx_cnt) {
|
||||
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++],
|
||||
+ ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++],
|
||||
&qup->btx.tag.start[0],
|
||||
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_cnt,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_PREP_FENCE);
|
||||
if (!txd) {
|
||||
@@ -794,7 +788,7 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
goto desc_err;
|
||||
}
|
||||
|
||||
- if (!rx_nents) {
|
||||
+ if (!rx_cnt) {
|
||||
txd->callback = qup_i2c_bam_cb;
|
||||
txd->callback_param = qup;
|
||||
}
|
||||
@@ -807,9 +801,9 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
|
||||
dma_async_issue_pending(qup->btx.dma);
|
||||
|
||||
- if (rx_nents) {
|
||||
+ if (rx_cnt) {
|
||||
rxd = dmaengine_prep_slave_sg(qup->brx.dma, qup->brx.sg,
|
||||
- rx_nents, DMA_DEV_TO_MEM,
|
||||
+ rx_cnt, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!rxd) {
|
||||
dev_err(qup->dev, "failed to get rx desc\n");
|
||||
@@ -844,7 +838,7 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
goto desc_err;
|
||||
}
|
||||
|
||||
- if (rx_nents)
|
||||
+ if (rx_cnt)
|
||||
writel(QUP_BAM_INPUT_EOT,
|
||||
qup->base + QUP_OUT_FIFO_BASE);
|
||||
|
||||
@@ -862,10 +856,10 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
}
|
||||
|
||||
desc_err:
|
||||
- dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE);
|
||||
+ dma_unmap_sg(qup->dev, qup->btx.sg, tx_cnt, DMA_TO_DEVICE);
|
||||
|
||||
- if (rx_nents)
|
||||
- dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents,
|
||||
+ if (rx_cnt)
|
||||
+ dma_unmap_sg(qup->dev, qup->brx.sg, rx_cnt,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
return ret;
|
@ -0,0 +1,126 @@
|
||||
From c5adc0fa63a930e3313c74bb7c1d4d158130eb41 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:54 +0530
|
||||
Subject: [PATCH 05/13] i2c: qup: schedule EOT and FLUSH tags at the end of
|
||||
transfer
|
||||
|
||||
The role of FLUSH and EOT tag is to flush already scheduled
|
||||
descriptors in BAM HW in case of error. EOT is required only
|
||||
when descriptors are scheduled in RX FIFO. If all the messages
|
||||
are WRITE, then only FLUSH tag will be used.
|
||||
|
||||
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.
|
||||
|
||||
Following is one of the example
|
||||
|
||||
READ, READ, READ, READ
|
||||
|
||||
Currently EOT and FLUSH tags are being written after each READ.
|
||||
If QUP gets 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 descriptors be
|
||||
flushed. All the scheduled descriptors should be cleared to
|
||||
generate BAM DMA 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.
|
||||
|
||||
Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Reviewed-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 39 ++++++++++++++++++++++--------------
|
||||
1 file changed, 24 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -551,7 +551,7 @@ static int qup_i2c_set_tags_smb(u16 addr
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -592,11 +592,6 @@ static int qup_i2c_set_tags(u8 *tags, st
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -605,7 +600,7 @@ static int qup_i2c_issue_xfer_v2(struct
|
||||
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 */
|
||||
@@ -701,7 +696,7 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
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 */
|
||||
@@ -729,17 +724,11 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
return ret;
|
||||
|
||||
off += len;
|
||||
- /* scratch buf to read the BAM EOT and FLUSH tags */
|
||||
- ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++],
|
||||
- &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_cnt++],
|
||||
@@ -779,6 +768,26 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
msg++;
|
||||
}
|
||||
|
||||
+ /* schedule the EOT and FLUSH I2C tags */
|
||||
+ len = 1;
|
||||
+ if (rx_cnt) {
|
||||
+ 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_cnt++],
|
||||
+ &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_cnt++], &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_cnt,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_PREP_FENCE);
|
@ -0,0 +1,33 @@
|
||||
From 7e6c35fe602df6821b3e7db5b1ba656162750fda Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:55 +0530
|
||||
Subject: [PATCH 06/13] 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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -774,10 +774,10 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
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_cnt++],
|
||||
&qup->brx.tag.start[0],
|
||||
- 2, qup, DMA_FROM_DEVICE);
|
||||
+ 1, qup, DMA_FROM_DEVICE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
From 3f450d3eea14799b14192231840c1753a660f150 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:56 +0530
|
||||
Subject: [PATCH 07/13] 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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Reviewed-by: Austin Christ <austinwc@codeaurora.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 25 ++++++++++++++++---------
|
||||
1 file changed, 16 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -219,9 +219,24 @@ static irqreturn_t qup_i2c_interrupt(int
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -847,20 +862,12 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
goto desc_err;
|
||||
}
|
||||
|
||||
- if (rx_cnt)
|
||||
- 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;
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
From 08f15963bc751bc818294c0f75a9aaca299c4052 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:57 +0530
|
||||
Subject: [PATCH 08/13] 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 selects DMA mode if the total length is greater than FIFO
|
||||
length.
|
||||
|
||||
Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Reviewed-by: Austin Christ <austinwc@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 13 +++++++------
|
||||
1 file changed, 7 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -1300,7 +1300,8 @@ static int qup_i2c_xfer_v2(struct i2c_ad
|
||||
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;
|
||||
@@ -1326,14 +1327,14 @@ static int qup_i2c_xfer_v2(struct i2c_ad
|
||||
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->out_fifo_sz ||
|
||||
+ total_len > qup->in_fifo_sz))
|
||||
qup->use_dma = true;
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
From ecb6e1e5f4352055a5761b945a833a925d51bf8d Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:58 +0530
|
||||
Subject: [PATCH 09/13] 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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -121,8 +121,12 @@
|
||||
#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
|
||||
+/*
|
||||
+ * Minimum transfer timeout for i2c transfers in seconds. It will be added on
|
||||
+ * the top of maximum transfer time calculated from i2c bus speed to compensate
|
||||
+ * the overheads.
|
||||
+ */
|
||||
+#define TOUT_MIN 2
|
||||
|
||||
/* Default values. Use these if FW query fails */
|
||||
#define DEFAULT_CLK_FREQ 100000
|
||||
@@ -163,6 +167,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;
|
||||
@@ -849,7 +854,7 @@ static int qup_i2c_bam_do_xfer(struct qu
|
||||
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;
|
||||
}
|
||||
@@ -1605,6 +1610,8 @@ nodma:
|
||||
*/
|
||||
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,
|
@ -0,0 +1,311 @@
|
||||
From 6f2f0f6465acbd59391c43352ff0df77df1f01db Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:44:59 +0530
|
||||
Subject: [PATCH 10/13] i2c: qup: fix buffer overflow for multiple msg of
|
||||
maximum xfer len
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 194 ++++++++++++++++++++---------------
|
||||
1 file changed, 110 insertions(+), 84 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -118,8 +118,12 @@
|
||||
#define ONE_BYTE 0x1
|
||||
#define QUP_I2C_MX_CONFIG_DURING_RUN BIT(31)
|
||||
|
||||
+/* Maximum transfer length for single DMA descriptor */
|
||||
#define MX_TX_RX_LEN SZ_64K
|
||||
#define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)
|
||||
+/* Maximum transfer length for all DMA descriptors */
|
||||
+#define MX_DMA_TX_RX_LEN (2 * MX_TX_RX_LEN)
|
||||
+#define MX_DMA_BLOCKS (MX_DMA_TX_RX_LEN / QUP_READ_LIMIT)
|
||||
|
||||
/*
|
||||
* Minimum transfer timeout for i2c transfers in seconds. It will be added on
|
||||
@@ -150,6 +154,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 {
|
||||
@@ -188,6 +193,8 @@ struct qup_i2c_dev {
|
||||
bool is_dma;
|
||||
/* To check if the current transfer is using DMA */
|
||||
bool use_dma;
|
||||
+ 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;
|
||||
@@ -692,102 +699,87 @@ static int qup_i2c_req_dma(struct qup_i2
|
||||
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)
|
||||
{
|
||||
- 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 len, blocks, rem;
|
||||
- u32 i, tlen, tx_len, tx_cnt = 0, rx_cnt = 0, off = 0;
|
||||
+ int ret = 0, limit = QUP_READ_LIMIT;
|
||||
+ u32 len = 0, blocks, rem;
|
||||
+ u32 i = 0, tlen, tx_len = 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);
|
||||
|
||||
- qup_i2c_set_blk_data(qup, msg);
|
||||
+ blocks = qup->blk.count;
|
||||
+ rem = msg->len - (blocks - 1) * limit;
|
||||
|
||||
- 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 (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_cnt++],
|
||||
- &qup->brx.tag.start[0],
|
||||
- 2, qup, DMA_FROM_DEVICE);
|
||||
-
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++],
|
||||
- &msg->buf[limit * i],
|
||||
- tlen, qup,
|
||||
- DMA_FROM_DEVICE);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- i++;
|
||||
- qup->blk.pos = i;
|
||||
- }
|
||||
- ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++],
|
||||
- &qup->start_tag.start[off],
|
||||
- len, qup, DMA_TO_DEVICE);
|
||||
+ 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;
|
||||
|
||||
- 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_cnt++],
|
||||
- tags, len,
|
||||
- qup, DMA_TO_DEVICE);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- tx_len += len;
|
||||
- ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++],
|
||||
- &msg->buf[limit * i],
|
||||
- tlen, qup, DMA_TO_DEVICE);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- i++;
|
||||
- qup->blk.pos = i;
|
||||
- }
|
||||
- off += tx_len;
|
||||
+ 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;
|
||||
|
||||
- if (idx == (num - 1)) {
|
||||
- len = 1;
|
||||
- if (rx_cnt) {
|
||||
- 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_cnt++],
|
||||
- &qup->btx.tag.start[0],
|
||||
- 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;
|
||||
}
|
||||
- idx++;
|
||||
- msg++;
|
||||
+
|
||||
+ 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;
|
||||
+ dma_cookie_t cookie_rx, cookie_tx;
|
||||
+ u32 len = 0;
|
||||
+ u32 tx_cnt = qup->btx.sg_cnt, rx_cnt = qup->brx.sg_cnt;
|
||||
+
|
||||
/* schedule the EOT and FLUSH I2C tags */
|
||||
len = 1;
|
||||
if (rx_cnt) {
|
||||
@@ -886,11 +878,19 @@ desc_err:
|
||||
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);
|
||||
@@ -913,9 +913,34 @@ static int qup_i2c_bam_xfer(struct i2c_a
|
||||
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);
|
||||
|
||||
@@ -1468,7 +1493,8 @@ static int qup_i2c_probe(struct platform
|
||||
else if (ret != 0)
|
||||
goto nodma;
|
||||
|
||||
- blocks = (MX_BLOCKS << 1) + 1;
|
||||
+ qup->max_xfer_sg_len = (MX_BLOCKS << 1);
|
||||
+ blocks = (MX_DMA_BLOCKS << 1) + 1;
|
||||
qup->btx.sg = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*qup->btx.sg) * blocks,
|
||||
GFP_KERNEL);
|
||||
@@ -1611,7 +1637,7 @@ nodma:
|
||||
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(MX_DMA_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,
|
@ -0,0 +1,43 @@
|
||||
From f7714b4e451bdcb7918b9aad14af22684ceac638 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:45:00 +0530
|
||||
Subject: [PATCH 11/13] i2c: qup: send NACK for last read sub transfers
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Austin Christ <austinwc@codeaurora.org>
|
||||
Reviewed-by: Andy Gross <andy.gross@linaro.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -104,6 +104,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 */
|
||||
@@ -606,7 +607,9 @@ static int qup_i2c_set_tags(u8 *tags, st
|
||||
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;
|
||||
}
|
@ -0,0 +1,579 @@
|
||||
From fbfab1ab065879370541caf0e514987368eb41b2 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Mon, 12 Mar 2018 18:45:01 +0530
|
||||
Subject: [PATCH 12/13] i2c: qup: reorganization of driver code to remove
|
||||
polling for qup v1
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
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 <absahu@codeaurora.org>
|
||||
Reviewed-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
|
||||
---
|
||||
drivers/i2c/busses/i2c-qup.c | 366 +++++++++++++++++++++--------------
|
||||
1 file changed, 223 insertions(+), 143 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-qup.c
|
||||
+++ b/drivers/i2c/busses/i2c-qup.c
|
||||
@@ -64,8 +64,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
|
||||
@@ -137,13 +140,36 @@
|
||||
#define DEFAULT_CLK_FREQ 100000
|
||||
#define DEFAULT_SRC_CLK 20000000
|
||||
|
||||
+/*
|
||||
+ * 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
|
||||
+ * rx_bytes_read: if all the bytes have been read from rx FIFO.
|
||||
+ * 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 rx_bytes_read;
|
||||
+ bool is_tx_blk_mode;
|
||||
+ bool is_rx_blk_mode;
|
||||
+ u8 tags[6];
|
||||
};
|
||||
|
||||
struct qup_i2c_tag {
|
||||
@@ -186,6 +212,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;
|
||||
@@ -202,11 +229,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;
|
||||
@@ -253,12 +287,48 @@ static irqreturn_t qup_i2c_interrupt(int
|
||||
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);
|
||||
|
||||
+ 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 (qup->msg->flags & I2C_M_RD) {
|
||||
+ if (!blk->rx_bytes_read)
|
||||
+ return IRQ_HANDLED;
|
||||
+ } else {
|
||||
+ /*
|
||||
+ * Ideally, QUP_MAX_OUTPUT_DONE_FLAG should be checked
|
||||
+ * for FIFO mode also. But, QUP_MAX_OUTPUT_DONE_FLAG lags
|
||||
+ * behind QUP_OUTPUT_SERVICE_FLAG sometimes. The only reason
|
||||
+ * of interrupt for write message in FIFO mode is
|
||||
+ * QUP_MAX_OUTPUT_DONE_FLAG condition.
|
||||
+ */
|
||||
+ if (blk->is_tx_blk_mode && !(opflags & QUP_MX_OUTPUT_DONE))
|
||||
+ return IRQ_HANDLED;
|
||||
+ }
|
||||
+
|
||||
done:
|
||||
qup->qup_err = qup_err;
|
||||
qup->bus_err = bus_err;
|
||||
@@ -324,6 +394,28 @@ static int qup_i2c_change_state(struct q
|
||||
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
|
||||
@@ -394,23 +486,6 @@ static void qup_i2c_set_write_mode_v2(st
|
||||
}
|
||||
}
|
||||
|
||||
-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;
|
||||
@@ -443,28 +518,25 @@ out:
|
||||
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
|
||||
@@ -481,11 +553,8 @@ static int qup_i2c_issue_write(struct qu
|
||||
|
||||
qup->pos++;
|
||||
idx++;
|
||||
+ blk->tx_fifo_free--;
|
||||
}
|
||||
-
|
||||
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
|
||||
-
|
||||
- return ret;
|
||||
}
|
||||
|
||||
static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
|
||||
@@ -1006,64 +1075,6 @@ err:
|
||||
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;
|
||||
@@ -1086,44 +1097,27 @@ static void qup_i2c_set_read_mode_v2(str
|
||||
}
|
||||
}
|
||||
|
||||
-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;
|
||||
+ int idx = 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)
|
||||
+ blk->rx_bytes_read = true;
|
||||
}
|
||||
|
||||
static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
|
||||
@@ -1224,49 +1218,130 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
+static void qup_i2c_write_rx_tags_v1(struct qup_i2c_dev *qup)
|
||||
{
|
||||
- int ret;
|
||||
+ struct i2c_msg *msg = qup->msg;
|
||||
+ u32 addr, len, val;
|
||||
|
||||
- qup->msg = msg;
|
||||
- qup->pos = 0;
|
||||
+ addr = i2c_8bit_addr_from_msg(msg);
|
||||
|
||||
- enable_irq(qup->irq);
|
||||
- qup_i2c_set_read_mode(qup, msg->len);
|
||||
+ /* 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 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;
|
||||
+ blk->rx_bytes_read = false;
|
||||
+}
|
||||
+
|
||||
+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_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;
|
||||
+
|
||||
+ reinit_completion(&qup->xfer);
|
||||
+ enable_irq(qup->irq);
|
||||
+ if (!blk->is_tx_blk_mode) {
|
||||
+ blk->tx_fifo_free = qup->out_fifo_sz;
|
||||
|
||||
- qup_i2c_issue_read(qup, msg);
|
||||
+ 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)
|
||||
@@ -1305,10 +1380,11 @@ static int qup_i2c_xfer(struct i2c_adapt
|
||||
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;
|
||||
@@ -1487,6 +1563,10 @@ static int qup_i2c_probe(struct platform
|
||||
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);
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user