Message ID | 20220525053444.27228-4-jamin_lin@aspeedtech.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | hw/gpio Add ASPEED GPIO model for AST1030 | expand |
On 5/25/22 07:34, Jamin Lin wrote: > 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> Thanks, C. > --- > hw/gpio/aspeed_gpio.c | 168 ++++++++++++++++++++++++++++++++++ > include/hw/gpio/aspeed_gpio.h | 14 +++ > 2 files changed, 182 insertions(+) > > diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c > index 5138fe812b..c834bf19f5 100644 > --- a/hw/gpio/aspeed_gpio.c > +++ b/hw/gpio/aspeed_gpio.c > @@ -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; > diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h > index 6dee3cd438..41b36524d0 100644 > --- a/include/hw/gpio/aspeed_gpio.h > +++ b/include/hw/gpio/aspeed_gpio.h > @@ -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;
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 5138fe812b..c834bf19f5 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -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; diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h index 6dee3cd438..41b36524d0 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -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;
It did not support GPIO index mode for read operation. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> --- hw/gpio/aspeed_gpio.c | 168 ++++++++++++++++++++++++++++++++++ include/hw/gpio/aspeed_gpio.h | 14 +++ 2 files changed, 182 insertions(+)