xref: /qemu/hw/isa/pc87312.c (revision cd9526ab7c04f2c32c63340b04401f6ed25682b9)
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 "qapi/error.h"
29 #include "qemu/error-report.h"
30 #include "sysemu/block-backend.h"
31 #include "sysemu/blockdev.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 bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
66 {
67     PC87312State *s = PC87312(sio);
68     return index ? false : s->regs[REG_FER] & FER_PARALLEL_EN;
69 }
70 
71 static const uint16_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
72 
73 static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
74 {
75     PC87312State *s = PC87312(sio);
76     return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
77 }
78 
79 static const unsigned int parallel_irq[] = { 5, 7, 5, 0 };
80 
81 static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
82 {
83     PC87312State *s = PC87312(sio);
84     int idx;
85     idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
86     if (idx == 0) {
87         return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
88     } else {
89         return parallel_irq[idx];
90     }
91 }
92 
93 
94 /* UARTs */
95 
96 static const uint16_t uart_base[2][4] = {
97     { 0x3e8, 0x338, 0x2e8, 0x220 },
98     { 0x2e8, 0x238, 0x2e0, 0x228 }
99 };
100 
101 static uint16_t get_uart_iobase(ISASuperIODevice *sio, uint8_t i)
102 {
103     PC87312State *s = PC87312(sio);
104     int idx;
105     idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
106     if (idx == 0) {
107         return 0x3f8;
108     } else if (idx == 1) {
109         return 0x2f8;
110     } else {
111         return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
112     }
113 }
114 
115 static unsigned int get_uart_irq(ISASuperIODevice *sio, uint8_t i)
116 {
117     PC87312State *s = PC87312(sio);
118     int idx;
119     idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
120     return (idx & 1) ? 3 : 4;
121 }
122 
123 static bool is_uart_enabled(ISASuperIODevice *sio, uint8_t i)
124 {
125     PC87312State *s = PC87312(sio);
126     return s->regs[REG_FER] & (FER_UART1_EN << i);
127 }
128 
129 
130 /* Floppy controller */
131 
132 static inline bool is_fdc_enabled(PC87312State *s)
133 {
134     return s->regs[REG_FER] & FER_FDC_EN;
135 }
136 
137 static inline uint16_t get_fdc_iobase(PC87312State *s)
138 {
139     return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
140 }
141 
142 
143 /* IDE controller */
144 
145 static inline bool is_ide_enabled(PC87312State *s)
146 {
147     return s->regs[REG_FER] & FER_IDE_EN;
148 }
149 
150 static inline uint16_t get_ide_iobase(PC87312State *s)
151 {
152     return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
153 }
154 
155 
156 static void reconfigure_devices(PC87312State *s)
157 {
158     error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
159                  s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
160 }
161 
162 static void pc87312_soft_reset(PC87312State *s)
163 {
164     static const uint8_t fer_init[] = {
165         0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
166         0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
167         0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
168         0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
169     };
170     static const uint8_t far_init[] = {
171         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
172         0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
173         0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
174         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
175     };
176     static const uint8_t ptr_init[] = {
177         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x02,
181     };
182 
183     s->read_id_step = 0;
184     s->selected_index = REG_FER;
185 
186     s->regs[REG_FER] = fer_init[s->config & 0x1f];
187     s->regs[REG_FAR] = far_init[s->config & 0x1f];
188     s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
189 }
190 
191 static void pc87312_hard_reset(PC87312State *s)
192 {
193     pc87312_soft_reset(s);
194 }
195 
196 static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
197                              unsigned int size)
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 uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
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 const MemoryRegionOps pc87312_io_ops = {
245     .read  = pc87312_io_read,
246     .write = pc87312_io_write,
247     .endianness = DEVICE_LITTLE_ENDIAN,
248     .valid = {
249         .min_access_size = 1,
250         .max_access_size = 1,
251     },
252 };
253 
254 static int pc87312_post_load(void *opaque, int version_id)
255 {
256     PC87312State *s = opaque;
257 
258     reconfigure_devices(s);
259     return 0;
260 }
261 
262 static void pc87312_reset(DeviceState *d)
263 {
264     PC87312State *s = PC87312(d);
265 
266     pc87312_soft_reset(s);
267 }
268 
269 static void pc87312_realize(DeviceState *dev, Error **errp)
270 {
271     PC87312State *s;
272     DeviceState *d;
273     ISADevice *isa;
274     ISABus *bus;
275     DriveInfo *drive;
276     Error *local_err = NULL;
277 
278     s = PC87312(dev);
279     isa = ISA_DEVICE(dev);
280     bus = isa_bus_from_device(isa);
281     isa_register_ioport(isa, &s->io, s->iobase);
282     pc87312_hard_reset(s);
283 
284     ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err);
285     if (local_err) {
286         error_propagate(errp, local_err);
287         return;
288     }
289 
290     if (is_fdc_enabled(s)) {
291         isa = isa_create(bus, "isa-fdc");
292         d = DEVICE(isa);
293         qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
294         qdev_prop_set_uint32(d, "irq", 6);
295         /* FIXME use a qdev drive property instead of drive_get() */
296         drive = drive_get(IF_FLOPPY, 0, 0);
297         if (drive != NULL) {
298             qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
299                                 &error_fatal);
300         }
301         /* FIXME use a qdev drive property instead of drive_get() */
302         drive = drive_get(IF_FLOPPY, 0, 1);
303         if (drive != NULL) {
304             qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
305                                 &error_fatal);
306         }
307         qdev_init_nofail(d);
308         s->fdc.dev = isa;
309         trace_pc87312_info_floppy(get_fdc_iobase(s));
310     }
311 
312     if (is_ide_enabled(s)) {
313         isa = isa_create(bus, "isa-ide");
314         d = DEVICE(isa);
315         qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
316         qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
317         qdev_prop_set_uint32(d, "irq", 14);
318         qdev_init_nofail(d);
319         s->ide.dev = isa;
320         trace_pc87312_info_ide(get_ide_iobase(s));
321     }
322 }
323 
324 static void pc87312_initfn(Object *obj)
325 {
326     PC87312State *s = PC87312(obj);
327 
328     memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
329 }
330 
331 static const VMStateDescription vmstate_pc87312 = {
332     .name = "pc87312",
333     .version_id = 1,
334     .minimum_version_id = 1,
335     .post_load = pc87312_post_load,
336     .fields = (VMStateField[]) {
337         VMSTATE_UINT8(read_id_step, PC87312State),
338         VMSTATE_UINT8(selected_index, PC87312State),
339         VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
340         VMSTATE_END_OF_LIST()
341     }
342 };
343 
344 static Property pc87312_properties[] = {
345     DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398),
346     DEFINE_PROP_UINT8("config", PC87312State, config, 1),
347     DEFINE_PROP_END_OF_LIST()
348 };
349 
350 static void pc87312_class_init(ObjectClass *klass, void *data)
351 {
352     DeviceClass *dc = DEVICE_CLASS(klass);
353     ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
354 
355     sc->parent_realize = dc->realize;
356     dc->realize = pc87312_realize;
357     dc->reset = pc87312_reset;
358     dc->vmsd = &vmstate_pc87312;
359     dc->props = pc87312_properties;
360 
361     sc->parallel = (ISASuperIOFuncs){
362         .count = 1,
363         .is_enabled = is_parallel_enabled,
364         .get_iobase = get_parallel_iobase,
365         .get_irq    = get_parallel_irq,
366     };
367     sc->serial = (ISASuperIOFuncs){
368         .count = 2,
369         .is_enabled = is_uart_enabled,
370         .get_iobase = get_uart_iobase,
371         .get_irq    = get_uart_irq,
372     };
373 }
374 
375 static const TypeInfo pc87312_type_info = {
376     .name          = TYPE_PC87312_SUPERIO,
377     .parent        = TYPE_ISA_SUPERIO,
378     .instance_size = sizeof(PC87312State),
379     .instance_init = pc87312_initfn,
380     .class_init    = pc87312_class_init,
381 };
382 
383 static void pc87312_register_types(void)
384 {
385     type_register_static(&pc87312_type_info);
386 }
387 
388 type_init(pc87312_register_types)
389