]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Merge tag 'pull-request-2024-03-25' of https://gitlab.com/thuth/qemu into staging
authorPeter Maydell <peter.maydell@linaro.org>
Mon, 25 Mar 2024 14:19:42 +0000 (14:19 +0000)
committerPeter Maydell <peter.maydell@linaro.org>
Mon, 25 Mar 2024 14:19:42 +0000 (14:19 +0000)
* Fix timeouts in Travis-CI jobs
* Mark devices with user_creatable = false that can crash QEMU otherwise
* Fix s390x TEST-AND-SET TCG instruction emulation
* Move pc955* devices to hw/gpio/

# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAmYBhdgRHHRodXRoQHJl
# ZGhhdC5jb20ACgkQLtnXdP5wLbVcfA/9FulEN4HrjD3ObyboA+WfibXURwChui98
# 8LvL/fAGe3BZXQtspuNmPyrKRtIOrIwHJyFuxf2N5+8BuvGhEHQIuvIhQIj/rvfy
# X14KlldmQ3w3HlI3Ud4YiebLyK3AAFC2ApIywzFsnN+HoaHJR2EyDIb+T7OsGJZf
# ZLE/Z7qANxoNeZ+a3+rQR3SVpijyS3fXxDSaILrq2uW4kCCs/55O8Rt3Qb+PFSVd
# fF+OlpG6o+z73ACZc1u9Io4IO1ZZc/NdkmDTNz4HknkvJLTLF6kOECAxLl0ytgAG
# YRzBGKes29Zpa9wn/9rc75/OYNS0Ks+B19sQnijWUNX0zq5FkReXNXiyVcbT7d4p
# 6jFzlFnjj4ifB8uQkZTGcx/lL4s4VkPzF+f7fgHq9CKNrNsx8uca0TyQ8s4y+NGb
# C98kJdHd+QhCcuNnAbifCwuFaxQ8C4BdgzxVbU/sGDKNkINNkiTp+uue4TxnRKvV
# MfhqdnWRvqgZ0Rl4TxqcNfODK72Z1YNv3933OKE/mRJYS1Q529TIq4vfp8WIMsWQ
# 7+ipo4WKXhkiSOJZD6AkCoFum1W8yaDzUDJTw2Xt2bPBL3+FXcQyKkKVUMfzIJ8M
# KLe0Bb9W/pYU1ToTciTP0dkQF/02tG0Vik273445wPgH0x8OyHJkPF/ny1a7lKFO
# 5jreYdMxWdc=
# =lfZM
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 25 Mar 2024 14:10:32 GMT
# gpg:                using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5
# gpg:                issuer "thuth@redhat.com"
# gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full]
# gpg:                 aka "Thomas Huth <thuth@redhat.com>" [full]
# gpg:                 aka "Thomas Huth <huth@tuxfamily.org>" [full]
# gpg:                 aka "Thomas Huth <th.huth@posteo.de>" [unknown]
# Primary key fingerprint: 27B8 8847 EEE0 2501 18F3  EAB9 2ED9 D774 FE70 2DB5

* tag 'pull-request-2024-03-25' of https://gitlab.com/thuth/qemu:
  tests/tcg/s390x: Test TEST AND SET
  target/s390x: Use mutable temporary value for op_ts
  libqos/virtio.c: Correct 'flags' reading in qvirtqueue_kick
  misc/pca955*: Move models under hw/gpio
  aspeed: Make the ast1030-a1 SoC not user creatable
  aspeed: Make the ast2600-a3 SoC not user creatable
  hw/microblaze: Do not allow xlnx-zynqmp-pmu-soc to be created by the user
  .travis.yml: Remove the unused xfslib-dev package
  .travis.yml: Shorten the runtime of the problematic jobs

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
28 files changed:
.travis.yml
MAINTAINERS
hw/arm/aspeed.c
hw/arm/aspeed_ast10x0.c
hw/arm/aspeed_ast2600.c
hw/gpio/meson.build
hw/gpio/pca9552.c [new file with mode: 0644]
hw/gpio/pca9554.c [new file with mode: 0644]
hw/gpio/trace-events
hw/microblaze/xlnx-zynqmp-pmu.c
hw/misc/meson.build
hw/misc/pca9552.c [deleted file]
hw/misc/pca9554.c [deleted file]
hw/misc/trace-events
include/hw/gpio/pca9552.h [new file with mode: 0644]
include/hw/gpio/pca9552_regs.h [new file with mode: 0644]
include/hw/gpio/pca9554.h [new file with mode: 0644]
include/hw/gpio/pca9554_regs.h [new file with mode: 0644]
include/hw/misc/pca9552.h [deleted file]
include/hw/misc/pca9552_regs.h [deleted file]
include/hw/misc/pca9554.h [deleted file]
include/hw/misc/pca9554_regs.h [deleted file]
target/s390x/tcg/translate.c
tests/qtest/libqos/virtio.c
tests/qtest/pca9552-test.c
tests/qtest/pnv-host-i2c-test.c
tests/tcg/s390x/Makefile.target
tests/tcg/s390x/ts.c [new file with mode: 0644]

index 597d151b802d7b8e31998e986fab8aef00baef3c..8a3ae76a7c3549d9ddffee84d10e9a8d46ddd1be 100644 (file)
@@ -184,8 +184,8 @@ jobs:
           - genisoimage
       env:
         - TEST_CMD="make check check-tcg V=1"
-        - CONFIG="--disable-containers --enable-fdt=system
-                  --target-list=${MAIN_SYSTEM_TARGETS},s390x-linux-user"
+        - CONFIG="--disable-containers
+            --target-list=hppa-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
         - UNRELIABLE=true
       script:
         - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
@@ -220,13 +220,12 @@ jobs:
           - libsnappy-dev
           - libzstd-dev
           - nettle-dev
-          - xfslibs-dev
           - ninja-build
           # Tests dependencies
           - genisoimage
       env:
-        - CONFIG="--disable-containers --enable-fdt=system --audio-drv-list=sdl
-                  --disable-user --target-list-exclude=${MAIN_SYSTEM_TARGETS}"
+        - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user
+            --target-list=arm-softmmu,avr-softmmu,microblaze-softmmu,sh4eb-softmmu,sparc64-softmmu,xtensaeb-softmmu"
 
     - name: "[s390x] GCC (user)"
       arch: s390x
@@ -240,6 +239,7 @@ jobs:
           - flex
           - bison
       env:
+        - TEST_CMD="make check check-tcg V=1"
         - CONFIG="--disable-containers --disable-system"
 
     - name: "[s390x] Clang (disable-tcg)"
index 409d7db4d457e3a8f66de1b5513916d80e85462e..a07af6b9d48e1869ec04c70bdc1f3a094a8d0f87 100644 (file)
@@ -1549,8 +1549,8 @@ M: Glenn Miles <milesg@linux.vnet.ibm.com>
 L: qemu-ppc@nongnu.org
 L: qemu-arm@nongnu.org
 S: Odd Fixes
-F: hw/misc/pca955*.c
-F: include/hw/misc/pca955*.h
+F: hw/gpio/pca955*.c
+F: include/hw/gpio/pca955*.h
 
 virtex_ml507
 M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
index 8854581ca8de916d599901ff506d7fe7b31a0997..93ca87fda26608687366f3889137781f33e0bd46 100644 (file)
@@ -18,7 +18,7 @@
 #include "hw/block/flash.h"
 #include "hw/i2c/i2c_mux_pca954x.h"
 #include "hw/i2c/smbus_eeprom.h"
-#include "hw/misc/pca9552.h"
+#include "hw/gpio/pca9552.h"
 #include "hw/nvram/eeprom_at24c.h"
 #include "hw/sensor/tmp105.h"
 #include "hw/misc/led.h"
index 2634e0f6544e95e508e0c4003c2517c695e3a7e9..9f98ad8e87ae33dad71fe98f11be799ffa89759c 100644 (file)
@@ -424,6 +424,8 @@ static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc);
 
+    /* Reason: The Aspeed SoC can only be instantiated from a board */
+    dc->user_creatable = false;
     dc->realize = aspeed_soc_ast1030_realize;
 
     sc->name = "ast1030-a1";
index 174be537709b059ae3ea4e478cd3a35bfa1dc946..31713de74a5fdfae70d38e4eb694c9f375ad0298 100644 (file)
@@ -656,6 +656,8 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
     AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
 
     dc->realize      = aspeed_soc_ast2600_realize;
+    /* Reason: The Aspeed SoC can only be instantiated from a board */
+    dc->user_creatable = false;
 
     sc->name         = "ast2600-a3";
     sc->valid_cpu_types = valid_cpu_types;
index 791e93a97bcc4583d77491a335038e3b35d4ba02..a7495d196ae94c7f208ec0be80d5ade636f1bb11 100644 (file)
@@ -2,6 +2,8 @@ system_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c'))
 system_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c'))
 system_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c'))
 system_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c'))
+system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c'))
 system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c'))
 
diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c
new file mode 100644 (file)
index 0000000..27d4db0
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * PCA9552 I2C LED blinker
+ *
+ *     https://www.nxp.com/docs/en/application-note/AN264.pdf
+ *
+ * Copyright (c) 2017-2018, IBM Corporation.
+ * Copyright (c) 2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/gpio/pca9552.h"
+#include "hw/gpio/pca9552_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA955xClass {
+    /*< private >*/
+    I2CSlaveClass parent_class;
+    /*< public >*/
+
+    uint8_t pin_count;
+    uint8_t max_reg;
+};
+typedef struct PCA955xClass PCA955xClass;
+
+DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
+                       TYPE_PCA955X)
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *        chips are the reverse of the PCA953X family of chips.
+ */
+#define PCA9552_LED_ON   0x0
+#define PCA9552_LED_OFF  0x1
+#define PCA9552_LED_PWM0 0x2
+#define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
+
+static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
+
+static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
+{
+    uint8_t reg   = PCA9552_LS0 + (pin / 4);
+    uint8_t shift = (pin % 4) << 1;
+
+    return extract32(s->regs[reg], shift, 2);
+}
+
+/* Return INPUT status (bit #N belongs to GPIO #N) */
+static uint16_t pca955x_pins_get_status(PCA955xState *s)
+{
+    return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
+}
+
+static void pca955x_display_pins_status(PCA955xState *s,
+                                        uint16_t previous_pins_status)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(s);
+    uint16_t pins_status, pins_changed;
+    int i;
+
+    pins_status = pca955x_pins_get_status(s);
+    pins_changed = previous_pins_status ^ pins_status;
+    if (!pins_changed) {
+        return;
+    }
+    if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
+        char *buf = g_newa(char, k->pin_count + 1);
+
+        for (i = 0; i < k->pin_count; i++) {
+            if (extract32(pins_status, i, 1)) {
+                buf[i] = '*';
+            } else {
+                buf[i] = '.';
+            }
+        }
+        buf[i] = '\0';
+        trace_pca955x_gpio_status(s->description, buf);
+    }
+    if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
+        for (i = 0; i < k->pin_count; i++) {
+            if (extract32(pins_changed, i, 1)) {
+                unsigned new_state = extract32(pins_status, i, 1);
+
+                /*
+                 * We display the state using the PCA logic ("active-high").
+                 * This is not the state of the LED, which signal might be
+                 * wired "active-low" on the board.
+                 */
+                trace_pca955x_gpio_change(s->description, i,
+                                          !new_state, new_state);
+            }
+        }
+    }
+}
+
+static void pca955x_update_pin_input(PCA955xState *s)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(s);
+    int i;
+
+    for (i = 0; i < k->pin_count; i++) {
+        uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
+        uint8_t bit_mask = 1 << (i % 8);
+        uint8_t config = pca955x_pin_get_config(s, i);
+        uint8_t old_value = s->regs[input_reg] & bit_mask;
+        uint8_t new_value;
+
+        switch (config) {
+        case PCA9552_LED_ON:
+            /* Pin is set to 0V to turn on LED */
+            s->regs[input_reg] &= ~bit_mask;
+            break;
+        case PCA9552_LED_OFF:
+            /*
+             * Pin is set to Hi-Z to turn off LED and
+             * pullup sets it to a logical 1 unless
+             * external device drives it low.
+             */
+            if (s->ext_state[i] == PCA9552_PIN_LOW) {
+                s->regs[input_reg] &= ~bit_mask;
+            } else {
+                s->regs[input_reg] |=  bit_mask;
+            }
+            break;
+        case PCA9552_LED_PWM0:
+        case PCA9552_LED_PWM1:
+            /* TODO */
+        default:
+            break;
+        }
+
+        /* update irq state only if pin state changed */
+        new_value = s->regs[input_reg] & bit_mask;
+        if (new_value != old_value) {
+            qemu_set_irq(s->gpio_out[i], !!new_value);
+        }
+    }
+}
+
+static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
+{
+    switch (reg) {
+    case PCA9552_INPUT0:
+    case PCA9552_INPUT1:
+    case PCA9552_PSC0:
+    case PCA9552_PWM0:
+    case PCA9552_PSC1:
+    case PCA9552_PWM1:
+    case PCA9552_LS0:
+    case PCA9552_LS1:
+    case PCA9552_LS2:
+    case PCA9552_LS3:
+        return s->regs[reg];
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+                      __func__, reg);
+        return 0xFF;
+    }
+}
+
+static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
+{
+    uint16_t pins_status;
+
+    switch (reg) {
+    case PCA9552_PSC0:
+    case PCA9552_PWM0:
+    case PCA9552_PSC1:
+    case PCA9552_PWM1:
+        s->regs[reg] = data;
+        break;
+
+    case PCA9552_LS0:
+    case PCA9552_LS1:
+    case PCA9552_LS2:
+    case PCA9552_LS3:
+        pins_status = pca955x_pins_get_status(s);
+        s->regs[reg] = data;
+        pca955x_update_pin_input(s);
+        pca955x_display_pins_status(s, pins_status);
+        break;
+
+    case PCA9552_INPUT0:
+    case PCA9552_INPUT1:
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+                      __func__, reg);
+    }
+}
+
+/*
+ * When Auto-Increment is on, the register address is incremented
+ * after each byte is sent to or received by the device. The index
+ * rollovers to 0 when the maximum register address is reached.
+ */
+static void pca955x_autoinc(PCA955xState *s)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+    if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
+        uint8_t reg = s->pointer & 0xf;
+
+        reg = (reg + 1) % (k->max_reg + 1);
+        s->pointer = reg | PCA9552_AUTOINC;
+    }
+}
+
+static uint8_t pca955x_recv(I2CSlave *i2c)
+{
+    PCA955xState *s = PCA955X(i2c);
+    uint8_t ret;
+
+    ret = pca955x_read(s, s->pointer & 0xf);
+
+    /*
+     * From the Specs:
+     *
+     *     Important Note: When a Read sequence is initiated and the
+     *     AI bit is set to Logic Level 1, the Read Sequence MUST
+     *     start by a register different from 0.
+     *
+     * I don't know what should be done in this case, so throw an
+     * error.
+     */
+    if (s->pointer == PCA9552_AUTOINC) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Autoincrement read starting with register 0\n",
+                      __func__);
+    }
+
+    pca955x_autoinc(s);
+
+    return ret;
+}
+
+static int pca955x_send(I2CSlave *i2c, uint8_t data)
+{
+    PCA955xState *s = PCA955X(i2c);
+
+    /* First byte sent by is the register address */
+    if (s->len == 0) {
+        s->pointer = data;
+        s->len++;
+    } else {
+        pca955x_write(s, s->pointer & 0xf, data);
+
+        pca955x_autoinc(s);
+    }
+
+    return 0;
+}
+
+static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
+{
+    PCA955xState *s = PCA955X(i2c);
+
+    s->len = 0;
+    return 0;
+}
+
+static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
+                            void *opaque, Error **errp)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(obj);
+    PCA955xState *s = PCA955X(obj);
+    int led, rc, reg;
+    uint8_t state;
+
+    rc = sscanf(name, "led%2d", &led);
+    if (rc != 1) {
+        error_setg(errp, "%s: error reading %s", __func__, name);
+        return;
+    }
+    if (led < 0 || led > k->pin_count) {
+        error_setg(errp, "%s invalid led %s", __func__, name);
+        return;
+    }
+    /*
+     * Get the LSx register as the qom interface should expose the device
+     * state, not the modeled 'input line' behaviour which would come from
+     * reading the INPUTx reg
+     */
+    reg = PCA9552_LS0 + led / 4;
+    state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
+    visit_type_str(v, name, (char **)&led_state[state], errp);
+}
+
+/*
+ * Return an LED selector register value based on an existing one, with
+ * the appropriate 2-bit state value set for the given LED number (0-3).
+ */
+static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
+{
+        return (oldval & (~(0x3 << (led_num << 1)))) |
+                ((state & 0x3) << (led_num << 1));
+}
+
+static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
+                            void *opaque, Error **errp)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(obj);
+    PCA955xState *s = PCA955X(obj);
+    int led, rc, reg, val;
+    uint8_t state;
+    char *state_str;
+
+    if (!visit_type_str(v, name, &state_str, errp)) {
+        return;
+    }
+    rc = sscanf(name, "led%2d", &led);
+    if (rc != 1) {
+        error_setg(errp, "%s: error reading %s", __func__, name);
+        return;
+    }
+    if (led < 0 || led > k->pin_count) {
+        error_setg(errp, "%s invalid led %s", __func__, name);
+        return;
+    }
+
+    for (state = 0; state < ARRAY_SIZE(led_state); state++) {
+        if (!strcmp(state_str, led_state[state])) {
+            break;
+        }
+    }
+    if (state >= ARRAY_SIZE(led_state)) {
+        error_setg(errp, "%s invalid led state %s", __func__, state_str);
+        return;
+    }
+
+    reg = PCA9552_LS0 + led / 4;
+    val = pca955x_read(s, reg);
+    val = pca955x_ledsel(val, led % 4, state);
+    pca955x_write(s, reg, val);
+}
+
+static const VMStateDescription pca9552_vmstate = {
+    .name = "PCA9552",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT8(len, PCA955xState),
+        VMSTATE_UINT8(pointer, PCA955xState),
+        VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+        VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
+        VMSTATE_I2C_SLAVE(i2c, PCA955xState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pca9552_reset(DeviceState *dev)
+{
+    PCA955xState *s = PCA955X(dev);
+
+    s->regs[PCA9552_PSC0] = 0xFF;
+    s->regs[PCA9552_PWM0] = 0x80;
+    s->regs[PCA9552_PSC1] = 0xFF;
+    s->regs[PCA9552_PWM1] = 0x80;
+    s->regs[PCA9552_LS0] = 0x55; /* all OFF */
+    s->regs[PCA9552_LS1] = 0x55;
+    s->regs[PCA9552_LS2] = 0x55;
+    s->regs[PCA9552_LS3] = 0x55;
+
+    memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
+    pca955x_update_pin_input(s);
+
+    s->pointer = 0xFF;
+    s->len = 0;
+}
+
+static void pca955x_initfn(Object *obj)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(obj);
+    int led;
+
+    assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
+    for (led = 0; led < k->pin_count; led++) {
+        char *name;
+
+        name = g_strdup_printf("led%d", led);
+        object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
+                            NULL, NULL);
+        g_free(name);
+    }
+}
+
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+    if (s->ext_state[pin] != level) {
+        uint16_t pins_status = pca955x_pins_get_status(s);
+        s->ext_state[pin] = level;
+        pca955x_update_pin_input(s);
+        pca955x_display_pins_status(s, pins_status);
+    }
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+    PCA955xState *s = PCA955X(opaque);
+    PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+    assert((pin >= 0) && (pin < k->pin_count));
+    pca955x_set_ext_state(s, pin, level);
+}
+
+static void pca955x_realize(DeviceState *dev, Error **errp)
+{
+    PCA955xClass *k = PCA955X_GET_CLASS(dev);
+    PCA955xState *s = PCA955X(dev);
+
+    if (!s->description) {
+        s->description = g_strdup("pca-unspecified");
+    }
+
+    qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
+    qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
+}
+
+static Property pca955x_properties[] = {
+    DEFINE_PROP_STRING("description", PCA955xState, description),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pca955x_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->event = pca955x_event;
+    k->recv = pca955x_recv;
+    k->send = pca955x_send;
+    dc->realize = pca955x_realize;
+    device_class_set_props(dc, pca955x_properties);
+}
+
+static const TypeInfo pca955x_info = {
+    .name          = TYPE_PCA955X,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_init = pca955x_initfn,
+    .instance_size = sizeof(PCA955xState),
+    .class_init    = pca955x_class_init,
+    .class_size    = sizeof(PCA955xClass),
+    .abstract      = true,
+};
+
+static void pca9552_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    PCA955xClass *pc = PCA955X_CLASS(oc);
+
+    dc->reset = pca9552_reset;
+    dc->vmsd = &pca9552_vmstate;
+    pc->max_reg = PCA9552_LS3;
+    pc->pin_count = 16;
+}
+
+static const TypeInfo pca9552_info = {
+    .name          = TYPE_PCA9552,
+    .parent        = TYPE_PCA955X,
+    .class_init    = pca9552_class_init,
+};
+
+static void pca955x_register_types(void)
+{
+    type_register_static(&pca955x_info);
+    type_register_static(&pca9552_info);
+}
+
+type_init(pca955x_register_types)
diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c
new file mode 100644 (file)
index 0000000..7d10a64
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/gpio/pca9554.h"
+#include "hw/gpio/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+    /*< private >*/
+    I2CSlaveClass parent_class;
+    /*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+                       TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+    int i;
+    uint8_t config = s->regs[PCA9554_CONFIG];
+    uint8_t output = s->regs[PCA9554_OUTPUT];
+    uint8_t internal_state = config | output;
+
+    for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+        uint8_t bit_mask = 1 << i;
+        uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+        uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+        uint8_t new_value;
+
+        switch (internal_pin_state) {
+        case PCA9554_PIN_LOW:
+            s->regs[PCA9554_INPUT] &= ~bit_mask;
+            break;
+        case PCA9554_PIN_HIZ:
+            /*
+             * pullup sets it to a logical 1 unless
+             * external device drives it low.
+             */
+            if (s->ext_state[i] == PCA9554_PIN_LOW) {
+                s->regs[PCA9554_INPUT] &= ~bit_mask;
+            } else {
+                s->regs[PCA9554_INPUT] |=  bit_mask;
+            }
+            break;
+        default:
+            break;
+        }
+
+        /* update irq state only if pin state changed */
+        new_value = s->regs[PCA9554_INPUT] & bit_mask;
+        if (new_value != old_value) {
+            if (new_value) {
+                /* changed from 0 to 1 */
+                qemu_set_irq(s->gpio_out[i], 1);
+            } else {
+                /* changed from 1 to 0 */
+                qemu_set_irq(s->gpio_out[i], 0);
+            }
+        }
+    }
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+    switch (reg) {
+    case PCA9554_INPUT:
+        return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+    case PCA9554_OUTPUT:
+    case PCA9554_POLARITY:
+    case PCA9554_CONFIG:
+        return s->regs[reg];
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+                      __func__, reg);
+        return 0xFF;
+    }
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+    switch (reg) {
+    case PCA9554_OUTPUT:
+    case PCA9554_CONFIG:
+        s->regs[reg] = data;
+        pca9554_update_pin_input(s);
+        break;
+    case PCA9554_POLARITY:
+        s->regs[reg] = data;
+        break;
+    case PCA9554_INPUT:
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+                      __func__, reg);
+    }
+}
+
+static uint8_t pca9554_recv(I2CSlave *i2c)
+{
+    PCA9554State *s = PCA9554(i2c);
+    uint8_t ret;
+
+    ret = pca9554_read(s, s->pointer & 0x3);
+
+    return ret;
+}
+
+static int pca9554_send(I2CSlave *i2c, uint8_t data)
+{
+    PCA9554State *s = PCA9554(i2c);
+
+    /* First byte sent by is the register address */
+    if (s->len == 0) {
+        s->pointer = data;
+        s->len++;
+    } else {
+        pca9554_write(s, s->pointer & 0x3, data);
+    }
+
+    return 0;
+}
+
+static int pca9554_event(I2CSlave *i2c, enum i2c_event event)
+{
+    PCA9554State *s = PCA9554(i2c);
+
+    s->len = 0;
+    return 0;
+}
+
+static void pca9554_get_pin(Object *obj, Visitor *v, const char *name,
+                            void *opaque, Error **errp)
+{
+    PCA9554State *s = PCA9554(obj);
+    int pin, rc;
+    uint8_t state;
+
+    rc = sscanf(name, "pin%2d", &pin);
+    if (rc != 1) {
+        error_setg(errp, "%s: error reading %s", __func__, name);
+        return;
+    }
+    if (pin < 0 || pin >= PCA9554_PIN_COUNT) {
+        error_setg(errp, "%s invalid pin %s", __func__, name);
+        return;
+    }
+
+    state = pca9554_read(s, PCA9554_CONFIG);
+    state |= pca9554_read(s, PCA9554_OUTPUT);
+    state = (state >> pin) & 0x1;
+    visit_type_str(v, name, (char **)&pin_state[state], errp);
+}
+
+static void pca9554_set_pin(Object *obj, Visitor *v, const char *name,
+                            void *opaque, Error **errp)
+{
+    PCA9554State *s = PCA9554(obj);
+    int pin, rc, val;
+    uint8_t state, mask;
+    char *state_str;
+
+    if (!visit_type_str(v, name, &state_str, errp)) {
+        return;
+    }
+    rc = sscanf(name, "pin%2d", &pin);
+    if (rc != 1) {
+        error_setg(errp, "%s: error reading %s", __func__, name);
+        return;
+    }
+    if (pin < 0 || pin >= PCA9554_PIN_COUNT) {
+        error_setg(errp, "%s invalid pin %s", __func__, name);
+        return;
+    }
+
+    for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
+        if (!strcmp(state_str, pin_state[state])) {
+            break;
+        }
+    }
+    if (state >= ARRAY_SIZE(pin_state)) {
+        error_setg(errp, "%s invalid pin state %s", __func__, state_str);
+        return;
+    }
+
+    /* First, modify the output register bit */
+    val = pca9554_read(s, PCA9554_OUTPUT);
+    mask = 0x1 << pin;
+    if (state == PCA9554_PIN_LOW) {
+        val &= ~(mask);
+    } else {
+        val |= mask;
+    }
+    pca9554_write(s, PCA9554_OUTPUT, val);
+
+    /* Then, clear the config register bit for output mode */
+    val = pca9554_read(s, PCA9554_CONFIG);
+    val &= ~mask;
+    pca9554_write(s, PCA9554_CONFIG, val);
+}
+
+static const VMStateDescription pca9554_vmstate = {
+    .name = "PCA9554",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(len, PCA9554State),
+        VMSTATE_UINT8(pointer, PCA9554State),
+        VMSTATE_UINT8_ARRAY(regs, PCA9554State, PCA9554_NR_REGS),
+        VMSTATE_UINT8_ARRAY(ext_state, PCA9554State, PCA9554_PIN_COUNT),
+        VMSTATE_I2C_SLAVE(i2c, PCA9554State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pca9554_reset(DeviceState *dev)
+{
+    PCA9554State *s = PCA9554(dev);
+
+    s->regs[PCA9554_INPUT] = 0xFF;
+    s->regs[PCA9554_OUTPUT] = 0xFF;
+    s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */
+    s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */
+
+    memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT);
+    pca9554_update_pin_input(s);
+
+    s->pointer = 0x0;
+    s->len = 0;
+}
+
+static void pca9554_initfn(Object *obj)
+{
+    int pin;
+
+    for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) {
+        char *name;
+
+        name = g_strdup_printf("pin%d", pin);
+        object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin,
+                            NULL, NULL);
+        g_free(name);
+    }
+}
+
+static void pca9554_set_ext_state(PCA9554State *s, int pin, int level)
+{
+    if (s->ext_state[pin] != level) {
+        s->ext_state[pin] = level;
+        pca9554_update_pin_input(s);
+    }
+}
+
+static void pca9554_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+    PCA9554State *s = PCA9554(opaque);
+
+    assert((pin >= 0) && (pin < PCA9554_PIN_COUNT));
+    pca9554_set_ext_state(s, pin, level);
+}
+
+static void pca9554_realize(DeviceState *dev, Error **errp)
+{
+    PCA9554State *s = PCA9554(dev);
+
+    if (!s->description) {
+        s->description = g_strdup("pca9554");
+    }
+
+    qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT);
+    qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT);
+}
+
+static Property pca9554_properties[] = {
+    DEFINE_PROP_STRING("description", PCA9554State, description),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pca9554_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->event = pca9554_event;
+    k->recv = pca9554_recv;
+    k->send = pca9554_send;
+    dc->realize = pca9554_realize;
+    dc->reset = pca9554_reset;
+    dc->vmsd = &pca9554_vmstate;
+    device_class_set_props(dc, pca9554_properties);
+}
+
+static const TypeInfo pca9554_info = {
+    .name          = TYPE_PCA9554,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_init = pca9554_initfn,
+    .instance_size = sizeof(PCA9554State),
+    .class_init    = pca9554_class_init,
+    .class_size    = sizeof(PCA9554Class),
+    .abstract      = false,
+};
+
+static void pca9554_register_types(void)
+{
+    type_register_static(&pca9554_info);
+}
+
+type_init(pca9554_register_types)
index 9331f4289d5a522c69b60c1cfa6563df238c1d5f..b91cc7e9a45d9be7e064e883f7088a766baba7a2 100644 (file)
@@ -13,6 +13,10 @@ nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x
 nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
 
+# pca9552.c
+pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]"
+pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u"
+
 # pl061.c
 pl061_update(const char *id, uint32_t dir, uint32_t data, uint32_t pullups, uint32_t floating) "%s GPIODIR 0x%x GPIODATA 0x%x pullups 0x%x floating 0x%x"
 pl061_set_output(const char *id, int gpio, int level) "%s setting output %d to %d"
index 5a2016672a3760aa8615f1f946f6463bd2b8ef33..1bfc9641d29f418da6086f6833610ab305c4ac60 100644 (file)
@@ -125,6 +125,8 @@ static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
 
+    /* xlnx-zynqmp-pmu-soc causes crashes when cold-plugged twice */
+    dc->user_creatable = false;
     dc->realize = xlnx_zynqmp_pmu_soc_realize;
 }
 
index 265b2c26274cfc671945b5a1863155efd115e69a..86596a388812df4681d399008ab72018646dfa1d 100644 (file)
@@ -3,8 +3,6 @@ system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c'))
 system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
-system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
-system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
deleted file mode 100644 (file)
index 2ae13af..0000000
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * PCA9552 I2C LED blinker
- *
- *     https://www.nxp.com/docs/en/application-note/AN264.pdf
- *
- * Copyright (c) 2017-2018, IBM Corporation.
- * Copyright (c) 2020 Philippe Mathieu-Daudé
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or
- * later. See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/log.h"
-#include "qemu/module.h"
-#include "qemu/bitops.h"
-#include "hw/qdev-properties.h"
-#include "hw/misc/pca9552.h"
-#include "hw/misc/pca9552_regs.h"
-#include "hw/irq.h"
-#include "migration/vmstate.h"
-#include "qapi/error.h"
-#include "qapi/visitor.h"
-#include "trace.h"
-#include "qom/object.h"
-
-struct PCA955xClass {
-    /*< private >*/
-    I2CSlaveClass parent_class;
-    /*< public >*/
-
-    uint8_t pin_count;
-    uint8_t max_reg;
-};
-typedef struct PCA955xClass PCA955xClass;
-
-DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
-                       TYPE_PCA955X)
-/*
- * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
- *        chips are the reverse of the PCA953X family of chips.
- */
-#define PCA9552_LED_ON   0x0
-#define PCA9552_LED_OFF  0x1
-#define PCA9552_LED_PWM0 0x2
-#define PCA9552_LED_PWM1 0x3
-#define PCA9552_PIN_LOW  0x0
-#define PCA9552_PIN_HIZ  0x1
-
-static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
-
-static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
-{
-    uint8_t reg   = PCA9552_LS0 + (pin / 4);
-    uint8_t shift = (pin % 4) << 1;
-
-    return extract32(s->regs[reg], shift, 2);
-}
-
-/* Return INPUT status (bit #N belongs to GPIO #N) */
-static uint16_t pca955x_pins_get_status(PCA955xState *s)
-{
-    return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
-}
-
-static void pca955x_display_pins_status(PCA955xState *s,
-                                        uint16_t previous_pins_status)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(s);
-    uint16_t pins_status, pins_changed;
-    int i;
-
-    pins_status = pca955x_pins_get_status(s);
-    pins_changed = previous_pins_status ^ pins_status;
-    if (!pins_changed) {
-        return;
-    }
-    if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
-        char *buf = g_newa(char, k->pin_count + 1);
-
-        for (i = 0; i < k->pin_count; i++) {
-            if (extract32(pins_status, i, 1)) {
-                buf[i] = '*';
-            } else {
-                buf[i] = '.';
-            }
-        }
-        buf[i] = '\0';
-        trace_pca955x_gpio_status(s->description, buf);
-    }
-    if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
-        for (i = 0; i < k->pin_count; i++) {
-            if (extract32(pins_changed, i, 1)) {
-                unsigned new_state = extract32(pins_status, i, 1);
-
-                /*
-                 * We display the state using the PCA logic ("active-high").
-                 * This is not the state of the LED, which signal might be
-                 * wired "active-low" on the board.
-                 */
-                trace_pca955x_gpio_change(s->description, i,
-                                          !new_state, new_state);
-            }
-        }
-    }
-}
-
-static void pca955x_update_pin_input(PCA955xState *s)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(s);
-    int i;
-
-    for (i = 0; i < k->pin_count; i++) {
-        uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-        uint8_t bit_mask = 1 << (i % 8);
-        uint8_t config = pca955x_pin_get_config(s, i);
-        uint8_t old_value = s->regs[input_reg] & bit_mask;
-        uint8_t new_value;
-
-        switch (config) {
-        case PCA9552_LED_ON:
-            /* Pin is set to 0V to turn on LED */
-            s->regs[input_reg] &= ~bit_mask;
-            break;
-        case PCA9552_LED_OFF:
-            /*
-             * Pin is set to Hi-Z to turn off LED and
-             * pullup sets it to a logical 1 unless
-             * external device drives it low.
-             */
-            if (s->ext_state[i] == PCA9552_PIN_LOW) {
-                s->regs[input_reg] &= ~bit_mask;
-            } else {
-                s->regs[input_reg] |=  bit_mask;
-            }
-            break;
-        case PCA9552_LED_PWM0:
-        case PCA9552_LED_PWM1:
-            /* TODO */
-        default:
-            break;
-        }
-
-        /* update irq state only if pin state changed */
-        new_value = s->regs[input_reg] & bit_mask;
-        if (new_value != old_value) {
-            qemu_set_irq(s->gpio_out[i], !!new_value);
-        }
-    }
-}
-
-static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
-{
-    switch (reg) {
-    case PCA9552_INPUT0:
-    case PCA9552_INPUT1:
-    case PCA9552_PSC0:
-    case PCA9552_PWM0:
-    case PCA9552_PSC1:
-    case PCA9552_PWM1:
-    case PCA9552_LS0:
-    case PCA9552_LS1:
-    case PCA9552_LS2:
-    case PCA9552_LS3:
-        return s->regs[reg];
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
-                      __func__, reg);
-        return 0xFF;
-    }
-}
-
-static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
-{
-    uint16_t pins_status;
-
-    switch (reg) {
-    case PCA9552_PSC0:
-    case PCA9552_PWM0:
-    case PCA9552_PSC1:
-    case PCA9552_PWM1:
-        s->regs[reg] = data;
-        break;
-
-    case PCA9552_LS0:
-    case PCA9552_LS1:
-    case PCA9552_LS2:
-    case PCA9552_LS3:
-        pins_status = pca955x_pins_get_status(s);
-        s->regs[reg] = data;
-        pca955x_update_pin_input(s);
-        pca955x_display_pins_status(s, pins_status);
-        break;
-
-    case PCA9552_INPUT0:
-    case PCA9552_INPUT1:
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
-                      __func__, reg);
-    }
-}
-
-/*
- * When Auto-Increment is on, the register address is incremented
- * after each byte is sent to or received by the device. The index
- * rollovers to 0 when the maximum register address is reached.
- */
-static void pca955x_autoinc(PCA955xState *s)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(s);
-
-    if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
-        uint8_t reg = s->pointer & 0xf;
-
-        reg = (reg + 1) % (k->max_reg + 1);
-        s->pointer = reg | PCA9552_AUTOINC;
-    }
-}
-
-static uint8_t pca955x_recv(I2CSlave *i2c)
-{
-    PCA955xState *s = PCA955X(i2c);
-    uint8_t ret;
-
-    ret = pca955x_read(s, s->pointer & 0xf);
-
-    /*
-     * From the Specs:
-     *
-     *     Important Note: When a Read sequence is initiated and the
-     *     AI bit is set to Logic Level 1, the Read Sequence MUST
-     *     start by a register different from 0.
-     *
-     * I don't know what should be done in this case, so throw an
-     * error.
-     */
-    if (s->pointer == PCA9552_AUTOINC) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: Autoincrement read starting with register 0\n",
-                      __func__);
-    }
-
-    pca955x_autoinc(s);
-
-    return ret;
-}
-
-static int pca955x_send(I2CSlave *i2c, uint8_t data)
-{
-    PCA955xState *s = PCA955X(i2c);
-
-    /* First byte sent by is the register address */
-    if (s->len == 0) {
-        s->pointer = data;
-        s->len++;
-    } else {
-        pca955x_write(s, s->pointer & 0xf, data);
-
-        pca955x_autoinc(s);
-    }
-
-    return 0;
-}
-
-static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
-{
-    PCA955xState *s = PCA955X(i2c);
-
-    s->len = 0;
-    return 0;
-}
-
-static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    PCA955xState *s = PCA955X(obj);
-    int led, rc, reg;
-    uint8_t state;
-
-    rc = sscanf(name, "led%2d", &led);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (led < 0 || led > k->pin_count) {
-        error_setg(errp, "%s invalid led %s", __func__, name);
-        return;
-    }
-    /*
-     * Get the LSx register as the qom interface should expose the device
-     * state, not the modeled 'input line' behaviour which would come from
-     * reading the INPUTx reg
-     */
-    reg = PCA9552_LS0 + led / 4;
-    state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
-    visit_type_str(v, name, (char **)&led_state[state], errp);
-}
-
-/*
- * Return an LED selector register value based on an existing one, with
- * the appropriate 2-bit state value set for the given LED number (0-3).
- */
-static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
-{
-        return (oldval & (~(0x3 << (led_num << 1)))) |
-                ((state & 0x3) << (led_num << 1));
-}
-
-static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    PCA955xState *s = PCA955X(obj);
-    int led, rc, reg, val;
-    uint8_t state;
-    char *state_str;
-
-    if (!visit_type_str(v, name, &state_str, errp)) {
-        return;
-    }
-    rc = sscanf(name, "led%2d", &led);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (led < 0 || led > k->pin_count) {
-        error_setg(errp, "%s invalid led %s", __func__, name);
-        return;
-    }
-
-    for (state = 0; state < ARRAY_SIZE(led_state); state++) {
-        if (!strcmp(state_str, led_state[state])) {
-            break;
-        }
-    }
-    if (state >= ARRAY_SIZE(led_state)) {
-        error_setg(errp, "%s invalid led state %s", __func__, state_str);
-        return;
-    }
-
-    reg = PCA9552_LS0 + led / 4;
-    val = pca955x_read(s, reg);
-    val = pca955x_ledsel(val, led % 4, state);
-    pca955x_write(s, reg, val);
-}
-
-static const VMStateDescription pca9552_vmstate = {
-    .name = "PCA9552",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .fields = (const VMStateField[]) {
-        VMSTATE_UINT8(len, PCA955xState),
-        VMSTATE_UINT8(pointer, PCA955xState),
-        VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
-        VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
-        VMSTATE_I2C_SLAVE(i2c, PCA955xState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pca9552_reset(DeviceState *dev)
-{
-    PCA955xState *s = PCA955X(dev);
-
-    s->regs[PCA9552_PSC0] = 0xFF;
-    s->regs[PCA9552_PWM0] = 0x80;
-    s->regs[PCA9552_PSC1] = 0xFF;
-    s->regs[PCA9552_PWM1] = 0x80;
-    s->regs[PCA9552_LS0] = 0x55; /* all OFF */
-    s->regs[PCA9552_LS1] = 0x55;
-    s->regs[PCA9552_LS2] = 0x55;
-    s->regs[PCA9552_LS3] = 0x55;
-
-    memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
-    pca955x_update_pin_input(s);
-
-    s->pointer = 0xFF;
-    s->len = 0;
-}
-
-static void pca955x_initfn(Object *obj)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    int led;
-
-    assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
-    for (led = 0; led < k->pin_count; led++) {
-        char *name;
-
-        name = g_strdup_printf("led%d", led);
-        object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
-                            NULL, NULL);
-        g_free(name);
-    }
-}
-
-static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
-{
-    if (s->ext_state[pin] != level) {
-        uint16_t pins_status = pca955x_pins_get_status(s);
-        s->ext_state[pin] = level;
-        pca955x_update_pin_input(s);
-        pca955x_display_pins_status(s, pins_status);
-    }
-}
-
-static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
-{
-
-    PCA955xState *s = PCA955X(opaque);
-    PCA955xClass *k = PCA955X_GET_CLASS(s);
-
-    assert((pin >= 0) && (pin < k->pin_count));
-    pca955x_set_ext_state(s, pin, level);
-}
-
-static void pca955x_realize(DeviceState *dev, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(dev);
-    PCA955xState *s = PCA955X(dev);
-
-    if (!s->description) {
-        s->description = g_strdup("pca-unspecified");
-    }
-
-    qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
-    qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
-}
-
-static Property pca955x_properties[] = {
-    DEFINE_PROP_STRING("description", PCA955xState, description),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pca955x_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->event = pca955x_event;
-    k->recv = pca955x_recv;
-    k->send = pca955x_send;
-    dc->realize = pca955x_realize;
-    device_class_set_props(dc, pca955x_properties);
-}
-
-static const TypeInfo pca955x_info = {
-    .name          = TYPE_PCA955X,
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_init = pca955x_initfn,
-    .instance_size = sizeof(PCA955xState),
-    .class_init    = pca955x_class_init,
-    .class_size    = sizeof(PCA955xClass),
-    .abstract      = true,
-};
-
-static void pca9552_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    PCA955xClass *pc = PCA955X_CLASS(oc);
-
-    dc->reset = pca9552_reset;
-    dc->vmsd = &pca9552_vmstate;
-    pc->max_reg = PCA9552_LS3;
-    pc->pin_count = 16;
-}
-
-static const TypeInfo pca9552_info = {
-    .name          = TYPE_PCA9552,
-    .parent        = TYPE_PCA955X,
-    .class_init    = pca9552_class_init,
-};
-
-static void pca955x_register_types(void)
-{
-    type_register_static(&pca955x_info);
-    type_register_static(&pca9552_info);
-}
-
-type_init(pca955x_register_types)
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
deleted file mode 100644 (file)
index 5e31696..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * PCA9554 I/O port
- *
- * Copyright (c) 2023, IBM Corporation.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "qemu/osdep.h"
-#include "qemu/log.h"
-#include "qemu/module.h"
-#include "qemu/bitops.h"
-#include "hw/qdev-properties.h"
-#include "hw/misc/pca9554.h"
-#include "hw/misc/pca9554_regs.h"
-#include "hw/irq.h"
-#include "migration/vmstate.h"
-#include "qapi/error.h"
-#include "qapi/visitor.h"
-#include "trace.h"
-#include "qom/object.h"
-
-struct PCA9554Class {
-    /*< private >*/
-    I2CSlaveClass parent_class;
-    /*< public >*/
-};
-typedef struct PCA9554Class PCA9554Class;
-
-DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
-                       TYPE_PCA9554)
-
-#define PCA9554_PIN_LOW  0x0
-#define PCA9554_PIN_HIZ  0x1
-
-static const char *pin_state[] = {"low", "high"};
-
-static void pca9554_update_pin_input(PCA9554State *s)
-{
-    int i;
-    uint8_t config = s->regs[PCA9554_CONFIG];
-    uint8_t output = s->regs[PCA9554_OUTPUT];
-    uint8_t internal_state = config | output;
-
-    for (i = 0; i < PCA9554_PIN_COUNT; i++) {
-        uint8_t bit_mask = 1 << i;
-        uint8_t internal_pin_state = (internal_state >> i) & 0x1;
-        uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
-        uint8_t new_value;
-
-        switch (internal_pin_state) {
-        case PCA9554_PIN_LOW:
-            s->regs[PCA9554_INPUT] &= ~bit_mask;
-            break;
-        case PCA9554_PIN_HIZ:
-            /*
-             * pullup sets it to a logical 1 unless
-             * external device drives it low.
-             */
-            if (s->ext_state[i] == PCA9554_PIN_LOW) {
-                s->regs[PCA9554_INPUT] &= ~bit_mask;
-            } else {
-                s->regs[PCA9554_INPUT] |=  bit_mask;
-            }
-            break;
-        default:
-            break;
-        }
-
-        /* update irq state only if pin state changed */
-        new_value = s->regs[PCA9554_INPUT] & bit_mask;
-        if (new_value != old_value) {
-            if (new_value) {
-                /* changed from 0 to 1 */
-                qemu_set_irq(s->gpio_out[i], 1);
-            } else {
-                /* changed from 1 to 0 */
-                qemu_set_irq(s->gpio_out[i], 0);
-            }
-        }
-    }
-}
-
-static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
-{
-    switch (reg) {
-    case PCA9554_INPUT:
-        return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
-    case PCA9554_OUTPUT:
-    case PCA9554_POLARITY:
-    case PCA9554_CONFIG:
-        return s->regs[reg];
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
-                      __func__, reg);
-        return 0xFF;
-    }
-}
-
-static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
-{
-    switch (reg) {
-    case PCA9554_OUTPUT:
-    case PCA9554_CONFIG:
-        s->regs[reg] = data;
-        pca9554_update_pin_input(s);
-        break;
-    case PCA9554_POLARITY:
-        s->regs[reg] = data;
-        break;
-    case PCA9554_INPUT:
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
-                      __func__, reg);
-    }
-}
-
-static uint8_t pca9554_recv(I2CSlave *i2c)
-{
-    PCA9554State *s = PCA9554(i2c);
-    uint8_t ret;
-
-    ret = pca9554_read(s, s->pointer & 0x3);
-
-    return ret;
-}
-
-static int pca9554_send(I2CSlave *i2c, uint8_t data)
-{
-    PCA9554State *s = PCA9554(i2c);
-
-    /* First byte sent by is the register address */
-    if (s->len == 0) {
-        s->pointer = data;
-        s->len++;
-    } else {
-        pca9554_write(s, s->pointer & 0x3, data);
-    }
-
-    return 0;
-}
-
-static int pca9554_event(I2CSlave *i2c, enum i2c_event event)
-{
-    PCA9554State *s = PCA9554(i2c);
-
-    s->len = 0;
-    return 0;
-}
-
-static void pca9554_get_pin(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA9554State *s = PCA9554(obj);
-    int pin, rc;
-    uint8_t state;
-
-    rc = sscanf(name, "pin%2d", &pin);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (pin < 0 || pin >= PCA9554_PIN_COUNT) {
-        error_setg(errp, "%s invalid pin %s", __func__, name);
-        return;
-    }
-
-    state = pca9554_read(s, PCA9554_CONFIG);
-    state |= pca9554_read(s, PCA9554_OUTPUT);
-    state = (state >> pin) & 0x1;
-    visit_type_str(v, name, (char **)&pin_state[state], errp);
-}
-
-static void pca9554_set_pin(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA9554State *s = PCA9554(obj);
-    int pin, rc, val;
-    uint8_t state, mask;
-    char *state_str;
-
-    if (!visit_type_str(v, name, &state_str, errp)) {
-        return;
-    }
-    rc = sscanf(name, "pin%2d", &pin);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (pin < 0 || pin >= PCA9554_PIN_COUNT) {
-        error_setg(errp, "%s invalid pin %s", __func__, name);
-        return;
-    }
-
-    for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
-        if (!strcmp(state_str, pin_state[state])) {
-            break;
-        }
-    }
-    if (state >= ARRAY_SIZE(pin_state)) {
-        error_setg(errp, "%s invalid pin state %s", __func__, state_str);
-        return;
-    }
-
-    /* First, modify the output register bit */
-    val = pca9554_read(s, PCA9554_OUTPUT);
-    mask = 0x1 << pin;
-    if (state == PCA9554_PIN_LOW) {
-        val &= ~(mask);
-    } else {
-        val |= mask;
-    }
-    pca9554_write(s, PCA9554_OUTPUT, val);
-
-    /* Then, clear the config register bit for output mode */
-    val = pca9554_read(s, PCA9554_CONFIG);
-    val &= ~mask;
-    pca9554_write(s, PCA9554_CONFIG, val);
-}
-
-static const VMStateDescription pca9554_vmstate = {
-    .name = "PCA9554",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(len, PCA9554State),
-        VMSTATE_UINT8(pointer, PCA9554State),
-        VMSTATE_UINT8_ARRAY(regs, PCA9554State, PCA9554_NR_REGS),
-        VMSTATE_UINT8_ARRAY(ext_state, PCA9554State, PCA9554_PIN_COUNT),
-        VMSTATE_I2C_SLAVE(i2c, PCA9554State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pca9554_reset(DeviceState *dev)
-{
-    PCA9554State *s = PCA9554(dev);
-
-    s->regs[PCA9554_INPUT] = 0xFF;
-    s->regs[PCA9554_OUTPUT] = 0xFF;
-    s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */
-    s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */
-
-    memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT);
-    pca9554_update_pin_input(s);
-
-    s->pointer = 0x0;
-    s->len = 0;
-}
-
-static void pca9554_initfn(Object *obj)
-{
-    int pin;
-
-    for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) {
-        char *name;
-
-        name = g_strdup_printf("pin%d", pin);
-        object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin,
-                            NULL, NULL);
-        g_free(name);
-    }
-}
-
-static void pca9554_set_ext_state(PCA9554State *s, int pin, int level)
-{
-    if (s->ext_state[pin] != level) {
-        s->ext_state[pin] = level;
-        pca9554_update_pin_input(s);
-    }
-}
-
-static void pca9554_gpio_in_handler(void *opaque, int pin, int level)
-{
-
-    PCA9554State *s = PCA9554(opaque);
-
-    assert((pin >= 0) && (pin < PCA9554_PIN_COUNT));
-    pca9554_set_ext_state(s, pin, level);
-}
-
-static void pca9554_realize(DeviceState *dev, Error **errp)
-{
-    PCA9554State *s = PCA9554(dev);
-
-    if (!s->description) {
-        s->description = g_strdup("pca9554");
-    }
-
-    qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT);
-    qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT);
-}
-
-static Property pca9554_properties[] = {
-    DEFINE_PROP_STRING("description", PCA9554State, description),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pca9554_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->event = pca9554_event;
-    k->recv = pca9554_recv;
-    k->send = pca9554_send;
-    dc->realize = pca9554_realize;
-    dc->reset = pca9554_reset;
-    dc->vmsd = &pca9554_vmstate;
-    device_class_set_props(dc, pca9554_properties);
-}
-
-static const TypeInfo pca9554_info = {
-    .name          = TYPE_PCA9554,
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_init = pca9554_initfn,
-    .instance_size = sizeof(PCA9554State),
-    .class_init    = pca9554_class_init,
-    .class_size    = sizeof(PCA9554Class),
-    .abstract      = false,
-};
-
-static void pca9554_register_types(void)
-{
-    type_register_static(&pca9554_info);
-}
-
-type_init(pca9554_register_types)
index 7cab1d5cb50e8997d1b7b6781019959e488c1b34..5d241cb40aabca763f80d4f564a0a57c3fa68e3c 100644 (file)
@@ -327,10 +327,6 @@ grlib_apb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "APB PnP read a
 led_set_intensity(const char *color, const char *desc, uint8_t intensity_percent) "LED desc:'%s' color:%s intensity: %u%%"
 led_change_intensity(const char *color, const char *desc, uint8_t old_intensity_percent, uint8_t new_intensity_percent) "LED desc:'%s' color:%s intensity %u%% -> %u%%"
 
-# pca9552.c
-pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]"
-pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u"
-
 # bcm2835_cprman.c
 bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
 bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
diff --git a/include/hw/gpio/pca9552.h b/include/hw/gpio/pca9552.h
new file mode 100644 (file)
index 0000000..c36525f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * PCA9552 I2C LED blinker
+ *
+ * Copyright (c) 2017-2018, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#ifndef PCA9552_H
+#define PCA9552_H
+
+#include "hw/i2c/i2c.h"
+#include "qom/object.h"
+
+#define TYPE_PCA9552 "pca9552"
+#define TYPE_PCA955X "pca955x"
+typedef struct PCA955xState PCA955xState;
+DECLARE_INSTANCE_CHECKER(PCA955xState, PCA955X,
+                         TYPE_PCA955X)
+
+#define PCA955X_NR_REGS 10
+#define PCA955X_PIN_COUNT_MAX 16
+
+struct PCA955xState {
+    /*< private >*/
+    I2CSlave i2c;
+    /*< public >*/
+
+    uint8_t len;
+    uint8_t pointer;
+
+    uint8_t regs[PCA955X_NR_REGS];
+    qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX];
+    uint8_t ext_state[PCA955X_PIN_COUNT_MAX];
+    char *description; /* For debugging purpose only */
+};
+
+#endif
diff --git a/include/hw/gpio/pca9552_regs.h b/include/hw/gpio/pca9552_regs.h
new file mode 100644 (file)
index 0000000..d8051cf
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PCA9552 I2C LED blinker registers
+ *
+ * Copyright (c) 2017-2018, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#ifndef PCA9552_REGS_H
+#define PCA9552_REGS_H
+
+/*
+ * Bits [0:3] are used to address a specific register.
+ */
+#define PCA9552_INPUT0   0 /* read only input register 0 */
+#define PCA9552_INPUT1   1 /* read only input register 1  */
+#define PCA9552_PSC0     2 /* read/write frequency prescaler 0 */
+#define PCA9552_PWM0     3 /* read/write PWM register 0 */
+#define PCA9552_PSC1     4 /* read/write frequency prescaler 1 */
+#define PCA9552_PWM1     5 /* read/write PWM register 1 */
+#define PCA9552_LS0      6 /* read/write LED0 to LED3 selector */
+#define PCA9552_LS1      7 /* read/write LED4 to LED7 selector */
+#define PCA9552_LS2      8 /* read/write LED8 to LED11 selector */
+#define PCA9552_LS3      9 /* read/write LED12 to LED15 selector */
+
+/*
+ * Bit [4] is used to activate the Auto-Increment option of the
+ * register address
+ */
+#define PCA9552_AUTOINC  (1 << 4)
+
+#endif
diff --git a/include/hw/gpio/pca9554.h b/include/hw/gpio/pca9554.h
new file mode 100644 (file)
index 0000000..54bfc4c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef PCA9554_H
+#define PCA9554_H
+
+#include "hw/i2c/i2c.h"
+#include "qom/object.h"
+
+#define TYPE_PCA9554 "pca9554"
+typedef struct PCA9554State PCA9554State;
+DECLARE_INSTANCE_CHECKER(PCA9554State, PCA9554,
+                         TYPE_PCA9554)
+
+#define PCA9554_NR_REGS 4
+#define PCA9554_PIN_COUNT 8
+
+struct PCA9554State {
+    /*< private >*/
+    I2CSlave i2c;
+    /*< public >*/
+
+    uint8_t len;
+    uint8_t pointer;
+
+    uint8_t regs[PCA9554_NR_REGS];
+    qemu_irq gpio_out[PCA9554_PIN_COUNT];
+    uint8_t ext_state[PCA9554_PIN_COUNT];
+    char *description; /* For debugging purpose only */
+};
+
+#endif
diff --git a/include/hw/gpio/pca9554_regs.h b/include/hw/gpio/pca9554_regs.h
new file mode 100644 (file)
index 0000000..602c4a9
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * PCA9554 I/O port registers
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef PCA9554_REGS_H
+#define PCA9554_REGS_H
+
+/*
+ * Bits [0:1] are used to address a specific register.
+ */
+#define PCA9554_INPUT       0 /* read only input register */
+#define PCA9554_OUTPUT      1 /* read/write pin output state */
+#define PCA9554_POLARITY    2 /* Set polarity of input register */
+#define PCA9554_CONFIG      3 /* Set pins as inputs our ouputs */
+
+#endif
diff --git a/include/hw/misc/pca9552.h b/include/hw/misc/pca9552.h
deleted file mode 100644 (file)
index c36525f..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * PCA9552 I2C LED blinker
- *
- * Copyright (c) 2017-2018, IBM Corporation.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or
- * later. See the COPYING file in the top-level directory.
- */
-#ifndef PCA9552_H
-#define PCA9552_H
-
-#include "hw/i2c/i2c.h"
-#include "qom/object.h"
-
-#define TYPE_PCA9552 "pca9552"
-#define TYPE_PCA955X "pca955x"
-typedef struct PCA955xState PCA955xState;
-DECLARE_INSTANCE_CHECKER(PCA955xState, PCA955X,
-                         TYPE_PCA955X)
-
-#define PCA955X_NR_REGS 10
-#define PCA955X_PIN_COUNT_MAX 16
-
-struct PCA955xState {
-    /*< private >*/
-    I2CSlave i2c;
-    /*< public >*/
-
-    uint8_t len;
-    uint8_t pointer;
-
-    uint8_t regs[PCA955X_NR_REGS];
-    qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX];
-    uint8_t ext_state[PCA955X_PIN_COUNT_MAX];
-    char *description; /* For debugging purpose only */
-};
-
-#endif
diff --git a/include/hw/misc/pca9552_regs.h b/include/hw/misc/pca9552_regs.h
deleted file mode 100644 (file)
index d8051cf..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * PCA9552 I2C LED blinker registers
- *
- * Copyright (c) 2017-2018, IBM Corporation.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or
- * later. See the COPYING file in the top-level directory.
- */
-#ifndef PCA9552_REGS_H
-#define PCA9552_REGS_H
-
-/*
- * Bits [0:3] are used to address a specific register.
- */
-#define PCA9552_INPUT0   0 /* read only input register 0 */
-#define PCA9552_INPUT1   1 /* read only input register 1  */
-#define PCA9552_PSC0     2 /* read/write frequency prescaler 0 */
-#define PCA9552_PWM0     3 /* read/write PWM register 0 */
-#define PCA9552_PSC1     4 /* read/write frequency prescaler 1 */
-#define PCA9552_PWM1     5 /* read/write PWM register 1 */
-#define PCA9552_LS0      6 /* read/write LED0 to LED3 selector */
-#define PCA9552_LS1      7 /* read/write LED4 to LED7 selector */
-#define PCA9552_LS2      8 /* read/write LED8 to LED11 selector */
-#define PCA9552_LS3      9 /* read/write LED12 to LED15 selector */
-
-/*
- * Bit [4] is used to activate the Auto-Increment option of the
- * register address
- */
-#define PCA9552_AUTOINC  (1 << 4)
-
-#endif
diff --git a/include/hw/misc/pca9554.h b/include/hw/misc/pca9554.h
deleted file mode 100644 (file)
index 54bfc4c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * PCA9554 I/O port
- *
- * Copyright (c) 2023, IBM Corporation.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-#ifndef PCA9554_H
-#define PCA9554_H
-
-#include "hw/i2c/i2c.h"
-#include "qom/object.h"
-
-#define TYPE_PCA9554 "pca9554"
-typedef struct PCA9554State PCA9554State;
-DECLARE_INSTANCE_CHECKER(PCA9554State, PCA9554,
-                         TYPE_PCA9554)
-
-#define PCA9554_NR_REGS 4
-#define PCA9554_PIN_COUNT 8
-
-struct PCA9554State {
-    /*< private >*/
-    I2CSlave i2c;
-    /*< public >*/
-
-    uint8_t len;
-    uint8_t pointer;
-
-    uint8_t regs[PCA9554_NR_REGS];
-    qemu_irq gpio_out[PCA9554_PIN_COUNT];
-    uint8_t ext_state[PCA9554_PIN_COUNT];
-    char *description; /* For debugging purpose only */
-};
-
-#endif
diff --git a/include/hw/misc/pca9554_regs.h b/include/hw/misc/pca9554_regs.h
deleted file mode 100644 (file)
index 602c4a9..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * PCA9554 I/O port registers
- *
- * Copyright (c) 2023, IBM Corporation.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-#ifndef PCA9554_REGS_H
-#define PCA9554_REGS_H
-
-/*
- * Bits [0:1] are used to address a specific register.
- */
-#define PCA9554_INPUT       0 /* read only input register */
-#define PCA9554_OUTPUT      1 /* read/write pin output state */
-#define PCA9554_POLARITY    2 /* Set polarity of input register */
-#define PCA9554_CONFIG      3 /* Set pins as inputs our ouputs */
-
-#endif
index 0d0c672c9596f9652f7541f34dfbb5e6b3fb512f..57b7db1ee9686e7f8b4ec917e40585486c36bc51 100644 (file)
@@ -4781,9 +4781,10 @@ static DisasJumpType op_trXX(DisasContext *s, DisasOps *o)
 
 static DisasJumpType op_ts(DisasContext *s, DisasOps *o)
 {
-    TCGv_i32 t1 = tcg_constant_i32(0xff);
+    TCGv_i32 ff = tcg_constant_i32(0xff);
+    TCGv_i32 t1 = tcg_temp_new_i32();
 
-    tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB);
+    tcg_gen_atomic_xchg_i32(t1, o->in2, ff, get_mem_index(s), MO_UB);
     tcg_gen_extract_i32(cc_op, t1, 7, 1);
     set_cc_static(s);
     return DISAS_NEXT;
index 82a6e122bf1c43ada87c18da985edf5a1f558547..a21b6eee9cd637ef2ae0b49fe1402a7058fafdde 100644 (file)
@@ -394,7 +394,7 @@ void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
     qvirtio_writew(d, qts, vq->avail + 2, idx + 1);
 
     /* Must read after idx is updated */
-    flags = qvirtio_readw(d, qts, vq->avail);
+    flags = qvirtio_readw(d, qts, vq->used);
     avail_event = qvirtio_readw(d, qts, vq->used + 4 +
                                 sizeof(struct vring_used_elem) * vq->size);
 
index ccca2b3d91408e532dd14a79dda8ccdd7ed149da..74749576923947a51a4d00cb3c0aaedd75ecdec2 100644 (file)
@@ -12,7 +12,7 @@
 #include "libqtest.h"
 #include "libqos/qgraph.h"
 #include "libqos/i2c.h"
-#include "hw/misc/pca9552_regs.h"
+#include "hw/gpio/pca9552_regs.h"
 
 #define PCA9552_TEST_ID   "pca9552-test"
 #define PCA9552_TEST_ADDR 0x60
index c6351772520ca06d858afb91d1b239f61a1aa8bd..7f64d597ac1d1126122a1a3e79d58404d8e487d2 100644 (file)
@@ -8,8 +8,8 @@
  */
 #include "qemu/osdep.h"
 #include "libqtest.h"
-#include "hw/misc/pca9554_regs.h"
-#include "hw/misc/pca9552_regs.h"
+#include "hw/gpio/pca9554_regs.h"
+#include "hw/gpio/pca9552_regs.h"
 #include "pnv-xscom.h"
 
 #define PPC_BIT(bit)            (0x8000000000000000ULL >> (bit))
index e2aba2ec274b1c66283655581dc9c23532fc19a1..a8f86c9449874d0ad0d4c34f2d1f00b59c4930bb 100644 (file)
@@ -47,6 +47,7 @@ TESTS+=add-logical-with-carry
 TESTS+=lae
 TESTS+=cvd
 TESTS+=cvb
+TESTS+=ts
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/ts.c b/tests/tcg/s390x/ts.c
new file mode 100644 (file)
index 0000000..441faf3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Test the TEST AND SET instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <assert.h>
+#include <stdlib.h>
+
+static int ts(char *p)
+{
+    int cc;
+
+    asm("ts %[p]\n"
+        "ipm %[cc]"
+        : [cc] "=r" (cc)
+        , [p] "+Q" (*p)
+        : : "cc");
+
+    return (cc >> 28) & 3;
+}
+
+int main(void)
+{
+    char c;
+
+    c = 0x80;
+    assert(ts(&c) == 1);
+    assert(c == 0xff);
+
+    c = 0x7f;
+    assert(ts(&c) == 0);
+    assert(c == 0xff);
+
+    return EXIT_SUCCESS;
+}