From c4227568de92470d1c6472da34b616d12cd70077 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Sun, 19 Jul 2026 21:56:40 -0400 Subject: [PATCH] Add xteink machine for wasm build --- hw/adc/esp32c3_adc.c | 101 +++++++++++ hw/adc/meson.build | 1 + hw/display/meson.build | 1 + hw/display/xteink_x3_eink.c | 248 ++++++++++++++++++++++++++++ hw/gpio/esp32_gpio.c | 108 +++++++++++- hw/i2c/esp32_i2c.c | 21 ++- hw/i2c/meson.build | 1 + hw/i2c/xteink_fingerprint.c | 105 ++++++++++++ hw/riscv/Kconfig | 1 + hw/riscv/esp32c3.c | 141 +++++++++++++++- hw/sd/ssi-sd.c | 15 +- hw/ssi/esp32c3_spi2.c | 101 +++++++++++ hw/ssi/meson.build | 2 +- include/hw/adc/esp32c3_adc.h | 17 ++ include/hw/display/xteink_x3_eink.h | 30 ++++ include/hw/gpio/esp32_gpio.h | 8 + include/hw/i2c/esp32_i2c.h | 1 + include/hw/ssi/esp32c3_spi2.h | 17 ++ 18 files changed, 905 insertions(+), 14 deletions(-) create mode 100644 hw/adc/esp32c3_adc.c create mode 100644 hw/display/xteink_x3_eink.c create mode 100644 hw/i2c/xteink_fingerprint.c create mode 100644 hw/ssi/esp32c3_spi2.c create mode 100644 include/hw/adc/esp32c3_adc.h create mode 100644 include/hw/display/xteink_x3_eink.h create mode 100644 include/hw/ssi/esp32c3_spi2.h diff --git a/hw/adc/esp32c3_adc.c b/hw/adc/esp32c3_adc.c new file mode 100644 index 0000000000..89087429c7 --- /dev/null +++ b/hw/adc/esp32c3_adc.c @@ -0,0 +1,101 @@ +/* + * ESP32-C3 SAR ADC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "hw/adc/esp32c3_adc.h" + +#define ADC_ONETIME_SAMPLE 0x020 +#define ADC_DATA1 0x02c +#define ADC_DATA2 0x030 +#define ADC_INT_RAW 0x044 + +#define ADC_CHANNEL_SHIFT 25 +#define ADC_CHANNEL_MASK 0xf +#define ADC_DONE (BIT(31) | BIT(30)) +#define ADC_MAX 0xfff + +static uint64_t esp32c3_adc_read(void *opaque, hwaddr addr, unsigned size) +{ + ESP32C3AdcState *s = ESP32C3_ADC(opaque); + + if (addr == ADC_INT_RAW) { + return ADC_DONE; + } + if (addr == ADC_DATA1 || addr == ADC_DATA2) { + return s->channel < ESP32C3_ADC_CHANNELS + ? MIN(s->input[s->channel], ADC_MAX) + : ADC_MAX; + } + return s->regs[addr / 4]; +} + +static void esp32c3_adc_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ESP32C3AdcState *s = ESP32C3_ADC(opaque); + s->regs[addr / 4] = value; + + if (addr == ADC_ONETIME_SAMPLE) { + s->channel = (value >> ADC_CHANNEL_SHIFT) & ADC_CHANNEL_MASK; + } +} + +static const MemoryRegionOps esp32c3_adc_ops = { + .read = esp32c3_adc_read, + .write = esp32c3_adc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void esp32c3_adc_reset_hold(Object *obj, ResetType type) +{ + ESP32C3AdcState *s = ESP32C3_ADC(obj); + memset(s->regs, 0, sizeof(s->regs)); + for (int i = 0; i < ESP32C3_ADC_CHANNELS; i++) { + s->input[i] = ADC_MAX; + } + s->channel = 0; +} + +static void esp32c3_adc_init(Object *obj) +{ + ESP32C3AdcState *s = ESP32C3_ADC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32c3_adc_ops, s, + TYPE_ESP32C3_ADC, ESP32C3_ADC_IO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + for (int i = 0; i < ESP32C3_ADC_CHANNELS; i++) { + object_property_add_uint32_ptr(obj, "adci[*]", &s->input[i], + OBJ_PROP_FLAG_READWRITE); + } + esp32c3_adc_reset_hold(obj, RESET_TYPE_COLD); +} + +static void esp32c3_adc_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.hold = esp32c3_adc_reset_hold; +} + +static const TypeInfo esp32c3_adc_info = { + .name = TYPE_ESP32C3_ADC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ESP32C3AdcState), + .instance_init = esp32c3_adc_init, + .class_init = esp32c3_adc_class_init, +}; + +static void esp32c3_adc_register_types(void) +{ + type_register_static(&esp32c3_adc_info); +} + +type_init(esp32c3_adc_register_types) diff --git a/hw/adc/meson.build b/hw/adc/meson.build index 7f7acc1619..541d5d30e6 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,4 +1,5 @@ system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) +system_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32c3_adc.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) diff --git a/hw/display/meson.build b/hw/display/meson.build index 36a9ace56e..c73f3138d1 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -32,6 +32,7 @@ system_ss.add(when: 'CONFIG_CG3', if_true: files('cg3.c')) system_ss.add(when: 'CONFIG_MACFB', if_true: files('macfb.c')) system_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c')) system_ss.add(when: 'CONFIG_ESP_RGB', if_true: files('esp_rgb.c')) +system_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('xteink_x3_eink.c')) system_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-dmabuf.c')) diff --git a/hw/display/xteink_x3_eink.c b/hw/display/xteink_x3_eink.c new file mode 100644 index 0000000000..b699abae4e --- /dev/null +++ b/hw/display/xteink_x3_eink.c @@ -0,0 +1,248 @@ +/* + * xteink X3 UC8253 e-ink panel + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "hw/irq.h" +#include "hw/display/xteink_x3_eink.h" + +#define CMD_POWER_OFF 0x02 +#define CMD_POWER_ON 0x04 +#define CMD_DTM1 0x10 +#define CMD_REFRESH 0x12 +#define CMD_DTM2 0x13 + +#define BUSY_TIME_NS (5 * SCALE_MS) + +static void xteink_x3_eink_render(XteinkX3EinkState *s) +{ + DisplaySurface *surface = qemu_console_surface(s->console); + uint32_t *pixels = (uint32_t *)surface_data(surface); + + for (unsigned y = 0; y < XTEINK_X3_HEIGHT; y++) { + const uint8_t *row = &s->new_plane[(XTEINK_X3_HEIGHT - 1 - y) * + XTEINK_X3_WIDTH_BYTES]; + for (unsigned x = 0; x < XTEINK_X3_WIDTH; x++) { + unsigned screen_x = XTEINK_X3_HEIGHT - 1 - y; + unsigned screen_y = x; + pixels[screen_y * XTEINK_X3_HEIGHT + screen_x] = + (row[x / 8] & (0x80 >> (x % 8))) ? 0x00ffffff : 0; + } + } + + dpy_gfx_update(s->console, 0, 0, XTEINK_X3_HEIGHT, XTEINK_X3_WIDTH); +} + +static void xteink_x3_eink_busy_done(void *opaque) +{ + XteinkX3EinkState *s = XTEINK_X3_EINK(opaque); + qemu_set_irq(s->busy, 1); +} + +static void xteink_x3_eink_pulse_busy(XteinkX3EinkState *s) +{ + qemu_set_irq(s->busy, 0); + timer_mod(s->busy_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + BUSY_TIME_NS); +} + +static void xteink_x3_eink_reset(DeviceState *dev) +{ + XteinkX3EinkState *s = XTEINK_X3_EINK(dev); + + s->command = 0; + s->plane_pos = 0; + memset(s->old_plane, 0xff, sizeof(s->old_plane)); + memset(s->new_plane, 0xff, sizeof(s->new_plane)); + qemu_set_irq(s->busy, 1); + xteink_x3_eink_render(s); +} + +static void xteink_x3_eink_set_dc(void *opaque, int n, int level) +{ + XteinkX3EinkState *s = XTEINK_X3_EINK(opaque); + s->dc = level; +} + +static void xteink_x3_eink_set_reset(void *opaque, int n, int level) +{ + if (!level) { + xteink_x3_eink_reset(DEVICE(opaque)); + } +} + +static uint32_t xteink_x3_eink_transfer(SSIPeripheral *peripheral, + uint32_t value) +{ + XteinkX3EinkState *s = XTEINK_X3_EINK(peripheral); + uint8_t byte = value; + + if (!s->dc) { + s->command = byte; + if (byte == CMD_DTM1 || byte == CMD_DTM2) { + s->plane_pos = 0; + } else if (byte == CMD_REFRESH) { + xteink_x3_eink_render(s); + xteink_x3_eink_pulse_busy(s); + } else if (byte == CMD_POWER_ON || byte == CMD_POWER_OFF) { + xteink_x3_eink_pulse_busy(s); + } + } else if (s->plane_pos < XTEINK_X3_PLANE_SIZE) { + if (s->command == CMD_DTM1) { + s->old_plane[s->plane_pos++] = byte; + } else if (s->command == CMD_DTM2) { + s->new_plane[s->plane_pos++] = byte; + } + } + + return 0xff; +} + +static void xteink_x3_eink_invalidate(void *opaque) +{ + xteink_x3_eink_render(XTEINK_X3_EINK(opaque)); +} + +static void xteink_x3_eink_update(void *opaque) +{ +} + +static const GraphicHwOps xteink_x3_eink_graphics_ops = { + .invalidate = xteink_x3_eink_invalidate, + .gfx_update = xteink_x3_eink_update, +}; + +static void xteink_x3_eink_init(Object *obj) +{ + XteinkX3EinkState *s = XTEINK_X3_EINK(obj); + + s->console = graphic_console_init(DEVICE(s), 0, + &xteink_x3_eink_graphics_ops, s); + dpy_gfx_replace_surface(s->console, + qemu_create_displaysurface(XTEINK_X3_HEIGHT, XTEINK_X3_WIDTH)); + s->busy_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + xteink_x3_eink_busy_done, s); + qdev_init_gpio_in_named(DEVICE(s), xteink_x3_eink_set_dc, + XTEINK_X3_EINK_DC, 1); + qdev_init_gpio_in_named(DEVICE(s), xteink_x3_eink_set_reset, + XTEINK_X3_EINK_RESET, 1); + qdev_init_gpio_out_named(DEVICE(s), &s->busy, + XTEINK_X3_EINK_BUSY, 1); +} + +static void xteink_x3_eink_realize(SSIPeripheral *peripheral, Error **errp) +{ +} + +static void xteink_x3_eink_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SSIPeripheralClass *ssi = SSI_PERIPHERAL_CLASS(klass); + + ssi->realize = xteink_x3_eink_realize; + ssi->transfer = xteink_x3_eink_transfer; + ssi->cs_polarity = SSI_CS_LOW; + device_class_set_legacy_reset(dc, xteink_x3_eink_reset); + dc->user_creatable = false; +} + +static const TypeInfo xteink_x3_eink_info = { + .name = TYPE_XTEINK_X3_EINK, + .parent = TYPE_SSI_PERIPHERAL, + .instance_size = sizeof(XteinkX3EinkState), + .instance_init = xteink_x3_eink_init, + .class_init = xteink_x3_eink_class_init, +}; + +typedef struct XteinkX4EinkState { + SSIPeripheral parent_obj; + QemuConsole *console; + qemu_irq busy; +} XteinkX4EinkState; + +#define XTEINK_X4_EINK(obj) \ + OBJECT_CHECK(XteinkX4EinkState, (obj), TYPE_XTEINK_X4_EINK) + +static void xteink_x4_eink_render(XteinkX4EinkState *s) +{ + DisplaySurface *surface = qemu_console_surface(s->console); + memset(surface_data(surface), 0xff, + surface_stride(surface) * surface_height(surface)); + dpy_gfx_update(s->console, 0, 0, 480, 800); +} + +static void xteink_x4_eink_reset(DeviceState *dev) +{ + XteinkX4EinkState *s = XTEINK_X4_EINK(dev); + qemu_set_irq(s->busy, 0); + xteink_x4_eink_render(s); +} + +static void xteink_x4_eink_set_input(void *opaque, int n, int level) +{ +} + +static uint32_t xteink_x4_eink_transfer(SSIPeripheral *peripheral, + uint32_t value) +{ + return 0xff; +} + +static void xteink_x4_eink_invalidate(void *opaque) +{ + xteink_x4_eink_render(XTEINK_X4_EINK(opaque)); +} + +static const GraphicHwOps xteink_x4_eink_graphics_ops = { + .invalidate = xteink_x4_eink_invalidate, +}; + +static void xteink_x4_eink_init(Object *obj) +{ + XteinkX4EinkState *s = XTEINK_X4_EINK(obj); + + s->console = graphic_console_init(DEVICE(s), 0, + &xteink_x4_eink_graphics_ops, s); + dpy_gfx_replace_surface(s->console, qemu_create_displaysurface(480, 800)); + qdev_init_gpio_in_named(DEVICE(s), xteink_x4_eink_set_input, + XTEINK_X3_EINK_DC, 1); + qdev_init_gpio_in_named(DEVICE(s), xteink_x4_eink_set_input, + XTEINK_X3_EINK_RESET, 1); + qdev_init_gpio_out_named(DEVICE(s), &s->busy, + XTEINK_X3_EINK_BUSY, 1); +} + +static void xteink_x4_eink_realize(SSIPeripheral *peripheral, Error **errp) +{ +} + +static void xteink_x4_eink_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SSIPeripheralClass *ssi = SSI_PERIPHERAL_CLASS(klass); + + ssi->realize = xteink_x4_eink_realize; + ssi->transfer = xteink_x4_eink_transfer; + ssi->cs_polarity = SSI_CS_LOW; + device_class_set_legacy_reset(dc, xteink_x4_eink_reset); + dc->user_creatable = false; +} + +static const TypeInfo xteink_x4_eink_info = { + .name = TYPE_XTEINK_X4_EINK, + .parent = TYPE_SSI_PERIPHERAL, + .instance_size = sizeof(XteinkX4EinkState), + .instance_init = xteink_x4_eink_init, + .class_init = xteink_x4_eink_class_init, +}; + +static void xteink_eink_register_types(void) +{ + type_register_static(&xteink_x3_eink_info); + type_register_static(&xteink_x4_eink_info); +} + +type_init(xteink_eink_register_types) diff --git a/hw/gpio/esp32_gpio.c b/hw/gpio/esp32_gpio.c index 4fea1f5cc7..9ec8bd9244 100644 --- a/hw/gpio/esp32_gpio.c +++ b/hw/gpio/esp32_gpio.c @@ -12,6 +12,7 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/registerfields.h" @@ -19,26 +20,99 @@ #include "hw/qdev-properties.h" #include "hw/gpio/esp32_gpio.h" +#define GPIO_OUT 0x04 +#define GPIO_OUT_W1TS 0x08 +#define GPIO_OUT_W1TC 0x0c +#define GPIO_ENABLE 0x20 +#define GPIO_ENABLE_W1TS 0x24 +#define GPIO_ENABLE_W1TC 0x28 +#define GPIO_IN 0x3c +static void esp32_gpio_drive_outputs(Esp32GpioState *s) +{ + for (int i = 0; i < ESP32_GPIO_COUNT; i++) { + int level = (s->output_enable & BIT(i)) + ? !!(s->output_level & BIT(i)) + : 1; + qemu_set_irq(s->output_lines[i], level); + } +} + +static void esp32_gpio_set_input(void *opaque, int pin, int level) +{ + Esp32GpioState *s = ESP32_GPIO(opaque); + s->input_level = deposit32(s->input_level, pin, 1, !!level); +} + +static void esp32_gpio_get_input(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Esp32GpioState *s = ESP32_GPIO(obj); + int pin = GPOINTER_TO_INT(opaque); + bool level = extract32(s->input_level, pin, 1); + visit_type_bool(v, name, &level, errp); +} + +static void esp32_gpio_set_input_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Esp32GpioState *s = ESP32_GPIO(obj); + int pin = GPOINTER_TO_INT(opaque); + bool level; + + if (visit_type_bool(v, name, &level, errp)) { + esp32_gpio_set_input(s, pin, level); + } +} static uint64_t esp32_gpio_read(void *opaque, hwaddr addr, unsigned int size) { Esp32GpioState *s = ESP32_GPIO(opaque); - uint64_t r = 0; - switch (addr) { - case A_GPIO_STRAP: - r = s->strap_mode; - break; + switch (addr) { + case GPIO_OUT: + return s->output_level; + case GPIO_ENABLE: + return s->output_enable; + case A_GPIO_STRAP: + return s->strap_mode; + case GPIO_IN: + return s->input_level; default: - break; + return 0; } - return r; } static void esp32_gpio_write(void *opaque, hwaddr addr, - uint64_t value, unsigned int size) + uint64_t value, unsigned int size) { + Esp32GpioState *s = ESP32_GPIO(opaque); + + switch (addr) { + case GPIO_OUT: + s->output_level = value; + break; + case GPIO_OUT_W1TS: + s->output_level |= value; + break; + case GPIO_OUT_W1TC: + s->output_level &= ~value; + break; + case GPIO_ENABLE: + s->output_enable = value; + break; + case GPIO_ENABLE_W1TS: + s->output_enable |= value; + break; + case GPIO_ENABLE_W1TC: + s->output_enable &= ~value; + break; + default: + return; + } + + esp32_gpio_drive_outputs(s); } static const MemoryRegionOps uart_ops = { @@ -49,6 +123,11 @@ static const MemoryRegionOps uart_ops = { static void esp32_gpio_reset_hold(Object *obj, ResetType type) { + Esp32GpioState *s = ESP32_GPIO(obj); + s->input_level = UINT32_MAX; + s->output_level = 0; + s->output_enable = 0; + esp32_gpio_drive_outputs(s); } static void esp32_gpio_realize(DeviceState *dev, Error **errp) @@ -67,6 +146,19 @@ static void esp32_gpio_init(Object *obj) TYPE_ESP32_GPIO, 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in_named(DEVICE(obj), esp32_gpio_set_input, + ESP32_GPIO_INPUT, ESP32_GPIO_COUNT); + qdev_init_gpio_out_named(DEVICE(obj), s->output_lines, + ESP32_GPIO_OUTPUT, ESP32_GPIO_COUNT); + for (int i = 0; i < ESP32_GPIO_COUNT; i++) { + char *name = g_strdup_printf("input-level[%d]", i); + object_property_add(obj, name, "bool", esp32_gpio_get_input, + esp32_gpio_set_input_property, NULL, + GINT_TO_POINTER(i)); + g_free(name); + } + + esp32_gpio_reset_hold(obj, RESET_TYPE_COLD); } static Property esp32_gpio_properties[] = { diff --git a/hw/i2c/esp32_i2c.c b/hw/i2c/esp32_i2c.c index 5f4022f0b1..2426d36c8c 100644 --- a/hw/i2c/esp32_i2c.c +++ b/hw/i2c/esp32_i2c.c @@ -4,6 +4,7 @@ #include "qemu/error-report.h" #include "hw/i2c/esp32_i2c.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" static void esp32_i2c_do_transaction(Esp32I2CState * s); static void esp32_i2c_update_irq(Esp32I2CState * s); @@ -177,7 +178,17 @@ static void esp32_i2c_do_transaction(Esp32I2CState * s) bool stop_or_end = false; for (int i_cmd = 0; i_cmd < ESP32_I2C_CMD_COUNT && !stop_or_end; ++i_cmd) { uint32_t cmd = s->cmd_reg[i_cmd]; - char opcode = FIELD_EX32(cmd, I2C_CMD, OPCODE); + int opcode = FIELD_EX32(cmd, I2C_CMD, OPCODE); + if (s->c3) { + switch (opcode) { + case 6: opcode = I2C_OPCODE_RSTART; break; + case 1: opcode = I2C_OPCODE_WRITE; break; + case 3: opcode = I2C_OPCODE_READ; break; + case 2: opcode = I2C_OPCODE_STOP; break; + case 4: opcode = I2C_OPCODE_END; break; + default: break; + } + } switch (opcode) { case I2C_OPCODE_RSTART: i2c_end_transfer(s->bus); @@ -261,9 +272,17 @@ static void esp32_i2c_init(Object * obj) fifo8_create(&s->rx_fifo, ESP32_I2C_FIFO_LENGTH); } +static const Property esp32_i2c_properties[] = { + DEFINE_PROP_BOOL("c3", Esp32I2CState, c3, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void esp32_i2c_class_init(ObjectClass * klass, void * data) { + DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_props(dc, esp32_i2c_properties); rc->phases.hold = esp32_i2c_reset_hold; } diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index e72dca07e5..5df10c2f05 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -17,6 +17,7 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) i2c_ss.add(when: 'CONFIG_XTENSA_ESP32', if_true: files('esp32_i2c.c')) i2c_ss.add(when: 'CONFIG_XTENSA_ESP32S3', if_true: files('esp32_i2c.c')) +i2c_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32_i2c.c', 'xteink_fingerprint.c')) i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c')) i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c')) diff --git a/hw/i2c/xteink_fingerprint.c b/hw/i2c/xteink_fingerprint.c new file mode 100644 index 0000000000..06a2e83e86 --- /dev/null +++ b/hw/i2c/xteink_fingerprint.c @@ -0,0 +1,105 @@ +/* + * xteink X3 I²C fingerprint chips. + * + * The crosspoint firmware distinguishes X3 from X4 by probing three I²C + * devices on SDA20/SCL0 (BQ27220 gauge @0x55, DS3231 RTC @0x68, QMI8658 IMU + * @0x6B). Presence of >=2/3 on two passes selects X3. These stubs return just + * enough register data to pass that probe; full gauge/RTC/IMU behaviour is not + * modelled (X3 runtime reads degrade gracefully, matching a missing sensor). + */ +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" + +#define TYPE_XTEINK_FPCHIP "xteink-fpchip" +OBJECT_DECLARE_SIMPLE_TYPE(XteinkFpChipState, XTEINK_FPCHIP) + +struct XteinkFpChipState { + I2CSlave parent_obj; + uint8_t reg; + bool reg_set; +}; + +/* Fixed register bytes the firmware fingerprint checks. Multi-byte values are + * little-endian and read via auto-incrementing register pointer. */ +static uint8_t fpchip_reg(uint8_t addr, uint8_t reg) +{ + switch (addr) { + case 0x55: /* BQ27220: SOC 0x2C (<=100%), Voltage 0x08 (2500..5000mV) */ + switch (reg) { + case 0x08: return 0x74; /* 0x0E74 = 3700 mV */ + case 0x09: return 0x0E; + case 0x2C: return 0x50; /* 80% */ + case 0x2D: return 0x00; + default: return 0x00; + } + case 0x68: /* DS3231: seconds 0x00 must be valid BCD */ + return 0x00; + case 0x6B: /* QMI8658: WHO_AM_I 0x00 == 0x05 */ + return (reg == 0x00) ? 0x05 : 0x00; + default: + return 0x00; + } +} + +static int fpchip_event(I2CSlave *i2c, enum i2c_event event) +{ + XteinkFpChipState *s = XTEINK_FPCHIP(i2c); + if (event == I2C_START_SEND) { + s->reg_set = false; + } + return 0; +} + +static int fpchip_send(I2CSlave *i2c, uint8_t data) +{ + XteinkFpChipState *s = XTEINK_FPCHIP(i2c); + if (!s->reg_set) { + s->reg = data; + s->reg_set = true; + } + return 0; +} + +static uint8_t fpchip_recv(I2CSlave *i2c) +{ + XteinkFpChipState *s = XTEINK_FPCHIP(i2c); + return fpchip_reg(i2c->address, s->reg++); +} + +static const VMStateDescription vmstate_fpchip = { + .name = TYPE_XTEINK_FPCHIP, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, XteinkFpChipState), + VMSTATE_UINT8(reg, XteinkFpChipState), + VMSTATE_BOOL(reg_set, XteinkFpChipState), + VMSTATE_END_OF_LIST() + } +}; + +static void fpchip_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->event = fpchip_event; + k->recv = fpchip_recv; + k->send = fpchip_send; + dc->vmsd = &vmstate_fpchip; +} + +static const TypeInfo fpchip_info = { + .name = TYPE_XTEINK_FPCHIP, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(XteinkFpChipState), + .class_init = fpchip_class_init, +}; + +static void fpchip_register_types(void) +{ + type_register_static(&fpchip_info); +} + +type_init(fpchip_register_types) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 192ab477b3..293808d773 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -119,4 +119,5 @@ config RISCV_ESP32C3 select OPENCORES_ETH select UNIMP select ESP_RGB + select I2C diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c index ed698fbb67..55ab156533 100644 --- a/hw/riscv/esp32c3.c +++ b/hw/riscv/esp32c3.c @@ -22,6 +22,7 @@ #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" #include "sysemu/device_tree.h" +#include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "sysemu/runstate.h" @@ -40,6 +41,9 @@ #include "hw/timer/esp32c3_timg.h" #include "hw/timer/esp32c3_systimer.h" #include "hw/ssi/esp32c3_spi.h" +#include "hw/ssi/esp32c3_spi2.h" +#include "hw/adc/esp32c3_adc.h" +#include "hw/i2c/esp32_i2c.h" #include "hw/misc/esp32c3_rtc_cntl.h" #include "hw/misc/esp32c3_aes.h" #include "hw/misc/esp32c3_rsa.h" @@ -49,6 +53,8 @@ #include "hw/misc/esp32c3_jtag.h" #include "hw/dma/esp32c3_gdma.h" #include "hw/display/esp_rgb.h" +#include "hw/display/xteink_x3_eink.h" +#include "hw/sd/sd.h" #include "hw/net/can/esp32c3_twai.h" #define ESP32C3_IO_WARNING 0 @@ -66,6 +72,8 @@ struct Esp32C3MachineState { EspRISCVCPU soc; BusState periph_bus; MemoryRegion iomem; + bool xteink; + bool x4; qemu_irq cpu_reset; @@ -86,6 +94,9 @@ struct Esp32C3MachineState { ESP32C3TimgState timg[2]; ESP32C3SysTimerState systimer; ESP32C3SpiState spi1; + ESP32C3Spi2State spi2; + ESP32C3AdcState adc; + Esp32I2CState i2c; ESP32C3RtcCntlState rtccntl; ESP32C3UsbJtagState jtag; ESPRgbState rgb; @@ -410,19 +421,31 @@ static void esp32c3_machine_init(MachineState *machine) object_initialize_child(OBJECT(machine), "efuse", &ms->efuse, TYPE_ESP32C3_EFUSE); object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK); object_initialize_child(OBJECT(machine), "sha", &ms->sha, TYPE_ESP32C3_SHA); +#ifdef CONFIG_GCRYPT object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES); +#endif object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA); +#ifdef CONFIG_GCRYPT object_initialize_child(OBJECT(machine), "rsa", &ms->rsa, TYPE_ESP32C3_RSA); +#endif object_initialize_child(OBJECT(machine), "hmac", &ms->hmac, TYPE_ESP32C3_HMAC); +#ifdef CONFIG_GCRYPT object_initialize_child(OBJECT(machine), "ds", &ms->ds, TYPE_ESP32C3_DS); object_initialize_child(OBJECT(machine), "xts_aes", &ms->xts_aes, TYPE_ESP32C3_XTS_AES); +#endif object_initialize_child(OBJECT(machine), "timg0", &ms->timg[0], TYPE_ESP32C3_TIMG); object_initialize_child(OBJECT(machine), "timg1", &ms->timg[1], TYPE_ESP32C3_TIMG); object_initialize_child(OBJECT(machine), "systimer", &ms->systimer, TYPE_ESP32C3_SYSTIMER); object_initialize_child(OBJECT(machine), "spi1", &ms->spi1, TYPE_ESP32C3_SPI); + object_initialize_child(OBJECT(machine), "spi2", &ms->spi2, TYPE_ESP32C3_SPI2); + object_initialize_child(OBJECT(machine), "adc", &ms->adc, TYPE_ESP32C3_ADC); + object_initialize_child(OBJECT(machine), "i2c", &ms->i2c, TYPE_ESP32_I2C); + qdev_prop_set_bit(DEVICE(&ms->i2c), "c3", true); object_initialize_child(OBJECT(machine), "rtccntl", &ms->rtccntl, TYPE_ESP32C3_RTC_CNTL); object_initialize_child(OBJECT(machine), "jtag", &ms->jtag, TYPE_ESP32C3_JTAG); - object_initialize_child(OBJECT(machine), "rgb", &ms->rgb, TYPE_ESP_RGB); + if (!ms->xteink) { + object_initialize_child(OBJECT(machine), "rgb", &ms->rgb, TYPE_ESP_RGB); + } object_initialize_child(OBJECT(machine), "twai", &ms->twai, TYPE_ESP32C3_TWAI); /* Realize all the I/O peripherals we depend on */ @@ -467,7 +490,9 @@ static void esp32c3_machine_init(MachineState *machine) /* SPI1 controller (SPI Flash) */ { +#ifdef CONFIG_GCRYPT ms->spi1.xts_aes = &ms->xts_aes; +#endif sysbus_realize(SYS_BUS_DEVICE(&ms->spi1), &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi1), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_SPI1_BASE, mr, 0); @@ -476,6 +501,31 @@ static void esp32c3_machine_init(MachineState *machine) } } + /* SPI2 controller (display and SD card) */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->spi2), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi2), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_SPI2_BASE, mr, 0); + } + + /* SAR ADC (battery and resistor-ladder buttons) */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->adc), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->adc), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_APB_SARADC_BASE, mr, 0); + } + + /* I2C controller (X3/X4 fingerprint chips live on SDA20/SCL0). The + * interrupt must reach the matrix or the ESP-IDF driver blocks on its + * completion semaphore and every probe times out. */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->i2c), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->i2c), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_I2C_EXT_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->i2c), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_I2C_EXT0_INTR_SOURCE)); + } + for (int i = 0; i < ESP32C3_UART_COUNT; ++i) { const hwaddr uart_base[] = { DR_REG_UART_BASE, DR_REG_UART1_BASE }; sysbus_realize(SYS_BUS_DEVICE(&ms->uart[i]), &error_fatal); @@ -497,7 +547,9 @@ static void esp32c3_machine_init(MachineState *machine) if (blk) { ms->cache.flash_blk = blk; } +#ifdef CONFIG_GCRYPT ms->cache.xts_aes = &ms->xts_aes; +#endif sysbus_realize(SYS_BUS_DEVICE(&ms->cache), &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->cache), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_EXTMEM_BASE, mr, 0); @@ -593,6 +645,7 @@ static void esp32c3_machine_init(MachineState *machine) qdev_get_gpio_in(intmatrix_dev, ETS_SHA_INTR_SOURCE)); } +#ifdef CONFIG_GCRYPT /* AES realization */ { ms->aes.parent.gdma = ESP_GDMA(&ms->gdma); @@ -612,6 +665,8 @@ static void esp32c3_machine_init(MachineState *machine) qdev_get_gpio_in(intmatrix_dev, ETS_RSA_INTR_SOURCE)); } +#endif + /* HMAC realization */ { ms->hmac.parent.efuse = ESP_EFUSE(&ms->efuse); @@ -620,6 +675,7 @@ static void esp32c3_machine_init(MachineState *machine) memory_region_add_subregion_overlap(sys_mem, DR_REG_HMAC_BASE, mr, 0); } +#ifdef CONFIG_GCRYPT /* Digital Signature realization */ { ms->ds.parent.hmac = ESP_HMAC(&ms->hmac); @@ -640,8 +696,10 @@ static void esp32c3_machine_init(MachineState *machine) memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_XTS_BASE, mr, 0); } +#endif + /* RGB display realization */ - { + if (!ms->xteink) { /* Give the internal RAM memory region to the display */ ms->rgb.intram = dram; sysbus_realize(SYS_BUS_DEVICE(&ms->rgb), &error_fatal); @@ -659,6 +717,50 @@ static void esp32c3_machine_init(MachineState *machine) } +static void xteink_machine_init(MachineState *machine) +{ + Esp32C3MachineState *ms = ESP32C3_MACHINE(machine); + ms->xteink = true; + esp32c3_machine_init(machine); + + /* X3 exposes the fingerprint chips; X4 omits them so stock firmware's + * all-NAK probe selects X4. The X4 panel is a blank protocol stub. */ + if (!ms->x4) { + i2c_slave_create_simple(ms->i2c.bus, "xteink-fpchip", 0x55); + i2c_slave_create_simple(ms->i2c.bus, "xteink-fpchip", 0x68); + i2c_slave_create_simple(ms->i2c.bus, "xteink-fpchip", 0x6B); + } + + DeviceState *panel = ssi_create_peripheral(ms->spi2.bus, + ms->x4 ? TYPE_XTEINK_X4_EINK : TYPE_XTEINK_X3_EINK); + qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 21, + qdev_get_gpio_in_named(panel, SSI_GPIO_CS, 0)); + qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 4, + qdev_get_gpio_in_named(panel, XTEINK_X3_EINK_DC, 0)); + qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 5, + qdev_get_gpio_in_named(panel, XTEINK_X3_EINK_RESET, 0)); + qdev_connect_gpio_out_named(panel, XTEINK_X3_EINK_BUSY, 0, + qdev_get_gpio_in_named(DEVICE(&ms->gpio), ESP32_GPIO_INPUT, 6)); + device_cold_reset(panel); + qemu_set_irq(qdev_get_gpio_in_named(panel, SSI_GPIO_CS, 0), 1); + + DeviceState *sd_adapter = qdev_new("ssi-sd"); + qdev_prop_set_uint8(sd_adapter, "cs", 1); + qdev_realize_and_unref(sd_adapter, BUS(ms->spi2.bus), &error_fatal); + qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 12, + qdev_get_gpio_in_named(sd_adapter, SSI_GPIO_CS, 0)); + qemu_set_irq(qdev_get_gpio_in_named(sd_adapter, SSI_GPIO_CS, 0), 1); + + DriveInfo *dinfo = drive_get(IF_SD, 0, 0); + DeviceState *sd_card = qdev_new(TYPE_SD_CARD_SPI); + qdev_prop_set_drive_err(sd_card, "drive", + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + &error_fatal); + qdev_realize_and_unref(sd_card, + qdev_get_child_bus(sd_adapter, "sd-bus"), + &error_fatal); +} + /* Initialize machine type */ static void esp32c3_machine_class_init(ObjectClass *oc, void *data) { @@ -683,9 +785,44 @@ static const TypeInfo esp32c3_info = { .class_init = esp32c3_machine_class_init, }; +static char *xteink_get_variant(Object *obj, Error **errp) +{ + return g_strdup(ESP32C3_MACHINE(obj)->x4 ? "x4" : "x3"); +} + +static void xteink_set_variant(Object *obj, const char *value, Error **errp) +{ + Esp32C3MachineState *ms = ESP32C3_MACHINE(obj); + if (g_str_equal(value, "x3")) { + ms->x4 = false; + } else if (g_str_equal(value, "x4")) { + ms->x4 = true; + } else { + error_setg(errp, "invalid variant '%s' (expected x3 or x4)", value); + } +} + +static void xteink_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "xteink X3/X4 (ESP32-C3); variant=x3|x4"; + mc->init = xteink_machine_init; + object_class_property_add_str(oc, "variant", + xteink_get_variant, xteink_set_variant); + object_class_property_set_description(oc, "variant", + "xteink model: x3 (default) or x4"); +} + +static const TypeInfo xteink_info = { + .name = MACHINE_TYPE_NAME("xteink"), + .parent = TYPE_ESP32C3_MACHINE, + .class_init = xteink_machine_class_init, +}; + static void esp32c3_machine_type_init(void) { type_register_static(&esp32c3_info); + type_register_static(&xteink_info); } type_init(esp32c3_machine_type_init); diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 15940515ab..552dc2c8ea 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -64,6 +64,7 @@ struct ssi_sd_state { int32_t arglen; int32_t response_pos; int32_t stopping; + bool idle; SDBus sdbus; }; @@ -180,7 +181,7 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) /* CMD8/CMD58 returns R3/R7 response */ DPRINTF("Returned R3/R7\n"); s->arglen = 5; - s->response[0] = 1; + s->response[0] = s->cmd == 58 ? s->idle : 1; memcpy(&s->response[1], longresp, 4); } else if (s->arglen != 4) { BADF("Unexpected response to cmd %d\n", s->cmd); @@ -236,6 +237,11 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) status |= SSI_SDR_PARAMETER_ERROR; s->response[0] = status >> 8; s->response[1] = status; + if (s->cmd == 0) { + s->idle = true; + } else if (s->cmd == 41 && s->response[0] == 0) { + s->idle = false; + } DPRINTF("Card status 0x%02x\n", status); } s->mode = SSI_SD_PREP_RESP; @@ -329,6 +335,9 @@ static int ssi_sd_post_load(void *opaque, int version_id) { ssi_sd_state *s = (ssi_sd_state *)opaque; + if (version_id < 8) { + s->idle = false; + } if (s->mode > SSI_SD_SKIP_CRC16) { return -EINVAL; } @@ -347,7 +356,7 @@ static int ssi_sd_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_ssi_sd = { .name = "ssi_sd", - .version_id = 7, + .version_id = 8, .minimum_version_id = 7, .post_load = ssi_sd_post_load, .fields = (const VMStateField []) { @@ -361,6 +370,7 @@ static const VMStateDescription vmstate_ssi_sd = { VMSTATE_INT32(arglen, ssi_sd_state), VMSTATE_INT32(response_pos, ssi_sd_state), VMSTATE_INT32(stopping, ssi_sd_state), + VMSTATE_BOOL_V(idle, ssi_sd_state, 8), VMSTATE_SSI_PERIPHERAL(ssidev, ssi_sd_state), VMSTATE_END_OF_LIST() } @@ -387,6 +397,7 @@ static void ssi_sd_reset(DeviceState *dev) s->arglen = 0; s->response_pos = 0; s->stopping = 0; + s->idle = true; } static void ssi_sd_class_init(ObjectClass *klass, void *data) diff --git a/hw/ssi/esp32c3_spi2.c b/hw/ssi/esp32c3_spi2.c new file mode 100644 index 0000000000..28fcc9d665 --- /dev/null +++ b/hw/ssi/esp32c3_spi2.c @@ -0,0 +1,101 @@ +/* + * ESP32-C3 general-purpose SPI2 controller + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "hw/ssi/esp32c3_spi2.h" + +#define SPI2_CMD 0x000 +#define SPI2_MS_DLEN 0x01c +#define SPI2_W0 0x098 + +#define SPI2_CMD_USR BIT(24) +#define SPI2_DLEN_MASK 0x3ffff +#define SPI2_BUFFER_SIZE 64 + +static void esp32c3_spi2_transfer(ESP32C3Spi2State *s) +{ + size_t bytes = (s->regs[SPI2_MS_DLEN / 4] & SPI2_DLEN_MASK) / 8 + 1; + bytes = MIN(bytes, SPI2_BUFFER_SIZE); + + for (size_t i = 0; i < bytes; i++) { + uint32_t *word = &s->regs[SPI2_W0 / 4 + i / 4]; + unsigned shift = (i % 4) * 8; + uint8_t tx = *word >> shift; + uint8_t rx = ssi_transfer(s->bus, tx); + *word = (*word & ~(0xffu << shift)) | ((uint32_t)rx << shift); + } +} + +static uint64_t esp32c3_spi2_read(void *opaque, hwaddr addr, unsigned size) +{ + ESP32C3Spi2State *s = ESP32C3_SPI2(opaque); + return s->regs[addr / 4]; +} + +static void esp32c3_spi2_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ESP32C3Spi2State *s = ESP32C3_SPI2(opaque); + + if (addr == SPI2_CMD) { + if (value & SPI2_CMD_USR) { + esp32c3_spi2_transfer(s); + } + s->regs[SPI2_CMD / 4] = 0; + return; + } + + s->regs[addr / 4] = value; +} + +static const MemoryRegionOps esp32c3_spi2_ops = { + .read = esp32c3_spi2_read, + .write = esp32c3_spi2_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void esp32c3_spi2_reset_hold(Object *obj, ResetType type) +{ + ESP32C3Spi2State *s = ESP32C3_SPI2(obj); + memset(s->regs, 0, sizeof(s->regs)); +} + +static void esp32c3_spi2_init(Object *obj) +{ + ESP32C3Spi2State *s = ESP32C3_SPI2(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32c3_spi2_ops, s, + TYPE_ESP32C3_SPI2, ESP32C3_SPI2_IO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + s->bus = ssi_create_bus(DEVICE(s), "spi"); +} + +static void esp32c3_spi2_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.hold = esp32c3_spi2_reset_hold; +} + +static const TypeInfo esp32c3_spi2_info = { + .name = TYPE_ESP32C3_SPI2, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ESP32C3Spi2State), + .instance_init = esp32c3_spi2_init, + .class_init = esp32c3_spi2_class_init, +}; + +static void esp32c3_spi2_register_types(void) +{ + type_register_static(&esp32c3_spi2_info); +} + +type_init(esp32c3_spi2_register_types) diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 30f1a871af..5524459585 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -11,7 +11,7 @@ system_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) system_ss.add(when: 'CONFIG_XTENSA_ESP32', if_true: files('esp32_spi.c')) -system_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32c3_spi.c')) +system_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32c3_spi.c', 'esp32c3_spi2.c')) system_ss.add(when: 'CONFIG_XTENSA_ESP32S3', if_true: files('esp32s3_spi.c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) diff --git a/include/hw/adc/esp32c3_adc.h b/include/hw/adc/esp32c3_adc.h new file mode 100644 index 0000000000..1b914e18da --- /dev/null +++ b/include/hw/adc/esp32c3_adc.h @@ -0,0 +1,17 @@ +#pragma once + +#include "hw/sysbus.h" + +#define TYPE_ESP32C3_ADC "esp32c3.adc" +OBJECT_DECLARE_SIMPLE_TYPE(ESP32C3AdcState, ESP32C3_ADC) + +#define ESP32C3_ADC_CHANNELS 10 +#define ESP32C3_ADC_IO_SIZE 0x1000 + +struct ESP32C3AdcState { + SysBusDevice parent_obj; + MemoryRegion iomem; + uint32_t regs[ESP32C3_ADC_IO_SIZE / sizeof(uint32_t)]; + uint32_t input[ESP32C3_ADC_CHANNELS]; + uint8_t channel; +}; diff --git a/include/hw/display/xteink_x3_eink.h b/include/hw/display/xteink_x3_eink.h new file mode 100644 index 0000000000..4adc5fca5a --- /dev/null +++ b/include/hw/display/xteink_x3_eink.h @@ -0,0 +1,30 @@ +#pragma once + +#include "hw/ssi/ssi.h" +#include "qemu/timer.h" +#include "ui/console.h" + +#define TYPE_XTEINK_X3_EINK "xteink-x3-eink" +#define TYPE_XTEINK_X4_EINK "xteink-x4-eink" +OBJECT_DECLARE_SIMPLE_TYPE(XteinkX3EinkState, XTEINK_X3_EINK) + +#define XTEINK_X3_EINK_DC "dc" +#define XTEINK_X3_EINK_RESET "reset" +#define XTEINK_X3_EINK_BUSY "busy" + +#define XTEINK_X3_WIDTH 792 +#define XTEINK_X3_HEIGHT 528 +#define XTEINK_X3_WIDTH_BYTES (XTEINK_X3_WIDTH / 8) +#define XTEINK_X3_PLANE_SIZE (XTEINK_X3_WIDTH_BYTES * XTEINK_X3_HEIGHT) + +struct XteinkX3EinkState { + SSIPeripheral parent_obj; + QemuConsole *console; + QEMUTimer *busy_timer; + qemu_irq busy; + bool dc; + uint8_t command; + uint32_t plane_pos; + uint8_t old_plane[XTEINK_X3_PLANE_SIZE]; + uint8_t new_plane[XTEINK_X3_PLANE_SIZE]; +}; diff --git a/include/hw/gpio/esp32_gpio.h b/include/hw/gpio/esp32_gpio.h index 163b41aa6b..e2f52ad702 100644 --- a/include/hw/gpio/esp32_gpio.h +++ b/include/hw/gpio/esp32_gpio.h @@ -14,12 +14,20 @@ REG32(GPIO_STRAP, 0x0038) #define ESP32_STRAP_MODE_FLASH_BOOT 0x12 #define ESP32_STRAP_MODE_UART_BOOT 0x0f +#define ESP32_GPIO_COUNT 32 +#define ESP32_GPIO_INPUT "gpio-in" +#define ESP32_GPIO_OUTPUT "gpio-out" + typedef struct Esp32GpioState { SysBusDevice parent_obj; MemoryRegion iomem; qemu_irq irq; + qemu_irq output_lines[ESP32_GPIO_COUNT]; uint32_t strap_mode; + uint32_t input_level; + uint32_t output_level; + uint32_t output_enable; } Esp32GpioState; typedef struct Esp32GpioClass { diff --git a/include/hw/i2c/esp32_i2c.h b/include/hw/i2c/esp32_i2c.h index b17dc75c8d..42f30c36be 100644 --- a/include/hw/i2c/esp32_i2c.h +++ b/include/hw/i2c/esp32_i2c.h @@ -24,6 +24,7 @@ typedef struct Esp32I2CState { Fifo8 rx_fifo; Fifo8 tx_fifo; bool trans_ongoing; + bool c3; uint32_t ctr_reg; uint32_t timeout_reg; diff --git a/include/hw/ssi/esp32c3_spi2.h b/include/hw/ssi/esp32c3_spi2.h new file mode 100644 index 0000000000..2155c36fba --- /dev/null +++ b/include/hw/ssi/esp32c3_spi2.h @@ -0,0 +1,17 @@ +#pragma once + +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" + +#define TYPE_ESP32C3_SPI2 "ssi.esp32c3.spi2" +OBJECT_DECLARE_SIMPLE_TYPE(ESP32C3Spi2State, ESP32C3_SPI2) + +#define ESP32C3_SPI2_IO_SIZE 0x1000 +#define ESP32C3_SPI2_REG_WORDS (ESP32C3_SPI2_IO_SIZE / sizeof(uint32_t)) + +struct ESP32C3Spi2State { + SysBusDevice parent_obj; + MemoryRegion iomem; + SSIBus *bus; + uint32_t regs[ESP32C3_SPI2_REG_WORDS]; +};