On 10/08/2018 02:45 PM, Tomi Valkeinen wrote:
> Hi Hans,
>
> On 04/10/18 12:08, Hans Verkuil wrote:
>> From: Hans Verkuil <[email protected]>
>>
>> The TX FIFO has to be cleared if the transmit failed due to e.g.
>> a NACK condition, otherwise the hardware will keep trying to
>> transmit the message.
>>
>> An attempt was made to do this, but it was done after the call to
>> cec_transmit_done, which can cause a race condition since the call
>> to cec_transmit_done can cause a new transmit to be issued, and
>> then attempting to clear the TX FIFO will actually clear the new
>> transmit instead of the old transmit and the new transmit simply
>> never happens.
>>
>> By clearing the FIFO before transmit_done is called this race
>> is fixed.
>>
>> Note that there is no reason to clear the FIFO if the transmit
>> was successful, so the attempt to clear the FIFO in that case
>> was dropped.
>>
>> Signed-off-by: Hans Verkuil <[email protected]>
>> ---
>> drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c | 35 ++++++++++++-------------
>> 1 file changed, 17 insertions(+), 18 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c
>> b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c
>> index 340383150fb9..dee66a5101b5 100644
>> --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c
>> +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c
>> @@ -106,6 +106,22 @@ static void hdmi_cec_received_msg(struct hdmi_core_data
>> *core)
>> }
>> }
>>
>> +static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap)
>> +{
>> + struct hdmi_core_data *core = cec_get_drvdata(adap);
>> + int retry = HDMI_CORE_CEC_RETRY;
>> + int temp;
>> +
>> + REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
>> + while (retry) {
>> + temp = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
>> + if (FLD_GET(temp, 7, 7) == 0)
>> + break;
>
> This is fine, but as you're using the helper macros already, there's
> REG_GET:
>
> REG_GET(core->base, HDMI_CEC_DBG_3, 7, 7)
>
> which removes the need for temp. Are you sure this works reliably?
> Usually when polling a register bit, I like to measure real-world-time
> in some way to ensure I actually poll for a certain amount of time.
I'll add a bit of debugging to double-check but as far as I remember this
is very fast and adding delays is overkill.
FYI: we (Cisco) use this code in our products and we'd have seen it if this
would fail.
>
> And just a matter of opinion, but I would've written:
>
> while (retry) {
> if (!REG_GET(..))
> return true;
> retry--;
> }
>
> return false;
>
>> + retry--;
>> + }
>> + return retry != 0;
>> +}
>> +
In this patch I just moved up the hdmi_cec_clear_tx_fifo so I can use it in
hdmi4_cec_irq. I rather not make any changes to that function.
Unless you object I prefer to make a new patch for 4.21 to improve it.
>> void hdmi4_cec_irq(struct hdmi_core_data *core)
>> {
>> u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0);
>> @@ -117,36 +133,19 @@ void hdmi4_cec_irq(struct hdmi_core_data *core)
>> if (stat0 & 0x20) {
>> cec_transmit_done(core->adap, CEC_TX_STATUS_OK,
>> 0, 0, 0, 0);
>> - REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
>> } else if (stat1 & 0x02) {
>> u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
>>
>> + hdmi_cec_clear_tx_fifo(core->adap);
>
> Would a dev_err be ok here?
Hmm. I would prefer a dev_err_once. Chances are that if this ever fails, it
might fail continuously (as in: something is very seriously wrong), and you
don't want that in an irq function.
Regards,
Hans