2 * QEMU Macintosh floppy disk controller emulator (SWIM)
4 * Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
6 * This work is licensed under the terms of the GNU GPL, version 2. See
7 * the COPYING file in the top-level directory.
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.
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"
35 #define IWM_INTDRIVE 10
36 #define IWM_EXTDRIVE 11
44 #define SWIM_WRITE_DATA 0
45 #define SWIM_WRITE_MARK 1
46 #define SWIM_WRITE_CRC 2
47 #define SWIM_WRITE_PARAMETER 3
48 #define SWIM_WRITE_PHASE 4
49 #define SWIM_WRITE_SETUP 5
50 #define SWIM_WRITE_MODE0 6
51 #define SWIM_WRITE_MODE1 7
53 #define SWIM_READ_DATA 8
54 #define SWIM_READ_MARK 9
55 #define SWIM_READ_ERROR 10
56 #define SWIM_READ_PARAMETER 11
57 #define SWIM_READ_PHASE 12
58 #define SWIM_READ_SETUP 13
59 #define SWIM_READ_STATUS 14
60 #define SWIM_READ_HANDSHAKE 15
64 #define SWIM_MODE_IWM 0
65 #define SWIM_MODE_SWIM 1
67 /* bits in phase register */
69 #define SWIM_SEEK_NEGATIVE 0x074
70 #define SWIM_STEP 0x071
71 #define SWIM_MOTOR_ON 0x072
72 #define SWIM_MOTOR_OFF 0x076
73 #define SWIM_INDEX 0x073
74 #define SWIM_EJECT 0x077
75 #define SWIM_SETMFM 0x171
76 #define SWIM_SETGCR 0x175
77 #define SWIM_RELAX 0x033
78 #define SWIM_LSTRB 0x008
79 #define SWIM_CA_MASK 0x077
81 /* Select values for swim_select and swim_readbit */
83 #define SWIM_READ_DATA_0 0x074
84 #define SWIM_TWOMEG_DRIVE 0x075
85 #define SWIM_SINGLE_SIDED 0x076
86 #define SWIM_DRIVE_PRESENT 0x077
87 #define SWIM_DISK_IN 0x170
88 #define SWIM_WRITE_PROT 0x171
89 #define SWIM_TRACK_ZERO 0x172
90 #define SWIM_TACHO 0x173
91 #define SWIM_READ_DATA_1 0x174
92 #define SWIM_MFM_MODE 0x175
93 #define SWIM_SEEK_COMPLETE 0x176
94 #define SWIM_ONEMEG_MEDIA 0x177
96 /* Bits in handshake register */
98 #define SWIM_MARK_BYTE 0x01
99 #define SWIM_CRC_ZERO 0x02
100 #define SWIM_RDDATA 0x04
101 #define SWIM_SENSE 0x08
102 #define SWIM_MOTEN 0x10
103 #define SWIM_ERROR 0x20
104 #define SWIM_DAT2BYTE 0x40
105 #define SWIM_DAT1BYTE 0x80
107 /* bits in setup register */
109 #define SWIM_S_INV_WDATA 0x01
110 #define SWIM_S_3_5_SELECT 0x02
111 #define SWIM_S_GCR 0x04
112 #define SWIM_S_FCLK_DIV2 0x08
113 #define SWIM_S_ERROR_CORR 0x10
114 #define SWIM_S_IBM_DRIVE 0x20
115 #define SWIM_S_GCR_WRITE 0x40
116 #define SWIM_S_TIMEOUT 0x80
118 /* bits in mode register */
120 #define SWIM_CLFIFO 0x01
121 #define SWIM_ENBL1 0x02
122 #define SWIM_ENBL2 0x04
123 #define SWIM_ACTION 0x08
124 #define SWIM_WRITE_MODE 0x10
125 #define SWIM_HEDSEL 0x20
126 #define SWIM_MOTON 0x80
128 static void fd_recalibrate(FDrive
*drive
)
132 static void swim_change_cb(void *opaque
, bool load
, Error
**errp
)
134 FDrive
*drive
= opaque
;
137 blk_set_perm(drive
->blk
, 0, BLK_PERM_ALL
, &error_abort
);
139 if (!blkconf_apply_backend_options(drive
->conf
,
140 !blk_supports_write_perm(drive
->blk
),
147 static const BlockDevOps swim_block_ops
= {
148 .change_media_cb
= swim_change_cb
,
151 static Property swim_drive_properties
[] = {
152 DEFINE_PROP_INT32("unit", SWIMDrive
, unit
, -1),
153 DEFINE_BLOCK_PROPERTIES(SWIMDrive
, conf
),
154 DEFINE_PROP_END_OF_LIST(),
157 static void swim_drive_realize(DeviceState
*qdev
, Error
**errp
)
159 SWIMDrive
*dev
= SWIM_DRIVE(qdev
);
160 SWIMBus
*bus
= SWIM_BUS(qdev
->parent_bus
);
164 if (dev
->unit
== -1) {
165 for (dev
->unit
= 0; dev
->unit
< SWIM_MAX_FD
; dev
->unit
++) {
166 drive
= &bus
->ctrl
->drives
[dev
->unit
];
173 if (dev
->unit
>= SWIM_MAX_FD
) {
174 error_setg(errp
, "Can't create floppy unit %d, bus supports "
175 "only %d units", dev
->unit
, SWIM_MAX_FD
);
179 drive
= &bus
->ctrl
->drives
[dev
->unit
];
181 error_setg(errp
, "Floppy unit %d is in use", dev
->unit
);
185 if (!dev
->conf
.blk
) {
186 /* Anonymous BlockBackend for an empty drive */
187 dev
->conf
.blk
= blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL
);
188 ret
= blk_attach_dev(dev
->conf
.blk
, qdev
);
192 if (!blkconf_blocksizes(&dev
->conf
, errp
)) {
196 if (dev
->conf
.logical_block_size
!= 512 ||
197 dev
->conf
.physical_block_size
!= 512)
199 error_setg(errp
, "Physical and logical block size must "
200 "be 512 for floppy");
205 * rerror/werror aren't supported by fdc and therefore not even registered
206 * with qdev. So set the defaults manually before they are used in
207 * blkconf_apply_backend_options().
209 dev
->conf
.rerror
= BLOCKDEV_ON_ERROR_AUTO
;
210 dev
->conf
.werror
= BLOCKDEV_ON_ERROR_AUTO
;
212 if (!blkconf_apply_backend_options(&dev
->conf
,
213 !blk_supports_write_perm(dev
->conf
.blk
),
219 * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
222 if (blk_get_on_error(dev
->conf
.blk
, 0) != BLOCKDEV_ON_ERROR_ENOSPC
&&
223 blk_get_on_error(dev
->conf
.blk
, 0) != BLOCKDEV_ON_ERROR_REPORT
) {
224 error_setg(errp
, "fdc doesn't support drive option werror");
227 if (blk_get_on_error(dev
->conf
.blk
, 1) != BLOCKDEV_ON_ERROR_REPORT
) {
228 error_setg(errp
, "fdc doesn't support drive option rerror");
232 drive
->conf
= &dev
->conf
;
233 drive
->blk
= dev
->conf
.blk
;
234 drive
->swimctrl
= bus
->ctrl
;
236 blk_set_dev_ops(drive
->blk
, &swim_block_ops
, drive
);
239 static void swim_drive_class_init(ObjectClass
*klass
, void *data
)
241 DeviceClass
*k
= DEVICE_CLASS(klass
);
242 k
->realize
= swim_drive_realize
;
243 set_bit(DEVICE_CATEGORY_STORAGE
, k
->categories
);
244 k
->bus_type
= TYPE_SWIM_BUS
;
245 device_class_set_props(k
, swim_drive_properties
);
246 k
->desc
= "virtual SWIM drive";
249 static const TypeInfo swim_drive_info
= {
250 .name
= TYPE_SWIM_DRIVE
,
251 .parent
= TYPE_DEVICE
,
252 .instance_size
= sizeof(SWIMDrive
),
253 .class_init
= swim_drive_class_init
,
256 static const TypeInfo swim_bus_info
= {
257 .name
= TYPE_SWIM_BUS
,
259 .instance_size
= sizeof(SWIMBus
),
262 static void iwmctrl_write(void *opaque
, hwaddr reg
, uint64_t value
,
265 SWIMCtrl
*swimctrl
= opaque
;
269 swimctrl
->regs
[reg
>> 1] = reg
& 1;
271 if (swimctrl
->regs
[IWM_Q6
] &&
272 swimctrl
->regs
[IWM_Q7
]) {
273 if (swimctrl
->regs
[IWM_MTR
]) {
275 swimctrl
->iwm_data
= value
;
278 swimctrl
->iwm_mode
= value
;
279 /* detect sequence to switch from IWM mode to SWIM mode */
280 switch (swimctrl
->iwm_switch
) {
283 swimctrl
->iwm_switch
++;
288 swimctrl
->iwm_switch
++;
293 swimctrl
->iwm_switch
++;
298 swimctrl
->mode
= SWIM_MODE_SWIM
;
299 swimctrl
->iwm_switch
= 0;
307 static uint64_t iwmctrl_read(void *opaque
, hwaddr reg
, unsigned size
)
309 SWIMCtrl
*swimctrl
= opaque
;
313 swimctrl
->regs
[reg
>> 1] = reg
& 1;
318 static void swimctrl_write(void *opaque
, hwaddr reg
, uint64_t value
,
321 SWIMCtrl
*swimctrl
= opaque
;
323 if (swimctrl
->mode
== SWIM_MODE_IWM
) {
324 iwmctrl_write(opaque
, reg
, value
, size
);
331 case SWIM_WRITE_PHASE
:
332 swimctrl
->swim_phase
= value
;
334 case SWIM_WRITE_MODE0
:
335 swimctrl
->swim_mode
&= ~value
;
337 case SWIM_WRITE_MODE1
:
338 swimctrl
->swim_mode
|= value
;
340 case SWIM_WRITE_DATA
:
341 case SWIM_WRITE_MARK
:
343 case SWIM_WRITE_PARAMETER
:
344 case SWIM_WRITE_SETUP
:
349 static uint64_t swimctrl_read(void *opaque
, hwaddr reg
, unsigned size
)
351 SWIMCtrl
*swimctrl
= opaque
;
354 if (swimctrl
->mode
== SWIM_MODE_IWM
) {
355 return iwmctrl_read(opaque
, reg
, size
);
361 case SWIM_READ_PHASE
:
362 value
= swimctrl
->swim_phase
;
364 case SWIM_READ_HANDSHAKE
:
365 if (swimctrl
->swim_phase
== SWIM_DRIVE_PRESENT
) {
366 /* always answer "no drive present" */
372 case SWIM_READ_ERROR
:
373 case SWIM_READ_PARAMETER
:
374 case SWIM_READ_SETUP
:
375 case SWIM_READ_STATUS
:
382 static const MemoryRegionOps swimctrl_mem_ops
= {
383 .write
= swimctrl_write
,
384 .read
= swimctrl_read
,
385 .endianness
= DEVICE_NATIVE_ENDIAN
,
388 static void sysbus_swim_reset(DeviceState
*d
)
391 SWIMCtrl
*ctrl
= &sys
->ctrl
;
395 ctrl
->iwm_switch
= 0;
396 for (i
= 0; i
< 8; i
++) {
401 ctrl
->swim_phase
= 0;
403 for (i
= 0; i
< SWIM_MAX_FD
; i
++) {
404 fd_recalibrate(&ctrl
->drives
[i
]);
408 static void sysbus_swim_init(Object
*obj
)
410 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
411 Swim
*sbs
= SWIM(obj
);
412 SWIMCtrl
*swimctrl
= &sbs
->ctrl
;
414 memory_region_init_io(&swimctrl
->iomem
, obj
, &swimctrl_mem_ops
, swimctrl
,
416 sysbus_init_mmio(sbd
, &swimctrl
->iomem
);
419 static void sysbus_swim_realize(DeviceState
*dev
, Error
**errp
)
421 Swim
*sys
= SWIM(dev
);
422 SWIMCtrl
*swimctrl
= &sys
->ctrl
;
424 qbus_init(&swimctrl
->bus
, sizeof(SWIMBus
), TYPE_SWIM_BUS
, dev
, NULL
);
425 swimctrl
->bus
.ctrl
= swimctrl
;
428 static const VMStateDescription vmstate_fdrive
= {
431 .minimum_version_id
= 1,
432 .fields
= (VMStateField
[]) {
433 VMSTATE_END_OF_LIST()
437 static const VMStateDescription vmstate_swim
= {
440 .minimum_version_id
= 1,
441 .fields
= (VMStateField
[]) {
442 VMSTATE_INT32(mode
, SWIMCtrl
),
444 VMSTATE_INT32(iwm_switch
, SWIMCtrl
),
445 VMSTATE_UINT16_ARRAY(regs
, SWIMCtrl
, 8),
446 VMSTATE_UINT8(iwm_data
, SWIMCtrl
),
447 VMSTATE_UINT8(iwm_mode
, SWIMCtrl
),
449 VMSTATE_UINT8(swim_phase
, SWIMCtrl
),
450 VMSTATE_UINT8(swim_mode
, SWIMCtrl
),
452 VMSTATE_STRUCT_ARRAY(drives
, SWIMCtrl
, SWIM_MAX_FD
, 1,
453 vmstate_fdrive
, FDrive
),
454 VMSTATE_END_OF_LIST()
458 static const VMStateDescription vmstate_sysbus_swim
= {
461 .fields
= (VMStateField
[]) {
462 VMSTATE_STRUCT(ctrl
, Swim
, 0, vmstate_swim
, SWIMCtrl
),
463 VMSTATE_END_OF_LIST()
467 static void sysbus_swim_class_init(ObjectClass
*oc
, void *data
)
469 DeviceClass
*dc
= DEVICE_CLASS(oc
);
471 dc
->realize
= sysbus_swim_realize
;
472 dc
->reset
= sysbus_swim_reset
;
473 dc
->vmsd
= &vmstate_sysbus_swim
;
476 static const TypeInfo sysbus_swim_info
= {
478 .parent
= TYPE_SYS_BUS_DEVICE
,
479 .instance_size
= sizeof(Swim
),
480 .instance_init
= sysbus_swim_init
,
481 .class_init
= sysbus_swim_class_init
,
484 static void swim_register_types(void)
486 type_register_static(&sysbus_swim_info
);
487 type_register_static(&swim_bus_info
);
488 type_register_static(&swim_drive_info
);
491 type_init(swim_register_types
)