123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- /*
- * Aspeed ADC
- *
- * Copyright 2017-2021 IBM Corp.
- *
- * Andrew Jeffery <andrew@aj.id.au>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
- #include "qemu/osdep.h"
- #include "qapi/error.h"
- #include "qemu/log.h"
- #include "hw/irq.h"
- #include "hw/qdev-properties.h"
- #include "migration/vmstate.h"
- #include "hw/adc/aspeed_adc.h"
- #include "trace.h"
- #define ASPEED_ADC_MEMORY_REGION_SIZE 0x1000
- #define ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE 0x100
- #define ASPEED_ADC_ENGINE_CH_EN_MASK 0xffff0000
- #define ASPEED_ADC_ENGINE_CH_EN(x) ((BIT(x)) << 16)
- #define ASPEED_ADC_ENGINE_INIT BIT(8)
- #define ASPEED_ADC_ENGINE_AUTO_COMP BIT(5)
- #define ASPEED_ADC_ENGINE_COMP BIT(4)
- #define ASPEED_ADC_ENGINE_MODE_MASK 0x0000000e
- #define ASPEED_ADC_ENGINE_MODE_OFF (0b000 << 1)
- #define ASPEED_ADC_ENGINE_MODE_STANDBY (0b001 << 1)
- #define ASPEED_ADC_ENGINE_MODE_NORMAL (0b111 << 1)
- #define ASPEED_ADC_ENGINE_EN BIT(0)
- #define ASPEED_ADC_HYST_EN BIT(31)
- #define ASPEED_ADC_L_MASK ((1 << 10) - 1)
- #define ASPEED_ADC_L(x) ((x) & ASPEED_ADC_L_MASK)
- #define ASPEED_ADC_H(x) (((x) >> 16) & ASPEED_ADC_L_MASK)
- #define ASPEED_ADC_LH_MASK (ASPEED_ADC_L_MASK << 16 | ASPEED_ADC_L_MASK)
- #define LOWER_CHANNEL_MASK ((1 << 10) - 1)
- #define LOWER_CHANNEL_DATA(x) ((x) & LOWER_CHANNEL_MASK)
- #define UPPER_CHANNEL_DATA(x) (((x) >> 16) & LOWER_CHANNEL_MASK)
- #define TO_REG(addr) (addr >> 2)
- #define ENGINE_CONTROL TO_REG(0x00)
- #define INTERRUPT_CONTROL TO_REG(0x04)
- #define VGA_DETECT_CONTROL TO_REG(0x08)
- #define CLOCK_CONTROL TO_REG(0x0C)
- #define DATA_CHANNEL_1_AND_0 TO_REG(0x10)
- #define DATA_CHANNEL_7_AND_6 TO_REG(0x1C)
- #define DATA_CHANNEL_9_AND_8 TO_REG(0x20)
- #define DATA_CHANNEL_15_AND_14 TO_REG(0x2C)
- #define BOUNDS_CHANNEL_0 TO_REG(0x30)
- #define BOUNDS_CHANNEL_7 TO_REG(0x4C)
- #define BOUNDS_CHANNEL_8 TO_REG(0x50)
- #define BOUNDS_CHANNEL_15 TO_REG(0x6C)
- #define HYSTERESIS_CHANNEL_0 TO_REG(0x70)
- #define HYSTERESIS_CHANNEL_7 TO_REG(0x8C)
- #define HYSTERESIS_CHANNEL_8 TO_REG(0x90)
- #define HYSTERESIS_CHANNEL_15 TO_REG(0xAC)
- #define INTERRUPT_SOURCE TO_REG(0xC0)
- #define COMPENSATING_AND_TRIMMING TO_REG(0xC4)
- static inline uint32_t update_channels(uint32_t current)
- {
- return ((((current >> 16) & ASPEED_ADC_L_MASK) + 7) << 16) |
- ((current + 5) & ASPEED_ADC_L_MASK);
- }
- static bool breaks_threshold(AspeedADCEngineState *s, int reg)
- {
- assert(reg >= DATA_CHANNEL_1_AND_0 &&
- reg < DATA_CHANNEL_1_AND_0 + s->nr_channels / 2);
- int a_bounds_reg = BOUNDS_CHANNEL_0 + (reg - DATA_CHANNEL_1_AND_0) * 2;
- int b_bounds_reg = a_bounds_reg + 1;
- uint32_t a_and_b = s->regs[reg];
- uint32_t a_bounds = s->regs[a_bounds_reg];
- uint32_t b_bounds = s->regs[b_bounds_reg];
- uint32_t a = ASPEED_ADC_L(a_and_b);
- uint32_t b = ASPEED_ADC_H(a_and_b);
- uint32_t a_lower = ASPEED_ADC_L(a_bounds);
- uint32_t a_upper = ASPEED_ADC_H(a_bounds);
- uint32_t b_lower = ASPEED_ADC_L(b_bounds);
- uint32_t b_upper = ASPEED_ADC_H(b_bounds);
- return (a < a_lower || a > a_upper) ||
- (b < b_lower || b > b_upper);
- }
- static uint32_t read_channel_sample(AspeedADCEngineState *s, int reg)
- {
- assert(reg >= DATA_CHANNEL_1_AND_0 &&
- reg < DATA_CHANNEL_1_AND_0 + s->nr_channels / 2);
- /* Poor man's sampling */
- uint32_t value = s->regs[reg];
- s->regs[reg] = update_channels(s->regs[reg]);
- if (breaks_threshold(s, reg)) {
- s->regs[INTERRUPT_CONTROL] |= BIT(reg - DATA_CHANNEL_1_AND_0);
- qemu_irq_raise(s->irq);
- }
- return value;
- }
- static uint64_t aspeed_adc_engine_read(void *opaque, hwaddr addr,
- unsigned int size)
- {
- AspeedADCEngineState *s = ASPEED_ADC_ENGINE(opaque);
- int reg = TO_REG(addr);
- uint32_t value = 0;
- switch (reg) {
- case BOUNDS_CHANNEL_8 ... BOUNDS_CHANNEL_15:
- if (s->nr_channels <= 8) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
- "bounds register %u invalid, only 0...7 valid\n",
- __func__, s->engine_id, reg - BOUNDS_CHANNEL_0);
- break;
- }
- /* fallthrough */
- case HYSTERESIS_CHANNEL_8 ... HYSTERESIS_CHANNEL_15:
- if (s->nr_channels <= 8) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
- "hysteresis register %u invalid, only 0...7 valid\n",
- __func__, s->engine_id, reg - HYSTERESIS_CHANNEL_0);
- break;
- }
- /* fallthrough */
- case BOUNDS_CHANNEL_0 ... BOUNDS_CHANNEL_7:
- case HYSTERESIS_CHANNEL_0 ... HYSTERESIS_CHANNEL_7:
- case ENGINE_CONTROL:
- case INTERRUPT_CONTROL:
- case VGA_DETECT_CONTROL:
- case CLOCK_CONTROL:
- case INTERRUPT_SOURCE:
- case COMPENSATING_AND_TRIMMING:
- value = s->regs[reg];
- break;
- case DATA_CHANNEL_9_AND_8 ... DATA_CHANNEL_15_AND_14:
- if (s->nr_channels <= 8) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
- "data register %u invalid, only 0...3 valid\n",
- __func__, s->engine_id, reg - DATA_CHANNEL_1_AND_0);
- break;
- }
- /* fallthrough */
- case DATA_CHANNEL_1_AND_0 ... DATA_CHANNEL_7_AND_6:
- value = read_channel_sample(s, reg);
- /* Allow 16-bit reads of the data registers */
- if (addr & 0x2) {
- assert(size == 2);
- value >>= 16;
- }
- break;
- default:
- qemu_log_mask(LOG_UNIMP, "%s: engine[%u]: 0x%" HWADDR_PRIx "\n",
- __func__, s->engine_id, addr);
- break;
- }
- trace_aspeed_adc_engine_read(s->engine_id, addr, value);
- return value;
- }
- static void aspeed_adc_engine_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned int size)
- {
- AspeedADCEngineState *s = ASPEED_ADC_ENGINE(opaque);
- int reg = TO_REG(addr);
- uint32_t init = 0;
- trace_aspeed_adc_engine_write(s->engine_id, addr, value);
- switch (reg) {
- case ENGINE_CONTROL:
- init = !!(value & ASPEED_ADC_ENGINE_EN);
- init *= ASPEED_ADC_ENGINE_INIT;
- value &= ~ASPEED_ADC_ENGINE_INIT;
- value |= init;
- value &= ~ASPEED_ADC_ENGINE_AUTO_COMP;
- break;
- case INTERRUPT_CONTROL:
- case VGA_DETECT_CONTROL:
- case CLOCK_CONTROL:
- break;
- case DATA_CHANNEL_9_AND_8 ... DATA_CHANNEL_15_AND_14:
- if (s->nr_channels <= 8) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
- "data register %u invalid, only 0...3 valid\n",
- __func__, s->engine_id, reg - DATA_CHANNEL_1_AND_0);
- return;
- }
- /* fallthrough */
- case BOUNDS_CHANNEL_8 ... BOUNDS_CHANNEL_15:
- if (s->nr_channels <= 8) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
- "bounds register %u invalid, only 0...7 valid\n",
- __func__, s->engine_id, reg - BOUNDS_CHANNEL_0);
- return;
- }
- /* fallthrough */
- case DATA_CHANNEL_1_AND_0 ... DATA_CHANNEL_7_AND_6:
- case BOUNDS_CHANNEL_0 ... BOUNDS_CHANNEL_7:
- value &= ASPEED_ADC_LH_MASK;
- break;
- case HYSTERESIS_CHANNEL_8 ... HYSTERESIS_CHANNEL_15:
- if (s->nr_channels <= 8) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
- "hysteresis register %u invalid, only 0...7 valid\n",
- __func__, s->engine_id, reg - HYSTERESIS_CHANNEL_0);
- return;
- }
- /* fallthrough */
- case HYSTERESIS_CHANNEL_0 ... HYSTERESIS_CHANNEL_7:
- value &= (ASPEED_ADC_HYST_EN | ASPEED_ADC_LH_MASK);
- break;
- case INTERRUPT_SOURCE:
- value &= 0xffff;
- break;
- case COMPENSATING_AND_TRIMMING:
- value &= 0xf;
- break;
- default:
- qemu_log_mask(LOG_UNIMP, "%s: engine[%u]: "
- "0x%" HWADDR_PRIx " 0x%" PRIx64 "\n",
- __func__, s->engine_id, addr, value);
- break;
- }
- s->regs[reg] = value;
- }
- static const MemoryRegionOps aspeed_adc_engine_ops = {
- .read = aspeed_adc_engine_read,
- .write = aspeed_adc_engine_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 2,
- .max_access_size = 4,
- .unaligned = false,
- },
- };
- static const uint32_t aspeed_adc_resets[ASPEED_ADC_NR_REGS] = {
- [ENGINE_CONTROL] = 0x00000000,
- [INTERRUPT_CONTROL] = 0x00000000,
- [VGA_DETECT_CONTROL] = 0x0000000f,
- [CLOCK_CONTROL] = 0x0000000f,
- };
- static void aspeed_adc_engine_reset(DeviceState *dev)
- {
- AspeedADCEngineState *s = ASPEED_ADC_ENGINE(dev);
- memcpy(s->regs, aspeed_adc_resets, sizeof(aspeed_adc_resets));
- }
- static void aspeed_adc_engine_realize(DeviceState *dev, Error **errp)
- {
- AspeedADCEngineState *s = ASPEED_ADC_ENGINE(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- g_autofree char *name = g_strdup_printf(TYPE_ASPEED_ADC_ENGINE ".%d",
- s->engine_id);
- assert(s->engine_id < 2);
- sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_adc_engine_ops, s, name,
- ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE);
- sysbus_init_mmio(sbd, &s->mmio);
- }
- static const VMStateDescription vmstate_aspeed_adc_engine = {
- .name = TYPE_ASPEED_ADC,
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, AspeedADCEngineState, ASPEED_ADC_NR_REGS),
- VMSTATE_END_OF_LIST(),
- }
- };
- static Property aspeed_adc_engine_properties[] = {
- DEFINE_PROP_UINT32("engine-id", AspeedADCEngineState, engine_id, 0),
- DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0),
- DEFINE_PROP_END_OF_LIST(),
- };
- static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- dc->realize = aspeed_adc_engine_realize;
- dc->reset = aspeed_adc_engine_reset;
- device_class_set_props(dc, aspeed_adc_engine_properties);
- dc->desc = "Aspeed Analog-to-Digital Engine";
- dc->vmsd = &vmstate_aspeed_adc_engine;
- }
- static const TypeInfo aspeed_adc_engine_info = {
- .name = TYPE_ASPEED_ADC_ENGINE,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(AspeedADCEngineState),
- .class_init = aspeed_adc_engine_class_init,
- };
- static void aspeed_adc_instance_init(Object *obj)
- {
- AspeedADCState *s = ASPEED_ADC(obj);
- AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(obj);
- uint32_t nr_channels = ASPEED_ADC_NR_CHANNELS / aac->nr_engines;
- for (int i = 0; i < aac->nr_engines; i++) {
- AspeedADCEngineState *engine = &s->engines[i];
- object_initialize_child(obj, "engine[*]", engine,
- TYPE_ASPEED_ADC_ENGINE);
- qdev_prop_set_uint32(DEVICE(engine), "engine-id", i);
- qdev_prop_set_uint32(DEVICE(engine), "nr-channels", nr_channels);
- }
- }
- static void aspeed_adc_set_irq(void *opaque, int n, int level)
- {
- AspeedADCState *s = opaque;
- AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(s);
- uint32_t pending = 0;
- /* TODO: update Global IRQ status register on AST2600 (Need specs) */
- for (int i = 0; i < aac->nr_engines; i++) {
- uint32_t irq_status = s->engines[i].regs[INTERRUPT_CONTROL] & 0xFF;
- pending |= irq_status << (i * 8);
- }
- qemu_set_irq(s->irq, !!pending);
- }
- static void aspeed_adc_realize(DeviceState *dev, Error **errp)
- {
- AspeedADCState *s = ASPEED_ADC(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(dev);
- qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_adc_set_irq,
- s, NULL, aac->nr_engines);
- sysbus_init_irq(sbd, &s->irq);
- memory_region_init(&s->mmio, OBJECT(s), TYPE_ASPEED_ADC,
- ASPEED_ADC_MEMORY_REGION_SIZE);
- sysbus_init_mmio(sbd, &s->mmio);
- for (int i = 0; i < aac->nr_engines; i++) {
- Object *eng = OBJECT(&s->engines[i]);
- if (!sysbus_realize(SYS_BUS_DEVICE(eng), errp)) {
- return;
- }
- sysbus_connect_irq(SYS_BUS_DEVICE(eng), 0,
- qdev_get_gpio_in(DEVICE(sbd), i));
- memory_region_add_subregion(&s->mmio,
- i * ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE,
- &s->engines[i].mmio);
- }
- }
- static void aspeed_adc_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- AspeedADCClass *aac = ASPEED_ADC_CLASS(klass);
- dc->realize = aspeed_adc_realize;
- dc->desc = "Aspeed Analog-to-Digital Converter";
- aac->nr_engines = 1;
- }
- static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- AspeedADCClass *aac = ASPEED_ADC_CLASS(klass);
- dc->desc = "ASPEED 2600 ADC Controller";
- aac->nr_engines = 2;
- }
- static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- AspeedADCClass *aac = ASPEED_ADC_CLASS(klass);
- dc->desc = "ASPEED 1030 ADC Controller";
- aac->nr_engines = 2;
- }
- static const TypeInfo aspeed_adc_info = {
- .name = TYPE_ASPEED_ADC,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_init = aspeed_adc_instance_init,
- .instance_size = sizeof(AspeedADCState),
- .class_init = aspeed_adc_class_init,
- .class_size = sizeof(AspeedADCClass),
- .abstract = true,
- };
- static const TypeInfo aspeed_2400_adc_info = {
- .name = TYPE_ASPEED_2400_ADC,
- .parent = TYPE_ASPEED_ADC,
- };
- static const TypeInfo aspeed_2500_adc_info = {
- .name = TYPE_ASPEED_2500_ADC,
- .parent = TYPE_ASPEED_ADC,
- };
- static const TypeInfo aspeed_2600_adc_info = {
- .name = TYPE_ASPEED_2600_ADC,
- .parent = TYPE_ASPEED_ADC,
- .class_init = aspeed_2600_adc_class_init,
- };
- static const TypeInfo aspeed_1030_adc_info = {
- .name = TYPE_ASPEED_1030_ADC,
- .parent = TYPE_ASPEED_ADC,
- .class_init = aspeed_1030_adc_class_init, /* No change since AST2600 */
- };
- static void aspeed_adc_register_types(void)
- {
- type_register_static(&aspeed_adc_engine_info);
- type_register_static(&aspeed_adc_info);
- type_register_static(&aspeed_2400_adc_info);
- type_register_static(&aspeed_2500_adc_info);
- type_register_static(&aspeed_2600_adc_info);
- type_register_static(&aspeed_1030_adc_info);
- }
- type_init(aspeed_adc_register_types);
|