xref: /openbmc/qemu/hw/block/fdc-isa.c (revision ee3d1f1b)
1 /*
2  * QEMU Floppy disk emulator (Intel 82078)
3  *
4  * Copyright (c) 2003, 2007 Jocelyn Mayer
5  * Copyright (c) 2008 Hervé Poussineau
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 /*
26  * The controller is used in Sun4m systems in a slightly different
27  * way. There are changes in DOR register and DMA is not available.
28  */
29 
30 #include "qemu/osdep.h"
31 #include "hw/block/fdc.h"
32 #include "qapi/error.h"
33 #include "qemu/error-report.h"
34 #include "qemu/timer.h"
35 #include "hw/acpi/acpi_aml_interface.h"
36 #include "hw/irq.h"
37 #include "hw/isa/isa.h"
38 #include "hw/qdev-properties.h"
39 #include "hw/qdev-properties-system.h"
40 #include "migration/vmstate.h"
41 #include "hw/block/block.h"
42 #include "sysemu/block-backend.h"
43 #include "sysemu/blockdev.h"
44 #include "sysemu/sysemu.h"
45 #include "exec/ioport.h"
46 #include "qemu/log.h"
47 #include "qemu/main-loop.h"
48 #include "qemu/module.h"
49 #include "trace.h"
50 #include "qom/object.h"
51 #include "fdc-internal.h"
52 
53 OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC)
54 
55 struct FDCtrlISABus {
56     /*< private >*/
57     ISADevice parent_obj;
58     /*< public >*/
59 
60     uint32_t iobase;
61     uint32_t irq;
62     uint32_t dma;
63     struct FDCtrl state;
64     PortioList portio_list;
65     int32_t bootindexA;
66     int32_t bootindexB;
67 };
68 
69 static void fdctrl_external_reset_isa(DeviceState *d)
70 {
71     FDCtrlISABus *isa = ISA_FDC(d);
72     FDCtrl *s = &isa->state;
73 
74     fdctrl_reset(s, 0);
75 }
76 
77 void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds)
78 {
79     fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds);
80 }
81 
82 static const MemoryRegionPortio fdc_portio_list[] = {
83     { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
84     { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
85     PORTIO_END_OF_LIST(),
86 };
87 
88 static void isabus_fdc_realize(DeviceState *dev, Error **errp)
89 {
90     ISADevice *isadev = ISA_DEVICE(dev);
91     ISABus *bus = isa_bus_from_device(isadev);
92     FDCtrlISABus *isa = ISA_FDC(dev);
93     FDCtrl *fdctrl = &isa->state;
94     Error *err = NULL;
95 
96     isa_register_portio_list(isadev, &isa->portio_list,
97                              isa->iobase, fdc_portio_list, fdctrl,
98                              "fdc");
99 
100     fdctrl->irq = isa_bus_get_irq(bus, isa->irq);
101     fdctrl->dma_chann = isa->dma;
102     if (fdctrl->dma_chann != -1) {
103         IsaDmaClass *k;
104         fdctrl->dma = isa_bus_get_dma(bus, isa->dma);
105         if (!fdctrl->dma) {
106             error_setg(errp, "ISA controller does not support DMA");
107             return;
108         }
109         k = ISADMA_GET_CLASS(fdctrl->dma);
110         k->register_channel(fdctrl->dma, fdctrl->dma_chann,
111                             &fdctrl_transfer_handler, fdctrl);
112     }
113 
114     qdev_set_legacy_instance_id(dev, isa->iobase, 2);
115 
116     fdctrl_realize_common(dev, fdctrl, &err);
117     if (err != NULL) {
118         error_propagate(errp, err);
119         return;
120     }
121 }
122 
123 FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
124 {
125     FDCtrlISABus *isa = ISA_FDC(fdc);
126 
127     return isa->state.drives[i].drive;
128 }
129 
130 static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc,
131                                       uint8_t *maxh, uint8_t *maxs)
132 {
133     const FDFormat *fdf;
134 
135     *maxc = *maxh = *maxs = 0;
136     for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
137         if (fdf->drive != type) {
138             continue;
139         }
140         if (*maxc < fdf->max_track) {
141             *maxc = fdf->max_track;
142         }
143         if (*maxh < fdf->max_head) {
144             *maxh = fdf->max_head;
145         }
146         if (*maxs < fdf->last_sect) {
147             *maxs = fdf->last_sect;
148         }
149     }
150     (*maxc)--;
151 }
152 
153 static Aml *build_fdinfo_aml(int idx, FloppyDriveType type)
154 {
155     Aml *dev, *fdi;
156     uint8_t maxc, maxh, maxs;
157 
158     isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs);
159 
160     dev = aml_device("FLP%c", 'A' + idx);
161 
162     aml_append(dev, aml_name_decl("_ADR", aml_int(idx)));
163 
164     fdi = aml_package(16);
165     aml_append(fdi, aml_int(idx));  /* Drive Number */
166     aml_append(fdi,
167         aml_int(cmos_get_fd_drive_type(type)));  /* Device Type */
168     /*
169      * the values below are the limits of the drive, and are thus independent
170      * of the inserted media
171      */
172     aml_append(fdi, aml_int(maxc));  /* Maximum Cylinder Number */
173     aml_append(fdi, aml_int(maxs));  /* Maximum Sector Number */
174     aml_append(fdi, aml_int(maxh));  /* Maximum Head Number */
175     /*
176      * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of
177      * the drive type, so shall we
178      */
179     aml_append(fdi, aml_int(0xAF));  /* disk_specify_1 */
180     aml_append(fdi, aml_int(0x02));  /* disk_specify_2 */
181     aml_append(fdi, aml_int(0x25));  /* disk_motor_wait */
182     aml_append(fdi, aml_int(0x02));  /* disk_sector_siz */
183     aml_append(fdi, aml_int(0x12));  /* disk_eot */
184     aml_append(fdi, aml_int(0x1B));  /* disk_rw_gap */
185     aml_append(fdi, aml_int(0xFF));  /* disk_dtl */
186     aml_append(fdi, aml_int(0x6C));  /* disk_formt_gap */
187     aml_append(fdi, aml_int(0xF6));  /* disk_fill */
188     aml_append(fdi, aml_int(0x0F));  /* disk_head_sttl */
189     aml_append(fdi, aml_int(0x08));  /* disk_motor_strt */
190 
191     aml_append(dev, aml_name_decl("_FDI", fdi));
192     return dev;
193 }
194 
195 int cmos_get_fd_drive_type(FloppyDriveType fd0)
196 {
197     int val;
198 
199     switch (fd0) {
200     case FLOPPY_DRIVE_TYPE_144:
201         /* 1.44 Mb 3"5 drive */
202         val = 4;
203         break;
204     case FLOPPY_DRIVE_TYPE_288:
205         /* 2.88 Mb 3"5 drive */
206         val = 5;
207         break;
208     case FLOPPY_DRIVE_TYPE_120:
209         /* 1.2 Mb 5"5 drive */
210         val = 2;
211         break;
212     case FLOPPY_DRIVE_TYPE_NONE:
213     default:
214         val = 0;
215         break;
216     }
217     return val;
218 }
219 
220 static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope)
221 {
222     FDCtrlISABus *isa = ISA_FDC(adev);
223     Aml *dev;
224     Aml *crs;
225     int i;
226 
227 #define ACPI_FDE_MAX_FD 4
228     uint32_t fde_buf[5] = {
229         0, 0, 0, 0,     /* presence of floppy drives #0 - #3 */
230         cpu_to_le32(2)  /* tape presence (2 == never present) */
231     };
232 
233     crs = aml_resource_template();
234     aml_append(crs,
235         aml_io(AML_DECODE16, isa->iobase + 2, isa->iobase + 2, 0x00, 0x04));
236     aml_append(crs,
237         aml_io(AML_DECODE16, isa->iobase + 7, isa->iobase + 7, 0x00, 0x01));
238     aml_append(crs, aml_irq_no_flags(isa->irq));
239     aml_append(crs,
240         aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, isa->dma));
241 
242     dev = aml_device("FDC0");
243     aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
244     aml_append(dev, aml_name_decl("_CRS", crs));
245 
246     for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
247         FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i);
248 
249         if (type < FLOPPY_DRIVE_TYPE_NONE) {
250             fde_buf[i] = cpu_to_le32(1);  /* drive present */
251             aml_append(dev, build_fdinfo_aml(i, type));
252         }
253     }
254     aml_append(dev, aml_name_decl("_FDE",
255                aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf)));
256 
257     aml_append(scope, dev);
258 }
259 
260 static const VMStateDescription vmstate_isa_fdc = {
261     .name = "fdc",
262     .version_id = 2,
263     .minimum_version_id = 2,
264     .fields = (const VMStateField[]) {
265         VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
266         VMSTATE_END_OF_LIST()
267     }
268 };
269 
270 static Property isa_fdc_properties[] = {
271     DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0),
272     DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
273     DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
274     DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type,
275                         FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
276                         FloppyDriveType),
277     DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type,
278                         FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
279                         FloppyDriveType),
280     DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback,
281                         FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type,
282                         FloppyDriveType),
283     DEFINE_PROP_END_OF_LIST(),
284 };
285 
286 static void isabus_fdc_class_init(ObjectClass *klass, void *data)
287 {
288     DeviceClass *dc = DEVICE_CLASS(klass);
289     AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
290 
291     dc->desc = "virtual floppy controller";
292     dc->realize = isabus_fdc_realize;
293     dc->fw_name = "fdc";
294     dc->reset = fdctrl_external_reset_isa;
295     dc->vmsd = &vmstate_isa_fdc;
296     adevc->build_dev_aml = build_fdc_aml;
297     device_class_set_props(dc, isa_fdc_properties);
298     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
299 }
300 
301 static void isabus_fdc_instance_init(Object *obj)
302 {
303     FDCtrlISABus *isa = ISA_FDC(obj);
304 
305     device_add_bootindex_property(obj, &isa->bootindexA,
306                                   "bootindexA", "/floppy@0",
307                                   DEVICE(obj));
308     device_add_bootindex_property(obj, &isa->bootindexB,
309                                   "bootindexB", "/floppy@1",
310                                   DEVICE(obj));
311 }
312 
313 static const TypeInfo isa_fdc_info = {
314     .name          = TYPE_ISA_FDC,
315     .parent        = TYPE_ISA_DEVICE,
316     .instance_size = sizeof(FDCtrlISABus),
317     .class_init    = isabus_fdc_class_init,
318     .instance_init = isabus_fdc_instance_init,
319     .interfaces = (InterfaceInfo[]) {
320         { TYPE_ACPI_DEV_AML_IF },
321         { },
322     },
323 };
324 
325 static void isa_fdc_register_types(void)
326 {
327     type_register_static(&isa_fdc_info);
328 }
329 
330 type_init(isa_fdc_register_types)
331