259
}
260
261
/*
262
- * When Auto-Increment is on, the register address is incremented
263
- * after each byte is sent to or received by the device. The index
264
- * rollovers to 0 when the maximum register address is reached.
262
+ * Advance the command pointer after each byte sent to or received from the
263
+ * device.
264
+ *
265
+ * The LED variant auto-increments only when the AI bit (bit 4) is set in the
266
+ * command byte, rolling over to 0 once the maximum register address is
267
+ * reached.
268
+ *
269
+ * The GPIO variants auto-increment on every access, toggling bit 0 so the
270
+ * pointer stays within the addressed register pair
271
+ * (input/output/polarity/config), as specified by their datasheet.
272
*/
273
static void pca955x_autoinc(PCA955xState *s)
274
{
275
PCA955xClass *k = PCA955X_GET_CLASS(s);
276
277
+ if (!k->has_led_support) {
278
+ s->pointer ^= 0x1;
279
+ return;
280
+ }
281
+
282
if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
283
uint8_t reg = s->pointer & 0xf;
284
287
}
288
}
289
290
+/*
291
+ * The LED variant addresses its registers with a 4-bit command field, while
292
+ * the GPIO variants only decode 3 bits (the command wraps into the 8-register
293
+ * window).
294
+ */
295
+static inline uint8_t pca955x_cmd_reg(PCA955xState *s)
296
+{
297
+ PCA955xClass *k = PCA955X_GET_CLASS(s);
298
+
299
+ return s->pointer & (k->has_led_support ? 0xf : 0x7);
300
+}
301
+
302
static uint8_t pca955x_recv(I2CSlave *i2c)
303
{
304
PCA955xState *s = PCA955X(i2c);
305
+ PCA955xClass *k = PCA955X_GET_CLASS(s);
306
uint8_t ret;
307
283
- ret = pca955x_read(s, s->pointer & 0xf);
308
+ ret = pca955x_read(s, pca955x_cmd_reg(s));
309
310
/*
311
* From the Specs:
317
* I don't know what should be done in this case, so throw an
318
* error.
319
*/
295
- if (s->pointer == PCA9552_AUTOINC) {
320
+ if (k->has_led_support && s->pointer == PCA9552_AUTOINC) {
321
qemu_log_mask(LOG_GUEST_ERROR,
322
"%s: Autoincrement read starting with register 0\n",
323
__func__);
337
s->pointer = data;
338
s->len++;
339
} else {
315
- pca955x_write(s, s->pointer & 0xf, data);
340
+ pca955x_write(s, pca955x_cmd_reg(s), data);
341
342
pca955x_autoinc(s);
343
}