|
@@ -0,0 +1,427 @@
|
|
|
+/*
|
|
|
+ * 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 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 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_init(aspeed_adc_register_types);
|