qemu/hw/adc/aspeed_adc.c
<<
>>
Prefs
   1/*
   2 * Aspeed ADC
   3 *
   4 * Copyright 2017-2021 IBM Corp.
   5 *
   6 * Andrew Jeffery <andrew@aj.id.au>
   7 *
   8 * SPDX-License-Identifier: GPL-2.0-or-later
   9 */
  10
  11#include "qemu/osdep.h"
  12#include "qapi/error.h"
  13#include "qemu/log.h"
  14#include "hw/irq.h"
  15#include "hw/qdev-properties.h"
  16#include "migration/vmstate.h"
  17#include "hw/adc/aspeed_adc.h"
  18#include "trace.h"
  19
  20#define ASPEED_ADC_MEMORY_REGION_SIZE           0x1000
  21#define ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE    0x100
  22#define  ASPEED_ADC_ENGINE_CH_EN_MASK           0xffff0000
  23#define   ASPEED_ADC_ENGINE_CH_EN(x)            ((BIT(x)) << 16)
  24#define  ASPEED_ADC_ENGINE_INIT                 BIT(8)
  25#define  ASPEED_ADC_ENGINE_AUTO_COMP            BIT(5)
  26#define  ASPEED_ADC_ENGINE_COMP                 BIT(4)
  27#define  ASPEED_ADC_ENGINE_MODE_MASK            0x0000000e
  28#define   ASPEED_ADC_ENGINE_MODE_OFF            (0b000 << 1)
  29#define   ASPEED_ADC_ENGINE_MODE_STANDBY        (0b001 << 1)
  30#define   ASPEED_ADC_ENGINE_MODE_NORMAL         (0b111 << 1)
  31#define  ASPEED_ADC_ENGINE_EN                   BIT(0)
  32#define ASPEED_ADC_HYST_EN                      BIT(31)
  33
  34#define ASPEED_ADC_L_MASK       ((1 << 10) - 1)
  35#define ASPEED_ADC_L(x)         ((x) & ASPEED_ADC_L_MASK)
  36#define ASPEED_ADC_H(x)         (((x) >> 16) & ASPEED_ADC_L_MASK)
  37#define ASPEED_ADC_LH_MASK      (ASPEED_ADC_L_MASK << 16 | ASPEED_ADC_L_MASK)
  38#define LOWER_CHANNEL_MASK      ((1 << 10) - 1)
  39#define LOWER_CHANNEL_DATA(x)   ((x) & LOWER_CHANNEL_MASK)
  40#define UPPER_CHANNEL_DATA(x)   (((x) >> 16) & LOWER_CHANNEL_MASK)
  41
  42#define TO_REG(addr) (addr >> 2)
  43
  44#define ENGINE_CONTROL              TO_REG(0x00)
  45#define INTERRUPT_CONTROL           TO_REG(0x04)
  46#define VGA_DETECT_CONTROL          TO_REG(0x08)
  47#define CLOCK_CONTROL               TO_REG(0x0C)
  48#define DATA_CHANNEL_1_AND_0        TO_REG(0x10)
  49#define DATA_CHANNEL_7_AND_6        TO_REG(0x1C)
  50#define DATA_CHANNEL_9_AND_8        TO_REG(0x20)
  51#define DATA_CHANNEL_15_AND_14      TO_REG(0x2C)
  52#define BOUNDS_CHANNEL_0            TO_REG(0x30)
  53#define BOUNDS_CHANNEL_7            TO_REG(0x4C)
  54#define BOUNDS_CHANNEL_8            TO_REG(0x50)
  55#define BOUNDS_CHANNEL_15           TO_REG(0x6C)
  56#define HYSTERESIS_CHANNEL_0        TO_REG(0x70)
  57#define HYSTERESIS_CHANNEL_7        TO_REG(0x8C)
  58#define HYSTERESIS_CHANNEL_8        TO_REG(0x90)
  59#define HYSTERESIS_CHANNEL_15       TO_REG(0xAC)
  60#define INTERRUPT_SOURCE            TO_REG(0xC0)
  61#define COMPENSATING_AND_TRIMMING   TO_REG(0xC4)
  62
  63static inline uint32_t update_channels(uint32_t current)
  64{
  65    return ((((current >> 16) & ASPEED_ADC_L_MASK) + 7) << 16) |
  66        ((current + 5) & ASPEED_ADC_L_MASK);
  67}
  68
  69static bool breaks_threshold(AspeedADCEngineState *s, int reg)
  70{
  71    assert(reg >= DATA_CHANNEL_1_AND_0 &&
  72           reg < DATA_CHANNEL_1_AND_0 + s->nr_channels / 2);
  73
  74    int a_bounds_reg = BOUNDS_CHANNEL_0 + (reg - DATA_CHANNEL_1_AND_0) * 2;
  75    int b_bounds_reg = a_bounds_reg + 1;
  76    uint32_t a_and_b = s->regs[reg];
  77    uint32_t a_bounds = s->regs[a_bounds_reg];
  78    uint32_t b_bounds = s->regs[b_bounds_reg];
  79    uint32_t a = ASPEED_ADC_L(a_and_b);
  80    uint32_t b = ASPEED_ADC_H(a_and_b);
  81    uint32_t a_lower = ASPEED_ADC_L(a_bounds);
  82    uint32_t a_upper = ASPEED_ADC_H(a_bounds);
  83    uint32_t b_lower = ASPEED_ADC_L(b_bounds);
  84    uint32_t b_upper = ASPEED_ADC_H(b_bounds);
  85
  86    return (a < a_lower || a > a_upper) ||
  87           (b < b_lower || b > b_upper);
  88}
  89
  90static uint32_t read_channel_sample(AspeedADCEngineState *s, int reg)
  91{
  92    assert(reg >= DATA_CHANNEL_1_AND_0 &&
  93           reg < DATA_CHANNEL_1_AND_0 + s->nr_channels / 2);
  94
  95    /* Poor man's sampling */
  96    uint32_t value = s->regs[reg];
  97    s->regs[reg] = update_channels(s->regs[reg]);
  98
  99    if (breaks_threshold(s, reg)) {
 100        s->regs[INTERRUPT_CONTROL] |= BIT(reg - DATA_CHANNEL_1_AND_0);
 101        qemu_irq_raise(s->irq);
 102    }
 103
 104    return value;
 105}
 106
 107static uint64_t aspeed_adc_engine_read(void *opaque, hwaddr addr,
 108                                       unsigned int size)
 109{
 110    AspeedADCEngineState *s = ASPEED_ADC_ENGINE(opaque);
 111    int reg = TO_REG(addr);
 112    uint32_t value = 0;
 113
 114    switch (reg) {
 115    case BOUNDS_CHANNEL_8 ... BOUNDS_CHANNEL_15:
 116        if (s->nr_channels <= 8) {
 117            qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
 118                          "bounds register %u invalid, only 0...7 valid\n",
 119                          __func__, s->engine_id, reg - BOUNDS_CHANNEL_0);
 120            break;
 121        }
 122        /* fallthrough */
 123    case HYSTERESIS_CHANNEL_8 ... HYSTERESIS_CHANNEL_15:
 124        if (s->nr_channels <= 8) {
 125            qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
 126                          "hysteresis register %u invalid, only 0...7 valid\n",
 127                          __func__, s->engine_id, reg - HYSTERESIS_CHANNEL_0);
 128            break;
 129        }
 130        /* fallthrough */
 131    case BOUNDS_CHANNEL_0 ... BOUNDS_CHANNEL_7:
 132    case HYSTERESIS_CHANNEL_0 ... HYSTERESIS_CHANNEL_7:
 133    case ENGINE_CONTROL:
 134    case INTERRUPT_CONTROL:
 135    case VGA_DETECT_CONTROL:
 136    case CLOCK_CONTROL:
 137    case INTERRUPT_SOURCE:
 138    case COMPENSATING_AND_TRIMMING:
 139        value = s->regs[reg];
 140        break;
 141    case DATA_CHANNEL_9_AND_8 ... DATA_CHANNEL_15_AND_14:
 142        if (s->nr_channels <= 8) {
 143            qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
 144                          "data register %u invalid, only 0...3 valid\n",
 145                          __func__, s->engine_id, reg - DATA_CHANNEL_1_AND_0);
 146            break;
 147        }
 148        /* fallthrough */
 149    case DATA_CHANNEL_1_AND_0 ... DATA_CHANNEL_7_AND_6:
 150        value = read_channel_sample(s, reg);
 151        /* Allow 16-bit reads of the data registers */
 152        if (addr & 0x2) {
 153            assert(size == 2);
 154            value >>= 16;
 155        }
 156        break;
 157    default:
 158        qemu_log_mask(LOG_UNIMP, "%s: engine[%u]: 0x%" HWADDR_PRIx "\n",
 159                      __func__, s->engine_id, addr);
 160        break;
 161    }
 162
 163    trace_aspeed_adc_engine_read(s->engine_id, addr, value);
 164    return value;
 165}
 166
 167static void aspeed_adc_engine_write(void *opaque, hwaddr addr, uint64_t value,
 168                                    unsigned int size)
 169{
 170    AspeedADCEngineState *s = ASPEED_ADC_ENGINE(opaque);
 171    int reg = TO_REG(addr);
 172    uint32_t init = 0;
 173
 174    trace_aspeed_adc_engine_write(s->engine_id, addr, value);
 175
 176    switch (reg) {
 177    case ENGINE_CONTROL:
 178        init = !!(value & ASPEED_ADC_ENGINE_EN);
 179        init *= ASPEED_ADC_ENGINE_INIT;
 180
 181        value &= ~ASPEED_ADC_ENGINE_INIT;
 182        value |= init;
 183
 184        value &= ~ASPEED_ADC_ENGINE_AUTO_COMP;
 185        break;
 186    case INTERRUPT_CONTROL:
 187    case VGA_DETECT_CONTROL:
 188    case CLOCK_CONTROL:
 189        break;
 190    case DATA_CHANNEL_9_AND_8 ... DATA_CHANNEL_15_AND_14:
 191        if (s->nr_channels <= 8) {
 192            qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
 193                          "data register %u invalid, only 0...3 valid\n",
 194                          __func__, s->engine_id, reg - DATA_CHANNEL_1_AND_0);
 195            return;
 196        }
 197        /* fallthrough */
 198    case BOUNDS_CHANNEL_8 ... BOUNDS_CHANNEL_15:
 199        if (s->nr_channels <= 8) {
 200            qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
 201                          "bounds register %u invalid, only 0...7 valid\n",
 202                          __func__, s->engine_id, reg - BOUNDS_CHANNEL_0);
 203            return;
 204        }
 205        /* fallthrough */
 206    case DATA_CHANNEL_1_AND_0 ... DATA_CHANNEL_7_AND_6:
 207    case BOUNDS_CHANNEL_0 ... BOUNDS_CHANNEL_7:
 208        value &= ASPEED_ADC_LH_MASK;
 209        break;
 210    case HYSTERESIS_CHANNEL_8 ... HYSTERESIS_CHANNEL_15:
 211        if (s->nr_channels <= 8) {
 212            qemu_log_mask(LOG_GUEST_ERROR, "%s: engine[%u]: "
 213                          "hysteresis register %u invalid, only 0...7 valid\n",
 214                          __func__, s->engine_id, reg - HYSTERESIS_CHANNEL_0);
 215            return;
 216        }
 217        /* fallthrough */
 218    case HYSTERESIS_CHANNEL_0 ... HYSTERESIS_CHANNEL_7:
 219        value &= (ASPEED_ADC_HYST_EN | ASPEED_ADC_LH_MASK);
 220        break;
 221    case INTERRUPT_SOURCE:
 222        value &= 0xffff;
 223        break;
 224    case COMPENSATING_AND_TRIMMING:
 225        value &= 0xf;
 226        break;
 227    default:
 228        qemu_log_mask(LOG_UNIMP, "%s: engine[%u]: "
 229                      "0x%" HWADDR_PRIx " 0x%" PRIx64 "\n",
 230                      __func__, s->engine_id, addr, value);
 231        break;
 232    }
 233
 234    s->regs[reg] = value;
 235}
 236
 237static const MemoryRegionOps aspeed_adc_engine_ops = {
 238    .read = aspeed_adc_engine_read,
 239    .write = aspeed_adc_engine_write,
 240    .endianness = DEVICE_LITTLE_ENDIAN,
 241    .valid = {
 242        .min_access_size = 2,
 243        .max_access_size = 4,
 244        .unaligned = false,
 245    },
 246};
 247
 248static const uint32_t aspeed_adc_resets[ASPEED_ADC_NR_REGS] = {
 249    [ENGINE_CONTROL]     = 0x00000000,
 250    [INTERRUPT_CONTROL]  = 0x00000000,
 251    [VGA_DETECT_CONTROL] = 0x0000000f,
 252    [CLOCK_CONTROL]      = 0x0000000f,
 253};
 254
 255static void aspeed_adc_engine_reset(DeviceState *dev)
 256{
 257    AspeedADCEngineState *s = ASPEED_ADC_ENGINE(dev);
 258
 259    memcpy(s->regs, aspeed_adc_resets, sizeof(aspeed_adc_resets));
 260}
 261
 262static void aspeed_adc_engine_realize(DeviceState *dev, Error **errp)
 263{
 264    AspeedADCEngineState *s = ASPEED_ADC_ENGINE(dev);
 265    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 266    g_autofree char *name = g_strdup_printf(TYPE_ASPEED_ADC_ENGINE ".%d",
 267                                            s->engine_id);
 268
 269    assert(s->engine_id < 2);
 270
 271    sysbus_init_irq(sbd, &s->irq);
 272
 273    memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_adc_engine_ops, s, name,
 274                          ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE);
 275
 276    sysbus_init_mmio(sbd, &s->mmio);
 277}
 278
 279static const VMStateDescription vmstate_aspeed_adc_engine = {
 280    .name = TYPE_ASPEED_ADC,
 281    .version_id = 1,
 282    .minimum_version_id = 1,
 283    .fields = (VMStateField[]) {
 284        VMSTATE_UINT32_ARRAY(regs, AspeedADCEngineState, ASPEED_ADC_NR_REGS),
 285        VMSTATE_END_OF_LIST(),
 286    }
 287};
 288
 289static Property aspeed_adc_engine_properties[] = {
 290    DEFINE_PROP_UINT32("engine-id", AspeedADCEngineState, engine_id, 0),
 291    DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0),
 292    DEFINE_PROP_END_OF_LIST(),
 293};
 294
 295static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data)
 296{
 297    DeviceClass *dc = DEVICE_CLASS(klass);
 298
 299    dc->realize = aspeed_adc_engine_realize;
 300    dc->reset = aspeed_adc_engine_reset;
 301    device_class_set_props(dc, aspeed_adc_engine_properties);
 302    dc->desc = "Aspeed Analog-to-Digital Engine";
 303    dc->vmsd = &vmstate_aspeed_adc_engine;
 304}
 305
 306static const TypeInfo aspeed_adc_engine_info = {
 307    .name = TYPE_ASPEED_ADC_ENGINE,
 308    .parent = TYPE_SYS_BUS_DEVICE,
 309    .instance_size = sizeof(AspeedADCEngineState),
 310    .class_init = aspeed_adc_engine_class_init,
 311};
 312
 313static void aspeed_adc_instance_init(Object *obj)
 314{
 315    AspeedADCState *s = ASPEED_ADC(obj);
 316    AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(obj);
 317    uint32_t nr_channels = ASPEED_ADC_NR_CHANNELS / aac->nr_engines;
 318
 319    for (int i = 0; i < aac->nr_engines; i++) {
 320        AspeedADCEngineState *engine = &s->engines[i];
 321        object_initialize_child(obj, "engine[*]", engine,
 322                                TYPE_ASPEED_ADC_ENGINE);
 323        qdev_prop_set_uint32(DEVICE(engine), "engine-id", i);
 324        qdev_prop_set_uint32(DEVICE(engine), "nr-channels", nr_channels);
 325    }
 326}
 327
 328static void aspeed_adc_set_irq(void *opaque, int n, int level)
 329{
 330    AspeedADCState *s = opaque;
 331    AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(s);
 332    uint32_t pending = 0;
 333
 334    /* TODO: update Global IRQ status register on AST2600 (Need specs) */
 335    for (int i = 0; i < aac->nr_engines; i++) {
 336        uint32_t irq_status = s->engines[i].regs[INTERRUPT_CONTROL] & 0xFF;
 337        pending |= irq_status << (i * 8);
 338    }
 339
 340    qemu_set_irq(s->irq, !!pending);
 341}
 342
 343static void aspeed_adc_realize(DeviceState *dev, Error **errp)
 344{
 345    AspeedADCState *s = ASPEED_ADC(dev);
 346    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 347    AspeedADCClass *aac = ASPEED_ADC_GET_CLASS(dev);
 348
 349    qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_adc_set_irq,
 350                                        s, NULL, aac->nr_engines);
 351
 352    sysbus_init_irq(sbd, &s->irq);
 353
 354    memory_region_init(&s->mmio, OBJECT(s), TYPE_ASPEED_ADC,
 355                       ASPEED_ADC_MEMORY_REGION_SIZE);
 356
 357    sysbus_init_mmio(sbd, &s->mmio);
 358
 359    for (int i = 0; i < aac->nr_engines; i++) {
 360        Object *eng = OBJECT(&s->engines[i]);
 361
 362        if (!sysbus_realize(SYS_BUS_DEVICE(eng), errp)) {
 363            return;
 364        }
 365        sysbus_connect_irq(SYS_BUS_DEVICE(eng), 0,
 366                           qdev_get_gpio_in(DEVICE(sbd), i));
 367        memory_region_add_subregion(&s->mmio,
 368                                    i * ASPEED_ADC_ENGINE_MEMORY_REGION_SIZE,
 369                                    &s->engines[i].mmio);
 370    }
 371}
 372
 373static void aspeed_adc_class_init(ObjectClass *klass, void *data)
 374{
 375    DeviceClass *dc = DEVICE_CLASS(klass);
 376    AspeedADCClass *aac = ASPEED_ADC_CLASS(klass);
 377
 378    dc->realize = aspeed_adc_realize;
 379    dc->desc = "Aspeed Analog-to-Digital Converter";
 380    aac->nr_engines = 1;
 381}
 382
 383static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data)
 384{
 385    DeviceClass *dc = DEVICE_CLASS(klass);
 386    AspeedADCClass *aac = ASPEED_ADC_CLASS(klass);
 387
 388    dc->desc = "ASPEED 2600 ADC Controller";
 389    aac->nr_engines = 2;
 390}
 391
 392static const TypeInfo aspeed_adc_info = {
 393    .name = TYPE_ASPEED_ADC,
 394    .parent = TYPE_SYS_BUS_DEVICE,
 395    .instance_init = aspeed_adc_instance_init,
 396    .instance_size = sizeof(AspeedADCState),
 397    .class_init = aspeed_adc_class_init,
 398    .class_size = sizeof(AspeedADCClass),
 399    .abstract   = true,
 400};
 401
 402static const TypeInfo aspeed_2400_adc_info = {
 403    .name = TYPE_ASPEED_2400_ADC,
 404    .parent = TYPE_ASPEED_ADC,
 405};
 406
 407static const TypeInfo aspeed_2500_adc_info = {
 408    .name = TYPE_ASPEED_2500_ADC,
 409    .parent = TYPE_ASPEED_ADC,
 410};
 411
 412static const TypeInfo aspeed_2600_adc_info = {
 413    .name = TYPE_ASPEED_2600_ADC,
 414    .parent = TYPE_ASPEED_ADC,
 415    .class_init = aspeed_2600_adc_class_init,
 416};
 417
 418static void aspeed_adc_register_types(void)
 419{
 420    type_register_static(&aspeed_adc_engine_info);
 421    type_register_static(&aspeed_adc_info);
 422    type_register_static(&aspeed_2400_adc_info);
 423    type_register_static(&aspeed_2500_adc_info);
 424    type_register_static(&aspeed_2600_adc_info);
 425}
 426
 427type_init(aspeed_adc_register_types);
 428