@samitouri / QOSamiQemu / commits / f41bc13152

tests/qtest: pca9552: test behaviour specific to the LED variant

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 <emmanuel.blot@free.fr> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-14-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater <clg@redhat.com>

Emmanuel Blot committed Jul 9, 2026 at 17:23 UTC f41bc13152c29261202c1394e3429188ac6c0fdd
1 file changed +76
tests/qtest/pca9552-test.c
+76
@@ -77,6 +77,76 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
77 g_assert_cmphex(value, ==, 0xEF);
78 }
79
80 +/* Verify the power-on reset defaults. */
81 +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc)
82 +{
83 + QI2CDevice *i2cdev = (QI2CDevice *)obj;
84 +
85 + /* Prescalers, PWM duty cycles and LED selectors (all LEDs off) */
86 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC0), ==, 0xFF);
87 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM0), ==, 0x80);
88 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC1), ==, 0xFF);
89 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM1), ==, 0x80);
90 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS0), ==, 0x55);
91 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS1), ==, 0x55);
92 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS2), ==, 0x55);
93 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS3), ==, 0x55);
94 +
95 + /* All LEDs off, so every pin floats high through its pull-up */
96 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT0), ==, 0xFF);
97 + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT1), ==, 0xFF);
98 +}
99 +
100 +/*
101 + * The PCA9552 only advances the command pointer when the AI bit is set, and
102 + * it wraps modulo the full 10-register map.
103 + */
104 +static void test_autoinc_requires_ai_bit(void *obj, void *data,
105 + QGuestAllocator *alloc)
106 +{
107 + QI2CDevice *i2cdev = (QI2CDevice *)obj;
108 + uint8_t reg;
109 + uint8_t resp;
110 +
111 + /*
112 + * With the AI bit, reading from LS3 (register 9) rolls over to INPUT0
113 + * (register 0), not to a sibling in a register pair. All LEDs are off
114 + * after reset so the input ports read 0xFF.
115 + */
116 + reg = PCA9552_LS3 | PCA9552_AUTOINC;
117 + qi2c_send(i2cdev, &reg, 1);
118 + qi2c_recv(i2cdev, &resp, 1); /* LS3 */
119 + g_assert_cmphex(resp, ==, 0x55);
120 + qi2c_recv(i2cdev, &resp, 1); /* wraps to INPUT0 */
121 + g_assert_cmphex(resp, ==, 0xFF);
122 + qi2c_recv(i2cdev, &resp, 1); /* INPUT1 */
123 + g_assert_cmphex(resp, ==, 0xFF);
124 +
125 + /*
126 + * Without the AI bit the pointer must not advance: repeated reads keep
127 + * returning the same register.
128 + */
129 + i2c_set8(i2cdev, PCA9552_LS0, 0x54);
130 + reg = PCA9552_LS0;
131 + qi2c_send(i2cdev, &reg, 1);
132 + qi2c_recv(i2cdev, &resp, 1);
133 + g_assert_cmphex(resp, ==, 0x54);
134 + qi2c_recv(i2cdev, &resp, 1);
135 + g_assert_cmphex(resp, ==, 0x54);
136 +}
137 +
138 +/*
139 + * The PCA9552 decodes a 4-bit command and has no register past LS3 (9), so
140 + * addressing register 0x0A reads back 0xFF.
141 + */
142 +static void test_command_out_of_range(void *obj, void *data,
143 + QGuestAllocator *alloc)
144 +{
145 + QI2CDevice *i2cdev = (QI2CDevice *)obj;
146 +
147 + g_assert_cmphex(i2c_get8(i2cdev, 0x0A), ==, 0xFF);
148 +}
149 +
150 static void pca9552_register_nodes(void)
151 {
152 QOSGraphEdgeOptions opts = {
@@ -89,5 +159,11 @@ static void pca9552_register_nodes(void)
159
160 qos_add_test("tx-rx", "pca9552", send_and_receive, NULL);
161 qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL);
162 + qos_add_test("reset-defaults", "pca9552", test_reset_defaults, NULL);
163 + qos_add_test("autoinc-requires-ai-bit", "pca9552",
164 + test_autoinc_requires_ai_bit, NULL);
165 + qos_add_test("command-out-of-range", "pca9552", test_command_out_of_range,
166 + NULL);
167 }
168 +
169 libqos_init(pca9552_register_nodes);