Reviewed-by: Glenn Miles <[email protected]>

Thanks,

Glenn

On Thu, 2026-07-09 at 17:23 +0200, Emmanuel Blot wrote:
> The PCA9552 shares its device model with the PCA9535/PCA9555 GPIO
> expanders but decodes registers differently. Add tests for the behaviour
> that is specific to the LED variant and diverges from the PCA9555:
> 
>   - the power-on reset defaults of the prescaler, PWM and LED-selector
>     registers;
>   - the prescaler/PWM registers (2-5), which are OUTPUT/POLARITY on the
>     PCA9555, as plain read/write storage;
>   - the auto-increment, which only advances when the AI command bit is set
>     and wraps modulo the full 10-register map (rather than toggling bit 0
>     within a register pair);
>   - the 4-bit command decode, where an out-of-range register reads back
>     0xFF instead of aliasing into the register window.
> 
> Signed-off-by: Emmanuel Blot <[email protected]>
> ---
>  tests/qtest/pca9552-test.c | 76 
> ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 76 insertions(+)
> 
> diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
> index 74749576923..3718dfbd227 100644
> --- a/tests/qtest/pca9552-test.c
> +++ b/tests/qtest/pca9552-test.c
> @@ -77,6 +77,76 @@ static void send_and_receive(void *obj, void *data, 
> QGuestAllocator *alloc)
>      g_assert_cmphex(value, ==, 0xEF);
>  }
>  
> +/* Verify the power-on reset defaults. */
> +static void test_reset_defaults(void *obj, void *data, QGuestAllocator 
> *alloc)
> +{
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +
> +    /* Prescalers, PWM duty cycles and LED selectors (all LEDs off) */
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC0), ==, 0xFF);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM0), ==, 0x80);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC1), ==, 0xFF);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM1), ==, 0x80);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS0), ==, 0x55);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS1), ==, 0x55);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS2), ==, 0x55);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS3), ==, 0x55);
> +
> +    /* All LEDs off, so every pin floats high through its pull-up */
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT0), ==, 0xFF);
> +    g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT1), ==, 0xFF);
> +}
> +
> +/*
> + * The PCA9552 only advances the command pointer when the AI bit is set, and
> + * it wraps modulo the full 10-register map.
> + */
> +static void test_autoinc_requires_ai_bit(void *obj, void *data,
> +                                         QGuestAllocator *alloc)
> +{
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +    uint8_t reg;
> +    uint8_t resp;
> +
> +    /*
> +     * With the AI bit, reading from LS3 (register 9) rolls over to INPUT0
> +     * (register 0), not to a sibling in a register pair. All LEDs are off
> +     * after reset so the input ports read 0xFF.
> +     */
> +    reg = PCA9552_LS3 | PCA9552_AUTOINC;
> +    qi2c_send(i2cdev, &reg, 1);
> +    qi2c_recv(i2cdev, &resp, 1); /* LS3 */
> +    g_assert_cmphex(resp, ==, 0x55);
> +    qi2c_recv(i2cdev, &resp, 1); /* wraps to INPUT0 */
> +    g_assert_cmphex(resp, ==, 0xFF);
> +    qi2c_recv(i2cdev, &resp, 1); /* INPUT1 */
> +    g_assert_cmphex(resp, ==, 0xFF);
> +
> +    /*
> +     * Without the AI bit the pointer must not advance: repeated reads keep
> +     * returning the same register.
> +     */
> +    i2c_set8(i2cdev, PCA9552_LS0, 0x54);
> +    reg = PCA9552_LS0;
> +    qi2c_send(i2cdev, &reg, 1);
> +    qi2c_recv(i2cdev, &resp, 1);
> +    g_assert_cmphex(resp, ==, 0x54);
> +    qi2c_recv(i2cdev, &resp, 1);
> +    g_assert_cmphex(resp, ==, 0x54);
> +}
> +
> +/*
> + * The PCA9552 decodes a 4-bit command and has no register past LS3 (9), so
> + * addressing register 0x0A reads back 0xFF.
> + */
> +static void test_command_out_of_range(void *obj, void *data,
> +                                      QGuestAllocator *alloc)
> +{
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +
> +    g_assert_cmphex(i2c_get8(i2cdev, 0x0A), ==, 0xFF);
> +}
> +
>  static void pca9552_register_nodes(void)
>  {
>      QOSGraphEdgeOptions opts = {
> @@ -89,5 +159,11 @@ static void pca9552_register_nodes(void)
>  
>      qos_add_test("tx-rx", "pca9552", send_and_receive, NULL);
>      qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL);
> +    qos_add_test("reset-defaults", "pca9552", test_reset_defaults, NULL);
> +    qos_add_test("autoinc-requires-ai-bit", "pca9552",
> +                 test_autoinc_requires_ai_bit, NULL);
> +    qos_add_test("command-out-of-range", "pca9552", 
> test_command_out_of_range,
> +                 NULL);
>  }
> +
>  libqos_init(pca9552_register_nodes);
> 


Reply via email to