qemu/hw/misc/max111x.c
<<
>>
Prefs
   1/*
   2 * Maxim MAX1110/1111 ADC chip emulation.
   3 *
   4 * Copyright (c) 2006 Openedhand Ltd.
   5 * Written by Andrzej Zaborowski <balrog@zabor.org>
   6 *
   7 * This code is licensed under the GNU GPLv2.
   8 *
   9 * Contributions after 2012-01-13 are licensed under the terms of the
  10 * GNU GPL, version 2 or (at your option) any later version.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "hw/irq.h"
  15#include "hw/ssi/ssi.h"
  16#include "migration/vmstate.h"
  17#include "qemu/module.h"
  18
  19typedef struct {
  20    SSISlave parent_obj;
  21
  22    qemu_irq interrupt;
  23    uint8_t tb1, rb2, rb3;
  24    int cycle;
  25
  26    uint8_t input[8];
  27    int inputs, com;
  28} MAX111xState;
  29
  30#define TYPE_MAX_111X "max111x"
  31
  32#define MAX_111X(obj) \
  33    OBJECT_CHECK(MAX111xState, (obj), TYPE_MAX_111X)
  34
  35#define TYPE_MAX_1110 "max1110"
  36#define TYPE_MAX_1111 "max1111"
  37
  38/* Control-byte bitfields */
  39#define CB_PD0          (1 << 0)
  40#define CB_PD1          (1 << 1)
  41#define CB_SGL          (1 << 2)
  42#define CB_UNI          (1 << 3)
  43#define CB_SEL0         (1 << 4)
  44#define CB_SEL1         (1 << 5)
  45#define CB_SEL2         (1 << 6)
  46#define CB_START        (1 << 7)
  47
  48#define CHANNEL_NUM(v, b0, b1, b2)      \
  49                        ((((v) >> (2 + (b0))) & 4) |    \
  50                         (((v) >> (3 + (b1))) & 2) |    \
  51                         (((v) >> (4 + (b2))) & 1))
  52
  53static uint32_t max111x_read(MAX111xState *s)
  54{
  55    if (!s->tb1)
  56        return 0;
  57
  58    switch (s->cycle ++) {
  59    case 1:
  60        return s->rb2;
  61    case 2:
  62        return s->rb3;
  63    }
  64
  65    return 0;
  66}
  67
  68/* Interpret a control-byte */
  69static void max111x_write(MAX111xState *s, uint32_t value)
  70{
  71    int measure, chan;
  72
  73    /* Ignore the value if START bit is zero */
  74    if (!(value & CB_START))
  75        return;
  76
  77    s->cycle = 0;
  78
  79    if (!(value & CB_PD1)) {
  80        s->tb1 = 0;
  81        return;
  82    }
  83
  84    s->tb1 = value;
  85
  86    if (s->inputs == 8)
  87        chan = CHANNEL_NUM(value, 1, 0, 2);
  88    else
  89        chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
  90
  91    if (value & CB_SGL)
  92        measure = s->input[chan] - s->com;
  93    else
  94        measure = s->input[chan] - s->input[chan ^ 1];
  95
  96    if (!(value & CB_UNI))
  97        measure ^= 0x80;
  98
  99    s->rb2 = (measure >> 2) & 0x3f;
 100    s->rb3 = (measure << 6) & 0xc0;
 101
 102    /* FIXME: When should the IRQ be lowered?  */
 103    qemu_irq_raise(s->interrupt);
 104}
 105
 106static uint32_t max111x_transfer(SSISlave *dev, uint32_t value)
 107{
 108    MAX111xState *s = MAX_111X(dev);
 109    max111x_write(s, value);
 110    return max111x_read(s);
 111}
 112
 113static const VMStateDescription vmstate_max111x = {
 114    .name = "max111x",
 115    .version_id = 1,
 116    .minimum_version_id = 1,
 117    .fields = (VMStateField[]) {
 118        VMSTATE_SSI_SLAVE(parent_obj, MAX111xState),
 119        VMSTATE_UINT8(tb1, MAX111xState),
 120        VMSTATE_UINT8(rb2, MAX111xState),
 121        VMSTATE_UINT8(rb3, MAX111xState),
 122        VMSTATE_INT32_EQUAL(inputs, MAX111xState, NULL),
 123        VMSTATE_INT32(com, MAX111xState),
 124        VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs,
 125                                   vmstate_info_uint8, uint8_t),
 126        VMSTATE_END_OF_LIST()
 127    }
 128};
 129
 130static int max111x_init(SSISlave *d, int inputs)
 131{
 132    DeviceState *dev = DEVICE(d);
 133    MAX111xState *s = MAX_111X(dev);
 134
 135    qdev_init_gpio_out(dev, &s->interrupt, 1);
 136
 137    s->inputs = inputs;
 138    /* TODO: add a user interface for setting these */
 139    s->input[0] = 0xf0;
 140    s->input[1] = 0xe0;
 141    s->input[2] = 0xd0;
 142    s->input[3] = 0xc0;
 143    s->input[4] = 0xb0;
 144    s->input[5] = 0xa0;
 145    s->input[6] = 0x90;
 146    s->input[7] = 0x80;
 147    s->com = 0;
 148
 149    vmstate_register(dev, -1, &vmstate_max111x, s);
 150    return 0;
 151}
 152
 153static void max1110_realize(SSISlave *dev, Error **errp)
 154{
 155    max111x_init(dev, 8);
 156}
 157
 158static void max1111_realize(SSISlave *dev, Error **errp)
 159{
 160    max111x_init(dev, 4);
 161}
 162
 163void max111x_set_input(DeviceState *dev, int line, uint8_t value)
 164{
 165    MAX111xState *s = MAX_111X(dev);
 166    assert(line >= 0 && line < s->inputs);
 167    s->input[line] = value;
 168}
 169
 170static void max111x_class_init(ObjectClass *klass, void *data)
 171{
 172    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 173
 174    k->transfer = max111x_transfer;
 175}
 176
 177static const TypeInfo max111x_info = {
 178    .name          = TYPE_MAX_111X,
 179    .parent        = TYPE_SSI_SLAVE,
 180    .instance_size = sizeof(MAX111xState),
 181    .class_init    = max111x_class_init,
 182    .abstract      = true,
 183};
 184
 185static void max1110_class_init(ObjectClass *klass, void *data)
 186{
 187    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 188
 189    k->realize = max1110_realize;
 190}
 191
 192static const TypeInfo max1110_info = {
 193    .name          = TYPE_MAX_1110,
 194    .parent        = TYPE_MAX_111X,
 195    .class_init    = max1110_class_init,
 196};
 197
 198static void max1111_class_init(ObjectClass *klass, void *data)
 199{
 200    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 201
 202    k->realize = max1111_realize;
 203}
 204
 205static const TypeInfo max1111_info = {
 206    .name          = TYPE_MAX_1111,
 207    .parent        = TYPE_MAX_111X,
 208    .class_init    = max1111_class_init,
 209};
 210
 211static void max111x_register_types(void)
 212{
 213    type_register_static(&max111x_info);
 214    type_register_static(&max1110_info);
 215    type_register_static(&max1111_info);
 216}
 217
 218type_init(max111x_register_types)
 219