d613b079ef
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.
107 lines
2.9 KiB
C
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)
|