]> Git Repo - qemu.git/blob - hw/pc87312.c
38af4c1d107c1731f3a70bb9bfbe7f7495820bc2
[qemu.git] / hw / pc87312.c
1 /*
2  * QEMU National Semiconductor PC87312 (Super I/O)
3  *
4  * Copyright (c) 2010-2012 Herve Poussineau
5  * Copyright (c) 2011-2012 Andreas Färber
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25
26 #include "pc87312.h"
27 #include "sysemu/blockdev.h"
28 #include "sysemu/sysemu.h"
29 #include "char/char.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 /* Parallel port */
62
63 static inline bool is_parallel_enabled(PC87312State *s)
64 {
65     return s->regs[REG_FER] & FER_PARALLEL_EN;
66 }
67
68 static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
69
70 static inline uint32_t get_parallel_iobase(PC87312State *s)
71 {
72     return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
73 }
74
75 static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
76
77 static inline uint32_t get_parallel_irq(PC87312State *s)
78 {
79     int idx;
80     idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
81     if (idx == 0) {
82         return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
83     } else {
84         return parallel_irq[idx];
85     }
86 }
87
88 static inline bool is_parallel_epp(PC87312State *s)
89 {
90     return s->regs[REG_PTR] & PTR_EPP_MODE;
91 }
92
93
94 /* UARTs */
95
96 static const uint32_t uart_base[2][4] = {
97     { 0x3e8, 0x338, 0x2e8, 0x220 },
98     { 0x2e8, 0x238, 0x2e0, 0x228 }
99 };
100
101 static inline uint32_t get_uart_iobase(PC87312State *s, int i)
102 {
103     int idx;
104     idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
105     if (idx == 0) {
106         return 0x3f8;
107     } else if (idx == 1) {
108         return 0x2f8;
109     } else {
110         return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
111     }
112 }
113
114 static inline uint32_t get_uart_irq(PC87312State *s, int i)
115 {
116     int idx;
117     idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
118     return (idx & 1) ? 3 : 4;
119 }
120
121 static inline bool is_uart_enabled(PC87312State *s, int i)
122 {
123     return s->regs[REG_FER] & (FER_UART1_EN << i);
124 }
125
126
127 /* Floppy controller */
128
129 static inline bool is_fdc_enabled(PC87312State *s)
130 {
131     return s->regs[REG_FER] & FER_FDC_EN;
132 }
133
134 static inline uint32_t get_fdc_iobase(PC87312State *s)
135 {
136     return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
137 }
138
139
140 /* IDE controller */
141
142 static inline bool is_ide_enabled(PC87312State *s)
143 {
144     return s->regs[REG_FER] & FER_IDE_EN;
145 }
146
147 static inline uint32_t get_ide_iobase(PC87312State *s)
148 {
149     return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
150 }
151
152
153 static void reconfigure_devices(PC87312State *s)
154 {
155     error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
156                  s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
157 }
158
159 static void pc87312_soft_reset(PC87312State *s)
160 {
161     static const uint8_t fer_init[] = {
162         0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
163         0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
164         0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
165         0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
166     };
167     static const uint8_t far_init[] = {
168         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
169         0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
170         0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
171         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
172     };
173     static const uint8_t ptr_init[] = {
174         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
178     };
179
180     s->read_id_step = 0;
181     s->selected_index = REG_FER;
182
183     s->regs[REG_FER] = fer_init[s->config & 0x1f];
184     s->regs[REG_FAR] = far_init[s->config & 0x1f];
185     s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
186 }
187
188 static void pc87312_hard_reset(PC87312State *s)
189 {
190     pc87312_soft_reset(s);
191 }
192
193 static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
194                              unsigned int size)
195 {
196     PC87312State *s = opaque;
197
198     trace_pc87312_io_write(addr, val);
199
200     if ((addr & 1) == 0) {
201         /* Index register */
202         s->read_id_step = 2;
203         s->selected_index = val;
204     } else {
205         /* Data register */
206         if (s->selected_index < 3) {
207             s->regs[s->selected_index] = val;
208             reconfigure_devices(s);
209         }
210     }
211 }
212
213 static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
214 {
215     PC87312State *s = opaque;
216     uint32_t val;
217
218     if ((addr & 1) == 0) {
219         /* Index register */
220         if (s->read_id_step++ == 0) {
221             val = 0x88;
222         } else if (s->read_id_step++ == 1) {
223             val = 0;
224         } else {
225             val = s->selected_index;
226         }
227     } else {
228         /* Data register */
229         if (s->selected_index < 3) {
230             val = s->regs[s->selected_index];
231         } else {
232             /* Invalid selected index */
233             val = 0;
234         }
235     }
236
237     trace_pc87312_io_read(addr, val);
238     return val;
239 }
240
241 static const MemoryRegionOps pc87312_io_ops = {
242     .read  = pc87312_io_read,
243     .write = pc87312_io_write,
244     .endianness = DEVICE_LITTLE_ENDIAN,
245     .valid = {
246         .min_access_size = 1,
247         .max_access_size = 1,
248     },
249 };
250
251 static int pc87312_post_load(void *opaque, int version_id)
252 {
253     PC87312State *s = opaque;
254
255     reconfigure_devices(s);
256     return 0;
257 }
258
259 static void pc87312_reset(DeviceState *d)
260 {
261     PC87312State *s = PC87312(d);
262
263     pc87312_soft_reset(s);
264 }
265
266 static int pc87312_init(ISADevice *dev)
267 {
268     PC87312State *s;
269     DeviceState *d;
270     ISADevice *isa;
271     ISABus *bus;
272     CharDriverState *chr;
273     DriveInfo *drive;
274     char name[5];
275     int i;
276
277     s = PC87312(dev);
278     bus = isa_bus_from_device(dev);
279     pc87312_hard_reset(s);
280     isa_register_ioport(dev, &s->io, s->iobase);
281
282     if (is_parallel_enabled(s)) {
283         chr = parallel_hds[0];
284         if (chr == NULL) {
285             chr = qemu_chr_new("par0", "null", NULL);
286         }
287         isa = isa_create(bus, "isa-parallel");
288         d = DEVICE(isa);
289         qdev_prop_set_uint32(d, "index", 0);
290         qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
291         qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
292         qdev_prop_set_chr(d, "chardev", chr);
293         qdev_init_nofail(d);
294         s->parallel.dev = isa;
295         trace_pc87312_info_parallel(get_parallel_iobase(s),
296                                     get_parallel_irq(s));
297     }
298
299     for (i = 0; i < 2; i++) {
300         if (is_uart_enabled(s, i)) {
301             chr = serial_hds[i];
302             if (chr == NULL) {
303                 snprintf(name, sizeof(name), "ser%d", i);
304                 chr = qemu_chr_new(name, "null", NULL);
305             }
306             isa = isa_create(bus, "isa-serial");
307             d = DEVICE(isa);
308             qdev_prop_set_uint32(d, "index", i);
309             qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
310             qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
311             qdev_prop_set_chr(d, "chardev", chr);
312             qdev_init_nofail(d);
313             s->uart[i].dev = isa;
314             trace_pc87312_info_serial(i, get_uart_iobase(s, i),
315                                       get_uart_irq(s, i));
316         }
317     }
318
319     if (is_fdc_enabled(s)) {
320         isa = isa_create(bus, "isa-fdc");
321         d = DEVICE(isa);
322         qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
323         qdev_prop_set_uint32(d, "irq", 6);
324         drive = drive_get(IF_FLOPPY, 0, 0);
325         if (drive != NULL) {
326             qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv);
327         }
328         drive = drive_get(IF_FLOPPY, 0, 1);
329         if (drive != NULL) {
330             qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv);
331         }
332         qdev_init_nofail(d);
333         s->fdc.dev = isa;
334         trace_pc87312_info_floppy(get_fdc_iobase(s));
335     }
336
337     if (is_ide_enabled(s)) {
338         isa = isa_create(bus, "isa-ide");
339         d = DEVICE(isa);
340         qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
341         qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
342         qdev_prop_set_uint32(d, "irq", 14);
343         qdev_init_nofail(d);
344         s->ide.dev = isa;
345         trace_pc87312_info_ide(get_ide_iobase(s));
346     }
347
348     return 0;
349 }
350
351 static void pc87312_initfn(Object *obj)
352 {
353     PC87312State *s = PC87312(obj);
354
355     memory_region_init_io(&s->io, &pc87312_io_ops, s, "pc87312", 2);
356 }
357
358 static const VMStateDescription vmstate_pc87312 = {
359     .name = "pc87312",
360     .version_id = 1,
361     .minimum_version_id = 1,
362     .post_load = pc87312_post_load,
363     .fields = (VMStateField[]) {
364         VMSTATE_UINT8(read_id_step, PC87312State),
365         VMSTATE_UINT8(selected_index, PC87312State),
366         VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
367         VMSTATE_END_OF_LIST()
368     }
369 };
370
371 static Property pc87312_properties[] = {
372     DEFINE_PROP_HEX32("iobase", PC87312State, iobase, 0x398),
373     DEFINE_PROP_UINT8("config", PC87312State, config, 1),
374     DEFINE_PROP_END_OF_LIST()
375 };
376
377 static void pc87312_class_init(ObjectClass *klass, void *data)
378 {
379     DeviceClass *dc = DEVICE_CLASS(klass);
380     ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
381
382     ic->init = pc87312_init;
383     dc->reset = pc87312_reset;
384     dc->vmsd = &vmstate_pc87312;
385     dc->props = pc87312_properties;
386 }
387
388 static const TypeInfo pc87312_type_info = {
389     .name          = TYPE_PC87312,
390     .parent        = TYPE_ISA_DEVICE,
391     .instance_size = sizeof(PC87312State),
392     .instance_init = pc87312_initfn,
393     .class_init    = pc87312_class_init,
394 };
395
396 static void pc87312_register_types(void)
397 {
398     type_register_static(&pc87312_type_info);
399 }
400
401 type_init(pc87312_register_types)
This page took 0.037218 seconds and 2 git commands to generate.