xref: /openbmc/qemu/hw/isa/pc87312.c (revision ddbb0d09)
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         /* FIXME use a qdev chardev prop instead of parallel_hds[] */
282         chr = parallel_hds[0];
283         if (chr == NULL) {
284             chr = qemu_chr_new("par0", "null", NULL);
285         }
286         isa = isa_create(bus, "isa-parallel");
287         d = DEVICE(isa);
288         qdev_prop_set_uint32(d, "index", 0);
289         qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
290         qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
291         qdev_prop_set_chr(d, "chardev", chr);
292         qdev_init_nofail(d);
293         s->parallel.dev = isa;
294         trace_pc87312_info_parallel(get_parallel_iobase(s),
295                                     get_parallel_irq(s));
296     }
297 
298     for (i = 0; i < 2; i++) {
299         if (is_uart_enabled(s, i)) {
300             /* FIXME use a qdev chardev prop instead of serial_hds[] */
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         /* FIXME use a qdev drive property instead of drive_get() */
325         drive = drive_get(IF_FLOPPY, 0, 0);
326         if (drive != NULL) {
327             qdev_prop_set_drive_nofail(d, "driveA",
328                                        blk_by_legacy_dinfo(drive));
329         }
330         /* FIXME use a qdev drive property instead of drive_get() */
331         drive = drive_get(IF_FLOPPY, 0, 1);
332         if (drive != NULL) {
333             qdev_prop_set_drive_nofail(d, "driveB",
334                                        blk_by_legacy_dinfo(drive));
335         }
336         qdev_init_nofail(d);
337         s->fdc.dev = isa;
338         trace_pc87312_info_floppy(get_fdc_iobase(s));
339     }
340 
341     if (is_ide_enabled(s)) {
342         isa = isa_create(bus, "isa-ide");
343         d = DEVICE(isa);
344         qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
345         qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
346         qdev_prop_set_uint32(d, "irq", 14);
347         qdev_init_nofail(d);
348         s->ide.dev = isa;
349         trace_pc87312_info_ide(get_ide_iobase(s));
350     }
351 }
352 
353 static void pc87312_initfn(Object *obj)
354 {
355     PC87312State *s = PC87312(obj);
356 
357     memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
358 }
359 
360 static const VMStateDescription vmstate_pc87312 = {
361     .name = "pc87312",
362     .version_id = 1,
363     .minimum_version_id = 1,
364     .post_load = pc87312_post_load,
365     .fields = (VMStateField[]) {
366         VMSTATE_UINT8(read_id_step, PC87312State),
367         VMSTATE_UINT8(selected_index, PC87312State),
368         VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
369         VMSTATE_END_OF_LIST()
370     }
371 };
372 
373 static Property pc87312_properties[] = {
374     DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398),
375     DEFINE_PROP_UINT8("config", PC87312State, config, 1),
376     DEFINE_PROP_END_OF_LIST()
377 };
378 
379 static void pc87312_class_init(ObjectClass *klass, void *data)
380 {
381     DeviceClass *dc = DEVICE_CLASS(klass);
382 
383     dc->realize = pc87312_realize;
384     dc->reset = pc87312_reset;
385     dc->vmsd = &vmstate_pc87312;
386     dc->props = pc87312_properties;
387 }
388 
389 static const TypeInfo pc87312_type_info = {
390     .name          = TYPE_PC87312,
391     .parent        = TYPE_ISA_DEVICE,
392     .instance_size = sizeof(PC87312State),
393     .instance_init = pc87312_initfn,
394     .class_init    = pc87312_class_init,
395 };
396 
397 static void pc87312_register_types(void)
398 {
399     type_register_static(&pc87312_type_info);
400 }
401 
402 type_init(pc87312_register_types)
403