* [PATCH 2/3] hw: aspeed_gpio: Split GPIOSet handling from accessors
2022-02-07 15:04 [PATCH 0/3] hw: aspeed_gpio: Model new interface for the AST2600 Andrew Jeffery
2022-02-07 15:04 ` [PATCH 1/3] hw: aspeed_gpio: Cleanup stray semicolon after switch Andrew Jeffery
@ 2022-02-07 15:04 ` Andrew Jeffery
2022-02-07 15:04 ` [PATCH 3/3] hw: aspeed_gpio: Support the AST2600's indexed register interface Andrew Jeffery
2022-02-09 11:09 ` [PATCH 0/3] hw: aspeed_gpio: Model new interface for the AST2600 Andrew Jeffery
3 siblings, 0 replies; 5+ messages in thread
From: Andrew Jeffery @ 2022-02-07 15:04 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, openbmc, qemu-arm, clg
Pave the way for implementing the new register interface for GPIO
control provided by the AST2600. We need a consistent data model, so
do some work to enable use of the AspeedGPIOReg / GPIOSets data
structures for both.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
hw/gpio/aspeed_gpio.c | 105 ++++++++++++++++++++++++------------------
1 file changed, 60 insertions(+), 45 deletions(-)
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
index c63634d3d3e2..1d4d1aedc4b5 100644
--- a/hw/gpio/aspeed_gpio.c
+++ b/hw/gpio/aspeed_gpio.c
@@ -516,28 +516,11 @@ static const AspeedGPIOReg aspeed_1_8v_gpios[GPIO_1_8V_REG_ARRAY_SIZE] = {
[GPIO_1_8V_E_INPUT_MASK] = {1, gpio_reg_input_mask},
};
-static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
+static uint64_t
+aspeed_gpio_set_read(const AspeedGPIOState *s, const AspeedGPIOReg *reg)
{
- AspeedGPIOState *s = ASPEED_GPIO(opaque);
- AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
- uint64_t idx = -1;
- const AspeedGPIOReg *reg;
- GPIOSets *set;
+ const GPIOSets *set = &s->sets[reg->set_idx];
- idx = offset >> 2;
- if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
- idx -= GPIO_DEBOUNCE_TIME_1;
- return (uint64_t) s->debounce_regs[idx];
- }
-
- reg = &agc->reg_table[idx];
- if (reg->set_idx >= agc->nr_gpio_sets) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
- HWADDR_PRIx"\n", __func__, offset);
- return 0;
- }
-
- set = &s->sets[reg->set_idx];
switch (reg->type) {
case gpio_reg_data_value:
return set->data_value;
@@ -567,37 +550,44 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
return set->data_read;
case gpio_reg_input_mask:
return set->input_mask;
- default:
+ case gpio_not_a_reg:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid register: %d\n", __func__,
+ reg->type);
+ }
+
+ return 0;
+}
+
+static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
+{
+ AspeedGPIOState *s = ASPEED_GPIO(opaque);
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+ const AspeedGPIOReg *reg;
+ uint64_t idx = -1;
+
+ idx = offset >> 2;
+ if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
+ idx -= GPIO_DEBOUNCE_TIME_1;
+ return (uint64_t) s->debounce_regs[idx];
+ }
+
+ reg = &agc->reg_table[idx];
+ if (reg->set_idx >= agc->nr_gpio_sets) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
HWADDR_PRIx"\n", __func__, offset);
return 0;
}
+
+ return aspeed_gpio_set_read(s, reg);
}
-static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
- uint32_t size)
+static void aspeed_gpio_set_write(AspeedGPIOState *s, const AspeedGPIOReg *reg,
+ uint32_t data)
{
- AspeedGPIOState *s = ASPEED_GPIO(opaque);
AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
const GPIOSetProperties *props;
- uint64_t idx = -1;
- const AspeedGPIOReg *reg;
- GPIOSets *set;
uint32_t cleared;
-
- idx = offset >> 2;
- if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
- idx -= GPIO_DEBOUNCE_TIME_1;
- s->debounce_regs[idx] = (uint32_t) data;
- return;
- }
-
- reg = &agc->reg_table[idx];
- if (reg->set_idx >= agc->nr_gpio_sets) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
- HWADDR_PRIx"\n", __func__, offset);
- return;
- }
+ GPIOSets *set;
set = &s->sets[reg->set_idx];
props = &agc->props[reg->set_idx];
@@ -678,13 +668,38 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
*/
set->input_mask = data & props->input;
break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
- HWADDR_PRIx"\n", __func__, offset);
+ case gpio_not_a_reg:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid register: %d\n", __func__,
+ reg->type);
return;
}
+
aspeed_gpio_update(s, set, set->data_value);
- return;
+}
+
+static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
+ uint32_t size)
+{
+ AspeedGPIOState *s = ASPEED_GPIO(opaque);
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+ const AspeedGPIOReg *reg;
+ uint64_t idx = -1;
+
+ idx = offset >> 2;
+ if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
+ idx -= GPIO_DEBOUNCE_TIME_1;
+ s->debounce_regs[idx] = (uint32_t) data;
+ return;
+ }
+
+ reg = &agc->reg_table[idx];
+ if (reg->set_idx >= agc->nr_gpio_sets) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
+ HWADDR_PRIx"\n", __func__, offset);
+ return;
+ }
+
+ aspeed_gpio_set_write(s, reg, data);
}
static int get_set_idx(AspeedGPIOState *s, const char *group, int *group_idx)
--
2.32.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/3] hw: aspeed_gpio: Support the AST2600's indexed register interface
2022-02-07 15:04 [PATCH 0/3] hw: aspeed_gpio: Model new interface for the AST2600 Andrew Jeffery
2022-02-07 15:04 ` [PATCH 1/3] hw: aspeed_gpio: Cleanup stray semicolon after switch Andrew Jeffery
2022-02-07 15:04 ` [PATCH 2/3] hw: aspeed_gpio: Split GPIOSet handling from accessors Andrew Jeffery
@ 2022-02-07 15:04 ` Andrew Jeffery
2022-02-09 11:09 ` [PATCH 0/3] hw: aspeed_gpio: Model new interface for the AST2600 Andrew Jeffery
3 siblings, 0 replies; 5+ messages in thread
From: Andrew Jeffery @ 2022-02-07 15:04 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, openbmc, qemu-arm, clg
A new register interface was added to the AST2600 GPIO controller that
allows a single 32 bit register to drive configuration of up to 208
GPIOs. This makes way for a very simple driver implementation in
early-boot firmware such as u-boot. The old register interface required
drivers implement a tedious data model, but allowed efficient multi-line
bit-banging.
Either way, the hardware model in qemu becomes quite complex, though it
would have been less so had the new interface been the only one
available.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
hw/gpio/aspeed_gpio.c | 202 +++++++++++++++++++++++++++++++++-
include/hw/gpio/aspeed_gpio.h | 3 +
2 files changed, 202 insertions(+), 3 deletions(-)
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
index 1d4d1aedc4b5..cee1a9a2e065 100644
--- a/hw/gpio/aspeed_gpio.c
+++ b/hw/gpio/aspeed_gpio.c
@@ -160,7 +160,42 @@
#define GPIO_YZAAAB_DIRECTION (0x1E4 >> 2)
#define GPIO_AC_DATA_VALUE (0x1E8 >> 2)
#define GPIO_AC_DIRECTION (0x1EC >> 2)
-#define GPIO_3_3V_MEM_SIZE 0x1F0
+#define GPIO_INDEX (0x2AC >> 2)
+#define GPIO_INDEX_DATA_SHIFT 20
+#define GPIO_INDEX_DATA_LEN 12
+#define GPIO_INDEX_DATA_DATA 20
+#define GPIO_INDEX_DATA_DIR 20
+#define GPIO_INDEX_DATA_IRQ_EN 20
+#define GPIO_INDEX_DATA_IRQ_TY0 21
+#define GPIO_INDEX_DATA_IRQ_TY1 22
+#define GPIO_INDEX_DATA_IRQ_TY2 23
+#define GPIO_INDEX_DATA_IRQ_STS 24
+#define GPIO_INDEX_DATA_DB1 20
+#define GPIO_INDEX_DATA_DB2 21
+#define GPIO_INDEX_DATA_TOL 20
+#define GPIO_INDEX_DATA_SRC0 20
+#define GPIO_INDEX_DATA_SRC1 20
+#define GPIO_INDEX_DATA_INPUT 20
+#define GPIO_INDEX_DATA_WR_SRC 20
+#define GPIO_INDEX_TYPE_SHIFT 16
+#define GPIO_INDEX_TYPE_LEN 4
+#define GPIO_INDEX_TYPE_DATA 0
+#define GPIO_INDEX_TYPE_DIR 1
+#define GPIO_INDEX_TYPE_IRQ 2
+#define GPIO_INDEX_TYPE_DEBOUNCE 3
+#define GPIO_INDEX_TYPE_TOL 4
+#define GPIO_INDEX_TYPE_SRC 5
+#define GPIO_INDEX_TYPE_INPUT 6
+#define GPIO_INDEX_TYPE_RSVD 7
+#define GPIO_INDEX_TYPE_WR_SRC 8
+#define GPIO_INDEX_TYPE_RD_SRC 9
+#define GPIO_INDEX_CMD_SHIFT 12
+#define GPIO_INDEX_CMD_LEN 1
+#define GPIO_INDEX_CMD_WRITE 0
+#define GPIO_INDEX_CMD_READ 1
+#define GPIO_INDEX_NR_SHIFT 0
+#define GPIO_INDEX_NR_LEN 8
+#define GPIO_3_3V_MEM_SIZE 0x2B0
#define GPIO_3_3V_REG_ARRAY_SIZE (GPIO_3_3V_MEM_SIZE >> 2)
/* AST2600 only - 1.8V gpios */
@@ -571,6 +606,11 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
return (uint64_t) s->debounce_regs[idx];
}
+ /* This is a (new, indirect) register interface for configuring GPIOs */
+ if (agc->have_index_reg && idx == GPIO_INDEX) {
+ return (uint64_t) s->index;
+ }
+
reg = &agc->reg_table[idx];
if (reg->set_idx >= agc->nr_gpio_sets) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
@@ -581,8 +621,73 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
return aspeed_gpio_set_read(s, reg);
}
-static void aspeed_gpio_set_write(AspeedGPIOState *s, const AspeedGPIOReg *reg,
- uint32_t data)
+static int aspeed_gpio_set_offset_read(AspeedGPIOState *s, int set, enum GPIORegType reg,
+ int offset)
+{
+ return !!(aspeed_gpio_set_read(s, &(AspeedGPIOReg){set, reg}) & BIT(offset));
+}
+
+static const enum GPIORegType aspeed_gpio_index_type_map[] = {
+ [GPIO_INDEX_TYPE_DATA] = gpio_reg_data_value,
+ [GPIO_INDEX_TYPE_DIR] = gpio_reg_direction,
+ [GPIO_INDEX_TYPE_TOL] = gpio_reg_reset_tolerant,
+ [GPIO_INDEX_TYPE_INPUT] = gpio_reg_input_mask,
+ [GPIO_INDEX_TYPE_WR_SRC] = gpio_reg_input_mask /* See GPIO2AC doc */
+};
+
+static void
+aspeed_gpio_index_read(AspeedGPIOState *s, uint32_t type, uint32_t number)
+{
+ int pin = number % 32;
+ int set = number / 32;
+
+ /* Clear the data field so we can OR into it without further data dependencies */
+ s->index = deposit32(s->index, GPIO_INDEX_DATA_SHIFT, GPIO_INDEX_DATA_LEN, 0);
+
+ switch (type) {
+ case GPIO_INDEX_TYPE_DATA:
+ case GPIO_INDEX_TYPE_DIR:
+ case GPIO_INDEX_TYPE_TOL:
+ case GPIO_INDEX_TYPE_INPUT:
+ case GPIO_INDEX_TYPE_WR_SRC:
+ {
+ enum GPIORegType reg = aspeed_gpio_index_type_map[type];
+ s->index |= deposit32(0, GPIO_INDEX_DATA_SHIFT, GPIO_INDEX_DATA_LEN,
+ aspeed_gpio_set_offset_read(s, set, reg, pin));
+ break;
+ }
+ case GPIO_INDEX_TYPE_IRQ:
+ s->index |= deposit32(0, GPIO_INDEX_DATA_IRQ_EN, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_int_enable, pin));
+ s->index |= deposit32(0, GPIO_INDEX_DATA_IRQ_TY0, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_int_sens_0, pin));
+ s->index |= deposit32(0, GPIO_INDEX_DATA_IRQ_TY1, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_int_sens_1, pin));
+ s->index |= deposit32(0, GPIO_INDEX_DATA_IRQ_TY2, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_int_sens_2, pin));
+ s->index |= deposit32(0, GPIO_INDEX_DATA_IRQ_STS, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_int_status, pin));
+ break;
+ case GPIO_INDEX_TYPE_DEBOUNCE:
+ s->index |= deposit32(0, GPIO_INDEX_DATA_DB1, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_debounce_1, pin));
+ s->index |= deposit32(0, GPIO_INDEX_DATA_DB2, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_debounce_2, pin));
+ break;
+ case GPIO_INDEX_TYPE_SRC:
+ s->index |= deposit32(0, GPIO_INDEX_DATA_SRC0, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_cmd_source_0, pin));
+ s->index |= deposit32(0, GPIO_INDEX_DATA_SRC1, 1,
+ aspeed_gpio_set_offset_read(s, set, gpio_reg_cmd_source_1, pin));
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no such command type: %" PRIu32 "\n",
+ __func__, type);
+ }
+}
+
+static void
+aspeed_gpio_set_write(AspeedGPIOState *s, const AspeedGPIOReg *reg, uint32_t data)
{
AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
const GPIOSetProperties *props;
@@ -677,6 +782,87 @@ static void aspeed_gpio_set_write(AspeedGPIOState *s, const AspeedGPIOReg *reg,
aspeed_gpio_update(s, set, set->data_value);
}
+static void
+aspeed_gpio_set_offset_write(AspeedGPIOState *s, int set, enum GPIORegType reg,
+ int offset, int val)
+{
+ AspeedGPIOReg agr = { set, reg };
+ uint32_t data;
+
+ data = aspeed_gpio_set_read(s, &agr);
+ data = deposit32(data, offset, 1, val);
+ aspeed_gpio_set_write(s, &agr, data);
+}
+
+static void
+aspeed_gpio_index_write(AspeedGPIOState *s, uint32_t type, uint32_t number, uint32_t data)
+{
+ int pin = number % 32;
+ int set = number / 32;
+
+ switch (type) {
+ case GPIO_INDEX_TYPE_DATA:
+ case GPIO_INDEX_TYPE_DIR:
+ case GPIO_INDEX_TYPE_TOL:
+ case GPIO_INDEX_TYPE_INPUT:
+ case GPIO_INDEX_TYPE_WR_SRC:
+ {
+ enum GPIORegType reg = aspeed_gpio_index_type_map[type];
+ aspeed_gpio_set_offset_write(s, set, reg, pin, data);
+ break;
+ }
+ case GPIO_INDEX_TYPE_IRQ:
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_int_enable, pin,
+ extract32(data, GPIO_INDEX_DATA_IRQ_EN, 1));
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_int_sens_0, pin,
+ extract32(data, GPIO_INDEX_DATA_IRQ_TY0, 1));
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_int_sens_1, pin,
+ extract32(data, GPIO_INDEX_DATA_IRQ_TY1, 1));
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_int_sens_2, pin,
+ extract32(data, GPIO_INDEX_DATA_IRQ_TY2, 1));
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_int_status, pin,
+ extract32(data, GPIO_INDEX_DATA_IRQ_STS, 1));
+ break;
+ case GPIO_INDEX_TYPE_DEBOUNCE:
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_debounce_1, pin,
+ extract32(data, GPIO_INDEX_DATA_DB1, 1));
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_debounce_2, pin,
+ extract32(data, GPIO_INDEX_DATA_DB2, 1));
+ break;
+ case GPIO_INDEX_TYPE_SRC:
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_cmd_source_0, pin,
+ extract32(data, GPIO_INDEX_DATA_SRC0, 1));
+ aspeed_gpio_set_offset_write(s, set, gpio_reg_cmd_source_1, pin,
+ extract32(data, GPIO_INDEX_DATA_SRC1, 1));
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no such command type: %" PRIu32 "\n",
+ __func__, type);
+ };
+}
+
+static void aspeed_gpio_index_command(AspeedGPIOState *s, uint32_t index)
+{
+ uint32_t command, number, type;
+
+ s->index = index;
+
+ command = extract32(index, GPIO_INDEX_CMD_SHIFT, GPIO_INDEX_CMD_LEN);
+ number = extract32(index, GPIO_INDEX_NR_SHIFT, GPIO_INDEX_NR_LEN);
+ type = extract32(index, GPIO_INDEX_TYPE_SHIFT, GPIO_INDEX_TYPE_LEN);
+
+ if (command == GPIO_INDEX_CMD_WRITE) {
+ uint32_t data;
+
+ data = extract32(index, GPIO_INDEX_DATA_SHIFT, GPIO_INDEX_DATA_LEN);
+ aspeed_gpio_index_write(s, type, number, data);
+
+ return;
+ }
+
+ aspeed_gpio_index_read(s, type, number);
+}
+
static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
uint32_t size)
{
@@ -692,6 +878,12 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
return;
}
+ /* This is a (new, indirect) register interface for configuring GPIOs */
+ if (agc->have_index_reg && idx == GPIO_INDEX) {
+ aspeed_gpio_index_command(s, data);
+ return;
+ }
+
reg = &agc->reg_table[idx];
if (reg->set_idx >= agc->nr_gpio_sets) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
@@ -930,6 +1122,7 @@ static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data)
agc->nr_gpio_pins = 216;
agc->nr_gpio_sets = 7;
agc->reg_table = aspeed_3_3v_gpios;
+ agc->have_index_reg = false;
}
static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data)
@@ -940,6 +1133,7 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data)
agc->nr_gpio_pins = 228;
agc->nr_gpio_sets = 8;
agc->reg_table = aspeed_3_3v_gpios;
+ agc->have_index_reg = false;
}
static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data)
@@ -950,6 +1144,7 @@ static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data)
agc->nr_gpio_pins = 208;
agc->nr_gpio_sets = 7;
agc->reg_table = aspeed_3_3v_gpios;
+ agc->have_index_reg = true;
}
static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
@@ -960,6 +1155,7 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
agc->nr_gpio_pins = 36;
agc->nr_gpio_sets = 2;
agc->reg_table = aspeed_1_8v_gpios;
+ agc->have_index_reg = true;
}
static const TypeInfo aspeed_gpio_info = {
diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h
index 801846befb3b..57188fcb4098 100644
--- a/include/hw/gpio/aspeed_gpio.h
+++ b/include/hw/gpio/aspeed_gpio.h
@@ -61,6 +61,7 @@ struct AspeedGPIOClass {
uint32_t nr_gpio_pins;
uint32_t nr_gpio_sets;
const AspeedGPIOReg *reg_table;
+ bool have_index_reg;
};
struct AspeedGPIOState {
@@ -91,6 +92,8 @@ struct AspeedGPIOState {
uint32_t debounce_2;
uint32_t input_mask;
} sets[ASPEED_GPIO_MAX_NR_SETS];
+
+ uint32_t index;
};
#endif /* _ASPEED_GPIO_H_ */
--
2.32.0
^ permalink raw reply related [flat|nested] 5+ messages in thread