]>
Commit | Line | Data |
---|---|---|
c824cacd AZ |
1 | /* |
2 | * Maxim MAX1110/1111 ADC chip emulation. | |
3 | * | |
4 | * Copyright (c) 2006 Openedhand Ltd. | |
5 | * Written by Andrzej Zaborowski <[email protected]> | |
6 | * | |
7 | * This code is licensed under the GNU GPLv2. | |
6b620ca3 PB |
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. | |
c824cacd AZ |
11 | */ |
12 | ||
83c9f4ca | 13 | #include "hw/ssi.h" |
c824cacd | 14 | |
a984a69e | 15 | typedef struct { |
7c77b654 PC |
16 | SSISlave parent_obj; |
17 | ||
c824cacd AZ |
18 | qemu_irq interrupt; |
19 | uint8_t tb1, rb2, rb3; | |
20 | int cycle; | |
21 | ||
54d970d1 | 22 | uint8_t input[8]; |
c824cacd | 23 | int inputs, com; |
a984a69e | 24 | } MAX111xState; |
c824cacd | 25 | |
5ef4a1c3 PC |
26 | #define TYPE_MAX_111X "max111x" |
27 | ||
7c77b654 PC |
28 | #define MAX_111X(obj) \ |
29 | OBJECT_CHECK(MAX111xState, (obj), TYPE_MAX_111X) | |
30 | ||
5ef4a1c3 PC |
31 | #define TYPE_MAX_1110 "max1110" |
32 | #define TYPE_MAX_1111 "max1111" | |
33 | ||
c824cacd AZ |
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 | ||
a984a69e | 49 | static uint32_t max111x_read(MAX111xState *s) |
c824cacd | 50 | { |
c824cacd AZ |
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 */ | |
a984a69e | 65 | static void max111x_write(MAX111xState *s, uint32_t value) |
c824cacd | 66 | { |
c824cacd AZ |
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 | ||
a984a69e PB |
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 | { | |
7c77b654 | 104 | MAX111xState *s = MAX_111X(dev); |
a984a69e PB |
105 | max111x_write(s, value); |
106 | return max111x_read(s); | |
c824cacd AZ |
107 | } |
108 | ||
38cb3aa9 JQ |
109 | static const VMStateDescription vmstate_max111x = { |
110 | .name = "max111x", | |
66530953 PC |
111 | .version_id = 1, |
112 | .minimum_version_id = 1, | |
8f1e884b | 113 | .fields = (VMStateField[]) { |
7c77b654 | 114 | VMSTATE_SSI_SLAVE(parent_obj, MAX111xState), |
38cb3aa9 JQ |
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 | }; | |
aa941b94 | 125 | |
1a7d9ee6 | 126 | static int max111x_init(SSISlave *d, int inputs) |
c824cacd | 127 | { |
1a7d9ee6 | 128 | DeviceState *dev = DEVICE(d); |
7c77b654 | 129 | MAX111xState *s = MAX_111X(dev); |
c824cacd | 130 | |
1a7d9ee6 | 131 | qdev_init_gpio_out(dev, &s->interrupt, 1); |
c824cacd | 132 | |
a984a69e | 133 | s->inputs = inputs; |
c824cacd AZ |
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; | |
aa941b94 | 144 | |
1a7d9ee6 | 145 | vmstate_register(dev, -1, &vmstate_max111x, s); |
81a322d4 | 146 | return 0; |
c824cacd AZ |
147 | } |
148 | ||
81a322d4 | 149 | static int max1110_init(SSISlave *dev) |
c824cacd | 150 | { |
81a322d4 | 151 | return max111x_init(dev, 8); |
c824cacd AZ |
152 | } |
153 | ||
81a322d4 | 154 | static int max1111_init(SSISlave *dev) |
c824cacd | 155 | { |
81a322d4 | 156 | return max111x_init(dev, 4); |
c824cacd AZ |
157 | } |
158 | ||
a984a69e | 159 | void max111x_set_input(DeviceState *dev, int line, uint8_t value) |
c824cacd | 160 | { |
7c77b654 | 161 | MAX111xState *s = MAX_111X(dev); |
a984a69e | 162 | assert(line >= 0 && line < s->inputs); |
c824cacd AZ |
163 | s->input[line] = value; |
164 | } | |
a984a69e | 165 | |
5ef4a1c3 | 166 | static void max111x_class_init(ObjectClass *klass, void *data) |
cd6c4cf2 AL |
167 | { |
168 | SSISlaveClass *k = SSI_SLAVE_CLASS(klass); | |
169 | ||
cd6c4cf2 AL |
170 | k->transfer = max111x_transfer; |
171 | } | |
172 | ||
5ef4a1c3 PC |
173 | static const TypeInfo max111x_info = { |
174 | .name = TYPE_MAX_111X, | |
39bffca2 AL |
175 | .parent = TYPE_SSI_SLAVE, |
176 | .instance_size = sizeof(MAX111xState), | |
5ef4a1c3 PC |
177 | .class_init = max111x_class_init, |
178 | .abstract = true, | |
179 | }; | |
180 | ||
181 | static void max1110_class_init(ObjectClass *klass, void *data) | |
182 | { | |
183 | SSISlaveClass *k = SSI_SLAVE_CLASS(klass); | |
184 | ||
185 | k->init = max1110_init; | |
186 | } | |
187 | ||
188 | static const TypeInfo max1110_info = { | |
189 | .name = TYPE_MAX_1110, | |
190 | .parent = TYPE_MAX_111X, | |
39bffca2 | 191 | .class_init = max1110_class_init, |
a984a69e PB |
192 | }; |
193 | ||
cd6c4cf2 AL |
194 | static void max1111_class_init(ObjectClass *klass, void *data) |
195 | { | |
196 | SSISlaveClass *k = SSI_SLAVE_CLASS(klass); | |
197 | ||
198 | k->init = max1111_init; | |
cd6c4cf2 AL |
199 | } |
200 | ||
8c43a6f0 | 201 | static const TypeInfo max1111_info = { |
5ef4a1c3 PC |
202 | .name = TYPE_MAX_1111, |
203 | .parent = TYPE_MAX_111X, | |
39bffca2 | 204 | .class_init = max1111_class_init, |
a984a69e PB |
205 | }; |
206 | ||
83f7d43a | 207 | static void max111x_register_types(void) |
a984a69e | 208 | { |
5ef4a1c3 | 209 | type_register_static(&max111x_info); |
39bffca2 AL |
210 | type_register_static(&max1110_info); |
211 | type_register_static(&max1111_info); | |
a984a69e PB |
212 | } |
213 | ||
83f7d43a | 214 | type_init(max111x_register_types) |