Hi Jamin,
On Wed, 2024-09-25 at 11:34 +0800, Jamin Lin wrote:
> The interrupt status field is W1C, where a set bit on read indicates an
> interrupt is pending. If the bit extracted from data is set it should
> clear the corresponding bit in group_value. However, if the extracted
> bit is clear then the value of the corresponding bit in group_value
> should be unchanged.
>
> SHARED_FIELD_EX32() extracts the interrupt status bit from the write
> (data). group_value is set to the set's interrupt status, which means
> that for any pin with an interrupt pending, the corresponding bit is
> set. The deposit32() call updates the bit at pin_idx in the group,
> using the value extracted from the write (data).
>
> The result is that if multiple interrupt status bits
> were pending and the write was acknowledging specific one bit,
> then the all interrupt status bits will be cleared.
> However, it is index mode and should only clear the corresponding bit.
>
> For example, say we have an interrupt pending for GPIOA0, where the
> following statements are true:
>
> set->int_status == 0b01
> s->pending == 1
>
> Before it is acknowledged, an interrupt becomes pending for GPIOA1:
>
> set->int_status == 0b11
> s->pending == 2
>
> A write is issued to acknowledge the interrupt for GPIOA0. This causes
> the following sequence:
>
> reg_value == 0b11
> pending == 2
> s->pending = 0
Note that I had a typo in my reply that prompted this patch, this was
meant to be:
s->pending == 0
> set->int_status == 0b00
>
> It should only clear bit 0 in index mode and the correct result
> should be as following.
>
> set->int_status == 0b11
> s->pending == 2
>
> pending == 1
> s->pending = 1
Same typo here now :)
s->pending == 1
> set->int_status == 0b10
>
> Signed-off-by: Jamin Lin <jamin_...@aspeedtech.com>
> ---
> hw/gpio/aspeed_gpio.c | 19 ++++++++++---------
> 1 file changed, 10 insertions(+), 9 deletions(-)
>
> diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
> index 6e6ab48b56..58ae63e3c1 100644
> --- a/hw/gpio/aspeed_gpio.c
> +++ b/hw/gpio/aspeed_gpio.c
> @@ -642,7 +642,7 @@ static void aspeed_gpio_write_index_mode(void *opaque,
> hwaddr offset,
> uint32_t pin_idx = reg_idx_number % ASPEED_GPIOS_PER_SET;
> uint32_t group_idx = pin_idx / GPIOS_PER_GROUP;
> uint32_t reg_value = 0;
> - uint32_t cleared;
> + uint32_t pending = 0;
>
> set = &s->sets[set_idx];
> props = &agc->props[set_idx];
> @@ -705,15 +705,16 @@ static void aspeed_gpio_write_index_mode(void *opaque,
> hwaddr offset,
> set->int_sens_2 = update_value_control_source(set, set->int_sens_2,
> reg_value);
> /* set interrupt status */
> - reg_value = set->int_status;
> - reg_value = deposit32(reg_value, pin_idx, 1,
> - FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS));
> - cleared = ctpop32(reg_value & set->int_status);
> - if (s->pending && cleared) {
> - assert(s->pending >= cleared);
> - s->pending -= cleared;
> + if (FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS)) {
> + pending = extract32(set->int_status, pin_idx, 1);
> + if (pending) {
> + if (s->pending) {
> + assert(s->pending >= pending);
> + s->pending -= pending;
> + }
> + set->int_status = deposit32(set->int_status, pin_idx, 1, 0);
> + }
So I think we can get away without the nested conditionals?
if (FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS)) {
/* pending is either 1 or 0 for a 1-bit field */
pending = extract32(set->int_status, pin_idx, 1);
/*
* The assert is effectively a compressed form of
*
* assert((s->pending == 0 && pending == 0) ||
* (s->pending >= 1));
*
* This seems reasonable.
*
* Another way to write it is:
*
* assert(!pending || s->pending));
*/
assert(s->pending >= pending);
/* No change to s->pending if pending is 0 */
s->pending -= pending;
/*
* The write acknowledged the interrupt regardless of whether it
* was pending or not. The post-condition is that it mustn't be
* pending. Unconditionally clear the status bit.
*/
s->int_status = deposit32(set->int_status, pin_idx, 1, 0);
}
Thoughts?
Up to you whether you keep the comments if the idea makes sense and you
choose to adopt it.
Andrew