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