|
@@ -227,6 +227,38 @@ REG32(GPIO_INDEX_REG, 0x2AC)
|
|
FIELD(GPIO_INDEX_REG, COMMAND_SRC_1, 21, 1)
|
|
FIELD(GPIO_INDEX_REG, COMMAND_SRC_1, 21, 1)
|
|
FIELD(GPIO_INDEX_REG, INPUT_MASK, 20, 1)
|
|
FIELD(GPIO_INDEX_REG, INPUT_MASK, 20, 1)
|
|
|
|
|
|
|
|
+/* AST2700 GPIO Register Address Offsets */
|
|
|
|
+REG32(GPIO_2700_DEBOUNCE_TIME_1, 0x000)
|
|
|
|
+REG32(GPIO_2700_DEBOUNCE_TIME_2, 0x004)
|
|
|
|
+REG32(GPIO_2700_DEBOUNCE_TIME_3, 0x008)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_1, 0x100)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_2, 0x104)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_3, 0x108)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_4, 0x10C)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_5, 0x110)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_6, 0x114)
|
|
|
|
+REG32(GPIO_2700_INT_STATUS_7, 0x118)
|
|
|
|
+/* GPIOA0 - GPIOAA7 Control Register */
|
|
|
|
+REG32(GPIO_A0_CONTROL, 0x180)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_OUT_DATA, 0, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_DIRECTION, 1, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_INT_ENABLE, 2, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_INT_SENS_0, 3, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_INT_SENS_1, 4, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_INT_SENS_2, 5, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_RESET_TOLERANCE, 6, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_DEBOUNCE_1, 7, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_DEBOUNCE_2, 8, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_INPUT_MASK, 9, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_BLINK_COUNTER_1, 10, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_BLINK_COUNTER_2, 11, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_INT_STATUS, 12, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_IN_DATA, 13, 1)
|
|
|
|
+ SHARED_FIELD(GPIO_CONTROL_RESERVED, 14, 18)
|
|
|
|
+REG32(GPIO_AA7_CONTROL, 0x4DC)
|
|
|
|
+#define GPIO_2700_MEM_SIZE 0x4E0
|
|
|
|
+#define GPIO_2700_REG_ARRAY_SIZE (GPIO_2700_MEM_SIZE >> 2)
|
|
|
|
+
|
|
static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio)
|
|
static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio)
|
|
{
|
|
{
|
|
uint32_t falling_edge = 0, rising_edge = 0;
|
|
uint32_t falling_edge = 0, rising_edge = 0;
|
|
@@ -340,7 +372,8 @@ static void aspeed_gpio_set_pin_level(AspeedGPIOState *s, uint32_t set_idx,
|
|
value &= ~pin_mask;
|
|
value &= ~pin_mask;
|
|
}
|
|
}
|
|
|
|
|
|
- aspeed_gpio_update(s, &s->sets[set_idx], value, ~s->sets[set_idx].direction);
|
|
|
|
|
|
+ aspeed_gpio_update(s, &s->sets[set_idx], value,
|
|
|
|
+ ~s->sets[set_idx].direction);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -629,7 +662,6 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
|
|
static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset,
|
|
static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset,
|
|
uint64_t data, uint32_t size)
|
|
uint64_t data, uint32_t size)
|
|
{
|
|
{
|
|
-
|
|
|
|
AspeedGPIOState *s = ASPEED_GPIO(opaque);
|
|
AspeedGPIOState *s = ASPEED_GPIO(opaque);
|
|
AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
|
|
AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
|
|
const GPIOSetProperties *props;
|
|
const GPIOSetProperties *props;
|
|
@@ -641,7 +673,7 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset,
|
|
uint32_t pin_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 group_idx = pin_idx / GPIOS_PER_GROUP;
|
|
uint32_t reg_value = 0;
|
|
uint32_t reg_value = 0;
|
|
- uint32_t cleared;
|
|
|
|
|
|
+ uint32_t pending = 0;
|
|
|
|
|
|
set = &s->sets[set_idx];
|
|
set = &s->sets[set_idx];
|
|
props = &agc->props[set_idx];
|
|
props = &agc->props[set_idx];
|
|
@@ -703,16 +735,23 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset,
|
|
FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_2));
|
|
FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_2));
|
|
set->int_sens_2 = update_value_control_source(set, set->int_sens_2,
|
|
set->int_sens_2 = update_value_control_source(set, set->int_sens_2,
|
|
reg_value);
|
|
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;
|
|
|
|
|
|
+ /* interrupt status */
|
|
|
|
+ if (FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS)) {
|
|
|
|
+ /* pending is either 1 or 0 for a 1-bit field */
|
|
|
|
+ pending = extract32(set->int_status, pin_idx, 1);
|
|
|
|
+
|
|
|
|
+ assert(s->pending >= pending);
|
|
|
|
+
|
|
|
|
+ /* No change to s->pending if pending is 0 */
|
|
|
|
+ s->pending -= pending;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The write acknowledged the interrupt regardless of whether it
|
|
|
|
+ * was pending or not. The post-condition is that it mustn't be
|
|
|
|
+ * pending. Unconditionally clear the status bit.
|
|
|
|
+ */
|
|
|
|
+ set->int_status = deposit32(set->int_status, pin_idx, 1, 0);
|
|
}
|
|
}
|
|
- set->int_status &= ~reg_value;
|
|
|
|
break;
|
|
break;
|
|
case gpio_reg_idx_debounce:
|
|
case gpio_reg_idx_debounce:
|
|
reg_value = set->debounce_1;
|
|
reg_value = set->debounce_1;
|
|
@@ -963,7 +1002,317 @@ static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name,
|
|
aspeed_gpio_set_pin_level(s, set_idx, pin, level);
|
|
aspeed_gpio_set_pin_level(s, set_idx, pin, level);
|
|
}
|
|
}
|
|
|
|
|
|
-/****************** Setup functions ******************/
|
|
|
|
|
|
+static uint64_t aspeed_gpio_2700_read_control_reg(AspeedGPIOState *s,
|
|
|
|
+ uint32_t pin)
|
|
|
|
+{
|
|
|
|
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
|
|
|
|
+ GPIOSets *set;
|
|
|
|
+ uint64_t value = 0;
|
|
|
|
+ uint32_t set_idx;
|
|
|
|
+ uint32_t pin_idx;
|
|
|
|
+
|
|
|
|
+ set_idx = pin / ASPEED_GPIOS_PER_SET;
|
|
|
|
+ pin_idx = pin % ASPEED_GPIOS_PER_SET;
|
|
|
|
+
|
|
|
|
+ if (set_idx >= agc->nr_gpio_sets) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: set index: %d, out of bounds\n",
|
|
|
|
+ __func__, set_idx);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ set = &s->sets[set_idx];
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_OUT_DATA,
|
|
|
|
+ extract32(set->data_read, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_DIRECTION,
|
|
|
|
+ extract32(set->direction, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_ENABLE,
|
|
|
|
+ extract32(set->int_enable, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_SENS_0,
|
|
|
|
+ extract32(set->int_sens_0, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_SENS_1,
|
|
|
|
+ extract32(set->int_sens_1, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_SENS_2,
|
|
|
|
+ extract32(set->int_sens_2, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_RESET_TOLERANCE,
|
|
|
|
+ extract32(set->reset_tol, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_DEBOUNCE_1,
|
|
|
|
+ extract32(set->debounce_1, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_DEBOUNCE_2,
|
|
|
|
+ extract32(set->debounce_2, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INPUT_MASK,
|
|
|
|
+ extract32(set->input_mask, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_STATUS,
|
|
|
|
+ extract32(set->int_status, pin_idx, 1));
|
|
|
|
+ value = SHARED_FIELD_DP32(value, GPIO_CONTROL_IN_DATA,
|
|
|
|
+ extract32(set->data_value, pin_idx, 1));
|
|
|
|
+ return value;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void aspeed_gpio_2700_write_control_reg(AspeedGPIOState *s,
|
|
|
|
+ uint32_t pin, uint64_t data)
|
|
|
|
+{
|
|
|
|
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
|
|
|
|
+ const GPIOSetProperties *props;
|
|
|
|
+ GPIOSets *set;
|
|
|
|
+ uint32_t set_idx;
|
|
|
|
+ uint32_t pin_idx;
|
|
|
|
+ uint32_t group_value = 0;
|
|
|
|
+ uint32_t pending = 0;
|
|
|
|
+
|
|
|
|
+ set_idx = pin / ASPEED_GPIOS_PER_SET;
|
|
|
|
+ pin_idx = pin % ASPEED_GPIOS_PER_SET;
|
|
|
|
+
|
|
|
|
+ if (set_idx >= agc->nr_gpio_sets) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: set index: %d, out of bounds\n",
|
|
|
|
+ __func__, set_idx);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ set = &s->sets[set_idx];
|
|
|
|
+ props = &agc->props[set_idx];
|
|
|
|
+
|
|
|
|
+ /* direction */
|
|
|
|
+ group_value = set->direction;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_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;
|
|
|
|
+ */
|
|
|
|
+ group_value = (group_value | ~props->input) & props->output;
|
|
|
|
+ set->direction = update_value_control_source(set, set->direction,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* out data */
|
|
|
|
+ group_value = set->data_read;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_OUT_DATA));
|
|
|
|
+ group_value &= props->output;
|
|
|
|
+ group_value = update_value_control_source(set, set->data_read,
|
|
|
|
+ group_value);
|
|
|
|
+ set->data_read = group_value;
|
|
|
|
+
|
|
|
|
+ /* interrupt enable */
|
|
|
|
+ group_value = set->int_enable;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_ENABLE));
|
|
|
|
+ set->int_enable = update_value_control_source(set, set->int_enable,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* interrupt sensitivity type 0 */
|
|
|
|
+ group_value = set->int_sens_0;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_SENS_0));
|
|
|
|
+ set->int_sens_0 = update_value_control_source(set, set->int_sens_0,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* interrupt sensitivity type 1 */
|
|
|
|
+ group_value = set->int_sens_1;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_SENS_1));
|
|
|
|
+ set->int_sens_1 = update_value_control_source(set, set->int_sens_1,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* interrupt sensitivity type 2 */
|
|
|
|
+ group_value = set->int_sens_2;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_SENS_2));
|
|
|
|
+ set->int_sens_2 = update_value_control_source(set, set->int_sens_2,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* reset tolerance enable */
|
|
|
|
+ group_value = set->reset_tol;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_RESET_TOLERANCE));
|
|
|
|
+ set->reset_tol = update_value_control_source(set, set->reset_tol,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* debounce 1 */
|
|
|
|
+ group_value = set->debounce_1;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_DEBOUNCE_1));
|
|
|
|
+ set->debounce_1 = update_value_control_source(set, set->debounce_1,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* debounce 2 */
|
|
|
|
+ group_value = set->debounce_2;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_DEBOUNCE_2));
|
|
|
|
+ set->debounce_2 = update_value_control_source(set, set->debounce_2,
|
|
|
|
+ group_value);
|
|
|
|
+
|
|
|
|
+ /* input mask */
|
|
|
|
+ group_value = set->input_mask;
|
|
|
|
+ group_value = deposit32(group_value, pin_idx, 1,
|
|
|
|
+ SHARED_FIELD_EX32(data, GPIO_CONTROL_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 = group_value & props->input;
|
|
|
|
+
|
|
|
|
+ /* blink counter 1 */
|
|
|
|
+ /* blink counter 2 */
|
|
|
|
+ /* unimplement */
|
|
|
|
+
|
|
|
|
+ /* interrupt status */
|
|
|
|
+ if (SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_STATUS)) {
|
|
|
|
+ /* pending is either 1 or 0 for a 1-bit field */
|
|
|
|
+ pending = extract32(set->int_status, pin_idx, 1);
|
|
|
|
+
|
|
|
|
+ assert(s->pending >= pending);
|
|
|
|
+
|
|
|
|
+ /* No change to s->pending if pending is 0 */
|
|
|
|
+ s->pending -= pending;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The write acknowledged the interrupt regardless of whether it
|
|
|
|
+ * was pending or not. The post-condition is that it mustn't be
|
|
|
|
+ * pending. Unconditionally clear the status bit.
|
|
|
|
+ */
|
|
|
|
+ set->int_status = deposit32(set->int_status, pin_idx, 1, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ aspeed_gpio_update(s, set, set->data_value, UINT32_MAX);
|
|
|
|
+ return;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static uint64_t aspeed_gpio_2700_read(void *opaque, hwaddr offset,
|
|
|
|
+ uint32_t size)
|
|
|
|
+{
|
|
|
|
+ AspeedGPIOState *s = ASPEED_GPIO(opaque);
|
|
|
|
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
|
|
|
|
+ GPIOSets *set;
|
|
|
|
+ uint64_t value;
|
|
|
|
+ uint64_t reg;
|
|
|
|
+ uint32_t pin;
|
|
|
|
+ uint32_t idx;
|
|
|
|
+
|
|
|
|
+ reg = offset >> 2;
|
|
|
|
+
|
|
|
|
+ if (reg >= agc->reg_table_count) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
+ "%s: offset 0x%" PRIx64 " out of bounds\n",
|
|
|
|
+ __func__, offset);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (reg) {
|
|
|
|
+ case R_GPIO_2700_DEBOUNCE_TIME_1 ... R_GPIO_2700_DEBOUNCE_TIME_3:
|
|
|
|
+ idx = reg - R_GPIO_2700_DEBOUNCE_TIME_1;
|
|
|
|
+
|
|
|
|
+ if (idx >= ASPEED_GPIO_NR_DEBOUNCE_REGS) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
+ "%s: debounce index: %d, out of bounds\n",
|
|
|
|
+ __func__, idx);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ value = (uint64_t) s->debounce_regs[idx];
|
|
|
|
+ break;
|
|
|
|
+ case R_GPIO_2700_INT_STATUS_1 ... R_GPIO_2700_INT_STATUS_7:
|
|
|
|
+ idx = reg - R_GPIO_2700_INT_STATUS_1;
|
|
|
|
+
|
|
|
|
+ if (idx >= agc->nr_gpio_sets) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
+ "%s: interrupt status index: %d, out of bounds\n",
|
|
|
|
+ __func__, idx);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ set = &s->sets[idx];
|
|
|
|
+ value = (uint64_t) set->int_status;
|
|
|
|
+ break;
|
|
|
|
+ case R_GPIO_A0_CONTROL ... R_GPIO_AA7_CONTROL:
|
|
|
|
+ pin = reg - R_GPIO_A0_CONTROL;
|
|
|
|
+
|
|
|
|
+ if (pin >= agc->nr_gpio_pins) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid pin number: %d\n",
|
|
|
|
+ __func__, pin);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ value = aspeed_gpio_2700_read_control_reg(s, pin);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
|
|
|
|
+ PRIx64"\n", __func__, offset);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ trace_aspeed_gpio_read(offset, value);
|
|
|
|
+ return value;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void aspeed_gpio_2700_write(void *opaque, hwaddr offset,
|
|
|
|
+ uint64_t data, uint32_t size)
|
|
|
|
+{
|
|
|
|
+ AspeedGPIOState *s = ASPEED_GPIO(opaque);
|
|
|
|
+ AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
|
|
|
|
+ uint64_t reg;
|
|
|
|
+ uint32_t pin;
|
|
|
|
+ uint32_t idx;
|
|
|
|
+
|
|
|
|
+ trace_aspeed_gpio_write(offset, data);
|
|
|
|
+
|
|
|
|
+ reg = offset >> 2;
|
|
|
|
+
|
|
|
|
+ if (reg >= agc->reg_table_count) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
+ "%s: offset 0x%" PRIx64 " out of bounds\n",
|
|
|
|
+ __func__, offset);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (reg) {
|
|
|
|
+ case R_GPIO_2700_DEBOUNCE_TIME_1 ... R_GPIO_2700_DEBOUNCE_TIME_3:
|
|
|
|
+ idx = reg - R_GPIO_2700_DEBOUNCE_TIME_1;
|
|
|
|
+
|
|
|
|
+ if (idx >= ASPEED_GPIO_NR_DEBOUNCE_REGS) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
+ "%s: debounce index: %d out of bounds\n",
|
|
|
|
+ __func__, idx);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ s->debounce_regs[idx] = (uint32_t) data;
|
|
|
|
+ break;
|
|
|
|
+ case R_GPIO_A0_CONTROL ... R_GPIO_AA7_CONTROL:
|
|
|
|
+ pin = reg - R_GPIO_A0_CONTROL;
|
|
|
|
+
|
|
|
|
+ if (pin >= agc->nr_gpio_pins) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid pin number: %d\n",
|
|
|
|
+ __func__, pin);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (SHARED_FIELD_EX32(data, GPIO_CONTROL_RESERVED)) {
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid reserved data: 0x%"
|
|
|
|
+ PRIx64"\n", __func__, data);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ aspeed_gpio_2700_write_control_reg(s, pin, data);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
|
|
|
|
+ PRIx64"\n", __func__, offset);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Setup functions */
|
|
static const GPIOSetProperties ast2400_set_props[ASPEED_GPIO_MAX_NR_SETS] = {
|
|
static const GPIOSetProperties ast2400_set_props[ASPEED_GPIO_MAX_NR_SETS] = {
|
|
[0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} },
|
|
[0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} },
|
|
[1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} },
|
|
[1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} },
|
|
@@ -1009,6 +1358,16 @@ static GPIOSetProperties ast1030_set_props[ASPEED_GPIO_MAX_NR_SETS] = {
|
|
[5] = {0x000000ff, 0x00000000, {"U"} },
|
|
[5] = {0x000000ff, 0x00000000, {"U"} },
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static GPIOSetProperties ast2700_set_props[ASPEED_GPIO_MAX_NR_SETS] = {
|
|
|
|
+ [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} },
|
|
|
|
+ [1] = {0x0fffffff, 0x0fffffff, {"E", "F", "G", "H"} },
|
|
|
|
+ [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} },
|
|
|
|
+ [3] = {0xffffffff, 0xffffffff, {"M", "N", "O", "P"} },
|
|
|
|
+ [4] = {0xffffffff, 0xffffffff, {"Q", "R", "S", "T"} },
|
|
|
|
+ [5] = {0xffffffff, 0xffffffff, {"U", "V", "W", "X"} },
|
|
|
|
+ [6] = {0x00ffffff, 0x00ffffff, {"Y", "Z", "AA"} },
|
|
|
|
+};
|
|
|
|
+
|
|
static const MemoryRegionOps aspeed_gpio_ops = {
|
|
static const MemoryRegionOps aspeed_gpio_ops = {
|
|
.read = aspeed_gpio_read,
|
|
.read = aspeed_gpio_read,
|
|
.write = aspeed_gpio_write,
|
|
.write = aspeed_gpio_write,
|
|
@@ -1017,6 +1376,14 @@ static const MemoryRegionOps aspeed_gpio_ops = {
|
|
.valid.max_access_size = 4,
|
|
.valid.max_access_size = 4,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static const MemoryRegionOps aspeed_gpio_2700_ops = {
|
|
|
|
+ .read = aspeed_gpio_2700_read,
|
|
|
|
+ .write = aspeed_gpio_2700_write,
|
|
|
|
+ .endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
+ .valid.min_access_size = 4,
|
|
|
|
+ .valid.max_access_size = 4,
|
|
|
|
+};
|
|
|
|
+
|
|
static void aspeed_gpio_reset(DeviceState *dev)
|
|
static void aspeed_gpio_reset(DeviceState *dev)
|
|
{
|
|
{
|
|
AspeedGPIOState *s = ASPEED_GPIO(dev);
|
|
AspeedGPIOState *s = ASPEED_GPIO(dev);
|
|
@@ -1046,8 +1413,8 @@ static void aspeed_gpio_realize(DeviceState *dev, Error **errp)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s,
|
|
|
|
- TYPE_ASPEED_GPIO, 0x800);
|
|
|
|
|
|
+ memory_region_init_io(&s->iomem, OBJECT(s), agc->reg_ops, s,
|
|
|
|
+ TYPE_ASPEED_GPIO, agc->mem_size);
|
|
|
|
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
}
|
|
}
|
|
@@ -1130,6 +1497,8 @@ static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data)
|
|
agc->nr_gpio_sets = 7;
|
|
agc->nr_gpio_sets = 7;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
|
|
+ agc->mem_size = 0x1000;
|
|
|
|
+ agc->reg_ops = &aspeed_gpio_ops;
|
|
}
|
|
}
|
|
|
|
|
|
static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data)
|
|
static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data)
|
|
@@ -1141,6 +1510,8 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data)
|
|
agc->nr_gpio_sets = 8;
|
|
agc->nr_gpio_sets = 8;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
|
|
+ agc->mem_size = 0x1000;
|
|
|
|
+ agc->reg_ops = &aspeed_gpio_ops;
|
|
}
|
|
}
|
|
|
|
|
|
static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data)
|
|
static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data)
|
|
@@ -1152,6 +1523,8 @@ static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data)
|
|
agc->nr_gpio_sets = 7;
|
|
agc->nr_gpio_sets = 7;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
|
|
+ agc->mem_size = 0x800;
|
|
|
|
+ agc->reg_ops = &aspeed_gpio_ops;
|
|
}
|
|
}
|
|
|
|
|
|
static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
|
|
static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
|
|
@@ -1163,6 +1536,8 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
|
|
agc->nr_gpio_sets = 2;
|
|
agc->nr_gpio_sets = 2;
|
|
agc->reg_table = aspeed_1_8v_gpios;
|
|
agc->reg_table = aspeed_1_8v_gpios;
|
|
agc->reg_table_count = GPIO_1_8V_REG_ARRAY_SIZE;
|
|
agc->reg_table_count = GPIO_1_8V_REG_ARRAY_SIZE;
|
|
|
|
+ agc->mem_size = 0x800;
|
|
|
|
+ agc->reg_ops = &aspeed_gpio_ops;
|
|
}
|
|
}
|
|
|
|
|
|
static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data)
|
|
static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data)
|
|
@@ -1174,6 +1549,20 @@ static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data)
|
|
agc->nr_gpio_sets = 6;
|
|
agc->nr_gpio_sets = 6;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table = aspeed_3_3v_gpios;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE;
|
|
|
|
+ agc->mem_size = 0x1000;
|
|
|
|
+ agc->reg_ops = &aspeed_gpio_ops;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void aspeed_gpio_2700_class_init(ObjectClass *klass, void *data)
|
|
|
|
+{
|
|
|
|
+ AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
|
|
|
|
+
|
|
|
|
+ agc->props = ast2700_set_props;
|
|
|
|
+ agc->nr_gpio_pins = 216;
|
|
|
|
+ agc->nr_gpio_sets = 7;
|
|
|
|
+ agc->reg_table_count = GPIO_2700_REG_ARRAY_SIZE;
|
|
|
|
+ agc->mem_size = 0x1000;
|
|
|
|
+ agc->reg_ops = &aspeed_gpio_2700_ops;
|
|
}
|
|
}
|
|
|
|
|
|
static const TypeInfo aspeed_gpio_info = {
|
|
static const TypeInfo aspeed_gpio_info = {
|
|
@@ -1220,6 +1609,13 @@ static const TypeInfo aspeed_gpio_ast1030_info = {
|
|
.instance_init = aspeed_gpio_init,
|
|
.instance_init = aspeed_gpio_init,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static const TypeInfo aspeed_gpio_ast2700_info = {
|
|
|
|
+ .name = TYPE_ASPEED_GPIO "-ast2700",
|
|
|
|
+ .parent = TYPE_ASPEED_GPIO,
|
|
|
|
+ .class_init = aspeed_gpio_2700_class_init,
|
|
|
|
+ .instance_init = aspeed_gpio_init,
|
|
|
|
+};
|
|
|
|
+
|
|
static void aspeed_gpio_register_types(void)
|
|
static void aspeed_gpio_register_types(void)
|
|
{
|
|
{
|
|
type_register_static(&aspeed_gpio_info);
|
|
type_register_static(&aspeed_gpio_info);
|
|
@@ -1228,6 +1624,7 @@ static void aspeed_gpio_register_types(void)
|
|
type_register_static(&aspeed_gpio_ast2600_3_3v_info);
|
|
type_register_static(&aspeed_gpio_ast2600_3_3v_info);
|
|
type_register_static(&aspeed_gpio_ast2600_1_8v_info);
|
|
type_register_static(&aspeed_gpio_ast2600_1_8v_info);
|
|
type_register_static(&aspeed_gpio_ast1030_info);
|
|
type_register_static(&aspeed_gpio_ast1030_info);
|
|
|
|
+ type_register_static(&aspeed_gpio_ast2700_info);
|
|
}
|
|
}
|
|
|
|
|
|
type_init(aspeed_gpio_register_types);
|
|
type_init(aspeed_gpio_register_types);
|