1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include "qemu/osdep.h"
27#include "hw/isa/pc87312.h"
28#include "qapi/error.h"
29#include "qemu/error-report.h"
30#include "trace.h"
31
32
33#define REG_FER 0
34#define REG_FAR 1
35#define REG_PTR 2
36
37#define FER_PARALLEL_EN 0x01
38#define FER_UART1_EN 0x02
39#define FER_UART2_EN 0x04
40#define FER_FDC_EN 0x08
41#define FER_FDC_4 0x10
42#define FER_FDC_ADDR 0x20
43#define FER_IDE_EN 0x40
44#define FER_IDE_ADDR 0x80
45
46#define FAR_PARALLEL_ADDR 0x03
47#define FAR_UART1_ADDR 0x0C
48#define FAR_UART2_ADDR 0x30
49#define FAR_UART_3_4 0xC0
50
51#define PTR_POWER_DOWN 0x01
52#define PTR_CLOCK_DOWN 0x02
53#define PTR_PWDN 0x04
54#define PTR_IRQ_5_7 0x08
55#define PTR_UART1_TEST 0x10
56#define PTR_UART2_TEST 0x20
57#define PTR_LOCK_CONF 0x40
58#define PTR_EPP_MODE 0x80
59
60
61
62
63static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
64{
65 PC87312State *s = PC87312(sio);
66 return index ? false : s->regs[REG_FER] & FER_PARALLEL_EN;
67}
68
69static const uint16_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
70
71static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
72{
73 PC87312State *s = PC87312(sio);
74 return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
75}
76
77static const unsigned int parallel_irq[] = { 5, 7, 5, 0 };
78
79static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
80{
81 PC87312State *s = PC87312(sio);
82 int idx;
83 idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
84 if (idx == 0) {
85 return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
86 } else {
87 return parallel_irq[idx];
88 }
89}
90
91
92
93
94static const uint16_t uart_base[2][4] = {
95 { 0x3e8, 0x338, 0x2e8, 0x220 },
96 { 0x2e8, 0x238, 0x2e0, 0x228 }
97};
98
99static uint16_t get_uart_iobase(ISASuperIODevice *sio, uint8_t i)
100{
101 PC87312State *s = PC87312(sio);
102 int idx;
103 idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
104 if (idx == 0) {
105 return 0x3f8;
106 } else if (idx == 1) {
107 return 0x2f8;
108 } else {
109 return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
110 }
111}
112
113static unsigned int get_uart_irq(ISASuperIODevice *sio, uint8_t i)
114{
115 PC87312State *s = PC87312(sio);
116 int idx;
117 idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
118 return (idx & 1) ? 3 : 4;
119}
120
121static bool is_uart_enabled(ISASuperIODevice *sio, uint8_t i)
122{
123 PC87312State *s = PC87312(sio);
124 return s->regs[REG_FER] & (FER_UART1_EN << i);
125}
126
127
128
129
130static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
131{
132 PC87312State *s = PC87312(sio);
133 assert(!index);
134 return s->regs[REG_FER] & FER_FDC_EN;
135}
136
137static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
138{
139 PC87312State *s = PC87312(sio);
140 assert(!index);
141 return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
142}
143
144static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
145{
146 assert(!index);
147 return 6;
148}
149
150
151
152
153static bool is_ide_enabled(ISASuperIODevice *sio, uint8_t index)
154{
155 PC87312State *s = PC87312(sio);
156
157 return s->regs[REG_FER] & FER_IDE_EN;
158}
159
160static uint16_t get_ide_iobase(ISASuperIODevice *sio, uint8_t index)
161{
162 PC87312State *s = PC87312(sio);
163
164 if (index == 1) {
165 return get_ide_iobase(sio, 0) + 0x206;
166 }
167 return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
168}
169
170static unsigned int get_ide_irq(ISASuperIODevice *sio, uint8_t index)
171{
172 assert(index == 0);
173 return 14;
174}
175
176static void reconfigure_devices(PC87312State *s)
177{
178 error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
179 s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
180}
181
182static void pc87312_soft_reset(PC87312State *s)
183{
184 static const uint8_t fer_init[] = {
185 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
186 0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
187 0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
188 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
189 };
190 static const uint8_t far_init[] = {
191 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
192 0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
193 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
194 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
195 };
196 static const uint8_t ptr_init[] = {
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
201 };
202
203 s->read_id_step = 0;
204 s->selected_index = REG_FER;
205
206 s->regs[REG_FER] = fer_init[s->config & 0x1f];
207 s->regs[REG_FAR] = far_init[s->config & 0x1f];
208 s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
209}
210
211static void pc87312_hard_reset(PC87312State *s)
212{
213 pc87312_soft_reset(s);
214}
215
216static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
217 unsigned int size)
218{
219 PC87312State *s = opaque;
220
221 trace_pc87312_io_write(addr, val);
222
223 if ((addr & 1) == 0) {
224
225 s->read_id_step = 2;
226 s->selected_index = val;
227 } else {
228
229 if (s->selected_index < 3) {
230 s->regs[s->selected_index] = val;
231 reconfigure_devices(s);
232 }
233 }
234}
235
236static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
237{
238 PC87312State *s = opaque;
239 uint32_t val;
240
241 if ((addr & 1) == 0) {
242
243 if (s->read_id_step++ == 0) {
244 val = 0x88;
245 } else if (s->read_id_step++ == 1) {
246 val = 0;
247 } else {
248 val = s->selected_index;
249 }
250 } else {
251
252 if (s->selected_index < 3) {
253 val = s->regs[s->selected_index];
254 } else {
255
256 val = 0;
257 }
258 }
259
260 trace_pc87312_io_read(addr, val);
261 return val;
262}
263
264static const MemoryRegionOps pc87312_io_ops = {
265 .read = pc87312_io_read,
266 .write = pc87312_io_write,
267 .endianness = DEVICE_LITTLE_ENDIAN,
268 .valid = {
269 .min_access_size = 1,
270 .max_access_size = 1,
271 },
272};
273
274static int pc87312_post_load(void *opaque, int version_id)
275{
276 PC87312State *s = opaque;
277
278 reconfigure_devices(s);
279 return 0;
280}
281
282static void pc87312_reset(DeviceState *d)
283{
284 PC87312State *s = PC87312(d);
285
286 pc87312_soft_reset(s);
287}
288
289static void pc87312_realize(DeviceState *dev, Error **errp)
290{
291 PC87312State *s;
292 ISADevice *isa;
293 Error *local_err = NULL;
294
295 s = PC87312(dev);
296 isa = ISA_DEVICE(dev);
297 isa_register_ioport(isa, &s->io, s->iobase);
298 pc87312_hard_reset(s);
299
300 ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err);
301 if (local_err) {
302 error_propagate(errp, local_err);
303 return;
304 }
305}
306
307static void pc87312_initfn(Object *obj)
308{
309 PC87312State *s = PC87312(obj);
310
311 memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
312}
313
314static const VMStateDescription vmstate_pc87312 = {
315 .name = "pc87312",
316 .version_id = 1,
317 .minimum_version_id = 1,
318 .post_load = pc87312_post_load,
319 .fields = (VMStateField[]) {
320 VMSTATE_UINT8(read_id_step, PC87312State),
321 VMSTATE_UINT8(selected_index, PC87312State),
322 VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
323 VMSTATE_END_OF_LIST()
324 }
325};
326
327static Property pc87312_properties[] = {
328 DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398),
329 DEFINE_PROP_UINT8("config", PC87312State, config, 1),
330 DEFINE_PROP_END_OF_LIST()
331};
332
333static void pc87312_class_init(ObjectClass *klass, void *data)
334{
335 DeviceClass *dc = DEVICE_CLASS(klass);
336 ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
337
338 sc->parent_realize = dc->realize;
339 dc->realize = pc87312_realize;
340 dc->reset = pc87312_reset;
341 dc->vmsd = &vmstate_pc87312;
342 dc->props = pc87312_properties;
343
344 sc->parallel = (ISASuperIOFuncs){
345 .count = 1,
346 .is_enabled = is_parallel_enabled,
347 .get_iobase = get_parallel_iobase,
348 .get_irq = get_parallel_irq,
349 };
350 sc->serial = (ISASuperIOFuncs){
351 .count = 2,
352 .is_enabled = is_uart_enabled,
353 .get_iobase = get_uart_iobase,
354 .get_irq = get_uart_irq,
355 };
356 sc->floppy = (ISASuperIOFuncs){
357 .count = 1,
358 .is_enabled = is_fdc_enabled,
359 .get_iobase = get_fdc_iobase,
360 .get_irq = get_fdc_irq,
361 };
362 sc->ide = (ISASuperIOFuncs){
363 .count = 1,
364 .is_enabled = is_ide_enabled,
365 .get_iobase = get_ide_iobase,
366 .get_irq = get_ide_irq,
367 };
368}
369
370static const TypeInfo pc87312_type_info = {
371 .name = TYPE_PC87312_SUPERIO,
372 .parent = TYPE_ISA_SUPERIO,
373 .instance_size = sizeof(PC87312State),
374 .instance_init = pc87312_initfn,
375 .class_init = pc87312_class_init,
376
377};
378
379static void pc87312_register_types(void)
380{
381 type_register_static(&pc87312_type_info);
382}
383
384type_init(pc87312_register_types)
385