xref: /openbmc/qemu/hw/block/swim.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
1c701ec62SLaurent Vivier /*
2c701ec62SLaurent Vivier  * QEMU Macintosh floppy disk controller emulator (SWIM)
3c701ec62SLaurent Vivier  *
4c701ec62SLaurent Vivier  * Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
5c701ec62SLaurent Vivier  *
6c701ec62SLaurent Vivier  * This work is licensed under the terms of the GNU GPL, version 2.  See
7c701ec62SLaurent Vivier  * the COPYING file in the top-level directory.
8c701ec62SLaurent Vivier  *
9c701ec62SLaurent Vivier  * Only the basic support: it allows to switch from IWM (Integrated WOZ
10c701ec62SLaurent Vivier  * Machine) mode to the SWIM mode and makes the linux driver happy.
11c701ec62SLaurent Vivier  */
12c701ec62SLaurent Vivier 
13c701ec62SLaurent Vivier #include "qemu/osdep.h"
14c701ec62SLaurent Vivier #include "qemu/main-loop.h"
15c701ec62SLaurent Vivier #include "qapi/error.h"
16c701ec62SLaurent Vivier #include "sysemu/block-backend.h"
17c701ec62SLaurent Vivier #include "hw/sysbus.h"
18c701ec62SLaurent Vivier #include "migration/vmstate.h"
19c701ec62SLaurent Vivier #include "hw/block/block.h"
20c701ec62SLaurent Vivier #include "hw/block/swim.h"
21c701ec62SLaurent Vivier #include "hw/qdev-properties.h"
22d05cad2bSMark Cave-Ayland #include "trace.h"
23c701ec62SLaurent Vivier 
24994af0b2SMark Cave-Ayland 
25994af0b2SMark Cave-Ayland /* IWM latch bits */
26994af0b2SMark Cave-Ayland 
27994af0b2SMark Cave-Ayland #define IWMLB_PHASE0            0
28994af0b2SMark Cave-Ayland #define IWMLB_PHASE1            1
29994af0b2SMark Cave-Ayland #define IWMLB_PHASE2            2
30994af0b2SMark Cave-Ayland #define IWMLB_PHASE3            3
31994af0b2SMark Cave-Ayland #define IWMLB_MOTORON           4
32994af0b2SMark Cave-Ayland #define IWMLB_DRIVESEL          5
33994af0b2SMark Cave-Ayland #define IWMLB_L6                6
34994af0b2SMark Cave-Ayland #define IWMLB_L7                7
35994af0b2SMark Cave-Ayland 
36c701ec62SLaurent Vivier /* IWM registers */
37c701ec62SLaurent Vivier 
38994af0b2SMark Cave-Ayland #define IWM_READALLONES         0
39994af0b2SMark Cave-Ayland #define IWM_READDATA            1
40994af0b2SMark Cave-Ayland #define IWM_READSTATUS0         2
41994af0b2SMark Cave-Ayland #define IWM_READSTATUS1         3
42994af0b2SMark Cave-Ayland #define IWM_READWHANDSHAKE0     4
43994af0b2SMark Cave-Ayland #define IWM_READWHANDSHAKE1     5
44994af0b2SMark Cave-Ayland #define IWM_WRITESETMODE        6
45994af0b2SMark Cave-Ayland #define IWM_WRITEDATA           7
46c701ec62SLaurent Vivier 
47c701ec62SLaurent Vivier /* SWIM registers */
48c701ec62SLaurent Vivier 
49c701ec62SLaurent Vivier #define SWIM_WRITE_DATA         0
50c701ec62SLaurent Vivier #define SWIM_WRITE_MARK         1
51c701ec62SLaurent Vivier #define SWIM_WRITE_CRC          2
52c701ec62SLaurent Vivier #define SWIM_WRITE_PARAMETER    3
53c701ec62SLaurent Vivier #define SWIM_WRITE_PHASE        4
54c701ec62SLaurent Vivier #define SWIM_WRITE_SETUP        5
55c701ec62SLaurent Vivier #define SWIM_WRITE_MODE0        6
56c701ec62SLaurent Vivier #define SWIM_WRITE_MODE1        7
57c701ec62SLaurent Vivier 
58c701ec62SLaurent Vivier #define SWIM_READ_DATA          8
59c701ec62SLaurent Vivier #define SWIM_READ_MARK          9
60c701ec62SLaurent Vivier #define SWIM_READ_ERROR         10
61c701ec62SLaurent Vivier #define SWIM_READ_PARAMETER     11
62c701ec62SLaurent Vivier #define SWIM_READ_PHASE         12
63c701ec62SLaurent Vivier #define SWIM_READ_SETUP         13
64c701ec62SLaurent Vivier #define SWIM_READ_STATUS        14
65c701ec62SLaurent Vivier #define SWIM_READ_HANDSHAKE     15
66c701ec62SLaurent Vivier 
67c701ec62SLaurent Vivier #define REG_SHIFT               9
68c701ec62SLaurent Vivier 
69994af0b2SMark Cave-Ayland #define SWIM_MODE_STATUS_BIT    6
70c701ec62SLaurent Vivier #define SWIM_MODE_IWM           0
71994af0b2SMark Cave-Ayland #define SWIM_MODE_ISM           1
72c701ec62SLaurent Vivier 
73c701ec62SLaurent Vivier /* bits in phase register */
74c701ec62SLaurent Vivier 
75c701ec62SLaurent Vivier #define SWIM_SEEK_NEGATIVE   0x074
76c701ec62SLaurent Vivier #define SWIM_STEP            0x071
77c701ec62SLaurent Vivier #define SWIM_MOTOR_ON        0x072
78c701ec62SLaurent Vivier #define SWIM_MOTOR_OFF       0x076
79c701ec62SLaurent Vivier #define SWIM_INDEX           0x073
80c701ec62SLaurent Vivier #define SWIM_EJECT           0x077
81c701ec62SLaurent Vivier #define SWIM_SETMFM          0x171
82c701ec62SLaurent Vivier #define SWIM_SETGCR          0x175
83c701ec62SLaurent Vivier #define SWIM_RELAX           0x033
84c701ec62SLaurent Vivier #define SWIM_LSTRB           0x008
85c701ec62SLaurent Vivier #define SWIM_CA_MASK         0x077
86c701ec62SLaurent Vivier 
87c701ec62SLaurent Vivier /* Select values for swim_select and swim_readbit */
88c701ec62SLaurent Vivier 
89c701ec62SLaurent Vivier #define SWIM_READ_DATA_0     0x074
90c701ec62SLaurent Vivier #define SWIM_TWOMEG_DRIVE    0x075
91c701ec62SLaurent Vivier #define SWIM_SINGLE_SIDED    0x076
92c701ec62SLaurent Vivier #define SWIM_DRIVE_PRESENT   0x077
93c701ec62SLaurent Vivier #define SWIM_DISK_IN         0x170
94c701ec62SLaurent Vivier #define SWIM_WRITE_PROT      0x171
95c701ec62SLaurent Vivier #define SWIM_TRACK_ZERO      0x172
96c701ec62SLaurent Vivier #define SWIM_TACHO           0x173
97c701ec62SLaurent Vivier #define SWIM_READ_DATA_1     0x174
98c701ec62SLaurent Vivier #define SWIM_MFM_MODE        0x175
99c701ec62SLaurent Vivier #define SWIM_SEEK_COMPLETE   0x176
100c701ec62SLaurent Vivier #define SWIM_ONEMEG_MEDIA    0x177
101c701ec62SLaurent Vivier 
102c701ec62SLaurent Vivier /* Bits in handshake register */
103c701ec62SLaurent Vivier 
104c701ec62SLaurent Vivier #define SWIM_MARK_BYTE       0x01
105c701ec62SLaurent Vivier #define SWIM_CRC_ZERO        0x02
106c701ec62SLaurent Vivier #define SWIM_RDDATA          0x04
107c701ec62SLaurent Vivier #define SWIM_SENSE           0x08
108c701ec62SLaurent Vivier #define SWIM_MOTEN           0x10
109c701ec62SLaurent Vivier #define SWIM_ERROR           0x20
110c701ec62SLaurent Vivier #define SWIM_DAT2BYTE        0x40
111c701ec62SLaurent Vivier #define SWIM_DAT1BYTE        0x80
112c701ec62SLaurent Vivier 
113c701ec62SLaurent Vivier /* bits in setup register */
114c701ec62SLaurent Vivier 
115c701ec62SLaurent Vivier #define SWIM_S_INV_WDATA     0x01
116c701ec62SLaurent Vivier #define SWIM_S_3_5_SELECT    0x02
117c701ec62SLaurent Vivier #define SWIM_S_GCR           0x04
118c701ec62SLaurent Vivier #define SWIM_S_FCLK_DIV2     0x08
119c701ec62SLaurent Vivier #define SWIM_S_ERROR_CORR    0x10
120c701ec62SLaurent Vivier #define SWIM_S_IBM_DRIVE     0x20
121c701ec62SLaurent Vivier #define SWIM_S_GCR_WRITE     0x40
122c701ec62SLaurent Vivier #define SWIM_S_TIMEOUT       0x80
123c701ec62SLaurent Vivier 
124c701ec62SLaurent Vivier /* bits in mode register */
125c701ec62SLaurent Vivier 
126c701ec62SLaurent Vivier #define SWIM_CLFIFO          0x01
127c701ec62SLaurent Vivier #define SWIM_ENBL1           0x02
128c701ec62SLaurent Vivier #define SWIM_ENBL2           0x04
129c701ec62SLaurent Vivier #define SWIM_ACTION          0x08
130c701ec62SLaurent Vivier #define SWIM_WRITE_MODE      0x10
131c701ec62SLaurent Vivier #define SWIM_HEDSEL          0x20
132c701ec62SLaurent Vivier #define SWIM_MOTON           0x80
133c701ec62SLaurent Vivier 
13457004204SMark Cave-Ayland static const char *iwm_reg_names[] = {
135994af0b2SMark Cave-Ayland     "READALLONES", "READDATA", "READSTATUS0", "READSTATUS1",
136994af0b2SMark Cave-Ayland     "READWHANDSHAKE0", "READWHANDSHAKE1", "WRITESETMODE", "WRITEDATA"
13757004204SMark Cave-Ayland };
13857004204SMark Cave-Ayland 
13957004204SMark Cave-Ayland static const char *ism_reg_names[] = {
140d05cad2bSMark Cave-Ayland     "WRITE_DATA", "WRITE_MARK", "WRITE_CRC", "WRITE_PARAMETER",
141d05cad2bSMark Cave-Ayland     "WRITE_PHASE", "WRITE_SETUP", "WRITE_MODE0", "WRITE_MODE1",
142d05cad2bSMark Cave-Ayland     "READ_DATA", "READ_MARK", "READ_ERROR", "READ_PARAMETER",
143d05cad2bSMark Cave-Ayland     "READ_PHASE", "READ_SETUP", "READ_STATUS", "READ_HANDSHAKE"
144d05cad2bSMark Cave-Ayland };
145d05cad2bSMark Cave-Ayland 
fd_recalibrate(FDrive * drive)146c701ec62SLaurent Vivier static void fd_recalibrate(FDrive *drive)
147c701ec62SLaurent Vivier {
148c701ec62SLaurent Vivier }
149c701ec62SLaurent Vivier 
swim_change_cb(void * opaque,bool load,Error ** errp)150c701ec62SLaurent Vivier static void swim_change_cb(void *opaque, bool load, Error **errp)
151c701ec62SLaurent Vivier {
152c701ec62SLaurent Vivier     FDrive *drive = opaque;
153c701ec62SLaurent Vivier 
154c701ec62SLaurent Vivier     if (!load) {
155c701ec62SLaurent Vivier         blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
156c701ec62SLaurent Vivier     } else {
157c701ec62SLaurent Vivier         if (!blkconf_apply_backend_options(drive->conf,
15886b1cf32SKevin Wolf                                            !blk_supports_write_perm(drive->blk),
15986b1cf32SKevin Wolf                                            false, errp)) {
160c701ec62SLaurent Vivier             return;
161c701ec62SLaurent Vivier         }
162c701ec62SLaurent Vivier     }
163c701ec62SLaurent Vivier }
164c701ec62SLaurent Vivier 
165c701ec62SLaurent Vivier static const BlockDevOps swim_block_ops = {
166c701ec62SLaurent Vivier     .change_media_cb = swim_change_cb,
167c701ec62SLaurent Vivier };
168c701ec62SLaurent Vivier 
169c701ec62SLaurent Vivier static Property swim_drive_properties[] = {
170c701ec62SLaurent Vivier     DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1),
171c701ec62SLaurent Vivier     DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf),
172c701ec62SLaurent Vivier     DEFINE_PROP_END_OF_LIST(),
173c701ec62SLaurent Vivier };
174c701ec62SLaurent Vivier 
swim_drive_realize(DeviceState * qdev,Error ** errp)175c701ec62SLaurent Vivier static void swim_drive_realize(DeviceState *qdev, Error **errp)
176c701ec62SLaurent Vivier {
177c701ec62SLaurent Vivier     SWIMDrive *dev = SWIM_DRIVE(qdev);
178c701ec62SLaurent Vivier     SWIMBus *bus = SWIM_BUS(qdev->parent_bus);
179c701ec62SLaurent Vivier     FDrive *drive;
180c701ec62SLaurent Vivier     int ret;
181c701ec62SLaurent Vivier 
182c701ec62SLaurent Vivier     if (dev->unit == -1) {
183c701ec62SLaurent Vivier         for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) {
184c701ec62SLaurent Vivier             drive = &bus->ctrl->drives[dev->unit];
185c701ec62SLaurent Vivier             if (!drive->blk) {
186c701ec62SLaurent Vivier                 break;
187c701ec62SLaurent Vivier             }
188c701ec62SLaurent Vivier         }
189c701ec62SLaurent Vivier     }
190c701ec62SLaurent Vivier 
191c701ec62SLaurent Vivier     if (dev->unit >= SWIM_MAX_FD) {
192c701ec62SLaurent Vivier         error_setg(errp, "Can't create floppy unit %d, bus supports "
193c701ec62SLaurent Vivier                    "only %d units", dev->unit, SWIM_MAX_FD);
194c701ec62SLaurent Vivier         return;
195c701ec62SLaurent Vivier     }
196c701ec62SLaurent Vivier 
197c701ec62SLaurent Vivier     drive = &bus->ctrl->drives[dev->unit];
198c701ec62SLaurent Vivier     if (drive->blk) {
199c701ec62SLaurent Vivier         error_setg(errp, "Floppy unit %d is in use", dev->unit);
200c701ec62SLaurent Vivier         return;
201c701ec62SLaurent Vivier     }
202c701ec62SLaurent Vivier 
203c701ec62SLaurent Vivier     if (!dev->conf.blk) {
204c701ec62SLaurent Vivier         /* Anonymous BlockBackend for an empty drive */
205c701ec62SLaurent Vivier         dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
206c701ec62SLaurent Vivier         ret = blk_attach_dev(dev->conf.blk, qdev);
207c701ec62SLaurent Vivier         assert(ret == 0);
208c701ec62SLaurent Vivier     }
209c701ec62SLaurent Vivier 
210c56ee92fSRoman Kagan     if (!blkconf_blocksizes(&dev->conf, errp)) {
211c56ee92fSRoman Kagan         return;
212c56ee92fSRoman Kagan     }
213c56ee92fSRoman Kagan 
214c701ec62SLaurent Vivier     if (dev->conf.logical_block_size != 512 ||
215c701ec62SLaurent Vivier         dev->conf.physical_block_size != 512)
216c701ec62SLaurent Vivier     {
217c701ec62SLaurent Vivier         error_setg(errp, "Physical and logical block size must "
218c701ec62SLaurent Vivier                    "be 512 for floppy");
219c701ec62SLaurent Vivier         return;
220c701ec62SLaurent Vivier     }
221c701ec62SLaurent Vivier 
222c701ec62SLaurent Vivier     /*
223c701ec62SLaurent Vivier      * rerror/werror aren't supported by fdc and therefore not even registered
224c701ec62SLaurent Vivier      * with qdev. So set the defaults manually before they are used in
225c701ec62SLaurent Vivier      * blkconf_apply_backend_options().
226c701ec62SLaurent Vivier      */
227c701ec62SLaurent Vivier     dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
228c701ec62SLaurent Vivier     dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
229c701ec62SLaurent Vivier 
230c701ec62SLaurent Vivier     if (!blkconf_apply_backend_options(&dev->conf,
23186b1cf32SKevin Wolf                                        !blk_supports_write_perm(dev->conf.blk),
232c701ec62SLaurent Vivier                                        false, errp)) {
233c701ec62SLaurent Vivier         return;
234c701ec62SLaurent Vivier     }
235c701ec62SLaurent Vivier 
236c701ec62SLaurent Vivier     /*
237c701ec62SLaurent Vivier      * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
238c701ec62SLaurent Vivier      * for empty drives.
239c701ec62SLaurent Vivier      */
240c701ec62SLaurent Vivier     if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
241c701ec62SLaurent Vivier         blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
242c701ec62SLaurent Vivier         error_setg(errp, "fdc doesn't support drive option werror");
243c701ec62SLaurent Vivier         return;
244c701ec62SLaurent Vivier     }
245c701ec62SLaurent Vivier     if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
246c701ec62SLaurent Vivier         error_setg(errp, "fdc doesn't support drive option rerror");
247c701ec62SLaurent Vivier         return;
248c701ec62SLaurent Vivier     }
249c701ec62SLaurent Vivier 
250c701ec62SLaurent Vivier     drive->conf = &dev->conf;
251c701ec62SLaurent Vivier     drive->blk = dev->conf.blk;
252c701ec62SLaurent Vivier     drive->swimctrl = bus->ctrl;
253c701ec62SLaurent Vivier 
254c701ec62SLaurent Vivier     blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
255c701ec62SLaurent Vivier }
256c701ec62SLaurent Vivier 
swim_drive_class_init(ObjectClass * klass,void * data)257c701ec62SLaurent Vivier static void swim_drive_class_init(ObjectClass *klass, void *data)
258c701ec62SLaurent Vivier {
259c701ec62SLaurent Vivier     DeviceClass *k = DEVICE_CLASS(klass);
260c701ec62SLaurent Vivier     k->realize = swim_drive_realize;
261c701ec62SLaurent Vivier     set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
262c701ec62SLaurent Vivier     k->bus_type = TYPE_SWIM_BUS;
2634f67d30bSMarc-André Lureau     device_class_set_props(k, swim_drive_properties);
264c701ec62SLaurent Vivier     k->desc = "virtual SWIM drive";
265c701ec62SLaurent Vivier }
266c701ec62SLaurent Vivier 
267c701ec62SLaurent Vivier static const TypeInfo swim_drive_info = {
268c701ec62SLaurent Vivier     .name = TYPE_SWIM_DRIVE,
269c701ec62SLaurent Vivier     .parent = TYPE_DEVICE,
270c701ec62SLaurent Vivier     .instance_size = sizeof(SWIMDrive),
271c701ec62SLaurent Vivier     .class_init = swim_drive_class_init,
272c701ec62SLaurent Vivier };
273c701ec62SLaurent Vivier 
274c701ec62SLaurent Vivier static const TypeInfo swim_bus_info = {
275c701ec62SLaurent Vivier     .name = TYPE_SWIM_BUS,
276c701ec62SLaurent Vivier     .parent = TYPE_BUS,
277c701ec62SLaurent Vivier     .instance_size = sizeof(SWIMBus),
278c701ec62SLaurent Vivier };
279c701ec62SLaurent Vivier 
iwmctrl_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)280994af0b2SMark Cave-Ayland static void iwmctrl_write(void *opaque, hwaddr addr, uint64_t value,
281c701ec62SLaurent Vivier                           unsigned size)
282c701ec62SLaurent Vivier {
283c701ec62SLaurent Vivier     SWIMCtrl *swimctrl = opaque;
284994af0b2SMark Cave-Ayland     uint8_t latch, reg, ism_bit;
285c701ec62SLaurent Vivier 
286994af0b2SMark Cave-Ayland     addr >>= REG_SHIFT;
287994af0b2SMark Cave-Ayland 
288994af0b2SMark Cave-Ayland     /* A3-A1 select a latch, A0 specifies the value */
289994af0b2SMark Cave-Ayland     latch = (addr >> 1) & 7;
290994af0b2SMark Cave-Ayland     if (addr & 1) {
291994af0b2SMark Cave-Ayland         swimctrl->iwm_latches |= (1 << latch);
292994af0b2SMark Cave-Ayland     } else {
293994af0b2SMark Cave-Ayland         swimctrl->iwm_latches &= ~(1 << latch);
294994af0b2SMark Cave-Ayland     }
295994af0b2SMark Cave-Ayland 
296994af0b2SMark Cave-Ayland     reg = (swimctrl->iwm_latches & 0xc0) >> 5 |
297994af0b2SMark Cave-Ayland           (swimctrl->iwm_latches & 0x10) >> 4;
298c701ec62SLaurent Vivier 
29957004204SMark Cave-Ayland     swimctrl->iwmregs[reg] = value;
30057004204SMark Cave-Ayland     trace_swim_iwmctrl_write(reg, iwm_reg_names[reg], size, value);
301c701ec62SLaurent Vivier 
302994af0b2SMark Cave-Ayland     switch (reg) {
303994af0b2SMark Cave-Ayland     case IWM_WRITESETMODE:
304c701ec62SLaurent Vivier         /* detect sequence to switch from IWM mode to SWIM mode */
305994af0b2SMark Cave-Ayland         ism_bit = (value & (1 << SWIM_MODE_STATUS_BIT));
306994af0b2SMark Cave-Ayland 
307c701ec62SLaurent Vivier         switch (swimctrl->iwm_switch) {
308c701ec62SLaurent Vivier         case 0:
309994af0b2SMark Cave-Ayland             if (ism_bit) {    /* 1 */
310c701ec62SLaurent Vivier                 swimctrl->iwm_switch++;
311c701ec62SLaurent Vivier             }
312c701ec62SLaurent Vivier             break;
313c701ec62SLaurent Vivier         case 1:
314994af0b2SMark Cave-Ayland             if (!ism_bit) {   /* 0 */
315c701ec62SLaurent Vivier                 swimctrl->iwm_switch++;
316c701ec62SLaurent Vivier             }
317c701ec62SLaurent Vivier             break;
318c701ec62SLaurent Vivier         case 2:
319994af0b2SMark Cave-Ayland             if (ism_bit) {    /* 1 */
320c701ec62SLaurent Vivier                 swimctrl->iwm_switch++;
321c701ec62SLaurent Vivier             }
322c701ec62SLaurent Vivier             break;
323c701ec62SLaurent Vivier         case 3:
324994af0b2SMark Cave-Ayland             if (ism_bit) {    /* 1 */
325994af0b2SMark Cave-Ayland                 swimctrl->iwm_switch++;
326994af0b2SMark Cave-Ayland 
327994af0b2SMark Cave-Ayland                 swimctrl->mode = SWIM_MODE_ISM;
328994af0b2SMark Cave-Ayland                 swimctrl->swim_mode |= (1 << SWIM_MODE_STATUS_BIT);
329c701ec62SLaurent Vivier                 swimctrl->iwm_switch = 0;
330994af0b2SMark Cave-Ayland                 trace_swim_switch_to_ism();
33157004204SMark Cave-Ayland 
33257004204SMark Cave-Ayland                 /* Switch to ISM registers */
333994af0b2SMark Cave-Ayland                 memory_region_del_subregion(&swimctrl->swim, &swimctrl->iwm);
33457004204SMark Cave-Ayland                 memory_region_add_subregion(&swimctrl->swim, 0x0,
33557004204SMark Cave-Ayland                                             &swimctrl->ism);
336c701ec62SLaurent Vivier             }
337c701ec62SLaurent Vivier             break;
338c701ec62SLaurent Vivier         }
339994af0b2SMark Cave-Ayland         break;
340994af0b2SMark Cave-Ayland     default:
341994af0b2SMark Cave-Ayland         break;
342c701ec62SLaurent Vivier     }
343c701ec62SLaurent Vivier }
344c701ec62SLaurent Vivier 
iwmctrl_read(void * opaque,hwaddr addr,unsigned size)345994af0b2SMark Cave-Ayland static uint64_t iwmctrl_read(void *opaque, hwaddr addr, unsigned size)
346c701ec62SLaurent Vivier {
347c701ec62SLaurent Vivier     SWIMCtrl *swimctrl = opaque;
348994af0b2SMark Cave-Ayland     uint8_t latch, reg, value;
349c701ec62SLaurent Vivier 
350994af0b2SMark Cave-Ayland     addr >>= REG_SHIFT;
351c701ec62SLaurent Vivier 
352994af0b2SMark Cave-Ayland     /* A3-A1 select a latch, A0 specifies the value */
353994af0b2SMark Cave-Ayland     latch = (addr >> 1) & 7;
354994af0b2SMark Cave-Ayland     if (addr & 1) {
355994af0b2SMark Cave-Ayland         swimctrl->iwm_latches |= (1 << latch);
356994af0b2SMark Cave-Ayland     } else {
357994af0b2SMark Cave-Ayland         swimctrl->iwm_latches &= ~(1 << latch);
358994af0b2SMark Cave-Ayland     }
359994af0b2SMark Cave-Ayland 
360994af0b2SMark Cave-Ayland     reg = (swimctrl->iwm_latches & 0xc0) >> 5 |
361994af0b2SMark Cave-Ayland           (swimctrl->iwm_latches & 0x10) >> 4;
362994af0b2SMark Cave-Ayland 
363994af0b2SMark Cave-Ayland     switch (reg) {
364994af0b2SMark Cave-Ayland     case IWM_READALLONES:
365994af0b2SMark Cave-Ayland         value = 0xff;
366994af0b2SMark Cave-Ayland         break;
367994af0b2SMark Cave-Ayland     default:
368994af0b2SMark Cave-Ayland         value = 0;
369994af0b2SMark Cave-Ayland         break;
370994af0b2SMark Cave-Ayland     }
371994af0b2SMark Cave-Ayland 
37257004204SMark Cave-Ayland     trace_swim_iwmctrl_read(reg, iwm_reg_names[reg], size, value);
37357004204SMark Cave-Ayland     return value;
374c701ec62SLaurent Vivier }
375c701ec62SLaurent Vivier 
37657004204SMark Cave-Ayland static const MemoryRegionOps swimctrl_iwm_ops = {
37757004204SMark Cave-Ayland     .write = iwmctrl_write,
37857004204SMark Cave-Ayland     .read = iwmctrl_read,
37957004204SMark Cave-Ayland     .endianness = DEVICE_BIG_ENDIAN,
38057004204SMark Cave-Ayland };
38157004204SMark Cave-Ayland 
ismctrl_write(void * opaque,hwaddr reg,uint64_t value,unsigned size)38257004204SMark Cave-Ayland static void ismctrl_write(void *opaque, hwaddr reg, uint64_t value,
383c701ec62SLaurent Vivier                           unsigned size)
384c701ec62SLaurent Vivier {
385c701ec62SLaurent Vivier     SWIMCtrl *swimctrl = opaque;
386c701ec62SLaurent Vivier 
387c701ec62SLaurent Vivier     reg >>= REG_SHIFT;
388c701ec62SLaurent Vivier 
389994af0b2SMark Cave-Ayland     trace_swim_ismctrl_write(reg, ism_reg_names[reg], size, value);
390d05cad2bSMark Cave-Ayland 
391c701ec62SLaurent Vivier     switch (reg) {
392c701ec62SLaurent Vivier     case SWIM_WRITE_PHASE:
393c701ec62SLaurent Vivier         swimctrl->swim_phase = value;
394c701ec62SLaurent Vivier         break;
395c701ec62SLaurent Vivier     case SWIM_WRITE_MODE0:
396c701ec62SLaurent Vivier         swimctrl->swim_mode &= ~value;
397994af0b2SMark Cave-Ayland         /* Any access to MODE0 register resets PRAM index */
398994af0b2SMark Cave-Ayland         swimctrl->pram_idx = 0;
399994af0b2SMark Cave-Ayland 
400994af0b2SMark Cave-Ayland         if (!(swimctrl->swim_mode & (1 << SWIM_MODE_STATUS_BIT))) {
401994af0b2SMark Cave-Ayland             /* Clearing the mode bit switches to IWM mode */
402994af0b2SMark Cave-Ayland             swimctrl->mode = SWIM_MODE_IWM;
403994af0b2SMark Cave-Ayland             swimctrl->iwm_latches = 0;
404994af0b2SMark Cave-Ayland             trace_swim_switch_to_iwm();
405994af0b2SMark Cave-Ayland 
406994af0b2SMark Cave-Ayland             /* Switch to IWM registers */
407994af0b2SMark Cave-Ayland             memory_region_del_subregion(&swimctrl->swim, &swimctrl->ism);
408994af0b2SMark Cave-Ayland             memory_region_add_subregion(&swimctrl->swim, 0x0,
409994af0b2SMark Cave-Ayland                                         &swimctrl->iwm);
410994af0b2SMark Cave-Ayland         }
411c701ec62SLaurent Vivier         break;
412c701ec62SLaurent Vivier     case SWIM_WRITE_MODE1:
413c701ec62SLaurent Vivier         swimctrl->swim_mode |= value;
414c701ec62SLaurent Vivier         break;
415994af0b2SMark Cave-Ayland     case SWIM_WRITE_PARAMETER:
416994af0b2SMark Cave-Ayland         swimctrl->pram[swimctrl->pram_idx++] = value;
417994af0b2SMark Cave-Ayland         swimctrl->pram_idx &= 0xf;
418994af0b2SMark Cave-Ayland         break;
419c701ec62SLaurent Vivier     case SWIM_WRITE_DATA:
420c701ec62SLaurent Vivier     case SWIM_WRITE_MARK:
421c701ec62SLaurent Vivier     case SWIM_WRITE_CRC:
422c701ec62SLaurent Vivier     case SWIM_WRITE_SETUP:
423c701ec62SLaurent Vivier         break;
424c701ec62SLaurent Vivier     }
425c701ec62SLaurent Vivier }
426c701ec62SLaurent Vivier 
ismctrl_read(void * opaque,hwaddr reg,unsigned size)42757004204SMark Cave-Ayland static uint64_t ismctrl_read(void *opaque, hwaddr reg, unsigned size)
428c701ec62SLaurent Vivier {
429c701ec62SLaurent Vivier     SWIMCtrl *swimctrl = opaque;
430c701ec62SLaurent Vivier     uint32_t value = 0;
431c701ec62SLaurent Vivier 
432c701ec62SLaurent Vivier     reg >>= REG_SHIFT;
433c701ec62SLaurent Vivier 
434c701ec62SLaurent Vivier     switch (reg) {
435c701ec62SLaurent Vivier     case SWIM_READ_PHASE:
436c701ec62SLaurent Vivier         value = swimctrl->swim_phase;
437c701ec62SLaurent Vivier         break;
438c701ec62SLaurent Vivier     case SWIM_READ_HANDSHAKE:
439c701ec62SLaurent Vivier         if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
440c701ec62SLaurent Vivier             /* always answer "no drive present" */
441c701ec62SLaurent Vivier             value = SWIM_SENSE;
442c701ec62SLaurent Vivier         }
443c701ec62SLaurent Vivier         break;
444994af0b2SMark Cave-Ayland     case SWIM_READ_PARAMETER:
445994af0b2SMark Cave-Ayland         value = swimctrl->pram[swimctrl->pram_idx++];
446994af0b2SMark Cave-Ayland         swimctrl->pram_idx &= 0xf;
447994af0b2SMark Cave-Ayland         break;
448994af0b2SMark Cave-Ayland     case SWIM_READ_STATUS:
449994af0b2SMark Cave-Ayland         value = swimctrl->swim_status & ~(1 << SWIM_MODE_STATUS_BIT);
450994af0b2SMark Cave-Ayland         if (swimctrl->swim_mode == SWIM_MODE_ISM) {
451994af0b2SMark Cave-Ayland             value |= (1 << SWIM_MODE_STATUS_BIT);
452994af0b2SMark Cave-Ayland         }
453994af0b2SMark Cave-Ayland         break;
454c701ec62SLaurent Vivier     case SWIM_READ_DATA:
455c701ec62SLaurent Vivier     case SWIM_READ_MARK:
456c701ec62SLaurent Vivier     case SWIM_READ_ERROR:
457c701ec62SLaurent Vivier     case SWIM_READ_SETUP:
458c701ec62SLaurent Vivier         break;
459c701ec62SLaurent Vivier     }
460c701ec62SLaurent Vivier 
461994af0b2SMark Cave-Ayland     trace_swim_ismctrl_read(reg, ism_reg_names[reg], size, value);
462c701ec62SLaurent Vivier     return value;
463c701ec62SLaurent Vivier }
464c701ec62SLaurent Vivier 
46557004204SMark Cave-Ayland static const MemoryRegionOps swimctrl_ism_ops = {
46657004204SMark Cave-Ayland     .write = ismctrl_write,
46757004204SMark Cave-Ayland     .read = ismctrl_read,
46857004204SMark Cave-Ayland     .endianness = DEVICE_BIG_ENDIAN,
469c701ec62SLaurent Vivier };
470c701ec62SLaurent Vivier 
sysbus_swim_reset(DeviceState * d)471c701ec62SLaurent Vivier static void sysbus_swim_reset(DeviceState *d)
472c701ec62SLaurent Vivier {
473b694ed1fSEduardo Habkost     Swim *sys = SWIM(d);
474c701ec62SLaurent Vivier     SWIMCtrl *ctrl = &sys->ctrl;
475c701ec62SLaurent Vivier     int i;
476c701ec62SLaurent Vivier 
477c701ec62SLaurent Vivier     ctrl->mode = 0;
478c701ec62SLaurent Vivier     ctrl->iwm_switch = 0;
479994af0b2SMark Cave-Ayland     memset(ctrl->iwmregs, 0, sizeof(ctrl->iwmregs));
48057004204SMark Cave-Ayland 
481c701ec62SLaurent Vivier     ctrl->swim_phase = 0;
482c701ec62SLaurent Vivier     ctrl->swim_mode = 0;
483994af0b2SMark Cave-Ayland     memset(ctrl->ismregs, 0, sizeof(ctrl->ismregs));
484c701ec62SLaurent Vivier     for (i = 0; i < SWIM_MAX_FD; i++) {
485c701ec62SLaurent Vivier         fd_recalibrate(&ctrl->drives[i]);
486c701ec62SLaurent Vivier     }
487c701ec62SLaurent Vivier }
488c701ec62SLaurent Vivier 
sysbus_swim_init(Object * obj)489c701ec62SLaurent Vivier static void sysbus_swim_init(Object *obj)
490c701ec62SLaurent Vivier {
491c701ec62SLaurent Vivier     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
492b694ed1fSEduardo Habkost     Swim *sbs = SWIM(obj);
493c701ec62SLaurent Vivier     SWIMCtrl *swimctrl = &sbs->ctrl;
494c701ec62SLaurent Vivier 
49557004204SMark Cave-Ayland     memory_region_init(&swimctrl->swim, obj, "swim", 0x2000);
49657004204SMark Cave-Ayland     memory_region_init_io(&swimctrl->iwm, obj, &swimctrl_iwm_ops, swimctrl,
49757004204SMark Cave-Ayland                           "iwm", 0x2000);
49857004204SMark Cave-Ayland     memory_region_init_io(&swimctrl->ism, obj, &swimctrl_ism_ops, swimctrl,
49957004204SMark Cave-Ayland                           "ism", 0x2000);
50057004204SMark Cave-Ayland     sysbus_init_mmio(sbd, &swimctrl->swim);
501c701ec62SLaurent Vivier }
502c701ec62SLaurent Vivier 
sysbus_swim_realize(DeviceState * dev,Error ** errp)503c701ec62SLaurent Vivier static void sysbus_swim_realize(DeviceState *dev, Error **errp)
504c701ec62SLaurent Vivier {
505b694ed1fSEduardo Habkost     Swim *sys = SWIM(dev);
506c701ec62SLaurent Vivier     SWIMCtrl *swimctrl = &sys->ctrl;
507c701ec62SLaurent Vivier 
508d637e1dcSPeter Maydell     qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL);
509c701ec62SLaurent Vivier     swimctrl->bus.ctrl = swimctrl;
51057004204SMark Cave-Ayland 
51157004204SMark Cave-Ayland     /* Default register set is IWM */
51257004204SMark Cave-Ayland     memory_region_add_subregion(&swimctrl->swim, 0x0, &swimctrl->iwm);
513c701ec62SLaurent Vivier }
514c701ec62SLaurent Vivier 
515c701ec62SLaurent Vivier static const VMStateDescription vmstate_fdrive = {
516c701ec62SLaurent Vivier     .name = "fdrive",
517c701ec62SLaurent Vivier     .version_id = 1,
518c701ec62SLaurent Vivier     .minimum_version_id = 1,
5197d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
520c701ec62SLaurent Vivier         VMSTATE_END_OF_LIST()
521c701ec62SLaurent Vivier     },
522c701ec62SLaurent Vivier };
523c701ec62SLaurent Vivier 
524c701ec62SLaurent Vivier static const VMStateDescription vmstate_swim = {
525c701ec62SLaurent Vivier     .name = "swim",
526c701ec62SLaurent Vivier     .version_id = 1,
527c701ec62SLaurent Vivier     .minimum_version_id = 1,
5287d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
529c701ec62SLaurent Vivier         VMSTATE_INT32(mode, SWIMCtrl),
530c701ec62SLaurent Vivier         /* IWM mode */
531c701ec62SLaurent Vivier         VMSTATE_INT32(iwm_switch, SWIMCtrl),
532994af0b2SMark Cave-Ayland         VMSTATE_UINT8(iwm_latches, SWIMCtrl),
533994af0b2SMark Cave-Ayland         VMSTATE_UINT8_ARRAY(iwmregs, SWIMCtrl, 8),
534c701ec62SLaurent Vivier         /* SWIM mode */
53557004204SMark Cave-Ayland         VMSTATE_UINT8_ARRAY(ismregs, SWIMCtrl, 16),
536c701ec62SLaurent Vivier         VMSTATE_UINT8(swim_phase, SWIMCtrl),
537c701ec62SLaurent Vivier         VMSTATE_UINT8(swim_mode, SWIMCtrl),
538c701ec62SLaurent Vivier         /* Drives */
539c701ec62SLaurent Vivier         VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
540c701ec62SLaurent Vivier                              vmstate_fdrive, FDrive),
541c701ec62SLaurent Vivier         VMSTATE_END_OF_LIST()
542c701ec62SLaurent Vivier     },
543c701ec62SLaurent Vivier };
544c701ec62SLaurent Vivier 
545c701ec62SLaurent Vivier static const VMStateDescription vmstate_sysbus_swim = {
546c701ec62SLaurent Vivier     .name = "SWIM",
547c701ec62SLaurent Vivier     .version_id = 1,
5487d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
549b694ed1fSEduardo Habkost         VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl),
550c701ec62SLaurent Vivier         VMSTATE_END_OF_LIST()
551c701ec62SLaurent Vivier     }
552c701ec62SLaurent Vivier };
553c701ec62SLaurent Vivier 
sysbus_swim_class_init(ObjectClass * oc,void * data)554c701ec62SLaurent Vivier static void sysbus_swim_class_init(ObjectClass *oc, void *data)
555c701ec62SLaurent Vivier {
556c701ec62SLaurent Vivier     DeviceClass *dc = DEVICE_CLASS(oc);
557c701ec62SLaurent Vivier 
558c701ec62SLaurent Vivier     dc->realize = sysbus_swim_realize;
559*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, sysbus_swim_reset);
560c701ec62SLaurent Vivier     dc->vmsd = &vmstate_sysbus_swim;
561c701ec62SLaurent Vivier }
562c701ec62SLaurent Vivier 
563c701ec62SLaurent Vivier static const TypeInfo sysbus_swim_info = {
564c701ec62SLaurent Vivier     .name          = TYPE_SWIM,
565c701ec62SLaurent Vivier     .parent        = TYPE_SYS_BUS_DEVICE,
566b694ed1fSEduardo Habkost     .instance_size = sizeof(Swim),
567c701ec62SLaurent Vivier     .instance_init = sysbus_swim_init,
568c701ec62SLaurent Vivier     .class_init    = sysbus_swim_class_init,
569c701ec62SLaurent Vivier };
570c701ec62SLaurent Vivier 
swim_register_types(void)571c701ec62SLaurent Vivier static void swim_register_types(void)
572c701ec62SLaurent Vivier {
573c701ec62SLaurent Vivier     type_register_static(&sysbus_swim_info);
574c701ec62SLaurent Vivier     type_register_static(&swim_bus_info);
575c701ec62SLaurent Vivier     type_register_static(&swim_drive_info);
576c701ec62SLaurent Vivier }
577c701ec62SLaurent Vivier 
578c701ec62SLaurent Vivier type_init(swim_register_types)
579