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