]> Git Repo - qemu.git/blob - hw/pc87312.c
Merge remote-tracking branch 'qmp/queue/qmp' into staging
[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 regs[REG_FER]
38 #define FAR regs[REG_FAR]
39 #define PTR regs[REG_PTR]
40
41 #define FER_PARALLEL_EN   0x01
42 #define FER_UART1_EN      0x02
43 #define FER_UART2_EN      0x04
44 #define FER_FDC_EN        0x08
45 #define FER_FDC_4         0x10
46 #define FER_FDC_ADDR      0x20
47 #define FER_IDE_EN        0x40
48 #define FER_IDE_ADDR      0x80
49
50 #define FAR_PARALLEL_ADDR 0x03
51 #define FAR_UART1_ADDR    0x0C
52 #define FAR_UART2_ADDR    0x30
53 #define FAR_UART_3_4      0xC0
54
55 #define PTR_POWER_DOWN    0x01
56 #define PTR_CLOCK_DOWN    0x02
57 #define PTR_PWDN          0x04
58 #define PTR_IRQ_5_7       0x08
59 #define PTR_UART1_TEST    0x10
60 #define PTR_UART2_TEST    0x20
61 #define PTR_LOCK_CONF     0x40
62 #define PTR_EPP_MODE      0x80
63
64
65 /* Parallel port */
66
67 static inline bool is_parallel_enabled(PC87312State *s)
68 {
69     return s->FER & FER_PARALLEL_EN;
70 }
71
72 static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
73
74 static inline uint32_t get_parallel_iobase(PC87312State *s)
75 {
76     return parallel_base[s->FAR & FAR_PARALLEL_ADDR];
77 }
78
79 static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
80
81 static inline uint32_t get_parallel_irq(PC87312State *s)
82 {
83     int idx;
84     idx = (s->FAR & FAR_PARALLEL_ADDR);
85     if (idx == 0) {
86         return (s->PTR & PTR_IRQ_5_7) ? 7 : 5;
87     } else {
88         return parallel_irq[idx];
89     }
90 }
91
92 static inline bool is_parallel_epp(PC87312State *s)
93 {
94     return s->PTR & PTR_EPP_MODE;
95 }
96
97
98 /* UARTs */
99
100 static const uint32_t uart_base[2][4] = {
101     { 0x3e8, 0x338, 0x2e8, 0x220 },
102     { 0x2e8, 0x238, 0x2e0, 0x228 }
103 };
104
105 static inline uint32_t get_uart_iobase(PC87312State *s, int i)
106 {
107     int idx;
108     idx = (s->FAR >> (2 * i + 2)) & 0x3;
109     if (idx == 0) {
110         return 0x3f8;
111     } else if (idx == 1) {
112         return 0x2f8;
113     } else {
114         return uart_base[idx & 1][(s->FAR & FAR_UART_3_4) >> 6];
115     }
116 }
117
118 static inline uint32_t get_uart_irq(PC87312State *s, int i)
119 {
120     int idx;
121     idx = (s->FAR >> (2 * i + 2)) & 0x3;
122     return (idx & 1) ? 3 : 4;
123 }
124
125 static inline bool is_uart_enabled(PC87312State *s, int i)
126 {
127     return s->FER & (FER_UART1_EN << i);
128 }
129
130
131 /* Floppy controller */
132
133 static inline bool is_fdc_enabled(PC87312State *s)
134 {
135     return s->FER & FER_FDC_EN;
136 }
137
138 static inline uint32_t get_fdc_iobase(PC87312State *s)
139 {
140     return (s->FER & FER_FDC_ADDR) ? 0x370 : 0x3f0;
141 }
142
143
144 /* IDE controller */
145
146 static inline bool is_ide_enabled(PC87312State *s)
147 {
148     return s->FER & FER_IDE_EN;
149 }
150
151 static inline uint32_t get_ide_iobase(PC87312State *s)
152 {
153     return (s->FER & FER_IDE_ADDR) ? 0x170 : 0x1f0;
154 }
155
156
157 static void reconfigure_devices(PC87312State *s)
158 {
159     error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
160                  s->FER, s->FAR, s->PTR);
161 }
162
163 static void pc87312_soft_reset(PC87312State *s)
164 {
165     static const uint8_t fer_init[] = {
166         0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
167         0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
168         0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
169         0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
170     };
171     static const uint8_t far_init[] = {
172         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
173         0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
174         0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
175         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
176     };
177     static const uint8_t ptr_init[] = {
178         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
182     };
183
184     s->read_id_step = 0;
185     s->selected_index = REG_FER;
186
187     s->FER = fer_init[s->config & 0x1f];
188     s->FAR = far_init[s->config & 0x1f];
189     s->PTR = ptr_init[s->config & 0x1f];
190 }
191
192 static void pc87312_hard_reset(PC87312State *s)
193 {
194     pc87312_soft_reset(s);
195 }
196
197 static void pc87312_ioport_write(void *opaque, uint32_t addr, uint32_t val)
198 {
199     PC87312State *s = opaque;
200
201     trace_pc87312_io_write(addr, val);
202
203     if ((addr & 1) == 0) {
204         /* Index register */
205         s->read_id_step = 2;
206         s->selected_index = val;
207     } else {
208         /* Data register */
209         if (s->selected_index < 3) {
210             s->regs[s->selected_index] = val;
211             reconfigure_devices(s);
212         }
213     }
214 }
215
216 static uint32_t pc87312_ioport_read(void *opaque, uint32_t addr)
217 {
218     PC87312State *s = opaque;
219     uint32_t val;
220
221     if ((addr & 1) == 0) {
222         /* Index register */
223         if (s->read_id_step++ == 0) {
224             val = 0x88;
225         } else if (s->read_id_step++ == 1) {
226             val = 0;
227         } else {
228             val = s->selected_index;
229         }
230     } else {
231         /* Data register */
232         if (s->selected_index < 3) {
233             val = s->regs[s->selected_index];
234         } else {
235             /* Invalid selected index */
236             val = 0;
237         }
238     }
239
240     trace_pc87312_io_read(addr, val);
241     return val;
242 }
243
244 static int pc87312_post_load(void *opaque, int version_id)
245 {
246     PC87312State *s = opaque;
247
248     reconfigure_devices(s);
249     return 0;
250 }
251
252 static void pc87312_reset(DeviceState *d)
253 {
254     PC87312State *s = PC87312(d);
255
256     pc87312_soft_reset(s);
257 }
258
259 static int pc87312_init(ISADevice *dev)
260 {
261     PC87312State *s;
262     DeviceState *d;
263     ISADevice *isa;
264     ISABus *bus;
265     CharDriverState *chr;
266     DriveInfo *drive;
267     char name[5];
268     int i;
269
270     s = PC87312(dev);
271     bus = isa_bus_from_device(dev);
272     pc87312_hard_reset(s);
273
274     if (is_parallel_enabled(s)) {
275         chr = parallel_hds[0];
276         if (chr == NULL) {
277             chr = qemu_chr_new("par0", "null", NULL);
278         }
279         isa = isa_create(bus, "isa-parallel");
280         d = DEVICE(isa);
281         qdev_prop_set_uint32(d, "index", 0);
282         qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
283         qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
284         qdev_prop_set_chr(d, "chardev", chr);
285         qdev_init_nofail(d);
286         s->parallel.dev = isa;
287         trace_pc87312_info_parallel(get_parallel_iobase(s),
288                                     get_parallel_irq(s));
289     }
290
291     for (i = 0; i < 2; i++) {
292         if (is_uart_enabled(s, i)) {
293             chr = serial_hds[i];
294             if (chr == NULL) {
295                 snprintf(name, sizeof(name), "ser%d", i);
296                 chr = qemu_chr_new(name, "null", NULL);
297             }
298             isa = isa_create(bus, "isa-serial");
299             d = DEVICE(isa);
300             qdev_prop_set_uint32(d, "index", i);
301             qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
302             qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
303             qdev_prop_set_chr(d, "chardev", chr);
304             qdev_init_nofail(d);
305             s->uart[i].dev = isa;
306             trace_pc87312_info_serial(i, get_uart_iobase(s, i),
307                                       get_uart_irq(s, i));
308         }
309     }
310
311     if (is_fdc_enabled(s)) {
312         isa = isa_create(bus, "isa-fdc");
313         d = DEVICE(isa);
314         qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
315         qdev_prop_set_uint32(d, "irq", 6);
316         drive = drive_get(IF_FLOPPY, 0, 0);
317         if (drive != NULL) {
318             qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv);
319         }
320         drive = drive_get(IF_FLOPPY, 0, 1);
321         if (drive != NULL) {
322             qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv);
323         }
324         qdev_init_nofail(d);
325         s->fdc.dev = isa;
326         trace_pc87312_info_floppy(get_fdc_iobase(s));
327     }
328
329     if (is_ide_enabled(s)) {
330         isa = isa_create(bus, "isa-ide");
331         d = DEVICE(isa);
332         qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
333         qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
334         qdev_prop_set_uint32(d, "irq", 14);
335         qdev_init_nofail(d);
336         s->ide.dev = isa;
337         trace_pc87312_info_ide(get_ide_iobase(s));
338     }
339
340     register_ioport_write(s->iobase, 2, 1, pc87312_ioport_write, s);
341     register_ioport_read(s->iobase, 2, 1, pc87312_ioport_read, s);
342     return 0;
343 }
344
345 static const VMStateDescription vmstate_pc87312 = {
346     .name = "pc87312",
347     .version_id = 1,
348     .minimum_version_id = 1,
349     .post_load = pc87312_post_load,
350     .fields = (VMStateField[]) {
351         VMSTATE_UINT8(read_id_step, PC87312State),
352         VMSTATE_UINT8(selected_index, PC87312State),
353         VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
354         VMSTATE_END_OF_LIST()
355     }
356 };
357
358 static Property pc87312_properties[] = {
359     DEFINE_PROP_HEX32("iobase", PC87312State, iobase, 0x398),
360     DEFINE_PROP_UINT8("config", PC87312State, config, 1),
361     DEFINE_PROP_END_OF_LIST()
362 };
363
364 static void pc87312_class_init(ObjectClass *klass, void *data)
365 {
366     DeviceClass *dc = DEVICE_CLASS(klass);
367     ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
368
369     ic->init = pc87312_init;
370     dc->reset = pc87312_reset;
371     dc->vmsd = &vmstate_pc87312;
372     dc->props = pc87312_properties;
373 }
374
375 static const TypeInfo pc87312_type_info = {
376     .name          = TYPE_PC87312,
377     .parent        = TYPE_ISA_DEVICE,
378     .instance_size = sizeof(PC87312State),
379     .class_init    = pc87312_class_init,
380 };
381
382 static void pc87312_register_types(void)
383 {
384     type_register_static(&pc87312_type_info);
385 }
386
387 type_init(pc87312_register_types)
This page took 0.051357 seconds and 4 git commands to generate.