diff options
author | Joel Stanley <joel@jms.id.au> | 2020-01-30 16:02:02 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-01-30 16:02:02 +0000 |
commit | a90d8f84674da3afaaa15fca7a47901fac5f47b5 (patch) | |
tree | 3540e0f0f6e675edf8d7920b0ce19dd0942c5dd3 /hw/misc/pca9552.c | |
parent | 1a15311a12fa6a5c865e7f779e6e1b2557440626 (diff) |
misc/pca9552: Add qom set and get
Following the pattern of the work recently done with the ASPEED GPIO
model, this adds support for inspecting and modifying the PCA9552 LEDs
from the monitor.
(qemu) qom-set /machine/unattached/device[17] led0 on
(qemu) qom-set /machine/unattached/device[17] led0 off
(qemu) qom-set /machine/unattached/device[17] led0 pwm0
(qemu) qom-set /machine/unattached/device[17] led0 pwm1
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Message-id: 20200114103433.30534-6-clg@kaod.org
[clg: - removed the "qom-get" examples from the commit log
- merged memory leak fixes from Joel ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/misc/pca9552.c')
-rw-r--r-- | hw/misc/pca9552.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c index 73be28d936..efd961e041 100644 --- a/hw/misc/pca9552.c +++ b/hw/misc/pca9552.c @@ -15,12 +15,16 @@ #include "hw/misc/pca9552.h" #include "hw/misc/pca9552_regs.h" #include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" #define PCA9552_LED_ON 0x0 #define PCA9552_LED_OFF 0x1 #define PCA9552_LED_PWM0 0x2 #define PCA9552_LED_PWM1 0x3 +static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; + static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin) { uint8_t reg = PCA9552_LS0 + (pin / 4); @@ -169,6 +173,82 @@ static int pca9552_event(I2CSlave *i2c, enum i2c_event event) return 0; } +static void pca9552_get_led(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA9552State *s = PCA9552(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 > s->nr_leds) { + 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 = (pca9552_read(s, reg) >> (led % 8)) & 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 pca9552_set_led(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA9552State *s = PCA9552(obj); + Error *local_err = NULL; + int led, rc, reg, val; + uint8_t state; + char *state_str; + + visit_type_str(v, name, &state_str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + rc = sscanf(name, "led%2d", &led); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (led < 0 || led > s->nr_leds) { + 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 = pca9552_read(s, reg); + val = pca955x_ledsel(val, led % 4, state); + pca9552_write(s, reg, val); +} + static const VMStateDescription pca9552_vmstate = { .name = "PCA9552", .version_id = 0, @@ -204,6 +284,7 @@ static void pca9552_reset(DeviceState *dev) static void pca9552_initfn(Object *obj) { PCA9552State *s = PCA9552(obj); + int led; /* If support for the other PCA955X devices are implemented, these * constant values might be part of class structure describing the @@ -211,6 +292,15 @@ static void pca9552_initfn(Object *obj) */ s->max_reg = PCA9552_LS3; s->nr_leds = 16; + + for (led = 0; led < s->nr_leds; led++) { + char *name; + + name = g_strdup_printf("led%d", led); + object_property_add(obj, name, "bool", pca9552_get_led, pca9552_set_led, + NULL, NULL, NULL); + g_free(name); + } } static void pca9552_class_init(ObjectClass *klass, void *data) |