xref: /openbmc/qemu/hw/isa/pc87312.c (revision db725815985654007ade0fd53590d613fd657208)
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 "migration/vmstate.h"
29 #include "qapi/error.h"
30 #include "qemu/error-report.h"
31 #include "qemu/module.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 bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
133 {
134     PC87312State *s = PC87312(sio);
135     assert(!index);
136     return s->regs[REG_FER] & FER_FDC_EN;
137 }
138 
139 static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
140 {
141     PC87312State *s = PC87312(sio);
142     assert(!index);
143     return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
144 }
145 
146 static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
147 {
148     assert(!index);
149     return 6;
150 }
151 
152 
153 /* IDE controller */
154 
155 static bool is_ide_enabled(ISASuperIODevice *sio, uint8_t index)
156 {
157     PC87312State *s = PC87312(sio);
158 
159     return s->regs[REG_FER] & FER_IDE_EN;
160 }
161 
162 static uint16_t get_ide_iobase(ISASuperIODevice *sio, uint8_t index)
163 {
164     PC87312State *s = PC87312(sio);
165 
166     if (index == 1) {
167         return get_ide_iobase(sio, 0) + 0x206;
168     }
169     return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
170 }
171 
172 static unsigned int get_ide_irq(ISASuperIODevice *sio, uint8_t index)
173 {
174     assert(index == 0);
175     return 14;
176 }
177 
178 static void reconfigure_devices(PC87312State *s)
179 {
180     error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
181                  s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
182 }
183 
184 static void pc87312_soft_reset(PC87312State *s)
185 {
186     static const uint8_t fer_init[] = {
187         0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
188         0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
189         0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
190         0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
191     };
192     static const uint8_t far_init[] = {
193         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
194         0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
195         0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
196         0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
197     };
198     static const uint8_t ptr_init[] = {
199         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
203     };
204 
205     s->read_id_step = 0;
206     s->selected_index = REG_FER;
207 
208     s->regs[REG_FER] = fer_init[s->config & 0x1f];
209     s->regs[REG_FAR] = far_init[s->config & 0x1f];
210     s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
211 }
212 
213 static void pc87312_hard_reset(PC87312State *s)
214 {
215     pc87312_soft_reset(s);
216 }
217 
218 static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
219                              unsigned int size)
220 {
221     PC87312State *s = opaque;
222 
223     trace_pc87312_io_write(addr, val);
224 
225     if ((addr & 1) == 0) {
226         /* Index register */
227         s->read_id_step = 2;
228         s->selected_index = val;
229     } else {
230         /* Data register */
231         if (s->selected_index < 3) {
232             s->regs[s->selected_index] = val;
233             reconfigure_devices(s);
234         }
235     }
236 }
237 
238 static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
239 {
240     PC87312State *s = opaque;
241     uint32_t val;
242 
243     if ((addr & 1) == 0) {
244         /* Index register */
245         if (s->read_id_step++ == 0) {
246             val = 0x88;
247         } else if (s->read_id_step++ == 1) {
248             val = 0;
249         } else {
250             val = s->selected_index;
251         }
252     } else {
253         /* Data register */
254         if (s->selected_index < 3) {
255             val = s->regs[s->selected_index];
256         } else {
257             /* Invalid selected index */
258             val = 0;
259         }
260     }
261 
262     trace_pc87312_io_read(addr, val);
263     return val;
264 }
265 
266 static const MemoryRegionOps pc87312_io_ops = {
267     .read  = pc87312_io_read,
268     .write = pc87312_io_write,
269     .endianness = DEVICE_LITTLE_ENDIAN,
270     .valid = {
271         .min_access_size = 1,
272         .max_access_size = 1,
273     },
274 };
275 
276 static int pc87312_post_load(void *opaque, int version_id)
277 {
278     PC87312State *s = opaque;
279 
280     reconfigure_devices(s);
281     return 0;
282 }
283 
284 static void pc87312_reset(DeviceState *d)
285 {
286     PC87312State *s = PC87312(d);
287 
288     pc87312_soft_reset(s);
289 }
290 
291 static void pc87312_realize(DeviceState *dev, Error **errp)
292 {
293     PC87312State *s;
294     ISADevice *isa;
295     Error *local_err = NULL;
296 
297     s = PC87312(dev);
298     isa = ISA_DEVICE(dev);
299     isa_register_ioport(isa, &s->io, s->iobase);
300     pc87312_hard_reset(s);
301 
302     ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err);
303     if (local_err) {
304         error_propagate(errp, local_err);
305         return;
306     }
307 }
308 
309 static void pc87312_initfn(Object *obj)
310 {
311     PC87312State *s = PC87312(obj);
312 
313     memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
314 }
315 
316 static const VMStateDescription vmstate_pc87312 = {
317     .name = "pc87312",
318     .version_id = 1,
319     .minimum_version_id = 1,
320     .post_load = pc87312_post_load,
321     .fields = (VMStateField[]) {
322         VMSTATE_UINT8(read_id_step, PC87312State),
323         VMSTATE_UINT8(selected_index, PC87312State),
324         VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
325         VMSTATE_END_OF_LIST()
326     }
327 };
328 
329 static Property pc87312_properties[] = {
330     DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398),
331     DEFINE_PROP_UINT8("config", PC87312State, config, 1),
332     DEFINE_PROP_END_OF_LIST()
333 };
334 
335 static void pc87312_class_init(ObjectClass *klass, void *data)
336 {
337     DeviceClass *dc = DEVICE_CLASS(klass);
338     ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
339 
340     sc->parent_realize = dc->realize;
341     dc->realize = pc87312_realize;
342     dc->reset = pc87312_reset;
343     dc->vmsd = &vmstate_pc87312;
344     dc->props = pc87312_properties;
345 
346     sc->parallel = (ISASuperIOFuncs){
347         .count = 1,
348         .is_enabled = is_parallel_enabled,
349         .get_iobase = get_parallel_iobase,
350         .get_irq    = get_parallel_irq,
351     };
352     sc->serial = (ISASuperIOFuncs){
353         .count = 2,
354         .is_enabled = is_uart_enabled,
355         .get_iobase = get_uart_iobase,
356         .get_irq    = get_uart_irq,
357     };
358     sc->floppy = (ISASuperIOFuncs){
359         .count = 1,
360         .is_enabled = is_fdc_enabled,
361         .get_iobase = get_fdc_iobase,
362         .get_irq    = get_fdc_irq,
363     };
364     sc->ide = (ISASuperIOFuncs){
365         .count = 1,
366         .is_enabled = is_ide_enabled,
367         .get_iobase = get_ide_iobase,
368         .get_irq    = get_ide_irq,
369     };
370 }
371 
372 static const TypeInfo pc87312_type_info = {
373     .name          = TYPE_PC87312_SUPERIO,
374     .parent        = TYPE_ISA_SUPERIO,
375     .instance_size = sizeof(PC87312State),
376     .instance_init = pc87312_initfn,
377     .class_init    = pc87312_class_init,
378     /* FIXME use a qdev drive property instead of drive_get() */
379 };
380 
381 static void pc87312_register_types(void)
382 {
383     type_register_static(&pc87312_type_info);
384 }
385 
386 type_init(pc87312_register_types)
387