[PATCH] clocksource: imx-tpm: Wait for CnV write to take effect
Daniel Lezcano
daniel.lezcano at linaro.org
Wed Oct 11 02:36:38 PDT 2023
On 11/10/2023 10:44, Jacky Bai wrote:
> Hi Daniel,
>
>> Subject: Re: [PATCH] clocksource: imx-tpm: Wait for CnV write to take effect
>>
>> On 09/10/2023 10:42, Jacky Bai wrote:
>>> The value write into the TPM CnV can only be updated into the HW when
>>> CNT increase. Additional writes to the CnV write buffer are ignored
>>> until the register has been updated. So we need to check if the CnV
>>> has been updated before continue. Wait for 1 CNT cycle in worst case.
>>
>> Is that a fix for an erratic observed behavior on the platform ?
>
> It is an IP requirement that we overlooked before. This driver originally used by NXP
> i.MX7ULP platform, now we use the same driver on NXP i.MX8ULP(arm64). due to the SoC
> arch and bus fabric(bus speed, core speed etc) difference, we meet the issue that
> if two consecutive 'tpm_set_next_event' call happens in one TPM CNT cycle, the
> secondary write will be ignored by the HW. So we need to wait at least one CNT cycle
> to make sure the first write has been updated successfully.
>
>>
>>> Additionally, current return check is not correct, if a max_delta need
>>> be set, it will return '-ETIME' wrongly due to the 'int' type cast, so
>>> refine the check logic to fix it.
>>
>> Please put that in a separate patch and double check the fix (now -
>> prev) which seems to be always nearly one CNT
>>
>
> Ok, will split it int a separate patch. It is expected, we need to make sure
> That now - prev should be less than the delta we want to set, otherwise
> It means the TPM will lost the compare event as the TPM can only generate
> Irq when CNT=C0V, if CNT > C0V, no irq. An ideal design should be IRQ assert when
> CNT >= C0V, but it is not the case for this IP.
>
>>
>> Fixes tag missing
>
> Ok, will be fixed.
>
>>
>>> Signed-off-by: Jacky Bai <ping.bai at nxp.com>
>>> ---
>>> drivers/clocksource/timer-imx-tpm.c | 16 ++++++++++++----
>>> 1 file changed, 12 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/clocksource/timer-imx-tpm.c
>> b/drivers/clocksource/timer-imx-tpm.c
>>> index bd64a8a8427f..92c025b70eb6 100644
>>> --- a/drivers/clocksource/timer-imx-tpm.c
>>> +++ b/drivers/clocksource/timer-imx-tpm.c
>>> @@ -83,20 +83,28 @@ static u64 notrace tpm_read_sched_clock(void)
>>> static int tpm_set_next_event(unsigned long delta,
>>> struct clock_event_device *evt)
>>> {
>>> - unsigned long next, now;
>>> + unsigned long next, prev, now;
>>>
>>> - next = tpm_read_counter();
>>> - next += delta;
>>> + prev = tpm_read_counter();
>>> + next = prev + delta;
>>> writel(next, timer_base + TPM_C0V);
>>> now = tpm_read_counter();
>>>
>>> + /*
>>> + * Need to wait CNT increase at least 1 cycle to make sure
>>> + * the C0V has been updated into HW.
>>> + */
>>> + if ((next & 0xffffffff) != readl(timer_base + TPM_C0V))
>>
>> Why do you use a mask for 'next' ?
>>
>
> 'unsigned long' is 64 bit on arm64 platform, while the register is 32bit, so
> the upper 32bit cleared before comparing.
But next is unsigned long and readl returns a long, no ?
>>> + while (now == tpm_read_counter())
>>> + ;
>>
>> IIUC, we can rely on one of these condition:
>>
>> Check against timer_base + TPM_C0V or tpm_read_counter()
>>
>> We can use the following routine:
>> while (readl(timer_base + TPM_C0V) != next);
>>
>> because as soon as 1 CNT cycle happened, TPM_C0V is updated.
>
> C0V does not update based the cnt cycle. it is static.
The description says it is not possible to write multiple value to the
TPM_C0V register within a CNT cycle.
The code writes to the register: writel(next, timer_base + TPM_C0V);
and then read the value back to check if it is different. That assumes,
the cycle did not happen yet and we wait for this cycle. Am I correct?
Given the initial statement, IMO that could be simplified to:
- read the TPM_C0V until the value is equal to the one set
or
- read the tpm_read_counter() until one CNT happened
or
- wait a duration corresponding to the cycle with udelay (which should
be equivalent to the tpm_read_counter()
IOW, it is just about adding a tempo after writing the TPM_C0V register
>> Other version could be:
>>
>> while (now == tpm_read_counter());
>>
> Same as the originally changes, but with ';' and 'while' in the same line?
No, both propositions are without the if statement
>> Or simply:
>>
>> usleep_range(min, max);
>>
>
> This function is called by the clock event framework with irq disable,
> Sleep procedure may be not suitable?
Yes, you are right. udelay() could be used.
> BR
>> Right?
>>
>>> /*
>>> * NOTE: We observed in a very small probability, the bus fabric
>>> * contention between GPU and A7 may results a few cycles delay
>>> * of writing CNT registers which may cause the min_delta event
>> got
>>> * missed, so we need add a ETIME check here in case it happened.
>>> */
>>> - return (int)(next - now) <= 0 ? -ETIME : 0;
>>> + return (now - prev) >= delta ? -ETIME : 0;
>>> }
>>>
>>> static int tpm_set_state_oneshot(struct clock_event_device *evt)
>>
>> --
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
More information about the linux-arm-kernel
mailing list