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);

-- 
2.50.1


Reply via email to