hw/misc/pca9552: Add generic PCA955xClass, parent of TYPE_PCA9552

Extract the code common to the PCA955x family in PCA955xClass,
keeping the PCA9552 specific parts into pca9552_class_init().
Remove the 'TODO' comment added in commit 5141d4158c.

Suggested-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Tested-by: Cédric Le Goater <clg@kaod.org>
Message-id: 20200623072723.6324-5-f4bug@amsat.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Philippe Mathieu-Daudé 2020-06-23 09:27:18 +02:00 committed by Peter Maydell
parent ec17228a25
commit 736132e455
2 changed files with 51 additions and 21 deletions

View File

@ -4,6 +4,7 @@
* 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.
@ -18,6 +19,20 @@
#include "qapi/error.h"
#include "qapi/visitor.h"
typedef struct PCA955xClass {
/*< private >*/
I2CSlaveClass parent_class;
/*< public >*/
uint8_t pin_count;
uint8_t max_reg;
} PCA955xClass;
#define PCA955X_CLASS(klass) \
OBJECT_CLASS_CHECK(PCA955xClass, (klass), TYPE_PCA955X)
#define PCA955X_GET_CLASS(obj) \
OBJECT_GET_CLASS(PCA955xClass, (obj), TYPE_PCA955X)
#define PCA9552_LED_ON 0x0
#define PCA9552_LED_OFF 0x1
#define PCA9552_LED_PWM0 0x2
@ -35,9 +50,10 @@ static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
static void pca955x_update_pin_input(PCA955xState *s)
{
PCA955xClass *k = PCA955X_GET_CLASS(s);
int i;
for (i = 0; i < s->pin_count; i++) {
for (i = 0; i < k->pin_count; i++) {
uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
uint8_t input_shift = (i % 8);
uint8_t config = pca955x_pin_get_config(s, i);
@ -112,10 +128,12 @@ static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
*/
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) % (s->max_reg + 1);
reg = (reg + 1) % (k->max_reg + 1);
s->pointer = reg | PCA9552_AUTOINC;
}
}
@ -176,6 +194,7 @@ static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
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;
@ -185,7 +204,7 @@ static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
error_setg(errp, "%s: error reading %s", __func__, name);
return;
}
if (led < 0 || led > s->pin_count) {
if (led < 0 || led > k->pin_count) {
error_setg(errp, "%s invalid led %s", __func__, name);
return;
}
@ -212,6 +231,7 @@ static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
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);
Error *local_err = NULL;
int led, rc, reg, val;
@ -228,7 +248,7 @@ static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
error_setg(errp, "%s: error reading %s", __func__, name);
return;
}
if (led < 0 || led > s->pin_count) {
if (led < 0 || led > k->pin_count) {
error_setg(errp, "%s invalid led %s", __func__, name);
return;
}
@ -283,17 +303,11 @@ static void pca9552_reset(DeviceState *dev)
static void pca955x_initfn(Object *obj)
{
PCA955xState *s = PCA955X(obj);
PCA955xClass *k = PCA955X_GET_CLASS(obj);
int led;
/* If support for the other PCA955X devices are implemented, these
* constant values might be part of class structure describing the
* PCA955X device
*/
s->max_reg = PCA9552_LS3;
s->pin_count = 16;
for (led = 0; led < s->pin_count; 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);
@ -303,28 +317,44 @@ static void pca955x_initfn(Object *obj)
}
}
static void pca9552_class_init(ObjectClass *klass, void *data)
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;
}
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,
.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_I2C_SLAVE,
.instance_init = pca955x_initfn,
.instance_size = sizeof(PCA955xState),
.parent = TYPE_PCA955X,
.class_init = pca9552_class_init,
};
static void pca955x_register_types(void)
{
type_register_static(&pca955x_info);
type_register_static(&pca9552_info);
}

View File

@ -12,9 +12,11 @@
#include "hw/i2c/i2c.h"
#define TYPE_PCA9552 "pca9552"
#define PCA955X(obj) OBJECT_CHECK(PCA955xState, (obj), TYPE_PCA9552)
#define TYPE_PCA955X "pca955x"
#define PCA955X(obj) OBJECT_CHECK(PCA955xState, (obj), TYPE_PCA955X)
#define PCA955X_NR_REGS 10
#define PCA955X_PIN_COUNT_MAX 16
typedef struct PCA955xState {
/*< private >*/
@ -25,8 +27,6 @@ typedef struct PCA955xState {
uint8_t pointer;
uint8_t regs[PCA955X_NR_REGS];
uint8_t max_reg;
uint8_t pin_count;
} PCA955xState;
#endif