hw/gpio support GPIO index mode for write operation.

It did not support GPIO index mode for read operation.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Message-Id: <20220525053444.27228-4-jamin_lin@aspeedtech.com>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
This commit is contained in:
Jamin Lin 2022-05-25 10:31:33 +02:00 committed by Cédric Le Goater
parent 17075ef244
commit 247c00294a
2 changed files with 182 additions and 0 deletions

View File

@ -16,6 +16,7 @@
#include "hw/irq.h"
#include "migration/vmstate.h"
#include "trace.h"
#include "hw/registerfields.h"
#define GPIOS_PER_GROUP 8
@ -204,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;
@ -596,6 +619,144 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
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,
uint32_t size)
{
@ -610,6 +771,13 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
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;

View File

@ -50,6 +50,20 @@ enum GPIORegType {
gpio_reg_input_mask,
};
/* GPIO index mode */
enum GPIORegIndexType {
gpio_reg_idx_data = 0,
gpio_reg_idx_direction,
gpio_reg_idx_interrupt,
gpio_reg_idx_debounce,
gpio_reg_idx_tolerance,
gpio_reg_idx_cmd_src,
gpio_reg_idx_input_mask,
gpio_reg_idx_reserved,
gpio_reg_idx_new_w_cmd_src,
gpio_reg_idx_new_r_cmd_src,
};
typedef struct AspeedGPIOReg {
uint16_t set_idx;
enum GPIORegType type;