xref: /qemu/hw/isa/pc87312.c (revision 7a4e543d)
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 "qemu/osdep.h"
27 #include "hw/isa/pc87312.h"
28 #include "qemu/error-report.h"
29 #include "sysemu/block-backend.h"
30 #include "sysemu/blockdev.h"
31 #include "sysemu/sysemu.h"
32 #include "sysemu/char.h"
33 #include "trace.h"
34 
35 
36 #define REG_FER 0
37 #define REG_FAR 1
38 #define REG_PTR 2
39 
40 #define FER_PARALLEL_EN   0x01
41 #define FER_UART1_EN      0x02
42 #define FER_UART2_EN      0x04
43 #define FER_FDC_EN        0x08
44 #define FER_FDC_4         0x10
45 #define FER_FDC_ADDR      0x20
46 #define FER_IDE_EN        0x40
47 #define FER_IDE_ADDR      0x80
48 
49 #define FAR_PARALLEL_ADDR 0x03
50 #define FAR_UART1_ADDR    0x0C
51 #define FAR_UART2_ADDR    0x30
52 #define FAR_UART_3_4      0xC0
53 
54 #define PTR_POWER_DOWN    0x01
55 #define PTR_CLOCK_DOWN    0x02
56 #define PTR_PWDN          0x04
57 #define PTR_IRQ_5_7       0x08
58 #define PTR_UART1_TEST    0x10
59 #define PTR_UART2_TEST    0x20
60 #define PTR_LOCK_CONF     0x40
61 #define PTR_EPP_MODE      0x80
62 
63 
64 /* Parallel port */
65 
66 static inline bool is_parallel_enabled(PC87312State *s)
67 {
68     return s->regs[REG_FER] & FER_PARALLEL_EN;
69 }
70 
71 static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
72 
73 static inline uint32_t get_parallel_iobase(PC87312State *s)
74 {
75     return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
76 }
77 
78 static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
79 
80 static inline uint32_t get_parallel_irq(PC87312State *s)
81 {
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 /* UARTs */
93 
94 static const uint32_t uart_base[2][4] = {
95     { 0x3e8, 0x338, 0x2e8, 0x220 },
96     { 0x2e8, 0x238, 0x2e0, 0x228 }
97 };
98 
99 static inline uint32_t get_uart_iobase(PC87312State *s, int i)
100 {
101     int idx;
102     idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
103     if (idx == 0) {
104         return 0x3f8;
105     } else if (idx == 1) {
106         return 0x2f8;
107     } else {
108         return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
109     }
110 }
111 
112 static inline uint32_t get_uart_irq(PC87312State *s, int i)
113 {
114     int idx;
115     idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
116     return (idx & 1) ? 3 : 4;
117 }
118 
119 static inline bool is_uart_enabled(PC87312State *s, int i)
120 {
121     return s->regs[REG_FER] & (FER_UART1_EN << i);
122 }
123 
124 
125 /* Floppy controller */
126 
127 static inline bool is_fdc_enabled(PC87312State *s)
128 {
129     return s->regs[REG_FER] & FER_FDC_EN;
130 }
131 
132 static inline uint32_t get_fdc_iobase(PC87312State *s)
133 {
134     return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
135 }
136 
137 
138 /* IDE controller */
139 
140 static inline bool is_ide_enabled(PC87312State *s)
141 {
142     return s->regs[REG_FER] & FER_IDE_EN;
143 }
144 
145 static inline uint32_t get_ide_iobase(PC87312State *s)
146 {
147     return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
148 }
149 
150 
151 static void reconfigure_devices(PC87312State *s)
152 {
153     error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
154                  s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
155 }
156 
157 static void pc87312_soft_reset(PC87312State *s)
158 {
159     static const uint8_t fer_init[] = {
160         0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
161         0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
162         0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
163         0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
164     };
165     static const uint8_t far_init[] = {
166         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
167         0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
168         0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
169         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
170     };
171     static const uint8_t ptr_init[] = {
172         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
176     };
177 
178     s->read_id_step = 0;
179     s->selected_index = REG_FER;
180 
181     s->regs[REG_FER] = fer_init[s->config & 0x1f];
182     s->regs[REG_FAR] = far_init[s->config & 0x1f];
183     s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
184 }
185 
186 static void pc87312_hard_reset(PC87312State *s)
187 {
188     pc87312_soft_reset(s);
189 }
190 
191 static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
192                              unsigned int size)
193 {
194     PC87312State *s = opaque;
195 
196     trace_pc87312_io_write(addr, val);
197 
198     if ((addr & 1) == 0) {
199         /* Index register */
200         s->read_id_step = 2;
201         s->selected_index = val;
202     } else {
203         /* Data register */
204         if (s->selected_index < 3) {
205             s->regs[s->selected_index] = val;
206             reconfigure_devices(s);
207         }
208     }
209 }
210 
211 static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
212 {
213     PC87312State *s = opaque;
214     uint32_t val;
215 
216     if ((addr & 1) == 0) {
217         /* Index register */
218         if (s->read_id_step++ == 0) {
219             val = 0x88;
220         } else if (s->read_id_step++ == 1) {
221             val = 0;
222         } else {
223             val = s->selected_index;
224         }
225     } else {
226         /* Data register */
227         if (s->selected_index < 3) {
228             val = s->regs[s->selected_index];
229         } else {
230             /* Invalid selected index */
231             val = 0;
232         }
233     }
234 
235     trace_pc87312_io_read(addr, val);
236     return val;
237 }
238 
239 static const MemoryRegionOps pc87312_io_ops = {
240     .read  = pc87312_io_read,
241     .write = pc87312_io_write,
242     .endianness = DEVICE_LITTLE_ENDIAN,
243     .valid = {
244         .min_access_size = 1,
245         .max_access_size = 1,
246     },
247 };
248 
249 static int pc87312_post_load(void *opaque, int version_id)
250 {
251     PC87312State *s = opaque;
252 
253     reconfigure_devices(s);
254     return 0;
255 }
256 
257 static void pc87312_reset(DeviceState *d)
258 {
259     PC87312State *s = PC87312(d);
260 
261     pc87312_soft_reset(s);
262 }
263 
264 static void pc87312_realize(DeviceState *dev, Error **errp)
265 {
266     PC87312State *s;
267     DeviceState *d;
268     ISADevice *isa;
269     ISABus *bus;
270     CharDriverState *chr;
271     DriveInfo *drive;
272     char name[5];
273     int i;
274 
275     s = PC87312(dev);
276     isa = ISA_DEVICE(dev);
277     bus = isa_bus_from_device(isa);
278     isa_register_ioport(isa, &s->io, s->iobase);
279     pc87312_hard_reset(s);
280 
281     if (is_parallel_enabled(s)) {
282         /* FIXME use a qdev chardev prop instead of parallel_hds[] */
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             /* FIXME use a qdev chardev prop instead of serial_hds[] */
302             chr = serial_hds[i];
303             if (chr == NULL) {
304                 snprintf(name, sizeof(name), "ser%d", i);
305                 chr = qemu_chr_new(name, "null", NULL);
306             }
307             isa = isa_create(bus, "isa-serial");
308             d = DEVICE(isa);
309             qdev_prop_set_uint32(d, "index", i);
310             qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
311             qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
312             qdev_prop_set_chr(d, "chardev", chr);
313             qdev_init_nofail(d);
314             s->uart[i].dev = isa;
315             trace_pc87312_info_serial(i, get_uart_iobase(s, i),
316                                       get_uart_irq(s, i));
317         }
318     }
319 
320     if (is_fdc_enabled(s)) {
321         isa = isa_create(bus, "isa-fdc");
322         d = DEVICE(isa);
323         qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
324         qdev_prop_set_uint32(d, "irq", 6);
325         /* FIXME use a qdev drive property instead of drive_get() */
326         drive = drive_get(IF_FLOPPY, 0, 0);
327         if (drive != NULL) {
328             qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
329                                 &error_fatal);
330         }
331         /* FIXME use a qdev drive property instead of drive_get() */
332         drive = drive_get(IF_FLOPPY, 0, 1);
333         if (drive != NULL) {
334             qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
335                                 &error_fatal);
336         }
337         qdev_init_nofail(d);
338         s->fdc.dev = isa;
339         trace_pc87312_info_floppy(get_fdc_iobase(s));
340     }
341 
342     if (is_ide_enabled(s)) {
343         isa = isa_create(bus, "isa-ide");
344         d = DEVICE(isa);
345         qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
346         qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
347         qdev_prop_set_uint32(d, "irq", 14);
348         qdev_init_nofail(d);
349         s->ide.dev = isa;
350         trace_pc87312_info_ide(get_ide_iobase(s));
351     }
352 }
353 
354 static void pc87312_initfn(Object *obj)
355 {
356     PC87312State *s = PC87312(obj);
357 
358     memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
359 }
360 
361 static const VMStateDescription vmstate_pc87312 = {
362     .name = "pc87312",
363     .version_id = 1,
364     .minimum_version_id = 1,
365     .post_load = pc87312_post_load,
366     .fields = (VMStateField[]) {
367         VMSTATE_UINT8(read_id_step, PC87312State),
368         VMSTATE_UINT8(selected_index, PC87312State),
369         VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
370         VMSTATE_END_OF_LIST()
371     }
372 };
373 
374 static Property pc87312_properties[] = {
375     DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398),
376     DEFINE_PROP_UINT8("config", PC87312State, config, 1),
377     DEFINE_PROP_END_OF_LIST()
378 };
379 
380 static void pc87312_class_init(ObjectClass *klass, void *data)
381 {
382     DeviceClass *dc = DEVICE_CLASS(klass);
383 
384     dc->realize = pc87312_realize;
385     dc->reset = pc87312_reset;
386     dc->vmsd = &vmstate_pc87312;
387     dc->props = pc87312_properties;
388 }
389 
390 static const TypeInfo pc87312_type_info = {
391     .name          = TYPE_PC87312,
392     .parent        = TYPE_ISA_DEVICE,
393     .instance_size = sizeof(PC87312State),
394     .instance_init = pc87312_initfn,
395     .class_init    = pc87312_class_init,
396 };
397 
398 static void pc87312_register_types(void)
399 {
400     type_register_static(&pc87312_type_info);
401 }
402 
403 type_init(pc87312_register_types)
404