Add xteink machine for wasm build
This commit is contained in:
@@ -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)
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
|
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_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_NPCM7XX', if_true: files('npcm7xx_adc.c'))
|
||||||
system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c'))
|
system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c'))
|
||||||
|
|||||||
@@ -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_MACFB', if_true: files('macfb.c'))
|
||||||
system_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.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_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_VGA', if_true: files('vga.c'))
|
||||||
system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-dmabuf.c'))
|
system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-dmabuf.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)
|
||||||
+99
-7
@@ -12,6 +12,7 @@
|
|||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/visitor.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/registerfields.h"
|
#include "hw/registerfields.h"
|
||||||
@@ -19,26 +20,99 @@
|
|||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "hw/gpio/esp32_gpio.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)
|
static uint64_t esp32_gpio_read(void *opaque, hwaddr addr, unsigned int size)
|
||||||
{
|
{
|
||||||
Esp32GpioState *s = ESP32_GPIO(opaque);
|
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:
|
default:
|
||||||
break;
|
return 0;
|
||||||
}
|
}
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void esp32_gpio_write(void *opaque, hwaddr addr,
|
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 = {
|
static const MemoryRegionOps uart_ops = {
|
||||||
@@ -49,6 +123,11 @@ static const MemoryRegionOps uart_ops = {
|
|||||||
|
|
||||||
static void esp32_gpio_reset_hold(Object *obj, ResetType type)
|
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)
|
static void esp32_gpio_realize(DeviceState *dev, Error **errp)
|
||||||
@@ -67,6 +146,19 @@ static void esp32_gpio_init(Object *obj)
|
|||||||
TYPE_ESP32_GPIO, 0x1000);
|
TYPE_ESP32_GPIO, 0x1000);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
sysbus_init_irq(sbd, &s->irq);
|
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[] = {
|
static Property esp32_gpio_properties[] = {
|
||||||
|
|||||||
+20
-1
@@ -4,6 +4,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "hw/i2c/esp32_i2c.h"
|
#include "hw/i2c/esp32_i2c.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
|
||||||
static void esp32_i2c_do_transaction(Esp32I2CState * s);
|
static void esp32_i2c_do_transaction(Esp32I2CState * s);
|
||||||
static void esp32_i2c_update_irq(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;
|
bool stop_or_end = false;
|
||||||
for (int i_cmd = 0; i_cmd < ESP32_I2C_CMD_COUNT && !stop_or_end; ++i_cmd) {
|
for (int i_cmd = 0; i_cmd < ESP32_I2C_CMD_COUNT && !stop_or_end; ++i_cmd) {
|
||||||
uint32_t cmd = s->cmd_reg[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) {
|
switch (opcode) {
|
||||||
case I2C_OPCODE_RSTART:
|
case I2C_OPCODE_RSTART:
|
||||||
i2c_end_transfer(s->bus);
|
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);
|
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)
|
static void esp32_i2c_class_init(ObjectClass * klass, void * data)
|
||||||
{
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
|
||||||
|
device_class_set_props(dc, esp32_i2c_properties);
|
||||||
rc->phases.hold = esp32_i2c_reset_hold;
|
rc->phases.hold = esp32_i2c_reset_hold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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_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_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_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_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_PMBUS', if_true: files('pmbus_device.c'))
|
||||||
i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c'))
|
i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.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)
|
||||||
@@ -119,4 +119,5 @@ config RISCV_ESP32C3
|
|||||||
select OPENCORES_ETH
|
select OPENCORES_ETH
|
||||||
select UNIMP
|
select UNIMP
|
||||||
select ESP_RGB
|
select ESP_RGB
|
||||||
|
select I2C
|
||||||
|
|
||||||
|
|||||||
+138
-1
@@ -22,6 +22,7 @@
|
|||||||
#include "hw/riscv/boot.h"
|
#include "hw/riscv/boot.h"
|
||||||
#include "hw/riscv/numa.h"
|
#include "hw/riscv/numa.h"
|
||||||
#include "sysemu/device_tree.h"
|
#include "sysemu/device_tree.h"
|
||||||
|
#include "sysemu/blockdev.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
@@ -40,6 +41,9 @@
|
|||||||
#include "hw/timer/esp32c3_timg.h"
|
#include "hw/timer/esp32c3_timg.h"
|
||||||
#include "hw/timer/esp32c3_systimer.h"
|
#include "hw/timer/esp32c3_systimer.h"
|
||||||
#include "hw/ssi/esp32c3_spi.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_rtc_cntl.h"
|
||||||
#include "hw/misc/esp32c3_aes.h"
|
#include "hw/misc/esp32c3_aes.h"
|
||||||
#include "hw/misc/esp32c3_rsa.h"
|
#include "hw/misc/esp32c3_rsa.h"
|
||||||
@@ -49,6 +53,8 @@
|
|||||||
#include "hw/misc/esp32c3_jtag.h"
|
#include "hw/misc/esp32c3_jtag.h"
|
||||||
#include "hw/dma/esp32c3_gdma.h"
|
#include "hw/dma/esp32c3_gdma.h"
|
||||||
#include "hw/display/esp_rgb.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"
|
#include "hw/net/can/esp32c3_twai.h"
|
||||||
|
|
||||||
#define ESP32C3_IO_WARNING 0
|
#define ESP32C3_IO_WARNING 0
|
||||||
@@ -66,6 +72,8 @@ struct Esp32C3MachineState {
|
|||||||
EspRISCVCPU soc;
|
EspRISCVCPU soc;
|
||||||
BusState periph_bus;
|
BusState periph_bus;
|
||||||
MemoryRegion iomem;
|
MemoryRegion iomem;
|
||||||
|
bool xteink;
|
||||||
|
bool x4;
|
||||||
|
|
||||||
qemu_irq cpu_reset;
|
qemu_irq cpu_reset;
|
||||||
|
|
||||||
@@ -86,6 +94,9 @@ struct Esp32C3MachineState {
|
|||||||
ESP32C3TimgState timg[2];
|
ESP32C3TimgState timg[2];
|
||||||
ESP32C3SysTimerState systimer;
|
ESP32C3SysTimerState systimer;
|
||||||
ESP32C3SpiState spi1;
|
ESP32C3SpiState spi1;
|
||||||
|
ESP32C3Spi2State spi2;
|
||||||
|
ESP32C3AdcState adc;
|
||||||
|
Esp32I2CState i2c;
|
||||||
ESP32C3RtcCntlState rtccntl;
|
ESP32C3RtcCntlState rtccntl;
|
||||||
ESP32C3UsbJtagState jtag;
|
ESP32C3UsbJtagState jtag;
|
||||||
ESPRgbState rgb;
|
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), "efuse", &ms->efuse, TYPE_ESP32C3_EFUSE);
|
||||||
object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK);
|
object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK);
|
||||||
object_initialize_child(OBJECT(machine), "sha", &ms->sha, TYPE_ESP32C3_SHA);
|
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);
|
object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES);
|
||||||
|
#endif
|
||||||
object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA);
|
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);
|
object_initialize_child(OBJECT(machine), "rsa", &ms->rsa, TYPE_ESP32C3_RSA);
|
||||||
|
#endif
|
||||||
object_initialize_child(OBJECT(machine), "hmac", &ms->hmac, TYPE_ESP32C3_HMAC);
|
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), "ds", &ms->ds, TYPE_ESP32C3_DS);
|
||||||
object_initialize_child(OBJECT(machine), "xts_aes", &ms->xts_aes, TYPE_ESP32C3_XTS_AES);
|
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), "timg0", &ms->timg[0], TYPE_ESP32C3_TIMG);
|
||||||
object_initialize_child(OBJECT(machine), "timg1", &ms->timg[1], 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), "systimer", &ms->systimer, TYPE_ESP32C3_SYSTIMER);
|
||||||
object_initialize_child(OBJECT(machine), "spi1", &ms->spi1, TYPE_ESP32C3_SPI);
|
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), "rtccntl", &ms->rtccntl, TYPE_ESP32C3_RTC_CNTL);
|
||||||
object_initialize_child(OBJECT(machine), "jtag", &ms->jtag, TYPE_ESP32C3_JTAG);
|
object_initialize_child(OBJECT(machine), "jtag", &ms->jtag, TYPE_ESP32C3_JTAG);
|
||||||
|
if (!ms->xteink) {
|
||||||
object_initialize_child(OBJECT(machine), "rgb", &ms->rgb, TYPE_ESP_RGB);
|
object_initialize_child(OBJECT(machine), "rgb", &ms->rgb, TYPE_ESP_RGB);
|
||||||
|
}
|
||||||
object_initialize_child(OBJECT(machine), "twai", &ms->twai, TYPE_ESP32C3_TWAI);
|
object_initialize_child(OBJECT(machine), "twai", &ms->twai, TYPE_ESP32C3_TWAI);
|
||||||
|
|
||||||
/* Realize all the I/O peripherals we depend on */
|
/* Realize all the I/O peripherals we depend on */
|
||||||
@@ -467,7 +490,9 @@ static void esp32c3_machine_init(MachineState *machine)
|
|||||||
|
|
||||||
/* SPI1 controller (SPI Flash) */
|
/* SPI1 controller (SPI Flash) */
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_GCRYPT
|
||||||
ms->spi1.xts_aes = &ms->xts_aes;
|
ms->spi1.xts_aes = &ms->xts_aes;
|
||||||
|
#endif
|
||||||
sysbus_realize(SYS_BUS_DEVICE(&ms->spi1), &error_fatal);
|
sysbus_realize(SYS_BUS_DEVICE(&ms->spi1), &error_fatal);
|
||||||
MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi1), 0);
|
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);
|
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) {
|
for (int i = 0; i < ESP32C3_UART_COUNT; ++i) {
|
||||||
const hwaddr uart_base[] = { DR_REG_UART_BASE, DR_REG_UART1_BASE };
|
const hwaddr uart_base[] = { DR_REG_UART_BASE, DR_REG_UART1_BASE };
|
||||||
sysbus_realize(SYS_BUS_DEVICE(&ms->uart[i]), &error_fatal);
|
sysbus_realize(SYS_BUS_DEVICE(&ms->uart[i]), &error_fatal);
|
||||||
@@ -497,7 +547,9 @@ static void esp32c3_machine_init(MachineState *machine)
|
|||||||
if (blk) {
|
if (blk) {
|
||||||
ms->cache.flash_blk = blk;
|
ms->cache.flash_blk = blk;
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_GCRYPT
|
||||||
ms->cache.xts_aes = &ms->xts_aes;
|
ms->cache.xts_aes = &ms->xts_aes;
|
||||||
|
#endif
|
||||||
sysbus_realize(SYS_BUS_DEVICE(&ms->cache), &error_fatal);
|
sysbus_realize(SYS_BUS_DEVICE(&ms->cache), &error_fatal);
|
||||||
MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->cache), 0);
|
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);
|
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));
|
qdev_get_gpio_in(intmatrix_dev, ETS_SHA_INTR_SOURCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_GCRYPT
|
||||||
/* AES realization */
|
/* AES realization */
|
||||||
{
|
{
|
||||||
ms->aes.parent.gdma = ESP_GDMA(&ms->gdma);
|
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));
|
qdev_get_gpio_in(intmatrix_dev, ETS_RSA_INTR_SOURCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* HMAC realization */
|
/* HMAC realization */
|
||||||
{
|
{
|
||||||
ms->hmac.parent.efuse = ESP_EFUSE(&ms->efuse);
|
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);
|
memory_region_add_subregion_overlap(sys_mem, DR_REG_HMAC_BASE, mr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_GCRYPT
|
||||||
/* Digital Signature realization */
|
/* Digital Signature realization */
|
||||||
{
|
{
|
||||||
ms->ds.parent.hmac = ESP_HMAC(&ms->hmac);
|
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);
|
memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_XTS_BASE, mr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* RGB display realization */
|
/* RGB display realization */
|
||||||
{
|
if (!ms->xteink) {
|
||||||
/* Give the internal RAM memory region to the display */
|
/* Give the internal RAM memory region to the display */
|
||||||
ms->rgb.intram = dram;
|
ms->rgb.intram = dram;
|
||||||
sysbus_realize(SYS_BUS_DEVICE(&ms->rgb), &error_fatal);
|
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 */
|
/* Initialize machine type */
|
||||||
static void esp32c3_machine_class_init(ObjectClass *oc, void *data)
|
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,
|
.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)
|
static void esp32c3_machine_type_init(void)
|
||||||
{
|
{
|
||||||
type_register_static(&esp32c3_info);
|
type_register_static(&esp32c3_info);
|
||||||
|
type_register_static(&xteink_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(esp32c3_machine_type_init);
|
type_init(esp32c3_machine_type_init);
|
||||||
|
|||||||
+13
-2
@@ -64,6 +64,7 @@ struct ssi_sd_state {
|
|||||||
int32_t arglen;
|
int32_t arglen;
|
||||||
int32_t response_pos;
|
int32_t response_pos;
|
||||||
int32_t stopping;
|
int32_t stopping;
|
||||||
|
bool idle;
|
||||||
SDBus sdbus;
|
SDBus sdbus;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,7 +181,7 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
|||||||
/* CMD8/CMD58 returns R3/R7 response */
|
/* CMD8/CMD58 returns R3/R7 response */
|
||||||
DPRINTF("Returned R3/R7\n");
|
DPRINTF("Returned R3/R7\n");
|
||||||
s->arglen = 5;
|
s->arglen = 5;
|
||||||
s->response[0] = 1;
|
s->response[0] = s->cmd == 58 ? s->idle : 1;
|
||||||
memcpy(&s->response[1], longresp, 4);
|
memcpy(&s->response[1], longresp, 4);
|
||||||
} else if (s->arglen != 4) {
|
} else if (s->arglen != 4) {
|
||||||
BADF("Unexpected response to cmd %d\n", s->cmd);
|
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;
|
status |= SSI_SDR_PARAMETER_ERROR;
|
||||||
s->response[0] = status >> 8;
|
s->response[0] = status >> 8;
|
||||||
s->response[1] = status;
|
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);
|
DPRINTF("Card status 0x%02x\n", status);
|
||||||
}
|
}
|
||||||
s->mode = SSI_SD_PREP_RESP;
|
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;
|
ssi_sd_state *s = (ssi_sd_state *)opaque;
|
||||||
|
|
||||||
|
if (version_id < 8) {
|
||||||
|
s->idle = false;
|
||||||
|
}
|
||||||
if (s->mode > SSI_SD_SKIP_CRC16) {
|
if (s->mode > SSI_SD_SKIP_CRC16) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@@ -347,7 +356,7 @@ static int ssi_sd_post_load(void *opaque, int version_id)
|
|||||||
|
|
||||||
static const VMStateDescription vmstate_ssi_sd = {
|
static const VMStateDescription vmstate_ssi_sd = {
|
||||||
.name = "ssi_sd",
|
.name = "ssi_sd",
|
||||||
.version_id = 7,
|
.version_id = 8,
|
||||||
.minimum_version_id = 7,
|
.minimum_version_id = 7,
|
||||||
.post_load = ssi_sd_post_load,
|
.post_load = ssi_sd_post_load,
|
||||||
.fields = (const VMStateField []) {
|
.fields = (const VMStateField []) {
|
||||||
@@ -361,6 +370,7 @@ static const VMStateDescription vmstate_ssi_sd = {
|
|||||||
VMSTATE_INT32(arglen, ssi_sd_state),
|
VMSTATE_INT32(arglen, ssi_sd_state),
|
||||||
VMSTATE_INT32(response_pos, ssi_sd_state),
|
VMSTATE_INT32(response_pos, ssi_sd_state),
|
||||||
VMSTATE_INT32(stopping, 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_SSI_PERIPHERAL(ssidev, ssi_sd_state),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
@@ -387,6 +397,7 @@ static void ssi_sd_reset(DeviceState *dev)
|
|||||||
s->arglen = 0;
|
s->arglen = 0;
|
||||||
s->response_pos = 0;
|
s->response_pos = 0;
|
||||||
s->stopping = 0;
|
s->stopping = 0;
|
||||||
|
s->idle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssi_sd_class_init(ObjectClass *klass, void *data)
|
static void ssi_sd_class_init(ObjectClass *klass, void *data)
|
||||||
|
|||||||
@@ -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)
|
||||||
+1
-1
@@ -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_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_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_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_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_IBEX', if_true: files('ibex_spi_host.c'))
|
||||||
system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c'))
|
system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c'))
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -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];
|
||||||
|
};
|
||||||
@@ -14,12 +14,20 @@ REG32(GPIO_STRAP, 0x0038)
|
|||||||
#define ESP32_STRAP_MODE_FLASH_BOOT 0x12
|
#define ESP32_STRAP_MODE_FLASH_BOOT 0x12
|
||||||
#define ESP32_STRAP_MODE_UART_BOOT 0x0f
|
#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 {
|
typedef struct Esp32GpioState {
|
||||||
SysBusDevice parent_obj;
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
MemoryRegion iomem;
|
MemoryRegion iomem;
|
||||||
qemu_irq irq;
|
qemu_irq irq;
|
||||||
|
qemu_irq output_lines[ESP32_GPIO_COUNT];
|
||||||
uint32_t strap_mode;
|
uint32_t strap_mode;
|
||||||
|
uint32_t input_level;
|
||||||
|
uint32_t output_level;
|
||||||
|
uint32_t output_enable;
|
||||||
} Esp32GpioState;
|
} Esp32GpioState;
|
||||||
|
|
||||||
typedef struct Esp32GpioClass {
|
typedef struct Esp32GpioClass {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ typedef struct Esp32I2CState {
|
|||||||
Fifo8 rx_fifo;
|
Fifo8 rx_fifo;
|
||||||
Fifo8 tx_fifo;
|
Fifo8 tx_fifo;
|
||||||
bool trans_ongoing;
|
bool trans_ongoing;
|
||||||
|
bool c3;
|
||||||
|
|
||||||
uint32_t ctr_reg;
|
uint32_t ctr_reg;
|
||||||
uint32_t timeout_reg;
|
uint32_t timeout_reg;
|
||||||
|
|||||||
@@ -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];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user