/*
 * 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(qatomic_read(&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)
