Files
xteink-web-emulator/qemu/patches/0001-xteink-x3-machine.patch
T
2026-07-19 19:23:51 -04:00

869 lines
27 KiB
Diff

diff --git i/hw/adc/esp32c3_adc.c w/hw/adc/esp32c3_adc.c
new file mode 100644
index 0000000..8908742
--- /dev/null
+++ w/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 i/hw/adc/meson.build w/hw/adc/meson.build
index 7f7acc1..541d5d3 100644
--- i/hw/adc/meson.build
+++ w/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 i/hw/display/meson.build w/hw/display/meson.build
index 36a9ace..c73f313 100644
--- i/hw/display/meson.build
+++ w/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 i/hw/display/xteink_x3_eink.c w/hw/display/xteink_x3_eink.c
new file mode 100644
index 0000000..bc9a18c
--- /dev/null
+++ w/hw/display/xteink_x3_eink.c
@@ -0,0 +1,165 @@
+/*
+ * 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,
+};
+
+static void xteink_x3_eink_register_types(void)
+{
+ type_register_static(&xteink_x3_eink_info);
+}
+
+type_init(xteink_x3_eink_register_types)
diff --git i/hw/gpio/esp32_gpio.c w/hw/gpio/esp32_gpio.c
index 4fea1f5..55cac08 100644
--- i/hw/gpio/esp32_gpio.c
+++ w/hw/gpio/esp32_gpio.c
@@ -19,26 +19,77 @@
#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 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 +100,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 +123,12 @@ 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);
+
+ esp32_gpio_reset_hold(obj, RESET_TYPE_COLD);
}
static Property esp32_gpio_properties[] = {
diff --git i/hw/riscv/esp32c3.c w/hw/riscv/esp32c3.c
index ed698fb..a49eae6 100644
--- i/hw/riscv/esp32c3.c
+++ w/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,8 @@
#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/misc/esp32c3_rtc_cntl.h"
#include "hw/misc/esp32c3_aes.h"
#include "hw/misc/esp32c3_rsa.h"
@@ -49,6 +52,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 +71,7 @@ struct Esp32C3MachineState {
EspRISCVCPU soc;
BusState periph_bus;
MemoryRegion iomem;
+ bool xteink_x3;
qemu_irq cpu_reset;
@@ -86,6 +92,8 @@ struct Esp32C3MachineState {
ESP32C3TimgState timg[2];
ESP32C3SysTimerState systimer;
ESP32C3SpiState spi1;
+ ESP32C3Spi2State spi2;
+ ESP32C3AdcState adc;
ESP32C3RtcCntlState rtccntl;
ESP32C3UsbJtagState jtag;
ESPRgbState rgb;
@@ -420,9 +428,13 @@ static void esp32c3_machine_init(MachineState *machine)
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), "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_x3) {
+ 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 */
@@ -476,6 +488,20 @@ 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);
+ }
+
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);
@@ -641,7 +667,7 @@ static void esp32c3_machine_init(MachineState *machine)
}
/* RGB display realization */
- {
+ if (!ms->xteink_x3) {
/* Give the internal RAM memory region to the display */
ms->rgb.intram = dram;
sysbus_realize(SYS_BUS_DEVICE(&ms->rgb), &error_fatal);
@@ -659,6 +685,42 @@ static void esp32c3_machine_init(MachineState *machine)
}
+static void xteink_x3_machine_init(MachineState *machine)
+{
+ Esp32C3MachineState *ms = ESP32C3_MACHINE(machine);
+ ms->xteink_x3 = true;
+ esp32c3_machine_init(machine);
+
+ DeviceState *panel = ssi_create_peripheral(ms->spi2.bus,
+ 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 +745,23 @@ static const TypeInfo esp32c3_info = {
.class_init = esp32c3_machine_class_init,
};
+static void xteink_x3_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ mc->desc = "xteink X3 (ESP32-C3, UC8253 e-ink)";
+ mc->init = xteink_x3_machine_init;
+}
+
+static const TypeInfo xteink_x3_info = {
+ .name = MACHINE_TYPE_NAME("xteink-x3"),
+ .parent = TYPE_ESP32C3_MACHINE,
+ .class_init = xteink_x3_machine_class_init,
+};
+
static void esp32c3_machine_type_init(void)
{
type_register_static(&esp32c3_info);
+ type_register_static(&xteink_x3_info);
}
type_init(esp32c3_machine_type_init);
diff --git i/hw/sd/ssi-sd.c w/hw/sd/ssi-sd.c
index 1594051..552dc2c 100644
--- i/hw/sd/ssi-sd.c
+++ w/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 i/hw/ssi/esp32c3_spi2.c w/hw/ssi/esp32c3_spi2.c
new file mode 100644
index 0000000..28fcc9d
--- /dev/null
+++ w/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 i/hw/ssi/meson.build w/hw/ssi/meson.build
index 30f1a87..5524459 100644
--- i/hw/ssi/meson.build
+++ w/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 i/include/hw/adc/esp32c3_adc.h w/include/hw/adc/esp32c3_adc.h
new file mode 100644
index 0000000..1b914e1
--- /dev/null
+++ w/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 i/include/hw/display/xteink_x3_eink.h w/include/hw/display/xteink_x3_eink.h
new file mode 100644
index 0000000..64de018
--- /dev/null
+++ w/include/hw/display/xteink_x3_eink.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "hw/ssi/ssi.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+
+#define TYPE_XTEINK_X3_EINK "xteink-x3-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 i/include/hw/gpio/esp32_gpio.h w/include/hw/gpio/esp32_gpio.h
index 163b41a..e2f52ad 100644
--- i/include/hw/gpio/esp32_gpio.h
+++ w/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 i/include/hw/ssi/esp32c3_spi2.h w/include/hw/ssi/esp32c3_spi2.h
new file mode 100644
index 0000000..2155c36
--- /dev/null
+++ w/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];
+};