qemu/hw/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
  10#include "ssi.h"
  11
  12typedef struct {
  13    SSISlave ssidev;
  14    qemu_irq interrupt;
  15    uint8_t tb1, rb2, rb3;
  16    int cycle;
  17
  18    uint8_t input[8];
  19    int inputs, com;
  20} MAX111xState;
  21
  22/* Control-byte bitfields */
  23#define CB_PD0          (1 << 0)
  24#define CB_PD1          (1 << 1)
  25#define CB_SGL          (1 << 2)
  26#define CB_UNI          (1 << 3)
  27#define CB_SEL0         (1 << 4)
  28#define CB_SEL1         (1 << 5)
  29#define CB_SEL2         (1 << 6)
  30#define CB_START        (1 << 7)
  31
  32#define CHANNEL_NUM(v, b0, b1, b2)      \
  33                        ((((v) >> (2 + (b0))) & 4) |    \
  34                         (((v) >> (3 + (b1))) & 2) |    \
  35                         (((v) >> (4 + (b2))) & 1))
  36
  37static uint32_t max111x_read(MAX111xState *s)
  38{
  39    if (!s->tb1)
  40        return 0;
  41
  42    switch (s->cycle ++) {
  43    case 1:
  44        return s->rb2;
  45    case 2:
  46        return s->rb3;
  47    }
  48
  49    return 0;
  50}
  51
  52/* Interpret a control-byte */
  53static void max111x_write(MAX111xState *s, uint32_t value)
  54{
  55    int measure, chan;
  56
  57    /* Ignore the value if START bit is zero */
  58    if (!(value & CB_START))
  59        return;
  60
  61    s->cycle = 0;
  62
  63    if (!(value & CB_PD1)) {
  64        s->tb1 = 0;
  65        return;
  66    }
  67
  68    s->tb1 = value;
  69
  70    if (s->inputs == 8)
  71        chan = CHANNEL_NUM(value, 1, 0, 2);
  72    else
  73        chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
  74
  75    if (value & CB_SGL)
  76        measure = s->input[chan] - s->com;
  77    else
  78        measure = s->input[chan] - s->input[chan ^ 1];
  79
  80    if (!(value & CB_UNI))
  81        measure ^= 0x80;
  82
  83    s->rb2 = (measure >> 2) & 0x3f;
  84    s->rb3 = (measure << 6) & 0xc0;
  85
  86    /* FIXME: When should the IRQ be lowered?  */
  87    qemu_irq_raise(s->interrupt);
  88}
  89
  90static uint32_t max111x_transfer(SSISlave *dev, uint32_t value)
  91{
  92    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
  93    max111x_write(s, value);
  94    return max111x_read(s);
  95}
  96
  97static const VMStateDescription vmstate_max111x = {
  98    .name = "max111x",
  99    .version_id = 0,
 100    .minimum_version_id = 0,
 101    .minimum_version_id_old = 0,
 102    .fields      = (VMStateField[]) {
 103        VMSTATE_UINT8(tb1, MAX111xState),
 104        VMSTATE_UINT8(rb2, MAX111xState),
 105        VMSTATE_UINT8(rb3, MAX111xState),
 106        VMSTATE_INT32_EQUAL(inputs, MAX111xState),
 107        VMSTATE_INT32(com, MAX111xState),
 108        VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs,
 109                                   vmstate_info_uint8, uint8_t),
 110        VMSTATE_END_OF_LIST()
 111    }
 112};
 113
 114static int max111x_init(SSISlave *dev, int inputs)
 115{
 116    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
 117
 118    qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
 119
 120    s->inputs = inputs;
 121    /* TODO: add a user interface for setting these */
 122    s->input[0] = 0xf0;
 123    s->input[1] = 0xe0;
 124    s->input[2] = 0xd0;
 125    s->input[3] = 0xc0;
 126    s->input[4] = 0xb0;
 127    s->input[5] = 0xa0;
 128    s->input[6] = 0x90;
 129    s->input[7] = 0x80;
 130    s->com = 0;
 131
 132    vmstate_register(&dev->qdev, -1, &vmstate_max111x, s);
 133    return 0;
 134}
 135
 136static int max1110_init(SSISlave *dev)
 137{
 138    return max111x_init(dev, 8);
 139}
 140
 141static int max1111_init(SSISlave *dev)
 142{
 143    return max111x_init(dev, 4);
 144}
 145
 146void max111x_set_input(DeviceState *dev, int line, uint8_t value)
 147{
 148    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, SSI_SLAVE_FROM_QDEV(dev));
 149    assert(line >= 0 && line < s->inputs);
 150    s->input[line] = value;
 151}
 152
 153static SSISlaveInfo max1110_info = {
 154    .qdev.name = "max1110",
 155    .qdev.size = sizeof(MAX111xState),
 156    .init = max1110_init,
 157    .transfer = max111x_transfer
 158};
 159
 160static SSISlaveInfo max1111_info = {
 161    .qdev.name = "max1111",
 162    .qdev.size = sizeof(MAX111xState),
 163    .init = max1111_init,
 164    .transfer = max111x_transfer
 165};
 166
 167static void max111x_register_devices(void)
 168{
 169    ssi_register_slave(&max1110_info);
 170    ssi_register_slave(&max1111_info);
 171}
 172
 173device_init(max111x_register_devices)
 174