xref: /openbmc/qemu/hw/block/swim.c (revision f101c9fe)
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"
22 
23 /* IWM registers */
24 
25 #define IWM_PH0L                0
26 #define IWM_PH0H                1
27 #define IWM_PH1L                2
28 #define IWM_PH1H                3
29 #define IWM_PH2L                4
30 #define IWM_PH2H                5
31 #define IWM_PH3L                6
32 #define IWM_PH3H                7
33 #define IWM_MTROFF              8
34 #define IWM_MTRON               9
35 #define IWM_INTDRIVE            10
36 #define IWM_EXTDRIVE            11
37 #define IWM_Q6L                 12
38 #define IWM_Q6H                 13
39 #define IWM_Q7L                 14
40 #define IWM_Q7H                 15
41 
42 /* SWIM registers */
43 
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
52 
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
61 
62 #define REG_SHIFT               9
63 
64 #define SWIM_MODE_IWM  0
65 #define SWIM_MODE_SWIM 1
66 
67 /* bits in phase register */
68 
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
80 
81 /* Select values for swim_select and swim_readbit */
82 
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
95 
96 /* Bits in handshake register */
97 
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
106 
107 /* bits in setup register */
108 
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
117 
118 /* bits in mode register */
119 
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
127 
128 static void fd_recalibrate(FDrive *drive)
129 {
130 }
131 
132 static void swim_change_cb(void *opaque, bool load, Error **errp)
133 {
134     FDrive *drive = opaque;
135 
136     if (!load) {
137         blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
138     } else {
139         if (!blkconf_apply_backend_options(drive->conf,
140                                            !blk_supports_write_perm(drive->blk),
141                                            false, errp)) {
142             return;
143         }
144     }
145 }
146 
147 static const BlockDevOps swim_block_ops = {
148     .change_media_cb = swim_change_cb,
149 };
150 
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(),
155 };
156 
157 static void swim_drive_realize(DeviceState *qdev, Error **errp)
158 {
159     SWIMDrive *dev = SWIM_DRIVE(qdev);
160     SWIMBus *bus = SWIM_BUS(qdev->parent_bus);
161     FDrive *drive;
162     int ret;
163 
164     if (dev->unit == -1) {
165         for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) {
166             drive = &bus->ctrl->drives[dev->unit];
167             if (!drive->blk) {
168                 break;
169             }
170         }
171     }
172 
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);
176         return;
177     }
178 
179     drive = &bus->ctrl->drives[dev->unit];
180     if (drive->blk) {
181         error_setg(errp, "Floppy unit %d is in use", dev->unit);
182         return;
183     }
184 
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);
189         assert(ret == 0);
190     }
191 
192     if (!blkconf_blocksizes(&dev->conf, errp)) {
193         return;
194     }
195 
196     if (dev->conf.logical_block_size != 512 ||
197         dev->conf.physical_block_size != 512)
198     {
199         error_setg(errp, "Physical and logical block size must "
200                    "be 512 for floppy");
201         return;
202     }
203 
204     /*
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().
208      */
209     dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
210     dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
211 
212     if (!blkconf_apply_backend_options(&dev->conf,
213                                        !blk_supports_write_perm(dev->conf.blk),
214                                        false, errp)) {
215         return;
216     }
217 
218     /*
219      * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
220      * for empty drives.
221      */
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");
225         return;
226     }
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");
229         return;
230     }
231 
232     drive->conf = &dev->conf;
233     drive->blk = dev->conf.blk;
234     drive->swimctrl = bus->ctrl;
235 
236     blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
237 }
238 
239 static void swim_drive_class_init(ObjectClass *klass, void *data)
240 {
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";
247 }
248 
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,
254 };
255 
256 static const TypeInfo swim_bus_info = {
257     .name = TYPE_SWIM_BUS,
258     .parent = TYPE_BUS,
259     .instance_size = sizeof(SWIMBus),
260 };
261 
262 static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value,
263                           unsigned size)
264 {
265     SWIMCtrl *swimctrl = opaque;
266 
267     reg >>= REG_SHIFT;
268 
269     swimctrl->regs[reg >> 1] = reg & 1;
270 
271     if (swimctrl->regs[IWM_Q6] &&
272         swimctrl->regs[IWM_Q7]) {
273         if (swimctrl->regs[IWM_MTR]) {
274             /* data register */
275             swimctrl->iwm_data = value;
276         } else {
277             /* mode register */
278             swimctrl->iwm_mode = value;
279             /* detect sequence to switch from IWM mode to SWIM mode */
280             switch (swimctrl->iwm_switch) {
281             case 0:
282                 if (value == 0x57) {
283                     swimctrl->iwm_switch++;
284                 }
285                 break;
286             case 1:
287                 if (value == 0x17) {
288                     swimctrl->iwm_switch++;
289                 }
290                 break;
291             case 2:
292                 if (value == 0x57) {
293                     swimctrl->iwm_switch++;
294                 }
295                 break;
296             case 3:
297                 if (value == 0x57) {
298                     swimctrl->mode = SWIM_MODE_SWIM;
299                     swimctrl->iwm_switch = 0;
300                 }
301                 break;
302             }
303         }
304     }
305 }
306 
307 static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size)
308 {
309     SWIMCtrl *swimctrl = opaque;
310 
311     reg >>= REG_SHIFT;
312 
313     swimctrl->regs[reg >> 1] = reg & 1;
314 
315     return 0;
316 }
317 
318 static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value,
319                            unsigned size)
320 {
321     SWIMCtrl *swimctrl = opaque;
322 
323     if (swimctrl->mode == SWIM_MODE_IWM) {
324         iwmctrl_write(opaque, reg, value, size);
325         return;
326     }
327 
328     reg >>= REG_SHIFT;
329 
330     switch (reg) {
331     case SWIM_WRITE_PHASE:
332         swimctrl->swim_phase = value;
333         break;
334     case SWIM_WRITE_MODE0:
335         swimctrl->swim_mode &= ~value;
336         break;
337     case SWIM_WRITE_MODE1:
338         swimctrl->swim_mode |= value;
339         break;
340     case SWIM_WRITE_DATA:
341     case SWIM_WRITE_MARK:
342     case SWIM_WRITE_CRC:
343     case SWIM_WRITE_PARAMETER:
344     case SWIM_WRITE_SETUP:
345         break;
346     }
347 }
348 
349 static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size)
350 {
351     SWIMCtrl *swimctrl = opaque;
352     uint32_t value = 0;
353 
354     if (swimctrl->mode == SWIM_MODE_IWM) {
355         return iwmctrl_read(opaque, reg, size);
356     }
357 
358     reg >>= REG_SHIFT;
359 
360     switch (reg) {
361     case SWIM_READ_PHASE:
362         value = swimctrl->swim_phase;
363         break;
364     case SWIM_READ_HANDSHAKE:
365         if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
366             /* always answer "no drive present" */
367             value = SWIM_SENSE;
368         }
369         break;
370     case SWIM_READ_DATA:
371     case SWIM_READ_MARK:
372     case SWIM_READ_ERROR:
373     case SWIM_READ_PARAMETER:
374     case SWIM_READ_SETUP:
375     case SWIM_READ_STATUS:
376         break;
377     }
378 
379     return value;
380 }
381 
382 static const MemoryRegionOps swimctrl_mem_ops = {
383     .write = swimctrl_write,
384     .read = swimctrl_read,
385     .endianness = DEVICE_NATIVE_ENDIAN,
386 };
387 
388 static void sysbus_swim_reset(DeviceState *d)
389 {
390     Swim *sys = SWIM(d);
391     SWIMCtrl *ctrl = &sys->ctrl;
392     int i;
393 
394     ctrl->mode = 0;
395     ctrl->iwm_switch = 0;
396     for (i = 0; i < 8; i++) {
397         ctrl->regs[i] = 0;
398     }
399     ctrl->iwm_data = 0;
400     ctrl->iwm_mode = 0;
401     ctrl->swim_phase = 0;
402     ctrl->swim_mode = 0;
403     for (i = 0; i < SWIM_MAX_FD; i++) {
404         fd_recalibrate(&ctrl->drives[i]);
405     }
406 }
407 
408 static void sysbus_swim_init(Object *obj)
409 {
410     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
411     Swim *sbs = SWIM(obj);
412     SWIMCtrl *swimctrl = &sbs->ctrl;
413 
414     memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl,
415                           "swim", 0x2000);
416     sysbus_init_mmio(sbd, &swimctrl->iomem);
417 }
418 
419 static void sysbus_swim_realize(DeviceState *dev, Error **errp)
420 {
421     Swim *sys = SWIM(dev);
422     SWIMCtrl *swimctrl = &sys->ctrl;
423 
424     qbus_create_inplace(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev,
425                         NULL);
426     swimctrl->bus.ctrl = swimctrl;
427 }
428 
429 static const VMStateDescription vmstate_fdrive = {
430     .name = "fdrive",
431     .version_id = 1,
432     .minimum_version_id = 1,
433     .fields = (VMStateField[]) {
434         VMSTATE_END_OF_LIST()
435     },
436 };
437 
438 static const VMStateDescription vmstate_swim = {
439     .name = "swim",
440     .version_id = 1,
441     .minimum_version_id = 1,
442     .fields = (VMStateField[]) {
443         VMSTATE_INT32(mode, SWIMCtrl),
444         /* IWM mode */
445         VMSTATE_INT32(iwm_switch, SWIMCtrl),
446         VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8),
447         VMSTATE_UINT8(iwm_data, SWIMCtrl),
448         VMSTATE_UINT8(iwm_mode, SWIMCtrl),
449         /* SWIM mode */
450         VMSTATE_UINT8(swim_phase, SWIMCtrl),
451         VMSTATE_UINT8(swim_mode, SWIMCtrl),
452         /* Drives */
453         VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
454                              vmstate_fdrive, FDrive),
455         VMSTATE_END_OF_LIST()
456     },
457 };
458 
459 static const VMStateDescription vmstate_sysbus_swim = {
460     .name = "SWIM",
461     .version_id = 1,
462     .fields = (VMStateField[]) {
463         VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl),
464         VMSTATE_END_OF_LIST()
465     }
466 };
467 
468 static void sysbus_swim_class_init(ObjectClass *oc, void *data)
469 {
470     DeviceClass *dc = DEVICE_CLASS(oc);
471 
472     dc->realize = sysbus_swim_realize;
473     dc->reset = sysbus_swim_reset;
474     dc->vmsd = &vmstate_sysbus_swim;
475 }
476 
477 static const TypeInfo sysbus_swim_info = {
478     .name          = TYPE_SWIM,
479     .parent        = TYPE_SYS_BUS_DEVICE,
480     .instance_size = sizeof(Swim),
481     .instance_init = sysbus_swim_init,
482     .class_init    = sysbus_swim_class_init,
483 };
484 
485 static void swim_register_types(void)
486 {
487     type_register_static(&sysbus_swim_info);
488     type_register_static(&swim_bus_info);
489     type_register_static(&swim_drive_info);
490 }
491 
492 type_init(swim_register_types)
493