]>
Commit | Line | Data |
---|---|---|
c701ec62 LV |
1 | /* |
2 | * QEMU Macintosh floppy disk controller emulator (SWIM) | |
3 | * | |
4 | * Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu> | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2. See | |
7 | * the COPYING file in the top-level directory. | |
8 | * | |
9 | * Only the basic support: it allows to switch from IWM (Integrated WOZ | |
10 | * Machine) mode to the SWIM mode and makes the linux driver happy. | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "qemu/main-loop.h" | |
15 | #include "qapi/error.h" | |
16 | #include "sysemu/block-backend.h" | |
17 | #include "hw/sysbus.h" | |
18 | #include "migration/vmstate.h" | |
19 | #include "hw/block/block.h" | |
20 | #include "hw/block/swim.h" | |
21 | #include "hw/qdev-properties.h" | |
d05cad2b | 22 | #include "trace.h" |
c701ec62 | 23 | |
994af0b2 MCA |
24 | |
25 | /* IWM latch bits */ | |
26 | ||
27 | #define IWMLB_PHASE0 0 | |
28 | #define IWMLB_PHASE1 1 | |
29 | #define IWMLB_PHASE2 2 | |
30 | #define IWMLB_PHASE3 3 | |
31 | #define IWMLB_MOTORON 4 | |
32 | #define IWMLB_DRIVESEL 5 | |
33 | #define IWMLB_L6 6 | |
34 | #define IWMLB_L7 7 | |
35 | ||
c701ec62 LV |
36 | /* IWM registers */ |
37 | ||
994af0b2 MCA |
38 | #define IWM_READALLONES 0 |
39 | #define IWM_READDATA 1 | |
40 | #define IWM_READSTATUS0 2 | |
41 | #define IWM_READSTATUS1 3 | |
42 | #define IWM_READWHANDSHAKE0 4 | |
43 | #define IWM_READWHANDSHAKE1 5 | |
44 | #define IWM_WRITESETMODE 6 | |
45 | #define IWM_WRITEDATA 7 | |
c701ec62 LV |
46 | |
47 | /* SWIM registers */ | |
48 | ||
49 | #define SWIM_WRITE_DATA 0 | |
50 | #define SWIM_WRITE_MARK 1 | |
51 | #define SWIM_WRITE_CRC 2 | |
52 | #define SWIM_WRITE_PARAMETER 3 | |
53 | #define SWIM_WRITE_PHASE 4 | |
54 | #define SWIM_WRITE_SETUP 5 | |
55 | #define SWIM_WRITE_MODE0 6 | |
56 | #define SWIM_WRITE_MODE1 7 | |
57 | ||
58 | #define SWIM_READ_DATA 8 | |
59 | #define SWIM_READ_MARK 9 | |
60 | #define SWIM_READ_ERROR 10 | |
61 | #define SWIM_READ_PARAMETER 11 | |
62 | #define SWIM_READ_PHASE 12 | |
63 | #define SWIM_READ_SETUP 13 | |
64 | #define SWIM_READ_STATUS 14 | |
65 | #define SWIM_READ_HANDSHAKE 15 | |
66 | ||
67 | #define REG_SHIFT 9 | |
68 | ||
994af0b2 MCA |
69 | #define SWIM_MODE_STATUS_BIT 6 |
70 | #define SWIM_MODE_IWM 0 | |
71 | #define SWIM_MODE_ISM 1 | |
c701ec62 LV |
72 | |
73 | /* bits in phase register */ | |
74 | ||
75 | #define SWIM_SEEK_NEGATIVE 0x074 | |
76 | #define SWIM_STEP 0x071 | |
77 | #define SWIM_MOTOR_ON 0x072 | |
78 | #define SWIM_MOTOR_OFF 0x076 | |
79 | #define SWIM_INDEX 0x073 | |
80 | #define SWIM_EJECT 0x077 | |
81 | #define SWIM_SETMFM 0x171 | |
82 | #define SWIM_SETGCR 0x175 | |
83 | #define SWIM_RELAX 0x033 | |
84 | #define SWIM_LSTRB 0x008 | |
85 | #define SWIM_CA_MASK 0x077 | |
86 | ||
87 | /* Select values for swim_select and swim_readbit */ | |
88 | ||
89 | #define SWIM_READ_DATA_0 0x074 | |
90 | #define SWIM_TWOMEG_DRIVE 0x075 | |
91 | #define SWIM_SINGLE_SIDED 0x076 | |
92 | #define SWIM_DRIVE_PRESENT 0x077 | |
93 | #define SWIM_DISK_IN 0x170 | |
94 | #define SWIM_WRITE_PROT 0x171 | |
95 | #define SWIM_TRACK_ZERO 0x172 | |
96 | #define SWIM_TACHO 0x173 | |
97 | #define SWIM_READ_DATA_1 0x174 | |
98 | #define SWIM_MFM_MODE 0x175 | |
99 | #define SWIM_SEEK_COMPLETE 0x176 | |
100 | #define SWIM_ONEMEG_MEDIA 0x177 | |
101 | ||
102 | /* Bits in handshake register */ | |
103 | ||
104 | #define SWIM_MARK_BYTE 0x01 | |
105 | #define SWIM_CRC_ZERO 0x02 | |
106 | #define SWIM_RDDATA 0x04 | |
107 | #define SWIM_SENSE 0x08 | |
108 | #define SWIM_MOTEN 0x10 | |
109 | #define SWIM_ERROR 0x20 | |
110 | #define SWIM_DAT2BYTE 0x40 | |
111 | #define SWIM_DAT1BYTE 0x80 | |
112 | ||
113 | /* bits in setup register */ | |
114 | ||
115 | #define SWIM_S_INV_WDATA 0x01 | |
116 | #define SWIM_S_3_5_SELECT 0x02 | |
117 | #define SWIM_S_GCR 0x04 | |
118 | #define SWIM_S_FCLK_DIV2 0x08 | |
119 | #define SWIM_S_ERROR_CORR 0x10 | |
120 | #define SWIM_S_IBM_DRIVE 0x20 | |
121 | #define SWIM_S_GCR_WRITE 0x40 | |
122 | #define SWIM_S_TIMEOUT 0x80 | |
123 | ||
124 | /* bits in mode register */ | |
125 | ||
126 | #define SWIM_CLFIFO 0x01 | |
127 | #define SWIM_ENBL1 0x02 | |
128 | #define SWIM_ENBL2 0x04 | |
129 | #define SWIM_ACTION 0x08 | |
130 | #define SWIM_WRITE_MODE 0x10 | |
131 | #define SWIM_HEDSEL 0x20 | |
132 | #define SWIM_MOTON 0x80 | |
133 | ||
57004204 | 134 | static const char *iwm_reg_names[] = { |
994af0b2 MCA |
135 | "READALLONES", "READDATA", "READSTATUS0", "READSTATUS1", |
136 | "READWHANDSHAKE0", "READWHANDSHAKE1", "WRITESETMODE", "WRITEDATA" | |
57004204 MCA |
137 | }; |
138 | ||
139 | static const char *ism_reg_names[] = { | |
d05cad2b MCA |
140 | "WRITE_DATA", "WRITE_MARK", "WRITE_CRC", "WRITE_PARAMETER", |
141 | "WRITE_PHASE", "WRITE_SETUP", "WRITE_MODE0", "WRITE_MODE1", | |
142 | "READ_DATA", "READ_MARK", "READ_ERROR", "READ_PARAMETER", | |
143 | "READ_PHASE", "READ_SETUP", "READ_STATUS", "READ_HANDSHAKE" | |
144 | }; | |
145 | ||
c701ec62 LV |
146 | static void fd_recalibrate(FDrive *drive) |
147 | { | |
148 | } | |
149 | ||
150 | static void swim_change_cb(void *opaque, bool load, Error **errp) | |
151 | { | |
152 | FDrive *drive = opaque; | |
153 | ||
154 | if (!load) { | |
155 | blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort); | |
156 | } else { | |
157 | if (!blkconf_apply_backend_options(drive->conf, | |
86b1cf32 KW |
158 | !blk_supports_write_perm(drive->blk), |
159 | false, errp)) { | |
c701ec62 LV |
160 | return; |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | static const BlockDevOps swim_block_ops = { | |
166 | .change_media_cb = swim_change_cb, | |
167 | }; | |
168 | ||
169 | static Property swim_drive_properties[] = { | |
170 | DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1), | |
171 | DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf), | |
172 | DEFINE_PROP_END_OF_LIST(), | |
173 | }; | |
174 | ||
175 | static void swim_drive_realize(DeviceState *qdev, Error **errp) | |
176 | { | |
177 | SWIMDrive *dev = SWIM_DRIVE(qdev); | |
178 | SWIMBus *bus = SWIM_BUS(qdev->parent_bus); | |
179 | FDrive *drive; | |
180 | int ret; | |
181 | ||
182 | if (dev->unit == -1) { | |
183 | for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) { | |
184 | drive = &bus->ctrl->drives[dev->unit]; | |
185 | if (!drive->blk) { | |
186 | break; | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
191 | if (dev->unit >= SWIM_MAX_FD) { | |
192 | error_setg(errp, "Can't create floppy unit %d, bus supports " | |
193 | "only %d units", dev->unit, SWIM_MAX_FD); | |
194 | return; | |
195 | } | |
196 | ||
197 | drive = &bus->ctrl->drives[dev->unit]; | |
198 | if (drive->blk) { | |
199 | error_setg(errp, "Floppy unit %d is in use", dev->unit); | |
200 | return; | |
201 | } | |
202 | ||
203 | if (!dev->conf.blk) { | |
204 | /* Anonymous BlockBackend for an empty drive */ | |
205 | dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); | |
206 | ret = blk_attach_dev(dev->conf.blk, qdev); | |
207 | assert(ret == 0); | |
208 | } | |
209 | ||
c56ee92f RK |
210 | if (!blkconf_blocksizes(&dev->conf, errp)) { |
211 | return; | |
212 | } | |
213 | ||
c701ec62 LV |
214 | if (dev->conf.logical_block_size != 512 || |
215 | dev->conf.physical_block_size != 512) | |
216 | { | |
217 | error_setg(errp, "Physical and logical block size must " | |
218 | "be 512 for floppy"); | |
219 | return; | |
220 | } | |
221 | ||
222 | /* | |
223 | * rerror/werror aren't supported by fdc and therefore not even registered | |
224 | * with qdev. So set the defaults manually before they are used in | |
225 | * blkconf_apply_backend_options(). | |
226 | */ | |
227 | dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO; | |
228 | dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO; | |
229 | ||
230 | if (!blkconf_apply_backend_options(&dev->conf, | |
86b1cf32 | 231 | !blk_supports_write_perm(dev->conf.blk), |
c701ec62 LV |
232 | false, errp)) { |
233 | return; | |
234 | } | |
235 | ||
236 | /* | |
237 | * 'enospc' is the default for -drive, 'report' is what blk_new() gives us | |
238 | * for empty drives. | |
239 | */ | |
240 | if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC && | |
241 | blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) { | |
242 | error_setg(errp, "fdc doesn't support drive option werror"); | |
243 | return; | |
244 | } | |
245 | if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { | |
246 | error_setg(errp, "fdc doesn't support drive option rerror"); | |
247 | return; | |
248 | } | |
249 | ||
250 | drive->conf = &dev->conf; | |
251 | drive->blk = dev->conf.blk; | |
252 | drive->swimctrl = bus->ctrl; | |
253 | ||
254 | blk_set_dev_ops(drive->blk, &swim_block_ops, drive); | |
255 | } | |
256 | ||
257 | static void swim_drive_class_init(ObjectClass *klass, void *data) | |
258 | { | |
259 | DeviceClass *k = DEVICE_CLASS(klass); | |
260 | k->realize = swim_drive_realize; | |
261 | set_bit(DEVICE_CATEGORY_STORAGE, k->categories); | |
262 | k->bus_type = TYPE_SWIM_BUS; | |
4f67d30b | 263 | device_class_set_props(k, swim_drive_properties); |
c701ec62 LV |
264 | k->desc = "virtual SWIM drive"; |
265 | } | |
266 | ||
267 | static const TypeInfo swim_drive_info = { | |
268 | .name = TYPE_SWIM_DRIVE, | |
269 | .parent = TYPE_DEVICE, | |
270 | .instance_size = sizeof(SWIMDrive), | |
271 | .class_init = swim_drive_class_init, | |
272 | }; | |
273 | ||
274 | static const TypeInfo swim_bus_info = { | |
275 | .name = TYPE_SWIM_BUS, | |
276 | .parent = TYPE_BUS, | |
277 | .instance_size = sizeof(SWIMBus), | |
278 | }; | |
279 | ||
994af0b2 | 280 | static void iwmctrl_write(void *opaque, hwaddr addr, uint64_t value, |
c701ec62 LV |
281 | unsigned size) |
282 | { | |
283 | SWIMCtrl *swimctrl = opaque; | |
994af0b2 | 284 | uint8_t latch, reg, ism_bit; |
c701ec62 | 285 | |
994af0b2 MCA |
286 | addr >>= REG_SHIFT; |
287 | ||
288 | /* A3-A1 select a latch, A0 specifies the value */ | |
289 | latch = (addr >> 1) & 7; | |
290 | if (addr & 1) { | |
291 | swimctrl->iwm_latches |= (1 << latch); | |
292 | } else { | |
293 | swimctrl->iwm_latches &= ~(1 << latch); | |
294 | } | |
295 | ||
296 | reg = (swimctrl->iwm_latches & 0xc0) >> 5 | | |
297 | (swimctrl->iwm_latches & 0x10) >> 4; | |
c701ec62 | 298 | |
57004204 MCA |
299 | swimctrl->iwmregs[reg] = value; |
300 | trace_swim_iwmctrl_write(reg, iwm_reg_names[reg], size, value); | |
c701ec62 | 301 | |
994af0b2 MCA |
302 | switch (reg) { |
303 | case IWM_WRITESETMODE: | |
304 | /* detect sequence to switch from IWM mode to SWIM mode */ | |
305 | ism_bit = (value & (1 << SWIM_MODE_STATUS_BIT)); | |
306 | ||
307 | switch (swimctrl->iwm_switch) { | |
308 | case 0: | |
309 | if (ism_bit) { /* 1 */ | |
310 | swimctrl->iwm_switch++; | |
311 | } | |
312 | break; | |
313 | case 1: | |
314 | if (!ism_bit) { /* 0 */ | |
315 | swimctrl->iwm_switch++; | |
316 | } | |
317 | break; | |
318 | case 2: | |
319 | if (ism_bit) { /* 1 */ | |
320 | swimctrl->iwm_switch++; | |
c701ec62 | 321 | } |
994af0b2 MCA |
322 | break; |
323 | case 3: | |
324 | if (ism_bit) { /* 1 */ | |
325 | swimctrl->iwm_switch++; | |
326 | ||
327 | swimctrl->mode = SWIM_MODE_ISM; | |
328 | swimctrl->swim_mode |= (1 << SWIM_MODE_STATUS_BIT); | |
329 | swimctrl->iwm_switch = 0; | |
330 | trace_swim_switch_to_ism(); | |
331 | ||
332 | /* Switch to ISM registers */ | |
333 | memory_region_del_subregion(&swimctrl->swim, &swimctrl->iwm); | |
334 | memory_region_add_subregion(&swimctrl->swim, 0x0, | |
335 | &swimctrl->ism); | |
336 | } | |
337 | break; | |
c701ec62 | 338 | } |
994af0b2 MCA |
339 | break; |
340 | default: | |
341 | break; | |
c701ec62 LV |
342 | } |
343 | } | |
344 | ||
994af0b2 | 345 | static uint64_t iwmctrl_read(void *opaque, hwaddr addr, unsigned size) |
c701ec62 LV |
346 | { |
347 | SWIMCtrl *swimctrl = opaque; | |
994af0b2 | 348 | uint8_t latch, reg, value; |
c701ec62 | 349 | |
994af0b2 | 350 | addr >>= REG_SHIFT; |
c701ec62 | 351 | |
994af0b2 MCA |
352 | /* A3-A1 select a latch, A0 specifies the value */ |
353 | latch = (addr >> 1) & 7; | |
354 | if (addr & 1) { | |
355 | swimctrl->iwm_latches |= (1 << latch); | |
356 | } else { | |
357 | swimctrl->iwm_latches &= ~(1 << latch); | |
358 | } | |
359 | ||
360 | reg = (swimctrl->iwm_latches & 0xc0) >> 5 | | |
361 | (swimctrl->iwm_latches & 0x10) >> 4; | |
362 | ||
363 | switch (reg) { | |
364 | case IWM_READALLONES: | |
365 | value = 0xff; | |
366 | break; | |
367 | default: | |
368 | value = 0; | |
369 | break; | |
370 | } | |
c701ec62 | 371 | |
994af0b2 | 372 | trace_swim_iwmctrl_read(reg, iwm_reg_names[reg], size, value); |
57004204 | 373 | return value; |
c701ec62 LV |
374 | } |
375 | ||
57004204 MCA |
376 | static const MemoryRegionOps swimctrl_iwm_ops = { |
377 | .write = iwmctrl_write, | |
378 | .read = iwmctrl_read, | |
379 | .endianness = DEVICE_BIG_ENDIAN, | |
380 | }; | |
381 | ||
382 | static void ismctrl_write(void *opaque, hwaddr reg, uint64_t value, | |
383 | unsigned size) | |
c701ec62 LV |
384 | { |
385 | SWIMCtrl *swimctrl = opaque; | |
386 | ||
c701ec62 LV |
387 | reg >>= REG_SHIFT; |
388 | ||
994af0b2 | 389 | trace_swim_ismctrl_write(reg, ism_reg_names[reg], size, value); |
d05cad2b | 390 | |
c701ec62 LV |
391 | switch (reg) { |
392 | case SWIM_WRITE_PHASE: | |
393 | swimctrl->swim_phase = value; | |
394 | break; | |
395 | case SWIM_WRITE_MODE0: | |
396 | swimctrl->swim_mode &= ~value; | |
994af0b2 MCA |
397 | /* Any access to MODE0 register resets PRAM index */ |
398 | swimctrl->pram_idx = 0; | |
399 | ||
400 | if (!(swimctrl->swim_mode & (1 << SWIM_MODE_STATUS_BIT))) { | |
401 | /* Clearing the mode bit switches to IWM mode */ | |
402 | swimctrl->mode = SWIM_MODE_IWM; | |
403 | swimctrl->iwm_latches = 0; | |
404 | trace_swim_switch_to_iwm(); | |
405 | ||
406 | /* Switch to IWM registers */ | |
407 | memory_region_del_subregion(&swimctrl->swim, &swimctrl->ism); | |
408 | memory_region_add_subregion(&swimctrl->swim, 0x0, | |
409 | &swimctrl->iwm); | |
410 | } | |
c701ec62 LV |
411 | break; |
412 | case SWIM_WRITE_MODE1: | |
413 | swimctrl->swim_mode |= value; | |
414 | break; | |
994af0b2 MCA |
415 | case SWIM_WRITE_PARAMETER: |
416 | swimctrl->pram[swimctrl->pram_idx++] = value; | |
417 | swimctrl->pram_idx &= 0xf; | |
418 | break; | |
c701ec62 LV |
419 | case SWIM_WRITE_DATA: |
420 | case SWIM_WRITE_MARK: | |
421 | case SWIM_WRITE_CRC: | |
c701ec62 LV |
422 | case SWIM_WRITE_SETUP: |
423 | break; | |
424 | } | |
425 | } | |
426 | ||
57004204 | 427 | static uint64_t ismctrl_read(void *opaque, hwaddr reg, unsigned size) |
c701ec62 LV |
428 | { |
429 | SWIMCtrl *swimctrl = opaque; | |
430 | uint32_t value = 0; | |
431 | ||
c701ec62 LV |
432 | reg >>= REG_SHIFT; |
433 | ||
434 | switch (reg) { | |
435 | case SWIM_READ_PHASE: | |
436 | value = swimctrl->swim_phase; | |
437 | break; | |
438 | case SWIM_READ_HANDSHAKE: | |
439 | if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) { | |
440 | /* always answer "no drive present" */ | |
441 | value = SWIM_SENSE; | |
442 | } | |
443 | break; | |
994af0b2 MCA |
444 | case SWIM_READ_PARAMETER: |
445 | value = swimctrl->pram[swimctrl->pram_idx++]; | |
446 | swimctrl->pram_idx &= 0xf; | |
447 | break; | |
448 | case SWIM_READ_STATUS: | |
449 | value = swimctrl->swim_status & ~(1 << SWIM_MODE_STATUS_BIT); | |
450 | if (swimctrl->swim_mode == SWIM_MODE_ISM) { | |
451 | value |= (1 << SWIM_MODE_STATUS_BIT); | |
452 | } | |
453 | break; | |
c701ec62 LV |
454 | case SWIM_READ_DATA: |
455 | case SWIM_READ_MARK: | |
456 | case SWIM_READ_ERROR: | |
c701ec62 | 457 | case SWIM_READ_SETUP: |
c701ec62 LV |
458 | break; |
459 | } | |
460 | ||
994af0b2 | 461 | trace_swim_ismctrl_read(reg, ism_reg_names[reg], size, value); |
c701ec62 LV |
462 | return value; |
463 | } | |
464 | ||
57004204 MCA |
465 | static const MemoryRegionOps swimctrl_ism_ops = { |
466 | .write = ismctrl_write, | |
467 | .read = ismctrl_read, | |
468 | .endianness = DEVICE_BIG_ENDIAN, | |
c701ec62 LV |
469 | }; |
470 | ||
471 | static void sysbus_swim_reset(DeviceState *d) | |
472 | { | |
b694ed1f | 473 | Swim *sys = SWIM(d); |
c701ec62 LV |
474 | SWIMCtrl *ctrl = &sys->ctrl; |
475 | int i; | |
476 | ||
477 | ctrl->mode = 0; | |
478 | ctrl->iwm_switch = 0; | |
994af0b2 | 479 | memset(ctrl->iwmregs, 0, sizeof(ctrl->iwmregs)); |
57004204 | 480 | |
c701ec62 LV |
481 | ctrl->swim_phase = 0; |
482 | ctrl->swim_mode = 0; | |
994af0b2 | 483 | memset(ctrl->ismregs, 0, sizeof(ctrl->ismregs)); |
c701ec62 LV |
484 | for (i = 0; i < SWIM_MAX_FD; i++) { |
485 | fd_recalibrate(&ctrl->drives[i]); | |
486 | } | |
487 | } | |
488 | ||
489 | static void sysbus_swim_init(Object *obj) | |
490 | { | |
491 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
b694ed1f | 492 | Swim *sbs = SWIM(obj); |
c701ec62 LV |
493 | SWIMCtrl *swimctrl = &sbs->ctrl; |
494 | ||
57004204 MCA |
495 | memory_region_init(&swimctrl->swim, obj, "swim", 0x2000); |
496 | memory_region_init_io(&swimctrl->iwm, obj, &swimctrl_iwm_ops, swimctrl, | |
497 | "iwm", 0x2000); | |
498 | memory_region_init_io(&swimctrl->ism, obj, &swimctrl_ism_ops, swimctrl, | |
499 | "ism", 0x2000); | |
500 | sysbus_init_mmio(sbd, &swimctrl->swim); | |
c701ec62 LV |
501 | } |
502 | ||
503 | static void sysbus_swim_realize(DeviceState *dev, Error **errp) | |
504 | { | |
b694ed1f | 505 | Swim *sys = SWIM(dev); |
c701ec62 LV |
506 | SWIMCtrl *swimctrl = &sys->ctrl; |
507 | ||
d637e1dc | 508 | qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL); |
c701ec62 | 509 | swimctrl->bus.ctrl = swimctrl; |
57004204 MCA |
510 | |
511 | /* Default register set is IWM */ | |
512 | memory_region_add_subregion(&swimctrl->swim, 0x0, &swimctrl->iwm); | |
c701ec62 LV |
513 | } |
514 | ||
515 | static const VMStateDescription vmstate_fdrive = { | |
516 | .name = "fdrive", | |
517 | .version_id = 1, | |
518 | .minimum_version_id = 1, | |
519 | .fields = (VMStateField[]) { | |
520 | VMSTATE_END_OF_LIST() | |
521 | }, | |
522 | }; | |
523 | ||
524 | static const VMStateDescription vmstate_swim = { | |
525 | .name = "swim", | |
526 | .version_id = 1, | |
527 | .minimum_version_id = 1, | |
528 | .fields = (VMStateField[]) { | |
529 | VMSTATE_INT32(mode, SWIMCtrl), | |
530 | /* IWM mode */ | |
531 | VMSTATE_INT32(iwm_switch, SWIMCtrl), | |
994af0b2 MCA |
532 | VMSTATE_UINT8(iwm_latches, SWIMCtrl), |
533 | VMSTATE_UINT8_ARRAY(iwmregs, SWIMCtrl, 8), | |
c701ec62 | 534 | /* SWIM mode */ |
57004204 | 535 | VMSTATE_UINT8_ARRAY(ismregs, SWIMCtrl, 16), |
c701ec62 LV |
536 | VMSTATE_UINT8(swim_phase, SWIMCtrl), |
537 | VMSTATE_UINT8(swim_mode, SWIMCtrl), | |
538 | /* Drives */ | |
539 | VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1, | |
540 | vmstate_fdrive, FDrive), | |
541 | VMSTATE_END_OF_LIST() | |
542 | }, | |
543 | }; | |
544 | ||
545 | static const VMStateDescription vmstate_sysbus_swim = { | |
546 | .name = "SWIM", | |
547 | .version_id = 1, | |
548 | .fields = (VMStateField[]) { | |
b694ed1f | 549 | VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl), |
c701ec62 LV |
550 | VMSTATE_END_OF_LIST() |
551 | } | |
552 | }; | |
553 | ||
554 | static void sysbus_swim_class_init(ObjectClass *oc, void *data) | |
555 | { | |
556 | DeviceClass *dc = DEVICE_CLASS(oc); | |
557 | ||
558 | dc->realize = sysbus_swim_realize; | |
559 | dc->reset = sysbus_swim_reset; | |
560 | dc->vmsd = &vmstate_sysbus_swim; | |
561 | } | |
562 | ||
563 | static const TypeInfo sysbus_swim_info = { | |
564 | .name = TYPE_SWIM, | |
565 | .parent = TYPE_SYS_BUS_DEVICE, | |
b694ed1f | 566 | .instance_size = sizeof(Swim), |
c701ec62 LV |
567 | .instance_init = sysbus_swim_init, |
568 | .class_init = sysbus_swim_class_init, | |
569 | }; | |
570 | ||
571 | static void swim_register_types(void) | |
572 | { | |
573 | type_register_static(&sysbus_swim_info); | |
574 | type_register_static(&swim_bus_info); | |
575 | type_register_static(&swim_drive_info); | |
576 | } | |
577 | ||
578 | type_init(swim_register_types) |