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