Files
evan d613b079ef feat(xteink): count guest ADC reads per channel
Host tooling needs to know when the guest has actually sampled a button,
since the firmware only polls the resistor ladder between e-ink refreshes.
2026-07-26 11:18:28 -04:00

107 lines
2.9 KiB
C

/*
* 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) {
if (s->channel >= ESP32C3_ADC_CHANNELS) {
return ADC_MAX;
}
qatomic_inc(&s->samples[s->channel]);
return MIN(qatomic_read(&s->input[s->channel]), 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->samples[i] = 0;
}
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);
object_property_add_uint32_ptr(obj, "adcsamples[*]", &s->samples[i],
OBJ_PROP_FLAG_READ);
}
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)