TDA4AP-Q1: Linux Driver ti-ecap-capture.c Only Sets One Interrupt Per 4 Captures

Part Number: TDA4AP-Q1

Hi,

I have an issue with reading ecap captures from /dev/counter* in Linux. Note: On the input is FAN with about 4800 rpm with 2 positive edges per revolution.

I add 4 watches:

   counter_watch watch;
   watch.component.type = COUNTER_COMPONENT_COUNT;
   watch.component.scope = COUNTER_SCOPE_COUNT;
   watch.component.parent = properties.count;
   watch.component.id = 0;
   watch.event = COUNTER_EVENT_CAPTURE;
   watch.channel = 0; // 0 .. numCapture-1.
   for (const size_t i : {0,1,2,3})
   {
      watch.channel = i;
      if (IOCTL_ERR == ioctl(m_chardevFd, COUNTER_ADD_WATCH_IOCTL, &watch))
      {
         log("Failed add watch#", errno);
         return false;
      }
   }

Then I enable events, 1 > enable and can read events. However the events have a strange timing (example of 9 events):

Timestamp 0: 256226416085143    Value 0: 2161223
Timestamp 1: 256226416095528    Value 1: 2162419
Timestamp 2: 256226416098028    Value 2: 2162575
Timestamp 3: 256226416099319    Value 3: 2162731
Timestamp 4: 256226441692567    Value 4: 5361771
Timestamp 5: 256226441699117    Value 5: 5362551
Timestamp 6: 256226441700607    Value 6: 5362759
Timestamp 7: 256226441702047    Value 7: 5362915
Timestamp 8: 256226467304975    Value 8: 8563151
Timestamp 9: 256226467311111    Value 9: 8563905
Diff 0: 10385 ms
Diff 1: 2500 ms
Diff 2: 1291 ms
Diff 3: 25593248 ms
Diff 4: 6550 ms
Diff 5: 1490 ms
Diff 6: 1440 ms
Diff 7: 25602928 ms
Diff 8: 6136 ms
Standard deviation: 10640372 ms
Average difference: 5691774 ms

4 events come within a few microsecconds, so I suspect there is no contex switch happening between them.The big gap between the 4 is nearly exactly the time for 2 fan revolutions.
So I had a look at the kernel driver ti-ecap-capture.c:

static irqreturn_t ecap_cnt_isr(int irq, void *dev_id)
{
...
        regmap_read(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG, &flg);

        /* Check capture events */
        for (i = 0 ; i < ECAP_NB_CEVT ; i++) {
                if (flg & ECAP_EVT_FLG_BIT(i)) {
                        counter_push_event(counter_dev, COUNTER_EVENT_CAPTURE, i);
                        clr |= ECAP_EVT_CLR_BIT(i);
                }
        }
...
}


This interupt sevice routine sends the events for eached latched interupt. My suspicion is that the same interrupt send all 4 events.

In the initialisation we can find this:

static void ecap_cnt_capture_enable(struct counter_device *counter)
{
..
        regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG,
                           ECAP_EVT_EN_MASK, ECAP_EVT_EN_MASK);
...
}


Where ECAP_EVT_EN_MASK  translates to 0b00011000. According to the Reference manual (p1861)

Seven interrupt events (CEVT1, CEVT2, CEVT3, CEVT4, CNTOVF, TSCNT = PRD, TSCNT = CMP) can be generated. The interrupt enable register (ECAP_ECINT_EN_FLG) is used to enable/disable individual interrupt event sources. The interrupt flag register (ECAP_ECINT_EN_FLG) indicates if any interrupt event has been latched and contains the global interrupt flag ECAP_ECINT_EN_FLG[16] INT_FLG bit. An interrupt pulse is generated to the interrupt controller only if any of the interrupt events are enabled, the flag bit is 1h, and the INT_FLG flag bit is 0h. The interrupt service routine must clear the global interrupt flag bit and the serviced event via the interrupt clear register (ECAP_ECINT_CLR_FRC) before any other interrupt pulses are generated. The interrupt force register (ECAP_ECINT_CLR_FRC) can force an interrupt event. This is useful for test purposes.

this means, that an interrupt is only sent for CEVT4 (=capture3) or CNTOVF (overflow). Not for the other 3 captures.

Am I correct with this analysis? Is this a bug in the driver?

My workaround is to only activate one watch and divide the period time accordingly. This would also work when the issue is fixed. This workaround is ok for fan control but might be an issue to other applications.

  • Hi,

    Thanks for the details and which SDK are you using? The pwm support is validated but the ecap part is not. Currently I would recommend going with the workaround.

    Beste Regards,

    Keerthy

  • Felix,

    As told earlier if it's okay please go ahead with the workaround.

    Best Regards,

    Keerthy 

  • I will. But don't you think this should be fixed? It took me hours to figure out and might be a problem to other applications like reading PWM.
    And can you give me a clear yes or no whether my observations are correct?

  • Felix,

    I am checking internally on this. I will get back once I have more feedback from the internal experts.

    Thanks,
    Keerthy

  • Hi Felix,

    Thanks for your patience. Below is the feedback from the expert:

    1) "this means, that an interrupt is only sent for CEVT4 (=capture3) or CNTOVF (overflow). Not for the other 3 captures. Am I correct with this analysis? Is this a bug in the driver?"

     

    Response: Yes, that's correct. This is not a bug in the driver.

    An interrupt is triggered for CEVT4 only and then, in the interrupt, CEVT1/2/3/4_FLG bits are checked to push the associated events to the user space (if the associated bit is set). The driver is designed for continuous measurements.

    An interrupt can also be triggered for CNTOVF, indeed.

    2) Little bit confused by the figures provided below:

    "Timestamp 0: 256226416085143"

    "Timestamp 1: 256226416095528"

    "Diff 0: 10385 ms"

    Where do these timestamps come from ? It's probably a SW timestamp, so it's not very reliable. Hence should use HW "count" values to compute the frequency ("Value 0, Value 1", etc...).

    ECAP device base clock ('frequency').

     

    So, it can be converted in "human" time by using the following info:

       /sys/bus/counter/devices/counterX/signalY/frequency

       /sys/bus/counter/devices/counterX/countY/num_overflows

       /sys/bus/counter/devices/counterX/countY/ceiling

       /sys/bus/counter/devices/counterX/countY/count

     

    Something like below:

    time_s = [(num_overflows * ceiling) + counter_value] / (2 * frequency) Not sure for the factor 2, but as far as I remember the count is incremented for both falling/rising edges of ECAP base clock)

     
    Counter API is not that trivial.

     

    3) Important stuff: results should not be printed directly to stdout while running the user app for high frequency signals.

    For data consistency analysis, stdout must be redirected to a log file (maybe that is done already, ...it's just to be sure).

     

    4) The driver is meant to work for "reasonable" frequencies: a few kHz.

    It's likely that it's signal (4800 rpm, with 2 edges per revolution) is close to the limit. For higher frequencies, a prescaler exists but it was not implemented in the driver.

    Best Regards,
    Keerthy

  • 1) As you can see from the logged values above (there are always 4 very close to each other), the chardevice-events do not contain the values in the capture register. They contain the counter value at the time of the interrupt. counter_push_event(counter_dev, COUNTER_EVENT_CAPTURE, i); cannot send captured registers as counter_dev does only know about the counter register, not the capture registers.

    This is what I would still consider a bug, even if "only one interrupt" is a feature.

    2) The shown timestamp is:

    timestamp: best estimate of time of event occurrence, in nanoseconds

    https://elixir.bootlin.com/linux/v6.9/source/include/uapi/linux/counter.h#L106

    This is, again, the timestamp at the time of the interrupt!

    I know how to calculate it from the counter values, but for above reasons this it not much more precise. In my expirience the timestamp is precise enough.

    3) Yes. Otherwise the event buffer might overflow (64 entries, see sysfs events_queue_size).

    4) At 4800rpm with 2 edges per revolution we have: 160Hz. This should be well below "a few kHrz". Since we only get an chardevice-event every 4 events (the workaround) this is even less a problem.

  • Felix,

    I will check with the expert again, '1' is the open issue that needs to be addressed as per the findings here. This driver is NOT validated on the j784s4.
    I will need to check with the internal team on when this will be supported in the j784s4 SDK.

    Till then support will be limited.

    Thanks,
    Keerthy

  • Felix, 

    No updates on this. The SDK for j784s4 is not validated for this. Check future release notes for this. 

    Best Regards,

    Keerthy