@samitouri / QOSamiQemu / commits / b3fa882508

tests/qtest: pca9554: test the PCA9536 4-bit variant

The PCA9536 shares the PCA9554 register map and code path but exposes only four pins. Add a pca9536 node and check its reset defaults and output-to-input reflection are masked to the low nibble. 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-23-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater <clg@redhat.com>

Emmanuel Blot committed Jul 9, 2026 at 17:23 UTC b3fa8825086dcf217ea34dd1073cfd2fc0a790c2
1 file changed +70
tests/qtest/pca9554-test.c
+70
@@ -129,6 +129,66 @@ static void test_no_autoincrement(void *obj, void *data,
129 g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x33);
130 }
131
132 +/*
133 + * The PCA9536 shares the PCA9554 register map but only has four pins, so its
134 + * reset defaults and pin logic are masked to the low nibble.
135 + */
136 +static void test_pca9536_reset_defaults(void *obj, void *data,
137 + QGuestAllocator *alloc)
138 +{
139 + QI2CDevice *dev = (QI2CDevice *)obj;
140 +
141 + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0F);
142 + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x0F);
143 + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x00);
144 + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0x0F);
145 +}
146 +
147 +/* Only the four low pins are driven; the upper nibble stays low. */
148 +static void test_pca9536_output_drives_input(void *obj, void *data,
149 + QGuestAllocator *alloc)
150 +{
151 + QI2CDevice *dev = (QI2CDevice *)obj;
152 +
153 + i2c_set8(dev, PCA9554_CONFIG, 0x00);
154 +
155 + i2c_set8(dev, PCA9554_OUTPUT, 0x0A);
156 + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0A);
157 +
158 + i2c_set8(dev, PCA9554_OUTPUT, 0x00);
159 + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00);
160 +}
161 +
162 +/*
163 + * The four upper bits address pins that do not exist on the PCA9536, so writes
164 + * to the register map discard them: the writable registers read back with bits
165 + * [7:4] cleared, and driving them onto the pins never surfaces in INPUT.
166 + */
167 +static void test_pca9536_ignores_upper_bits(void *obj, void *data,
168 + QGuestAllocator *alloc)
169 +{
170 + QI2CDevice *dev = (QI2CDevice *)obj;
171 +
172 + /* Bits [7:4] are dropped on write; bits [3:0] survive. */
173 + i2c_set8(dev, PCA9554_OUTPUT, 0xFA);
174 + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x0A);
175 +
176 + i2c_set8(dev, PCA9554_POLARITY, 0xF5);
177 + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x05);
178 +
179 + i2c_set8(dev, PCA9554_CONFIG, 0xF3);
180 + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0x03);
181 +
182 + /*
183 + * With all four pins as outputs, driving 0xFF only affects the low
184 + * nibble.
185 + */
186 + i2c_set8(dev, PCA9554_POLARITY, 0x00);
187 + i2c_set8(dev, PCA9554_CONFIG, 0x00);
188 + i2c_set8(dev, PCA9554_OUTPUT, 0xFF);
189 + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0F);
190 +}
191 +
192 static void pca9554_register_nodes(void)
193 {
194 QOSGraphEdgeOptions opts = {
@@ -148,6 +208,16 @@ static void pca9554_register_nodes(void)
208 qos_add_test("polarity-with-output", "pca9554", test_polarity_with_output,
209 NULL);
210 qos_add_test("no-autoincrement", "pca9554", test_no_autoincrement, NULL);
211 +
212 + qos_node_create_driver("pca9536", i2c_device_create);
213 + qos_node_consumes("pca9536", "i2c-bus", &opts);
214 +
215 + qos_add_test("reset-defaults", "pca9536", test_pca9536_reset_defaults,
216 + NULL);
217 + qos_add_test("output-drives-input", "pca9536",
218 + test_pca9536_output_drives_input, NULL);
219 + qos_add_test("ignores-upper-bits", "pca9536",
220 + test_pca9536_ignores_upper_bits, NULL);
221 }
222
223 libqos_init(pca9554_register_nodes);