Major code reorganization to make all i2c transfers working
Subsystems (2)
cover 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(-)
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 1/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.
--- drivers/i2c/busses/i2c-qup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-qup.c +3 -1
@@ -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;
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion
Hi Abhishek,
Quoted 25 lines · Abhishek Sahu
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_timeoutcompleted 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(-)drivers/i2c/busses/i2c-qup.c +0
-0
drivers/i2c/busses/i2c-qup.c line 2
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c +1
-1@@ -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.
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 851
Quoted 13 lines
drivers/i2c/busses/i2c-qup.c +2
-0* 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,
Regards,
Sricharan
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion
Quoted 23 lines · Sricharan R
Hi Abhishek,
Quoted 20 lines · Abhishek Sahu
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_timeoutcompleted 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(-)
Quoted 12 lines
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +0
-1@@ -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.
Quoted 2 lines
you can change the header to the new SPDX one.
Yes that would be better.
I will fix that also.
Thanks,
Abhishek
Quoted 20 lines
Quoted 12 lines
* Copyright (c) 2014, Sony Mobile Communications AB. *
*drivers/i2c/busses/i2c-qup.c +2
-0@@ -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" );Quoted 2 lines
goto
desc_err;Except for the above nit,
Acked-by Sricharan R <[email protected]>Regards,
Sricharan
Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion
In reply to Abhishek SahuTested on Centriq 2400
Quoted 41 lines · Abhishek Sahu
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_timeoutcompleted 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(-)drivers/i2c/busses/i2c-qup.c +3
-1@@ -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;
Signature · 3 lines
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.
Re: [PATCH 01/12] i2c: qup: fixed releasing dma without flush operation completion
In reply to Abhishek SahuQuoted 15 lines · Abhishek Sahu
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_timeoutcompleted immediately and will proceed for freeing the DMA resources where the descriptors are still in process.
Quoted 1 line
Signed-off-by Abhishek Sahu <[email protected]>
PATCH 2/12 i2c: qup: minor code reorganization for use_dma
In reply to Abhishek Sahu1. 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.
--- drivers/i2c/busses/i2c-qup.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
drivers/i2c/busses/i2c-qup.c +11 -8
@@ -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 );
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 02/12] i2c: qup: minor code reorganization for use_dma
Tested on Centriq 2400
Quoted 71 lines · Abhishek Sahu
1. Assigns
use_dmainqup_devstructure 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(-)drivers/i2c/busses/i2c-qup.c +11
-8@@ -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 );
Signature · 3 lines
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.
Re: [PATCH 02/12] i2c: qup: minor code reorganization for use_dma
In reply to Abhishek SahuQuoted 6 lines · Abhishek Sahu
1. Assigns
use_dmainqup_devstructure 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]>
PATCH 3/12 i2c: qup: remove redundant variables for BAM SG count
In reply to Abhishek SahuThe rx_nents and tx_nents are redundant. rx_buf and tx_buf can be used for total number of SG entries.
--- drivers/i2c/busses/i2c-qup.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-)
drivers/i2c/busses/i2c-qup.c +10 -16
@@ -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;
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count
Hi Abhishek,
Quoted 13 lines · Abhishek Sahu
The
rx_nentsandtx_nentsare redundant.rx_bufandtx_bufcan 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(-)drivers/i2c/busses/i2c-qup.c +0
-0
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 698
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c +1
-1@@ -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
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count
Quoted 20 lines · Sricharan R
Hi Abhishek,
Quoted 17 lines · Abhishek Sahu
The
rx_nentsandtx_nentsare redundant.rx_bufandtx_bufcan 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(-)drivers/i2c/busses/i2c-qup.c +0
-0@@ -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;
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 697
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +1
-1-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
Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count
In reply to Abhishek SahuI agree with Sricharan's comments about naming, otherwise looks good.
Tested on Centriq 2400
Quoted 110 lines · Abhishek Sahu
The
rx_nentsandtx_nentsare redundant.rx_bufandtx_bufcan 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(-)drivers/i2c/busses/i2c-qup.c +10
-16@@ -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;
Signature · 3 lines
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.
Re: [PATCH 03/12] i2c: qup: remove redundant variables for BAM SG count
In reply to Abhishek SahuQuoted 5 lines · Abhishek Sahu
The
rx_nentsandtx_nentsare redundant.rx_bufandtx_bufcan be used for total number of SG entries.Signed-off-by Abhishek Sahu <[email protected]>---
Naming conventions aside,
PATCH 4/12 i2c: qup: schedule EOT and FLUSH tags at the end of transfer
In reply to Abhishek SahuA 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.
--- drivers/i2c/busses/i2c-qup.c | 54 ++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 30 deletions(-)
drivers/i2c/busses/i2c-qup.c +24 -30
@@ -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 );
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer
Hi Abhishek,
Quoted 86 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +4
-24@@ -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 ;
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 763
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +0
-6- 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.
Quoted 11 lines
drivers/i2c/busses/i2c-qup.c +10
-0+/* 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 );
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 784
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+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
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer
Quoted 104 lines · Sricharan R
Hi Abhishek,
Quoted 101 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +1
-1@@ -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);drivers/i2c/busses/i2c-qup.c +1
-6int len =0 ;@@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, structqup_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;drivers/i2c/busses/i2c-qup.c +1
-1/* 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;drivers/i2c/busses/i2c-qup.c +1
-7/* 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;drivers/i2c/busses/i2c-qup.c +0
-15ret = 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++;
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct · line 764
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c +0
-0msg++; }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.
Quoted 19 lines
Quoted 19 lines
drivers/i2c/busses/i2c-qup.c +18
-0+/* 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)
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct · line 784
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c +2
-0+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
Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer
In reply to Abhishek SahuQuoted 25 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +1
-1@@ -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 ;
drivers/i2c/busses/i2c-qup.c static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup, · line 608
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c +0
-5@@ -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
Re: [PATCH 04/12] i2c: qup: schedule EOT and FLUSH tags at the end of transfer
Quoted 32 lines · Andy Gross
Quoted 31 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +1
-1@@ -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);drivers/i2c/busses/i2c-qup.c +0
-0int len =0 ;@@ -601,11 +601,6 @@ static int qup_i2c_set_tags(u8 *tags, structqup_i2c_dev *qup, else tags[len++] = data_len;
Quoted 12 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup, · line 608
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +0
-5-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
PATCH 5/12 i2c: qup: fix the transfer length for BAM rx EOT FLUSH tags
In reply to Abhishek SahuIn 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.
--- drivers/i2c/busses/i2c-qup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-qup.c +2 -2
@@ -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; }
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 05/12] i2c: qup: fix the transfer length for BAM rx EOT FLUSH tags
Quoted 6 lines · Abhishek Sahu
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]>
PATCH 6/12 i2c: qup: proper error handling for i2c error in BAM mode
In reply to Abhishek SahuCurrently 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.
--- drivers/i2c/busses/i2c-qup.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
drivers/i2c/busses/i2c-qup.c +16 -9
@@ -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 ; }
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode
Quoted 74 lines · Abhishek Sahu
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_FLUSHcommand needs to be executed while moving toRUN_STATEby writing to theQUP_STATEregister with theI2C_FLUSHbit 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_completionwhich 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(-)drivers/i2c/busses/i2c-qup.c +16
-7@@ -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);
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 867
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +0
-2/* 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 ; }
Regards,
Sricharan
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode
In reply to Abhishek SahuTested on Centriq 2400
Quoted 84 lines · Abhishek Sahu
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_FLUSHcommand needs to be executed while moving toRUN_STATEby writing to theQUP_STATEregister with theI2C_FLUSHbit 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_completionwhich 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(-)drivers/i2c/busses/i2c-qup.c +16
-9@@ -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 ; }
Signature · 3 lines
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.
Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode
In reply to Abhishek SahuOn Sat, Feb 03, 2018 at 01:28:11PM +0530, Abhishek Sahu wrote:
<snip>
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c +0
-4@@ -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 ); -
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, · line 857
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +0
-4-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
Re: [PATCH 06/12] i2c: qup: proper error handling for i2c error in BAM mode
Quoted 30 lines · Andy Gross
On Sat, Feb 03, 2018 at 01:28:11PM +0530, Abhishek Sahu wrote:
<snip>
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c +0
-4@@ -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 ); -drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_do_xfer(struct · line 858
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +0
-4-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.
Quoted 1 line
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
PATCH 7/12 i2c: qup: use the complete transfer length to choose DMA mode
In reply to Abhishek SahuCurrently 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.
--- drivers/i2c/busses/i2c-qup.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/i2c/busses/i2c-qup.c +9 -6
@@ -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 ;
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode
Quoted 57 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +8
-6@@ -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; }
drivers/i2c/busses/i2c-qup.c static int qup_i2c_probe(struct platform_device *pdev) · line 1592
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c +1
-0@@ -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
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode
Quoted 62 lines · Sricharan R
Quoted 61 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +4
-1@@ -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 ;drivers/i2c/busses/i2c-qup.c +4
-5qup->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)
Quoted 7 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_probe(struct platform_device *pdev) · line 1592
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c +1
-0size =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 inqup_i2c_xfer_v2itself.
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
Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode
In reply to Abhishek SahuTested on Centriq 2400
Quoted 65 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +9
-6@@ -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 ;
Signature · 3 lines
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.
Re: [PATCH 07/12] i2c: qup: use the complete transfer length to choose DMA mode
In reply to Abhishek SahuQuoted 11 lines · Abhishek Sahu
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.
PATCH 8/12 i2c: qup: change completion timeout according to transfer length
In reply to Abhishek SahuCurrently 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.
--- drivers/i2c/busses/i2c-qup.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/i2c/busses/i2c-qup.c +6 -3
@@ -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,
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length
Quoted 15 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +0
-0
drivers/i2c/busses/i2c-qup.c line 135
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c +2
-2@@ -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
Quoted 29 lines
drivers/i2c/busses/i2c-qup.c +4
-1/* 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,
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length
Quoted 19 lines · Sricharan R
Quoted 18 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +0
-0@@ -130,8 +130,8 @@#define MX_TX_RX_LEN SZ_64K#define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c line 135
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +2
-2-/* Max timeout in ms for 32k bytes */ -#define TOUT_MAX 300 +/* Min timeout for i2c transfers */ +#define TOUT_MIN 2may 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
Andy Gross
Reviewed-byRe: [PATCH 08/12] i2c: qup: change completion timeout according to transfer length
Quoted 29 lines · Abhishek Sahu
Quoted 19 lines · Sricharan R
Quoted 18 lines · Abhishek Sahu
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(-)drivers/i2c/busses/i2c-qup.c +0
-0@@ -130,8 +130,8 @@#define MX_TX_RX_LEN SZ_64K#define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)Quoted 8 lines
drivers/i2c/busses/i2c-qup.c line 135
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +2
-2-/* Max timeout in ms for 32k bytes */ -#define TOUT_MAX 300 +/* Min timeout for i2c transfers */ +#define TOUT_MIN 2may be you can mention, why is this 2 ?
Quoted 6 lines
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:
PATCH 9/12 i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
In reply to Abhishek SahuThe 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.
--- drivers/i2c/busses/i2c-qup.c | 199 +++++++++++++++++++++++++------------------ 1 file changed, 118 insertions(+), 81 deletions(-)
drivers/i2c/busses/i2c-qup.c +118 -81
@@ -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,
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
Hi Abhishek,
Quoted 249 lines · Abhishek Sahu
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 thesg_cntis 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(-)drivers/i2c/busses/i2c-qup.c +106
-77@@ -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 ||
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, · line 946
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +9
-0+ 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
Quoted 25 lines
drivers/i2c/busses/i2c-qup.c +3
-4- 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,
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
Quoted 19 lines · Sricharan R
Hi Abhishek,
Quoted 16 lines · Abhishek Sahu
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 thesg_cntis crossing this, then schedule the BAM transfer and subsequent transfer will again start from zero.
<snip>
Quoted 46 lines
Quoted 46 lines
drivers/i2c/busses/i2c-qup.c +7
-0+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,
drivers/i2c/busses/i2c-qup.c +25
-0int 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 ; +
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, · line 946
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c +3
-0+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.
Quoted 9 lines
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
Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
In reply to Abhishek SahuHey Abhishek,
Quoted 275 lines · Abhishek Sahu
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 thesg_cntis 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(-)drivers/i2c/busses/i2c-qup.c +117
-80@@ -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 );
drivers/i2c/busses/i2c-qup.c static int qup_i2c_probe(struct platform_device *pdev) · line 1643
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +1
-1@@ -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.
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c +0
-0dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", qup->in_blk_sz, qup->in_fifo_sz,
Signature · 3 lines
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.
Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
Quoted 21 lines · Austin
Hey Abhishek,
Quoted 17 lines · Abhishek Sahu
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 thesg_cntis crossing this, then schedule the BAM transfer and subsequent transfer will again start from zero.
<snip>
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_probe(struct platform_device · line 1644
Quoted 7 lines
drivers/i2c/busses/i2c-qup.c +1
-1@@ -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
Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
In reply to Abhishek SahuQuoted 17 lines · Abhishek Sahu
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 thesg_cntis crossing this, then schedule the BAM transfer and subsequent transfer will again start from zero.
Quoted 1 line
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?
Re: [PATCH 09/12] i2c: qup: fix buffer overflow for multiple msg of maximum xfer len
Quoted 18 lines · Andy Gross
Quoted 17 lines · Abhishek Sahu
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 thesg_cntis crossing this, then schedule the BAM transfer and subsequent transfer will again start from zero.
Quoted 7 lines
Quoted 1 line
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
PATCH 10/12 i2c: qup: send NACK for last read sub transfers
In reply to Abhishek SahuAccording 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.
--- drivers/i2c/busses/i2c-qup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-qup.c +4 -1
@@ -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 ; }
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers
Hi Abhishek,
Quoted 24 lines · Abhishek Sahu
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_NACKso added the same.Signed-off-by Abhishek Sahu <[email protected]>--- drivers/i2c/busses/i2c-qup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)drivers/i2c/busses/i2c-qup.c +1
-0@@ -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 */
drivers/i2c/busses/i2c-qup.c static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup, · line 617
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c +3
-1@@ -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
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers
In reply to Abhishek SahuTested on Centriq 2400
Quoted 35 lines · Abhishek Sahu
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_NACKso added the same.Signed-off-by Abhishek Sahu <[email protected]>--- drivers/i2c/busses/i2c-qup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)drivers/i2c/busses/i2c-qup.c +4
-1@@ -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 ; }
Signature · 3 lines
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.
Re: [PATCH 10/12] i2c: qup: send NACK for last read sub transfers
In reply to Abhishek SahuQuoted 7 lines · Abhishek Sahu
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_NACKso added the same.Signed-off-by Abhishek Sahu <[email protected]>
PATCH 11/12 i2c: qup: reorganization of driver code to remove polling for qup v1
In reply to Abhishek SahuFollowing 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.
--- drivers/i2c/busses/i2c-qup.c | 399 ++++++++++++++++++++++++++++--------------- 1 file changed, 257 insertions(+), 142 deletions(-)
drivers/i2c/busses/i2c-qup.c +257 -142
@@ -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);
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
kbuild test robot
build botRe: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1
Thank you for the patch! Perhaps something to improve:
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
Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1
In reply to Abhishek SahuHi Abhishek,
Quoted 194 lines · Abhishek Sahu
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 forOUT_BLOCK_WRITE_REQandIN_BLOCK_READ_REQ.Because of above, i2c v1 transfers of size greater than 32 are failing with following error message
i2c_qup78b6000.i2c: timeout for fifo out fullTo 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_REQandIN_BLOCK_READ_REQinterrupts 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 generateOUT_BLOCK_WRITE_REQinterrupt 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 forQUP_MX_INPUT_DONEinterrupt which will be generated after all the bytes have been copied in RX FIFO. For read Block mode, QUP will generateIN_BLOCK_READ_REQinterrupts 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(-)drivers/i2c/busses/i2c-qup.c +93
-9@@ -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); + }
drivers/i2c/busses/i2c-qup.c static irqreturn_t qup_i2c_interrupt(int irq, void *dev) · line 358
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+ } + +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
Quoted 408 lines
drivers/i2c/busses/i2c-qup.c +154
-133+ 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);
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1
Quoted 217 lines · Sricharan R
Hi Abhishek,
Quoted 214 lines · Abhishek Sahu
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 forOUT_BLOCK_WRITE_REQandIN_BLOCK_READ_REQ.Because of above, i2c v1 transfers of size greater than 32 are failing with following error message
i2c_qup78b6000.i2c: timeout for fifo out fullTo 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_REQandIN_BLOCK_READ_REQinterrupts 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_REQinterrupt 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 forQUP_MX_INPUT_DONEinterrupt which will be generated after all the bytes have been copied in RX FIFO.
For
read Block mode, QUP will generateIN_BLOCK_READ_REQinterrupts 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(-)drivers/i2c/busses/i2c-qup.c +27
-0@@ -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
+ */
structqup_i2c_block{drivers/i2c/busses/i2c-qup.c +66
-9-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 ; +
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c static irqreturn_t qup_i2c_interrupt(int irq, void *dev) · line 358
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c +2
-0+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_DONEand for RX -> based onQUP_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
Re: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1
Hi Abhishek,
Quoted 230 lines · Abhishek Sahu
Quoted 205 lines · Sricharan R
Hi Abhishek,
Quoted 202 lines · Abhishek Sahu
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 forOUT_BLOCK_WRITE_REQandIN_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_REQandIN_BLOCK_READ_REQinterrupts 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 generateOUT_BLOCK_WRITE_REQinterrupt 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 forQUP_MX_INPUT_DONEinterrupt which will be generated after all the bytes have been copied in RX FIFO. For read Block mode, QUP will generateIN_BLOCK_READ_REQinterrupts 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(-)drivers/i2c/busses/i2c-qup.c +101
-9@@ -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; +Quoted 8 lines
drivers/i2c/busses/i2c-qup.c static irqreturn_t qup_i2c_interrupt(int irq, void *dev) · line 358
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c +2
-0+ 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_DONEand for RX -> based onQUP_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, theQUP_MX_INPUT_DONEwill 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 behaviourIRQ with
QUP_MX_INPUT_DONEandIN_BLOCK_READ_REQ-> read 16 bytes IRQ withIN_BLOCK_READ_REQ-> read next 16 bytes
IRQ with IN_BLOCK_READ_REQ -> read next 16 bytes
IRQ withIN_BLOCK_READ_REQ-> read last 16 bytesSo for RX, we can't trigger complete by checking
QUP_BLK_EVENT_RX_DONEalone. We need to track the number of bytes read from FIFO. Instead of putting this check, I am taking one extra event bitQUP_BLK_EVENT_RX_DONEwhich will be set when all the bytes has been read.
Quoted 4 lines
I am not sure if checking
QUP_MX_INPUT_DONEwill always work since there may be some case, when for small transfers theQUP_MX_INPUT_DONEwill come beforeQUP_MX_OUTPUT_DONEso 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
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 12/12 i2c: qup: reorganization of driver code to remove polling for qup v2
In reply to Abhishek SahuFollowing 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.
--- drivers/i2c/busses/i2c-qup.c | 917 +++++++++++++++++++++++++------------------ 1 file changed, 533 insertions(+), 384 deletions(-)
drivers/i2c/busses/i2c-qup.c +533 -384
@@ -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 ;
Signature · 1 line
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2
Hi Abhishek,
Quoted 12 lines · Abhishek Sahu
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 forOUT_BLOCK_WRITE_REQandIN_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.
Quoted 547 lines
Because of above, i2c v2 transfers of size greater than 64 are failing with following error message
i2c_qup78b6000.i2c: timeout for fifo out fullTo 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_REQandIN_BLOCK_READ_REQinterrupts 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 generateOUT_BLOCK_WRITE_REQinterrupt 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 forQUP_MX_INPUT_DONEinterrupt which will be generated after all the bytes have been copied in RX FIFO. For read Block mode, QUP will generateIN_BLOCK_READ_REQinterrupts 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(-)drivers/i2c/busses/i2c-qup.c +45
-332@@ -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 ;
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1157
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c +0
-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.
Quoted 99 lines
drivers/i2c/busses/i2c-qup.c +99
-0+/* +* 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++; + } +
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1266
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+ 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 ?
Quoted 78 lines
drivers/i2c/busses/i2c-qup.c +78
-0+ 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
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1354
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+* 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
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +5
-0+*/ +static void qup_i2c_write_tx_fifo_v2 (struct qup_i2c_dev *qup) +{ +struct qup_i2c_block *blk = &qup->blk; +
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1369
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+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 ?
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c +6
-0+if (!blk->cur_blk_len) { +if (!blk->tx_fifo_data_pos) + goto tx_data_done; + +if (blk->tx_fifo_free) + goto send_last_word;
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1385
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+ + 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.
Quoted 43 lines
drivers/i2c/busses/i2c-qup.c +43
-0+} + +/* +* 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)
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1438
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+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.
Quoted 95 lines
drivers/i2c/busses/i2c-qup.c +95
-0+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; + } +
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1543
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+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
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1554
Quoted 11 lines
drivers/i2c/busses/i2c-qup.c +11
-0+* 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
Quoted 18 lines
drivers/i2c/busses/i2c-qup.c +18
-0+{ +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 ;
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1582
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-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 ?
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1584
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c +2
-0+ + 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.
Quoted 53 lines
drivers/i2c/busses/i2c-qup.c +29
-3+ (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++) {
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer_v2(struct i2c_adapter *adap, · line 1642
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +4
-5-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 ?
Quoted 118 lines
drivers/i2c/busses/i2c-qup.c +28
-44+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 ;
Signature · 1 line
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2
<snip>
Quoted 6 lines
Quoted 6 lines
static void qup_i2c_set_blk_event(struct
qup_i2c_dev*qup, boolis_rx)
{drivers/i2c/busses/i2c-qup.c +0
-0qup->cur_blk_events =0 ;@@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len) · line 478
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c +0
-0return 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.
Quoted 107 lines
Quoted 107 lines
(quoted diff) +2
-0+/* + * Function to configure registers related with reconfigurationduring run
+ * and will be done before each I2C sub transfer.
+ */drivers/i2c/busses/i2c-qup.c +28
-0+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.
+ */drivers/i2c/busses/i2c-qup.c +68
-0+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. */
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1266
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c +6
-0+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.
Quoted 104 lines
Quoted 104 lines
drivers/i2c/busses/i2c-qup.c +6
-0+ 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 ifrx_tags_fetchedis 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 theQUP_BLK_EVENT_RX_DATA_DONEin current block event and
return.
+ */drivers/i2c/busses/i2c-qup.c +17
-0+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 datatx_fifo_dataand will be written to TX
+ * FIFO when all the 4 bytes are available.
+ */(quoted diff) +2
-0+static void +qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsignedint *len)
drivers/i2c/busses/i2c-qup.c +21
-0+{ +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. */
drivers/i2c/busses/i2c-qup.c +13
-0+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 iftx_tags_sentis false i.e. the start of QUP block so write the
+ * tags to TX FIFO.
+ * 2. Check ifsend_last_wordis 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 intx_fifo_dataso
write this
+ * in FIFO and mark the tx done.
+ * 3. Write the data to TX FIFO and check forcur_blk_len. If this is non zero
+ * then more data is pending otherwise following 3 cases can be
possible
+ * a. iftx_fifo_data_posis zero that means all the data bytes in
this block
+ * have been written in TX FIFO so mark the tx done.
+ * b.tx_fifo_freeis zero. In this case, last few bytes (less
than 4
+ * bytes) are copied totx_fifo_databut couldn't be sent
because of
+ * FIFO full so makesend_last_wordtrue.
+ * c.tx_fifo_freeis non zero i.e tx FIFO is free so copy the
remaining data
Quoted 9 lines
Quoted 6 lines
+ * from
tx_fifo_datato tx FIFO and mark the tx done. Since,
+ *qup_i2c_write_blk_datado write in 4 bytes and FIFO space is
in
+ * multiple of 4 bytes sotx_fifo_freewill 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.
Quoted 10 lines
Quoted 10 lines
+ */
drivers/i2c/busses/i2c-qup.c +9
-0+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; + }
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1369
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +5
-0+ +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_lenzero 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 fromtx_fifo_data
otherwise mark send_last_word true so that when space will be available then this function will just sent those remaining bytes.
Quoted 12 lines
Quoted 12 lines
drivers/i2c/busses/i2c-qup.c +12
-0+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 ; +
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1385
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c +4
-0+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.
Quoted 53 lines
Quoted 53 lines
drivers/i2c/busses/i2c-qup.c +4
-0+} + +/* +* 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.
+ */(quoted diff) +2
-0+static int +qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool
is_first,drivers/i2c/busses/i2c-qup.c +8
-0+ 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
(quoted diff) +1
-0+ * done into 2 QUP reads. One with message length 1 while other oneis
drivers/i2c/busses/i2c-qup.c +30
-0+* 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 );
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1438
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c +5
-0+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.
Quoted 104 lines
Quoted 104 lines
drivers/i2c/busses/i2c-qup.c +4
-0+reinit_completion (&qup->xfer); +enable_irq (qup->irq); +/* +* In FIFO mode, tx FIFO can be written directly while in block mode the
drivers/i2c/busses/i2c-qup.c +33
-0+* 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 ofblk_xfer_limitdata length blocks and schedule each
+ * QUP block individually.
+ */
+static intqup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id, bool is_rx)drivers/i2c/busses/i2c-qup.c +59
-0+{ +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
Quoted 9 lines
Quoted 6 lines
+ * 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.
Quoted 42 lines
Quoted 16 lines
+ * 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.
+ */(quoted diff) +2
-0+static int +qup_i2c_determine_mode(struct qup_i2c_dev *qup, struct i2c_msgQuoted 1 line
msgs[], int num)
qup_i2c_determine_mode_v2Quoted 22 lines
drivers/i2c/busses/i2c-qup.c +22
-0+{ +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;
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1582
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c +6
-0+ + 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.
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1584
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c +2
-0+ + if (!no_dma && qup->is_dma &&why do we need
is_dmaanduse_dma?
Now that you have removed the need foris_dmain 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.
Quoted 62 lines
Quoted 62 lines
drivers/i2c/busses/i2c-qup.c +17
-1+ (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 );drivers/i2c/busses/i2c-qup.c +14
-6if (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 );
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer_v2(struct i2c_adapter *adap, · line 1642
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c +2
-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
Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2
In reply to Abhishek SahuQuoted 560 lines · Abhishek Sahu
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 forOUT_BLOCK_WRITE_REQandIN_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_qup78b6000.i2c: timeout for fifo out fullTo 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_REQandIN_BLOCK_READ_REQinterrupts 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 generateOUT_BLOCK_WRITE_REQinterrupt 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 forQUP_MX_INPUT_DONEinterrupt which will be generated after all the bytes have been copied in RX FIFO. For read Block mode, QUP will generateIN_BLOCK_READ_REQinterrupts 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(-)drivers/i2c/busses/i2c-qup.c +45
-332@@ -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 ;
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1161
Quoted 7 lines
drivers/i2c/busses/i2c-qup.c +4
-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.
Quoted 104 lines
drivers/i2c/busses/i2c-qup.c +104
-0+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 );
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1275
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+ 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
Quoted 53 lines
drivers/i2c/busses/i2c-qup.c +53
-0+* 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 );
drivers/i2c/busses/i2c-qup.c static int qup_i2c_xfer(struct i2c_adapter *adap, · line 1338
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c +10
-0+ + 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
Quoted 427 lines
drivers/i2c/busses/i2c-qup.c +307
-52+* 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 ;
Signature · 3 lines
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.
Re: [PATCH 12/12] i2c: qup: reorganization of driver code to remove polling for qup v2
<snip>
Quoted 16 lines
Quoted 10 lines
static void qup_i2c_set_blk_event(struct
qup_i2c_dev*qup, boolis_rx)
{
qup->cur_blk_events= 0;(quoted diff) +1
-0@@ -1442,13 +1155,452 @@ static int qup_i2c_xfer(struct i2c_adapter *adap, return ret; } +/* + * Function to configure registers related with reconfigurationQuoted 3 lines
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.
Quoted 4 lines
"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