xref: /openbmc/qemu/hw/block/swim.c (revision 988717b46b6424907618cb845ace9d69062703af)
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_is_read_only(drive->blk), false,
141                                             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      blkconf_blocksizes(&dev->conf);
193      if (dev->conf.logical_block_size != 512 ||
194          dev->conf.physical_block_size != 512)
195      {
196          error_setg(errp, "Physical and logical block size must "
197                     "be 512 for floppy");
198          return;
199      }
200  
201      /*
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().
205       */
206      dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
207      dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
208  
209      if (!blkconf_apply_backend_options(&dev->conf,
210                                         blk_is_read_only(dev->conf.blk),
211                                         false, errp)) {
212          return;
213      }
214  
215      /*
216       * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
217       * for empty drives.
218       */
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");
222          return;
223      }
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");
226          return;
227      }
228  
229      drive->conf = &dev->conf;
230      drive->blk = dev->conf.blk;
231      drive->swimctrl = bus->ctrl;
232  
233      blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
234  }
235  
236  static void swim_drive_class_init(ObjectClass *klass, void *data)
237  {
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";
244  }
245  
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,
251  };
252  
253  static const TypeInfo swim_bus_info = {
254      .name = TYPE_SWIM_BUS,
255      .parent = TYPE_BUS,
256      .instance_size = sizeof(SWIMBus),
257  };
258  
259  static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value,
260                            unsigned size)
261  {
262      SWIMCtrl *swimctrl = opaque;
263  
264      reg >>= REG_SHIFT;
265  
266      swimctrl->regs[reg >> 1] = reg & 1;
267  
268      if (swimctrl->regs[IWM_Q6] &&
269          swimctrl->regs[IWM_Q7]) {
270          if (swimctrl->regs[IWM_MTR]) {
271              /* data register */
272              swimctrl->iwm_data = value;
273          } else {
274              /* mode register */
275              swimctrl->iwm_mode = value;
276              /* detect sequence to switch from IWM mode to SWIM mode */
277              switch (swimctrl->iwm_switch) {
278              case 0:
279                  if (value == 0x57) {
280                      swimctrl->iwm_switch++;
281                  }
282                  break;
283              case 1:
284                  if (value == 0x17) {
285                      swimctrl->iwm_switch++;
286                  }
287                  break;
288              case 2:
289                  if (value == 0x57) {
290                      swimctrl->iwm_switch++;
291                  }
292                  break;
293              case 3:
294                  if (value == 0x57) {
295                      swimctrl->mode = SWIM_MODE_SWIM;
296                      swimctrl->iwm_switch = 0;
297                  }
298                  break;
299              }
300          }
301      }
302  }
303  
304  static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size)
305  {
306      SWIMCtrl *swimctrl = opaque;
307  
308      reg >>= REG_SHIFT;
309  
310      swimctrl->regs[reg >> 1] = reg & 1;
311  
312      return 0;
313  }
314  
315  static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value,
316                             unsigned size)
317  {
318      SWIMCtrl *swimctrl = opaque;
319  
320      if (swimctrl->mode == SWIM_MODE_IWM) {
321          iwmctrl_write(opaque, reg, value, size);
322          return;
323      }
324  
325      reg >>= REG_SHIFT;
326  
327      switch (reg) {
328      case SWIM_WRITE_PHASE:
329          swimctrl->swim_phase = value;
330          break;
331      case SWIM_WRITE_MODE0:
332          swimctrl->swim_mode &= ~value;
333          break;
334      case SWIM_WRITE_MODE1:
335          swimctrl->swim_mode |= value;
336          break;
337      case SWIM_WRITE_DATA:
338      case SWIM_WRITE_MARK:
339      case SWIM_WRITE_CRC:
340      case SWIM_WRITE_PARAMETER:
341      case SWIM_WRITE_SETUP:
342          break;
343      }
344  }
345  
346  static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size)
347  {
348      SWIMCtrl *swimctrl = opaque;
349      uint32_t value = 0;
350  
351      if (swimctrl->mode == SWIM_MODE_IWM) {
352          return iwmctrl_read(opaque, reg, size);
353      }
354  
355      reg >>= REG_SHIFT;
356  
357      switch (reg) {
358      case SWIM_READ_PHASE:
359          value = swimctrl->swim_phase;
360          break;
361      case SWIM_READ_HANDSHAKE:
362          if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
363              /* always answer "no drive present" */
364              value = SWIM_SENSE;
365          }
366          break;
367      case SWIM_READ_DATA:
368      case SWIM_READ_MARK:
369      case SWIM_READ_ERROR:
370      case SWIM_READ_PARAMETER:
371      case SWIM_READ_SETUP:
372      case SWIM_READ_STATUS:
373          break;
374      }
375  
376      return value;
377  }
378  
379  static const MemoryRegionOps swimctrl_mem_ops = {
380      .write = swimctrl_write,
381      .read = swimctrl_read,
382      .endianness = DEVICE_NATIVE_ENDIAN,
383  };
384  
385  static void sysbus_swim_reset(DeviceState *d)
386  {
387      SWIM *sys = SWIM(d);
388      SWIMCtrl *ctrl = &sys->ctrl;
389      int i;
390  
391      ctrl->mode = 0;
392      ctrl->iwm_switch = 0;
393      for (i = 0; i < 8; i++) {
394          ctrl->regs[i] = 0;
395      }
396      ctrl->iwm_data = 0;
397      ctrl->iwm_mode = 0;
398      ctrl->swim_phase = 0;
399      ctrl->swim_mode = 0;
400      for (i = 0; i < SWIM_MAX_FD; i++) {
401          fd_recalibrate(&ctrl->drives[i]);
402      }
403  }
404  
405  static void sysbus_swim_init(Object *obj)
406  {
407      SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
408      SWIM *sbs = SWIM(obj);
409      SWIMCtrl *swimctrl = &sbs->ctrl;
410  
411      memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl,
412                            "swim", 0x2000);
413      sysbus_init_mmio(sbd, &swimctrl->iomem);
414  }
415  
416  static void sysbus_swim_realize(DeviceState *dev, Error **errp)
417  {
418      SWIM *sys = SWIM(dev);
419      SWIMCtrl *swimctrl = &sys->ctrl;
420  
421      qbus_create_inplace(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev,
422                          NULL);
423      swimctrl->bus.ctrl = swimctrl;
424  }
425  
426  static const VMStateDescription vmstate_fdrive = {
427      .name = "fdrive",
428      .version_id = 1,
429      .minimum_version_id = 1,
430      .fields = (VMStateField[]) {
431          VMSTATE_END_OF_LIST()
432      },
433  };
434  
435  static const VMStateDescription vmstate_swim = {
436      .name = "swim",
437      .version_id = 1,
438      .minimum_version_id = 1,
439      .fields = (VMStateField[]) {
440          VMSTATE_INT32(mode, SWIMCtrl),
441          /* IWM mode */
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),
446          /* SWIM mode */
447          VMSTATE_UINT8(swim_phase, SWIMCtrl),
448          VMSTATE_UINT8(swim_mode, SWIMCtrl),
449          /* Drives */
450          VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
451                               vmstate_fdrive, FDrive),
452          VMSTATE_END_OF_LIST()
453      },
454  };
455  
456  static const VMStateDescription vmstate_sysbus_swim = {
457      .name = "SWIM",
458      .version_id = 1,
459      .fields = (VMStateField[]) {
460          VMSTATE_STRUCT(ctrl, SWIM, 0, vmstate_swim, SWIMCtrl),
461          VMSTATE_END_OF_LIST()
462      }
463  };
464  
465  static void sysbus_swim_class_init(ObjectClass *oc, void *data)
466  {
467      DeviceClass *dc = DEVICE_CLASS(oc);
468  
469      dc->realize = sysbus_swim_realize;
470      dc->reset = sysbus_swim_reset;
471      dc->vmsd = &vmstate_sysbus_swim;
472  }
473  
474  static const TypeInfo sysbus_swim_info = {
475      .name          = TYPE_SWIM,
476      .parent        = TYPE_SYS_BUS_DEVICE,
477      .instance_size = sizeof(SWIM),
478      .instance_init = sysbus_swim_init,
479      .class_init    = sysbus_swim_class_init,
480  };
481  
482  static void swim_register_types(void)
483  {
484      type_register_static(&sysbus_swim_info);
485      type_register_static(&swim_bus_info);
486      type_register_static(&swim_drive_info);
487  }
488  
489  type_init(swim_register_types)
490