lkml.org 
[lkml]   [2018]   [Feb]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
SubjectRe: [PATCH 11/12] i2c: qup: reorganization of driver code to remove polling for qup v1
From
Date
Hi Abhishek,

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

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

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

Regards,
Sricharan

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

\
 
 \ /
  Last update: 2018-02-20 05:33    [W:0.085 / U:0.100 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site