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_is_read_only(drive
->blk
), false,
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 blkconf_blocksizes(&dev
->conf
);
193 if (dev
->conf
.logical_block_size
!= 512 ||
194 dev
->conf
.physical_block_size
!= 512)
196 error_setg(errp
, "Physical and logical block size must "
197 "be 512 for floppy");
202 * rerror/werror aren't supported by fdc and therefore not even registered
203 * with qdev. So set the defaults manually before they are used in
204 * blkconf_apply_backend_options().
206 dev
->conf
.rerror
= BLOCKDEV_ON_ERROR_AUTO
;
207 dev
->conf
.werror
= BLOCKDEV_ON_ERROR_AUTO
;
209 if (!blkconf_apply_backend_options(&dev
->conf
,
210 blk_is_read_only(dev
->conf
.blk
),
216 * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
219 if (blk_get_on_error(dev
->conf
.blk
, 0) != BLOCKDEV_ON_ERROR_ENOSPC
&&
220 blk_get_on_error(dev
->conf
.blk
, 0) != BLOCKDEV_ON_ERROR_REPORT
) {
221 error_setg(errp
, "fdc doesn't support drive option werror");
224 if (blk_get_on_error(dev
->conf
.blk
, 1) != BLOCKDEV_ON_ERROR_REPORT
) {
225 error_setg(errp
, "fdc doesn't support drive option rerror");
229 drive
->conf
= &dev
->conf
;
230 drive
->blk
= dev
->conf
.blk
;
231 drive
->swimctrl
= bus
->ctrl
;
233 blk_set_dev_ops(drive
->blk
, &swim_block_ops
, drive
);
236 static void swim_drive_class_init(ObjectClass
*klass
, void *data
)
238 DeviceClass
*k
= DEVICE_CLASS(klass
);
239 k
->realize
= swim_drive_realize
;
240 set_bit(DEVICE_CATEGORY_STORAGE
, k
->categories
);
241 k
->bus_type
= TYPE_SWIM_BUS
;
242 device_class_set_props(k
, swim_drive_properties
);
243 k
->desc
= "virtual SWIM drive";
246 static const TypeInfo swim_drive_info
= {
247 .name
= TYPE_SWIM_DRIVE
,
248 .parent
= TYPE_DEVICE
,
249 .instance_size
= sizeof(SWIMDrive
),
250 .class_init
= swim_drive_class_init
,
253 static const TypeInfo swim_bus_info
= {
254 .name
= TYPE_SWIM_BUS
,
256 .instance_size
= sizeof(SWIMBus
),
259 static void iwmctrl_write(void *opaque
, hwaddr reg
, uint64_t value
,
262 SWIMCtrl
*swimctrl
= opaque
;
266 swimctrl
->regs
[reg
>> 1] = reg
& 1;
268 if (swimctrl
->regs
[IWM_Q6
] &&
269 swimctrl
->regs
[IWM_Q7
]) {
270 if (swimctrl
->regs
[IWM_MTR
]) {
272 swimctrl
->iwm_data
= value
;
275 swimctrl
->iwm_mode
= value
;
276 /* detect sequence to switch from IWM mode to SWIM mode */
277 switch (swimctrl
->iwm_switch
) {
280 swimctrl
->iwm_switch
++;
285 swimctrl
->iwm_switch
++;
290 swimctrl
->iwm_switch
++;
295 swimctrl
->mode
= SWIM_MODE_SWIM
;
296 swimctrl
->iwm_switch
= 0;
304 static uint64_t iwmctrl_read(void *opaque
, hwaddr reg
, unsigned size
)
306 SWIMCtrl
*swimctrl
= opaque
;
310 swimctrl
->regs
[reg
>> 1] = reg
& 1;
315 static void swimctrl_write(void *opaque
, hwaddr reg
, uint64_t value
,
318 SWIMCtrl
*swimctrl
= opaque
;
320 if (swimctrl
->mode
== SWIM_MODE_IWM
) {
321 iwmctrl_write(opaque
, reg
, value
, size
);
328 case SWIM_WRITE_PHASE
:
329 swimctrl
->swim_phase
= value
;
331 case SWIM_WRITE_MODE0
:
332 swimctrl
->swim_mode
&= ~value
;
334 case SWIM_WRITE_MODE1
:
335 swimctrl
->swim_mode
|= value
;
337 case SWIM_WRITE_DATA
:
338 case SWIM_WRITE_MARK
:
340 case SWIM_WRITE_PARAMETER
:
341 case SWIM_WRITE_SETUP
:
346 static uint64_t swimctrl_read(void *opaque
, hwaddr reg
, unsigned size
)
348 SWIMCtrl
*swimctrl
= opaque
;
351 if (swimctrl
->mode
== SWIM_MODE_IWM
) {
352 return iwmctrl_read(opaque
, reg
, size
);
358 case SWIM_READ_PHASE
:
359 value
= swimctrl
->swim_phase
;
361 case SWIM_READ_HANDSHAKE
:
362 if (swimctrl
->swim_phase
== SWIM_DRIVE_PRESENT
) {
363 /* always answer "no drive present" */
369 case SWIM_READ_ERROR
:
370 case SWIM_READ_PARAMETER
:
371 case SWIM_READ_SETUP
:
372 case SWIM_READ_STATUS
:
379 static const MemoryRegionOps swimctrl_mem_ops
= {
380 .write
= swimctrl_write
,
381 .read
= swimctrl_read
,
382 .endianness
= DEVICE_NATIVE_ENDIAN
,
385 static void sysbus_swim_reset(DeviceState
*d
)
388 SWIMCtrl
*ctrl
= &sys
->ctrl
;
392 ctrl
->iwm_switch
= 0;
393 for (i
= 0; i
< 8; i
++) {
398 ctrl
->swim_phase
= 0;
400 for (i
= 0; i
< SWIM_MAX_FD
; i
++) {
401 fd_recalibrate(&ctrl
->drives
[i
]);
405 static void sysbus_swim_init(Object
*obj
)
407 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
408 SWIM
*sbs
= SWIM(obj
);
409 SWIMCtrl
*swimctrl
= &sbs
->ctrl
;
411 memory_region_init_io(&swimctrl
->iomem
, obj
, &swimctrl_mem_ops
, swimctrl
,
413 sysbus_init_mmio(sbd
, &swimctrl
->iomem
);
416 static void sysbus_swim_realize(DeviceState
*dev
, Error
**errp
)
418 SWIM
*sys
= SWIM(dev
);
419 SWIMCtrl
*swimctrl
= &sys
->ctrl
;
421 qbus_create_inplace(&swimctrl
->bus
, sizeof(SWIMBus
), TYPE_SWIM_BUS
, dev
,
423 swimctrl
->bus
.ctrl
= swimctrl
;
426 static const VMStateDescription vmstate_fdrive
= {
429 .minimum_version_id
= 1,
430 .fields
= (VMStateField
[]) {
431 VMSTATE_END_OF_LIST()
435 static const VMStateDescription vmstate_swim
= {
438 .minimum_version_id
= 1,
439 .fields
= (VMStateField
[]) {
440 VMSTATE_INT32(mode
, SWIMCtrl
),
442 VMSTATE_INT32(iwm_switch
, SWIMCtrl
),
443 VMSTATE_UINT16_ARRAY(regs
, SWIMCtrl
, 8),
444 VMSTATE_UINT8(iwm_data
, SWIMCtrl
),
445 VMSTATE_UINT8(iwm_mode
, SWIMCtrl
),
447 VMSTATE_UINT8(swim_phase
, SWIMCtrl
),
448 VMSTATE_UINT8(swim_mode
, SWIMCtrl
),
450 VMSTATE_STRUCT_ARRAY(drives
, SWIMCtrl
, SWIM_MAX_FD
, 1,
451 vmstate_fdrive
, FDrive
),
452 VMSTATE_END_OF_LIST()
456 static const VMStateDescription vmstate_sysbus_swim
= {
459 .fields
= (VMStateField
[]) {
460 VMSTATE_STRUCT(ctrl
, SWIM
, 0, vmstate_swim
, SWIMCtrl
),
461 VMSTATE_END_OF_LIST()
465 static void sysbus_swim_class_init(ObjectClass
*oc
, void *data
)
467 DeviceClass
*dc
= DEVICE_CLASS(oc
);
469 dc
->realize
= sysbus_swim_realize
;
470 dc
->reset
= sysbus_swim_reset
;
471 dc
->vmsd
= &vmstate_sysbus_swim
;
474 static const TypeInfo sysbus_swim_info
= {
476 .parent
= TYPE_SYS_BUS_DEVICE
,
477 .instance_size
= sizeof(SWIM
),
478 .instance_init
= sysbus_swim_init
,
479 .class_init
= sysbus_swim_class_init
,
482 static void swim_register_types(void)
484 type_register_static(&sysbus_swim_info
);
485 type_register_static(&swim_bus_info
);
486 type_register_static(&swim_drive_info
);
489 type_init(swim_register_types
)