espressif[risc-v|xtensa]: Check events when reading PCNT counter value

Previously, if an event was generated in hardware after taking spin
lock it was not correctly accounted for in current reading cycle.

Now, we check for events and compensate count accordingly.

Signed-off-by: Martin Vajnar <martin.vajnar@gmail.com>
This commit is contained in:
Martin Vajnar 2025-06-06 17:08:02 +02:00 committed by Xiang Xiao
parent 907b487eb7
commit 0c9931cc99
2 changed files with 58 additions and 8 deletions

View file

@ -388,12 +388,12 @@ static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context,
unit = &pcnt_units[unit_id];
pcnt_ll_clear_intr_status(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
event_status = pcnt_ll_get_event_status(ctx.dev, unit_id);
flags = spin_lock_irqsave(&unit->lock);
while (event_status)
{
int event_id = __builtin_ffs(event_status) - 1;
event_status &= (event_status - 1);
flags = spin_lock_irqsave(&unit->lock);
if (unit->config.accum_count)
{
if (event_id == PCNT_LL_WATCH_EVENT_LOW_LIMIT)
@ -406,7 +406,6 @@ static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context,
}
}
spin_unlock_irqrestore(&unit->lock, flags);
if (unit->cb)
{
data.unit_id = unit_id;
@ -417,6 +416,8 @@ static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context,
unit->cb(irq, context, &data);
}
}
spin_unlock_irqrestore(&unit->lock, flags);
}
return 0;
@ -634,6 +635,9 @@ static int esp_pcnt_unit_get_count(struct cap_lowerhalf_s *dev, int *ret)
{
struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev;
irqstate_t flags;
int32_t tmp_count;
uint32_t event_status;
uint32_t intr_status;
if (!priv->unit_used)
{
@ -642,8 +646,29 @@ static int esp_pcnt_unit_get_count(struct cap_lowerhalf_s *dev, int *ret)
}
flags = spin_lock_irqsave(&priv->lock);
*ret = pcnt_ll_get_count(ctx.dev, priv->unit_id) +
priv->accum_value;
tmp_count = pcnt_ll_get_count(ctx.dev, priv->unit_id);
if (priv->config.accum_count)
{
intr_status = pcnt_ll_get_intr_status(ctx.dev);
if (intr_status & PCNT_LL_UNIT_WATCH_EVENT(priv->unit_id))
{
event_status = pcnt_ll_get_event_status(ctx.dev, priv->unit_id);
if ((event_status & (1 << PCNT_LL_WATCH_EVENT_LOW_LIMIT)) && \
(tmp_count >= (priv->config.low_limit / 2)))
{
tmp_count += priv->config.low_limit;
}
else if ((event_status & \
(1 << PCNT_LL_WATCH_EVENT_HIGH_LIMIT)) && \
(tmp_count <= (priv->config.high_limit / 2)))
{
tmp_count += priv->config.high_limit;
}
}
}
*ret = tmp_count + priv->accum_value;
spin_unlock_irqrestore(&priv->lock, flags);
return OK;

View file

@ -416,12 +416,12 @@ static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context,
unit = &pcnt_units[unit_id];
pcnt_ll_clear_intr_status(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
event_status = pcnt_ll_get_event_status(ctx.dev, unit_id);
flags = spin_lock_irqsave(&unit->lock);
while (event_status)
{
int event_id = __builtin_ffs(event_status) - 1;
event_status &= (event_status - 1);
flags = spin_lock_irqsave(&unit->lock);
if (unit->config.accum_count)
{
if (event_id == PCNT_LL_WATCH_EVENT_LOW_LIMIT)
@ -434,7 +434,6 @@ static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context,
}
}
spin_unlock_irqrestore(&unit->lock, flags);
if (unit->cb)
{
data.unit_id = unit_id;
@ -445,6 +444,8 @@ static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context,
unit->cb(irq, context, &data);
}
}
spin_unlock_irqrestore(&unit->lock, flags);
}
return 0;
@ -671,6 +672,9 @@ static int esp_pcnt_unit_get_count(struct cap_lowerhalf_s *dev, int *ret)
{
struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev;
irqstate_t flags;
int32_t tmp_count;
uint32_t event_status;
uint32_t intr_status;
if (!priv->unit_used)
{
@ -679,8 +683,29 @@ static int esp_pcnt_unit_get_count(struct cap_lowerhalf_s *dev, int *ret)
}
flags = spin_lock_irqsave(&priv->lock);
*ret = pcnt_ll_get_count(ctx.dev, priv->unit_id) +
priv->accum_value;
tmp_count = pcnt_ll_get_count(ctx.dev, priv->unit_id);
if (priv->config.accum_count)
{
intr_status = pcnt_ll_get_intr_status(ctx.dev);
if (intr_status & PCNT_LL_UNIT_WATCH_EVENT(priv->unit_id))
{
event_status = pcnt_ll_get_event_status(ctx.dev, priv->unit_id);
if ((event_status & (1 << PCNT_LL_WATCH_EVENT_LOW_LIMIT)) && \
(tmp_count >= (priv->config.low_limit / 2)))
{
tmp_count += priv->config.low_limit;
}
else if ((event_status & \
(1 << PCNT_LL_WATCH_EVENT_HIGH_LIMIT)) && \
(tmp_count <= (priv->config.high_limit / 2)))
{
tmp_count += priv->config.high_limit;
}
}
}
*ret = tmp_count + priv->accum_value;
spin_unlock_irqrestore(&priv->lock, flags);
return OK;