summaryrefslogtreecommitdiffstats
path: root/hw/gpio
diff options
context:
space:
mode:
authorRichard Henderson2022-05-25 20:36:04 +0200
committerRichard Henderson2022-05-25 20:36:04 +0200
commit6291d2588fff71e670673e3d25ee85526e28da38 (patch)
tree5af95a6ae364f19750ef69b5b3bcd012c6c7cd48 /hw/gpio
parentMerge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging (diff)
parenthw/arm/aspeed: Add i2c devices for AST2600 EVB (diff)
downloadqemu-6291d2588fff71e670673e3d25ee85526e28da38.tar.gz
qemu-6291d2588fff71e670673e3d25ee85526e28da38.tar.xz
qemu-6291d2588fff71e670673e3d25ee85526e28da38.zip
Merge tag 'pull-aspeed-20220525' of https://github.com/legoater/qemu into staging
aspeed queue: * Aspeed GPIO model extensions * GPIO support for the Aspeed AST1030 SoC * New fby35 machine (AST2600 based) * Extra unit tests for the GPIO and SMC models * Initialization of all UART with serial devices * AST2600 EVB and Documentation update # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmKOUhcACgkQUaNDx8/7 # 7KF3MBAAuIusIv5HXKEzLNJK2Gyk/qiXy+CAkfr+ZbpAk96JeA5y0fVHtkThSj6k # KbNNVAUojWC/AKsvldYxKkcyt5A8nNPkNP6H0c3CGUCrHUo8rdMW9otZGS91uH9+ # Xvdq7ANuP/BAGNSXXMJ3p3h6VwOVrJnnRAZR6Xy4ytWZpWnYhnJNca9//0JZ2lu+ # 2h/hOlx8IE/c8YcyfixyRtuL4ElobSaC1Ajf/wcByWINEGecbWBrsEJq9F6K8me8 # 8w2A3dBZaE3FfYJXEaDBqPzmB3dmgsui0DzvHqb6GKLZ1zzTPzc1xwqx0xyfb4iN # e3uxC+H1fp6VvHLN21bgl+nQtFEirSxUe0KQkeITjDDzqnnTECrdsSzxJXQ+/fUq # yhj63ceijsjqEfupuDtKqafSJTWz/ELNjx0mspFWm0a4zHbp+OzwNBK9eFW+h5gf # ydMpEB7hzpJFQT4g2UZSWrYOVRXRZRcswoK5ZxThx90+TDZ3Z+X3Nn8qqmWwbb8s # WzqRNMzvl0eh6hbAWcexkoDU1f5TxJ9kJRHQV3cdzp+BMNzMGTyqHetgC3d9MsdR # x5adfgMUblXO+SukxUNm+N1KLTET6XNTNAUlHDeb1KMqipbRH9tH5sxOyKFAGHkP # 0PY+zN4atV/H8hbAjHrg4b3BOQvHr4ro4Liw4I8XQT/gsjD4bBg= # =Vtgk # -----END PGP SIGNATURE----- # gpg: Signature made Wed 25 May 2022 08:58:15 AM PDT # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-aspeed-20220525' of https://github.com/legoater/qemu: hw/arm/aspeed: Add i2c devices for AST2600 EVB hw/gpio: replace HWADDR_PRIx with PRIx64 hw/gpio support GPIO index mode for write operation. hw/gpio: Add ASPEED GPIO model for AST1030 hw/gpio Add GPIO read/write trace event. hw: aspeed: Init all UART's with serial devices hw: aspeed: Introduce common UART init function hw: aspeed: Ensure AST1030 respects uart-default hw: aspeed: Add uarts_num SoC attribute hw: aspeed: Add missing UART's aspeed: Introduce a get_irq AspeedSoCClass method hw: m25p80: allow write_enable latch get/set docs: aspeed: Add fby35 board hw/arm/aspeed: Add fby35 machine type docs: add minibmc section in aspeed document Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw/gpio')
-rw-r--r--hw/gpio/aspeed_gpio.c257
-rw-r--r--hw/gpio/trace-events4
2 files changed, 242 insertions, 19 deletions
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
index 9b736e7a9f..a62a673857 100644
--- a/hw/gpio/aspeed_gpio.c
+++ b/hw/gpio/aspeed_gpio.c
@@ -15,6 +15,8 @@
#include "qapi/visitor.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
+#include "trace.h"
+#include "hw/registerfields.h"
#define GPIOS_PER_GROUP 8
@@ -203,6 +205,28 @@
#define GPIO_1_8V_MEM_SIZE 0x1D8
#define GPIO_1_8V_REG_ARRAY_SIZE (GPIO_1_8V_MEM_SIZE >> 2)
+/*
+ * GPIO index mode support
+ * It only supports write operation
+ */
+REG32(GPIO_INDEX_REG, 0x2AC)
+ FIELD(GPIO_INDEX_REG, NUMBER, 0, 8)
+ FIELD(GPIO_INDEX_REG, COMMAND, 12, 1)
+ FIELD(GPIO_INDEX_REG, TYPE, 16, 4)
+ FIELD(GPIO_INDEX_REG, DATA_VALUE, 20, 1)
+ FIELD(GPIO_INDEX_REG, DIRECTION, 20, 1)
+ FIELD(GPIO_INDEX_REG, INT_ENABLE, 20, 1)
+ FIELD(GPIO_INDEX_REG, INT_SENS_0, 21, 1)
+ FIELD(GPIO_INDEX_REG, INT_SENS_1, 22, 1)
+ FIELD(GPIO_INDEX_REG, INT_SENS_2, 23, 1)
+ FIELD(GPIO_INDEX_REG, INT_STATUS, 24, 1)
+ FIELD(GPIO_INDEX_REG, DEBOUNCE_1, 20, 1)
+ FIELD(GPIO_INDEX_REG, DEBOUNCE_2, 21, 1)
+ FIELD(GPIO_INDEX_REG, RESET_TOLERANT, 20, 1)
+ FIELD(GPIO_INDEX_REG, COMMAND_SRC_0, 20, 1)
+ FIELD(GPIO_INDEX_REG, COMMAND_SRC_1, 21, 1)
+ FIELD(GPIO_INDEX_REG, INPUT_MASK, 20, 1)
+
static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio)
{
uint32_t falling_edge = 0, rising_edge = 0;
@@ -523,55 +547,214 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
uint64_t idx = -1;
const AspeedGPIOReg *reg;
GPIOSets *set;
+ uint32_t value = 0;
+ uint64_t debounce_value;
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];
+ debounce_value = (uint64_t) s->debounce_regs[idx];
+ trace_aspeed_gpio_read(offset, debounce_value);
+ return debounce_value;
}
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);
+ PRIx64"\n", __func__, offset);
return 0;
}
set = &s->sets[reg->set_idx];
switch (reg->type) {
case gpio_reg_data_value:
- return set->data_value;
+ value = set->data_value;
+ break;
case gpio_reg_direction:
- return set->direction;
+ value = set->direction;
+ break;
case gpio_reg_int_enable:
- return set->int_enable;
+ value = set->int_enable;
+ break;
case gpio_reg_int_sens_0:
- return set->int_sens_0;
+ value = set->int_sens_0;
+ break;
case gpio_reg_int_sens_1:
- return set->int_sens_1;
+ value = set->int_sens_1;
+ break;
case gpio_reg_int_sens_2:
- return set->int_sens_2;
+ value = set->int_sens_2;
+ break;
case gpio_reg_int_status:
- return set->int_status;
+ value = set->int_status;
+ break;
case gpio_reg_reset_tolerant:
- return set->reset_tol;
+ value = set->reset_tol;
+ break;
case gpio_reg_debounce_1:
- return set->debounce_1;
+ value = set->debounce_1;
+ break;
case gpio_reg_debounce_2:
- return set->debounce_2;
+ value = set->debounce_2;
+ break;
case gpio_reg_cmd_source_0:
- return set->cmd_source_0;
+ value = set->cmd_source_0;
+ break;
case gpio_reg_cmd_source_1:
- return set->cmd_source_1;
+ value = set->cmd_source_1;
+ break;
case gpio_reg_data_read:
- return set->data_read;
+ value = set->data_read;
+ break;
case gpio_reg_input_mask:
- return set->input_mask;
+ value = set->input_mask;
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
- HWADDR_PRIx"\n", __func__, offset);
+ PRIx64"\n", __func__, offset);
return 0;
}
+
+ trace_aspeed_gpio_read(offset, value);
+ return value;
+}
+
+static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset,
+ uint64_t data, uint32_t size)
+{
+
+ AspeedGPIOState *s = ASPEED_GPIO(opaque);
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+ const GPIOSetProperties *props;
+ GPIOSets *set;
+ uint32_t reg_idx_number = FIELD_EX32(data, GPIO_INDEX_REG, NUMBER);
+ uint32_t reg_idx_type = FIELD_EX32(data, GPIO_INDEX_REG, TYPE);
+ uint32_t reg_idx_command = FIELD_EX32(data, GPIO_INDEX_REG, COMMAND);
+ uint32_t set_idx = reg_idx_number / ASPEED_GPIOS_PER_SET;
+ uint32_t pin_idx = reg_idx_number % ASPEED_GPIOS_PER_SET;
+ uint32_t group_idx = pin_idx / GPIOS_PER_GROUP;
+ uint32_t reg_value = 0;
+ uint32_t cleared;
+
+ set = &s->sets[set_idx];
+ props = &agc->props[set_idx];
+
+ if (reg_idx_command)
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%" PRIx64 "data 0x%"
+ PRIx64 "index mode wrong command 0x%x\n",
+ __func__, offset, data, reg_idx_command);
+
+ switch (reg_idx_type) {
+ case gpio_reg_idx_data:
+ reg_value = set->data_read;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, DATA_VALUE));
+ reg_value &= props->output;
+ reg_value = update_value_control_source(set, set->data_value,
+ reg_value);
+ set->data_read = reg_value;
+ aspeed_gpio_update(s, set, reg_value);
+ return;
+ case gpio_reg_idx_direction:
+ reg_value = set->direction;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, DIRECTION));
+ /*
+ * where data is the value attempted to be written to the pin:
+ * pin type | input mask | output mask | expected value
+ * ------------------------------------------------------------
+ * bidirectional | 1 | 1 | data
+ * input only | 1 | 0 | 0
+ * output only | 0 | 1 | 1
+ * no pin | 0 | 0 | 0
+ *
+ * which is captured by:
+ * data = ( data | ~input) & output;
+ */
+ reg_value = (reg_value | ~props->input) & props->output;
+ set->direction = update_value_control_source(set, set->direction,
+ reg_value);
+ break;
+ case gpio_reg_idx_interrupt:
+ reg_value = set->int_enable;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, INT_ENABLE));
+ set->int_enable = update_value_control_source(set, set->int_enable,
+ reg_value);
+ reg_value = set->int_sens_0;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_0));
+ set->int_sens_0 = update_value_control_source(set, set->int_sens_0,
+ reg_value);
+ reg_value = set->int_sens_1;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_1));
+ set->int_sens_1 = update_value_control_source(set, set->int_sens_1,
+ reg_value);
+ reg_value = set->int_sens_2;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_2));
+ set->int_sens_2 = update_value_control_source(set, set->int_sens_2,
+ reg_value);
+ /* set interrupt status */
+ reg_value = set->int_status;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS));
+ cleared = ctpop32(reg_value & set->int_status);
+ if (s->pending && cleared) {
+ assert(s->pending >= cleared);
+ s->pending -= cleared;
+ }
+ set->int_status &= ~reg_value;
+ break;
+ case gpio_reg_idx_debounce:
+ reg_value = set->debounce_1;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, DEBOUNCE_1));
+ set->debounce_1 = update_value_control_source(set, set->debounce_1,
+ reg_value);
+ reg_value = set->debounce_2;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, DEBOUNCE_2));
+ set->debounce_2 = update_value_control_source(set, set->debounce_2,
+ reg_value);
+ return;
+ case gpio_reg_idx_tolerance:
+ reg_value = set->reset_tol;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, RESET_TOLERANT));
+ set->reset_tol = update_value_control_source(set, set->reset_tol,
+ reg_value);
+ return;
+ case gpio_reg_idx_cmd_src:
+ reg_value = set->cmd_source_0;
+ reg_value = deposit32(reg_value, GPIOS_PER_GROUP * group_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, COMMAND_SRC_0));
+ set->cmd_source_0 = reg_value & ASPEED_CMD_SRC_MASK;
+ reg_value = set->cmd_source_1;
+ reg_value = deposit32(reg_value, GPIOS_PER_GROUP * group_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, COMMAND_SRC_1));
+ set->cmd_source_1 = reg_value & ASPEED_CMD_SRC_MASK;
+ return;
+ case gpio_reg_idx_input_mask:
+ reg_value = set->input_mask;
+ reg_value = deposit32(reg_value, pin_idx, 1,
+ FIELD_EX32(data, GPIO_INDEX_REG, INPUT_MASK));
+ /*
+ * feeds into interrupt generation
+ * 0: read from data value reg will be updated
+ * 1: read from data value reg will not be updated
+ */
+ set->input_mask = reg_value & props->input;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%" PRIx64 "data 0x%"
+ PRIx64 "index mode wrong type 0x%x\n",
+ __func__, offset, data, reg_idx_type);
+ return;
+ }
+ aspeed_gpio_update(s, set, set->data_value);
+ return;
}
static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
@@ -585,7 +768,16 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
GPIOSets *set;
uint32_t cleared;
+ trace_aspeed_gpio_write(offset, data);
+
idx = offset >> 2;
+
+ /* check gpio index mode */
+ if (idx == R_GPIO_INDEX_REG) {
+ aspeed_gpio_write_index_mode(opaque, offset, data, size);
+ return;
+ }
+
if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
idx -= GPIO_DEBOUNCE_TIME_1;
s->debounce_regs[idx] = (uint32_t) data;
@@ -595,7 +787,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
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);
+ PRIx64"\n", __func__, offset);
return;
}
@@ -680,7 +872,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
- HWADDR_PRIx"\n", __func__, offset);
+ PRIx64"\n", __func__, offset);
return;
}
aspeed_gpio_update(s, set, set->data_value);
@@ -795,6 +987,15 @@ static GPIOSetProperties ast2600_1_8v_set_props[ASPEED_GPIO_MAX_NR_SETS] = {
[1] = {0x0000000f, 0x0000000f, {"18E"} },
};
+static GPIOSetProperties ast1030_set_props[ASPEED_GPIO_MAX_NR_SETS] = {
+ [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} },
+ [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} },
+ [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} },
+ [3] = {0xffffff3f, 0xffffff3f, {"M", "N", "O", "P"} },
+ [4] = {0xff060c1f, 0x00060c1f, {"Q", "R", "S", "T"} },
+ [5] = {0x000000ff, 0x00000000, {"U"} },
+};
+
static const MemoryRegionOps aspeed_gpio_ops = {
.read = aspeed_gpio_read,
.write = aspeed_gpio_write,
@@ -947,6 +1148,16 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
agc->reg_table = aspeed_1_8v_gpios;
}
+static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data)
+{
+ AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
+
+ agc->props = ast1030_set_props;
+ agc->nr_gpio_pins = 151;
+ agc->nr_gpio_sets = 6;
+ agc->reg_table = aspeed_3_3v_gpios;
+}
+
static const TypeInfo aspeed_gpio_info = {
.name = TYPE_ASPEED_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
@@ -984,6 +1195,13 @@ static const TypeInfo aspeed_gpio_ast2600_1_8v_info = {
.instance_init = aspeed_gpio_init,
};
+static const TypeInfo aspeed_gpio_ast1030_info = {
+ .name = TYPE_ASPEED_GPIO "-ast1030",
+ .parent = TYPE_ASPEED_GPIO,
+ .class_init = aspeed_gpio_1030_class_init,
+ .instance_init = aspeed_gpio_init,
+};
+
static void aspeed_gpio_register_types(void)
{
type_register_static(&aspeed_gpio_info);
@@ -991,6 +1209,7 @@ static void aspeed_gpio_register_types(void)
type_register_static(&aspeed_gpio_ast2500_info);
type_register_static(&aspeed_gpio_ast2600_3_3v_info);
type_register_static(&aspeed_gpio_ast2600_1_8v_info);
+ type_register_static(&aspeed_gpio_ast1030_info);
}
type_init(aspeed_gpio_register_types);
diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events
index 1dab99c560..9736b362ac 100644
--- a/hw/gpio/trace-events
+++ b/hw/gpio/trace-events
@@ -27,3 +27,7 @@ sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" P
sifive_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64
sifive_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
sifive_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
+
+# aspeed_gpio.c
+aspeed_gpio_read(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64
+aspeed_gpio_write(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64