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