]> git.proxmox.com Git - mirror_qemu.git/blame - hw/isa/pc87312.c
migration: remove not used field xfer_limit
[mirror_qemu.git] / hw / isa / pc87312.c
CommitLineData
1ae41f44
HP
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
0d75590d 26#include "qemu/osdep.h"
0d09e41a 27#include "hw/isa/pc87312.h"
da34e65c 28#include "qapi/error.h"
b4a42f81 29#include "qemu/error-report.h"
1ae41f44
HP
30#include "trace.h"
31
32
33#define REG_FER 0
34#define REG_FAR 1
35#define REG_PTR 2
36
1ae41f44
HP
37#define FER_PARALLEL_EN 0x01
38#define FER_UART1_EN 0x02
39#define FER_UART2_EN 0x04
40#define FER_FDC_EN 0x08
41#define FER_FDC_4 0x10
42#define FER_FDC_ADDR 0x20
43#define FER_IDE_EN 0x40
44#define FER_IDE_ADDR 0x80
45
46#define FAR_PARALLEL_ADDR 0x03
47#define FAR_UART1_ADDR 0x0C
48#define FAR_UART2_ADDR 0x30
49#define FAR_UART_3_4 0xC0
50
51#define PTR_POWER_DOWN 0x01
52#define PTR_CLOCK_DOWN 0x02
53#define PTR_PWDN 0x04
54#define PTR_IRQ_5_7 0x08
55#define PTR_UART1_TEST 0x10
56#define PTR_UART2_TEST 0x20
57#define PTR_LOCK_CONF 0x40
58#define PTR_EPP_MODE 0x80
59
60
61/* Parallel port */
62
4c3119a6 63static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
1ae41f44 64{
4c3119a6
PMD
65 PC87312State *s = PC87312(sio);
66 return index ? false : s->regs[REG_FER] & FER_PARALLEL_EN;
1ae41f44
HP
67}
68
4e00105a 69static const uint16_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
1ae41f44 70
4c3119a6 71static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
1ae41f44 72{
4c3119a6 73 PC87312State *s = PC87312(sio);
08bb4a7c 74 return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
1ae41f44
HP
75}
76
818c9d99 77static const unsigned int parallel_irq[] = { 5, 7, 5, 0 };
1ae41f44 78
4c3119a6 79static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
1ae41f44 80{
4c3119a6 81 PC87312State *s = PC87312(sio);
1ae41f44 82 int idx;
08bb4a7c 83 idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
1ae41f44 84 if (idx == 0) {
08bb4a7c 85 return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
1ae41f44
HP
86 } else {
87 return parallel_irq[idx];
88 }
89}
90
1ae41f44
HP
91
92/* UARTs */
93
4e00105a 94static const uint16_t uart_base[2][4] = {
1ae41f44
HP
95 { 0x3e8, 0x338, 0x2e8, 0x220 },
96 { 0x2e8, 0x238, 0x2e0, 0x228 }
97};
98
cd9526ab 99static uint16_t get_uart_iobase(ISASuperIODevice *sio, uint8_t i)
1ae41f44 100{
cd9526ab 101 PC87312State *s = PC87312(sio);
1ae41f44 102 int idx;
08bb4a7c 103 idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
1ae41f44
HP
104 if (idx == 0) {
105 return 0x3f8;
106 } else if (idx == 1) {
107 return 0x2f8;
108 } else {
08bb4a7c 109 return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
1ae41f44
HP
110 }
111}
112
cd9526ab 113static unsigned int get_uart_irq(ISASuperIODevice *sio, uint8_t i)
1ae41f44 114{
cd9526ab 115 PC87312State *s = PC87312(sio);
1ae41f44 116 int idx;
08bb4a7c 117 idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
1ae41f44
HP
118 return (idx & 1) ? 3 : 4;
119}
120
cd9526ab 121static bool is_uart_enabled(ISASuperIODevice *sio, uint8_t i)
1ae41f44 122{
cd9526ab 123 PC87312State *s = PC87312(sio);
08bb4a7c 124 return s->regs[REG_FER] & (FER_UART1_EN << i);
1ae41f44
HP
125}
126
127
128/* Floppy controller */
129
6f6695b1 130static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
1ae41f44 131{
6f6695b1
PMD
132 PC87312State *s = PC87312(sio);
133 assert(!index);
08bb4a7c 134 return s->regs[REG_FER] & FER_FDC_EN;
1ae41f44
HP
135}
136
6f6695b1 137static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
1ae41f44 138{
6f6695b1
PMD
139 PC87312State *s = PC87312(sio);
140 assert(!index);
08bb4a7c 141 return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
1ae41f44
HP
142}
143
6f6695b1
PMD
144static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
145{
146 assert(!index);
147 return 6;
148}
149
1ae41f44
HP
150
151/* IDE controller */
152
c16a4e1b 153static bool is_ide_enabled(ISASuperIODevice *sio, uint8_t index)
1ae41f44 154{
c16a4e1b
PMD
155 PC87312State *s = PC87312(sio);
156
08bb4a7c 157 return s->regs[REG_FER] & FER_IDE_EN;
1ae41f44
HP
158}
159
c16a4e1b 160static uint16_t get_ide_iobase(ISASuperIODevice *sio, uint8_t index)
1ae41f44 161{
c16a4e1b
PMD
162 PC87312State *s = PC87312(sio);
163
164 if (index == 1) {
165 return get_ide_iobase(sio, 0) + 0x206;
166 }
08bb4a7c 167 return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
1ae41f44
HP
168}
169
c16a4e1b
PMD
170static unsigned int get_ide_irq(ISASuperIODevice *sio, uint8_t index)
171{
172 assert(index == 0);
173 return 14;
174}
1ae41f44
HP
175
176static void reconfigure_devices(PC87312State *s)
177{
178 error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
08bb4a7c 179 s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
1ae41f44
HP
180}
181
182static void pc87312_soft_reset(PC87312State *s)
183{
184 static const uint8_t fer_init[] = {
185 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
186 0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
187 0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
188 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
189 };
190 static const uint8_t far_init[] = {
191 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
192 0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
193 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
194 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
195 };
196 static const uint8_t ptr_init[] = {
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
201 };
202
203 s->read_id_step = 0;
204 s->selected_index = REG_FER;
205
08bb4a7c
BS
206 s->regs[REG_FER] = fer_init[s->config & 0x1f];
207 s->regs[REG_FAR] = far_init[s->config & 0x1f];
208 s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
1ae41f44
HP
209}
210
211static void pc87312_hard_reset(PC87312State *s)
212{
213 pc87312_soft_reset(s);
214}
215
328c24a9
AF
216static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
217 unsigned int size)
1ae41f44
HP
218{
219 PC87312State *s = opaque;
220
221 trace_pc87312_io_write(addr, val);
222
223 if ((addr & 1) == 0) {
224 /* Index register */
225 s->read_id_step = 2;
226 s->selected_index = val;
227 } else {
228 /* Data register */
229 if (s->selected_index < 3) {
230 s->regs[s->selected_index] = val;
231 reconfigure_devices(s);
232 }
233 }
234}
235
328c24a9 236static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
1ae41f44
HP
237{
238 PC87312State *s = opaque;
239 uint32_t val;
240
241 if ((addr & 1) == 0) {
242 /* Index register */
243 if (s->read_id_step++ == 0) {
244 val = 0x88;
245 } else if (s->read_id_step++ == 1) {
246 val = 0;
247 } else {
248 val = s->selected_index;
249 }
250 } else {
251 /* Data register */
252 if (s->selected_index < 3) {
253 val = s->regs[s->selected_index];
254 } else {
255 /* Invalid selected index */
256 val = 0;
257 }
258 }
259
260 trace_pc87312_io_read(addr, val);
261 return val;
262}
263
328c24a9
AF
264static const MemoryRegionOps pc87312_io_ops = {
265 .read = pc87312_io_read,
266 .write = pc87312_io_write,
267 .endianness = DEVICE_LITTLE_ENDIAN,
268 .valid = {
269 .min_access_size = 1,
270 .max_access_size = 1,
271 },
272};
273
1ae41f44
HP
274static int pc87312_post_load(void *opaque, int version_id)
275{
276 PC87312State *s = opaque;
277
278 reconfigure_devices(s);
279 return 0;
280}
281
282static void pc87312_reset(DeviceState *d)
283{
284 PC87312State *s = PC87312(d);
285
286 pc87312_soft_reset(s);
287}
288
db895a1e 289static void pc87312_realize(DeviceState *dev, Error **errp)
1ae41f44
HP
290{
291 PC87312State *s;
1ae41f44 292 ISADevice *isa;
63f01a74 293 Error *local_err = NULL;
1ae41f44
HP
294
295 s = PC87312(dev);
db895a1e 296 isa = ISA_DEVICE(dev);
db895a1e 297 isa_register_ioport(isa, &s->io, s->iobase);
1ae41f44
HP
298 pc87312_hard_reset(s);
299
63f01a74
PMD
300 ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err);
301 if (local_err) {
302 error_propagate(errp, local_err);
303 return;
304 }
1ae41f44
HP
305}
306
328c24a9
AF
307static void pc87312_initfn(Object *obj)
308{
309 PC87312State *s = PC87312(obj);
310
1437c94b 311 memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
328c24a9
AF
312}
313
1ae41f44
HP
314static const VMStateDescription vmstate_pc87312 = {
315 .name = "pc87312",
316 .version_id = 1,
317 .minimum_version_id = 1,
318 .post_load = pc87312_post_load,
319 .fields = (VMStateField[]) {
320 VMSTATE_UINT8(read_id_step, PC87312State),
321 VMSTATE_UINT8(selected_index, PC87312State),
322 VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
323 VMSTATE_END_OF_LIST()
324 }
325};
326
327static Property pc87312_properties[] = {
4e00105a 328 DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398),
1ae41f44
HP
329 DEFINE_PROP_UINT8("config", PC87312State, config, 1),
330 DEFINE_PROP_END_OF_LIST()
331};
332
333static void pc87312_class_init(ObjectClass *klass, void *data)
334{
335 DeviceClass *dc = DEVICE_CLASS(klass);
63f01a74 336 ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
1ae41f44 337
63f01a74 338 sc->parent_realize = dc->realize;
db895a1e 339 dc->realize = pc87312_realize;
1ae41f44
HP
340 dc->reset = pc87312_reset;
341 dc->vmsd = &vmstate_pc87312;
342 dc->props = pc87312_properties;
4c3119a6
PMD
343
344 sc->parallel = (ISASuperIOFuncs){
345 .count = 1,
346 .is_enabled = is_parallel_enabled,
347 .get_iobase = get_parallel_iobase,
348 .get_irq = get_parallel_irq,
349 };
cd9526ab
PMD
350 sc->serial = (ISASuperIOFuncs){
351 .count = 2,
352 .is_enabled = is_uart_enabled,
353 .get_iobase = get_uart_iobase,
354 .get_irq = get_uart_irq,
355 };
6f6695b1
PMD
356 sc->floppy = (ISASuperIOFuncs){
357 .count = 1,
358 .is_enabled = is_fdc_enabled,
359 .get_iobase = get_fdc_iobase,
360 .get_irq = get_fdc_irq,
361 };
c16a4e1b
PMD
362 sc->ide = (ISASuperIOFuncs){
363 .count = 1,
364 .is_enabled = is_ide_enabled,
365 .get_iobase = get_ide_iobase,
366 .get_irq = get_ide_irq,
367 };
1ae41f44
HP
368}
369
370static const TypeInfo pc87312_type_info = {
010d2dc4 371 .name = TYPE_PC87312_SUPERIO,
63f01a74 372 .parent = TYPE_ISA_SUPERIO,
1ae41f44 373 .instance_size = sizeof(PC87312State),
328c24a9 374 .instance_init = pc87312_initfn,
1ae41f44 375 .class_init = pc87312_class_init,
6f6695b1 376 /* FIXME use a qdev drive property instead of drive_get() */
1ae41f44
HP
377};
378
379static void pc87312_register_types(void)
380{
381 type_register_static(&pc87312_type_info);
382}
383
384type_init(pc87312_register_types)