1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 "qemu/log.h"
46#include "qemu/main-loop.h"
47#include "qemu/module.h"
48#include "trace.h"
49#include "qom/object.h"
50#include "fdc-internal.h"
51
52OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC)
53
54struct FDCtrlISABus {
55
56 ISADevice parent_obj;
57
58
59 uint32_t iobase;
60 uint32_t irq;
61 uint32_t dma;
62 struct FDCtrl state;
63 int32_t bootindexA;
64 int32_t bootindexB;
65};
66
67static void fdctrl_external_reset_isa(DeviceState *d)
68{
69 FDCtrlISABus *isa = ISA_FDC(d);
70 FDCtrl *s = &isa->state;
71
72 fdctrl_reset(s, 0);
73}
74
75void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds)
76{
77 fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds);
78}
79
80static const MemoryRegionPortio fdc_portio_list[] = {
81 { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
82 { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
83 PORTIO_END_OF_LIST(),
84};
85
86static void isabus_fdc_realize(DeviceState *dev, Error **errp)
87{
88 ISADevice *isadev = ISA_DEVICE(dev);
89 ISABus *bus = isa_bus_from_device(isadev);
90 FDCtrlISABus *isa = ISA_FDC(dev);
91 FDCtrl *fdctrl = &isa->state;
92 Error *err = NULL;
93
94 isa_register_portio_list(isadev, &fdctrl->portio_list,
95 isa->iobase, fdc_portio_list, fdctrl,
96 "fdc");
97
98 fdctrl->irq = isa_bus_get_irq(bus, isa->irq);
99 fdctrl->dma_chann = isa->dma;
100 if (fdctrl->dma_chann != -1) {
101 IsaDmaClass *k;
102 fdctrl->dma = isa_bus_get_dma(bus, isa->dma);
103 if (!fdctrl->dma) {
104 error_setg(errp, "ISA controller does not support DMA");
105 return;
106 }
107 k = ISADMA_GET_CLASS(fdctrl->dma);
108 k->register_channel(fdctrl->dma, fdctrl->dma_chann,
109 &fdctrl_transfer_handler, fdctrl);
110 }
111
112 qdev_set_legacy_instance_id(dev, isa->iobase, 2);
113
114 fdctrl_realize_common(dev, fdctrl, &err);
115 if (err != NULL) {
116 error_propagate(errp, err);
117 return;
118 }
119}
120
121FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
122{
123 FDCtrlISABus *isa = ISA_FDC(fdc);
124
125 return isa->state.drives[i].drive;
126}
127
128static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc,
129 uint8_t *maxh, uint8_t *maxs)
130{
131 const FDFormat *fdf;
132
133 *maxc = *maxh = *maxs = 0;
134 for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
135 if (fdf->drive != type) {
136 continue;
137 }
138 if (*maxc < fdf->max_track) {
139 *maxc = fdf->max_track;
140 }
141 if (*maxh < fdf->max_head) {
142 *maxh = fdf->max_head;
143 }
144 if (*maxs < fdf->last_sect) {
145 *maxs = fdf->last_sect;
146 }
147 }
148 (*maxc)--;
149}
150
151static Aml *build_fdinfo_aml(int idx, FloppyDriveType type)
152{
153 Aml *dev, *fdi;
154 uint8_t maxc, maxh, maxs;
155
156 isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs);
157
158 dev = aml_device("FLP%c", 'A' + idx);
159
160 aml_append(dev, aml_name_decl("_ADR", aml_int(idx)));
161
162 fdi = aml_package(16);
163 aml_append(fdi, aml_int(idx));
164 aml_append(fdi,
165 aml_int(cmos_get_fd_drive_type(type)));
166
167
168
169
170 aml_append(fdi, aml_int(maxc));
171 aml_append(fdi, aml_int(maxs));
172 aml_append(fdi, aml_int(maxh));
173
174
175
176
177 aml_append(fdi, aml_int(0xAF));
178 aml_append(fdi, aml_int(0x02));
179 aml_append(fdi, aml_int(0x25));
180 aml_append(fdi, aml_int(0x02));
181 aml_append(fdi, aml_int(0x12));
182 aml_append(fdi, aml_int(0x1B));
183 aml_append(fdi, aml_int(0xFF));
184 aml_append(fdi, aml_int(0x6C));
185 aml_append(fdi, aml_int(0xF6));
186 aml_append(fdi, aml_int(0x0F));
187 aml_append(fdi, aml_int(0x08));
188
189 aml_append(dev, aml_name_decl("_FDI", fdi));
190 return dev;
191}
192
193int cmos_get_fd_drive_type(FloppyDriveType fd0)
194{
195 int val;
196
197 switch (fd0) {
198 case FLOPPY_DRIVE_TYPE_144:
199
200 val = 4;
201 break;
202 case FLOPPY_DRIVE_TYPE_288:
203
204 val = 5;
205 break;
206 case FLOPPY_DRIVE_TYPE_120:
207
208 val = 2;
209 break;
210 case FLOPPY_DRIVE_TYPE_NONE:
211 default:
212 val = 0;
213 break;
214 }
215 return val;
216}
217
218static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope)
219{
220 FDCtrlISABus *isa = ISA_FDC(adev);
221 Aml *dev;
222 Aml *crs;
223 int i;
224
225#define ACPI_FDE_MAX_FD 4
226 uint32_t fde_buf[5] = {
227 0, 0, 0, 0,
228 cpu_to_le32(2)
229 };
230
231 crs = aml_resource_template();
232 aml_append(crs,
233 aml_io(AML_DECODE16, isa->iobase + 2, isa->iobase + 2, 0x00, 0x04));
234 aml_append(crs,
235 aml_io(AML_DECODE16, isa->iobase + 7, isa->iobase + 7, 0x00, 0x01));
236 aml_append(crs, aml_irq_no_flags(isa->irq));
237 aml_append(crs,
238 aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, isa->dma));
239
240 dev = aml_device("FDC0");
241 aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
242 aml_append(dev, aml_name_decl("_CRS", crs));
243
244 for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
245 FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i);
246
247 if (type < FLOPPY_DRIVE_TYPE_NONE) {
248 fde_buf[i] = cpu_to_le32(1);
249 aml_append(dev, build_fdinfo_aml(i, type));
250 }
251 }
252 aml_append(dev, aml_name_decl("_FDE",
253 aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf)));
254
255 aml_append(scope, dev);
256}
257
258static const VMStateDescription vmstate_isa_fdc = {
259 .name = "fdc",
260 .version_id = 2,
261 .minimum_version_id = 2,
262 .fields = (VMStateField[]) {
263 VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
264 VMSTATE_END_OF_LIST()
265 }
266};
267
268static Property isa_fdc_properties[] = {
269 DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0),
270 DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
271 DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
272 DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type,
273 FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
274 FloppyDriveType),
275 DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type,
276 FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
277 FloppyDriveType),
278 DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback,
279 FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type,
280 FloppyDriveType),
281 DEFINE_PROP_END_OF_LIST(),
282};
283
284static void isabus_fdc_class_init(ObjectClass *klass, void *data)
285{
286 DeviceClass *dc = DEVICE_CLASS(klass);
287 AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
288
289 dc->desc = "virtual floppy controller";
290 dc->realize = isabus_fdc_realize;
291 dc->fw_name = "fdc";
292 dc->reset = fdctrl_external_reset_isa;
293 dc->vmsd = &vmstate_isa_fdc;
294 adevc->build_dev_aml = build_fdc_aml;
295 device_class_set_props(dc, isa_fdc_properties);
296 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
297}
298
299static void isabus_fdc_instance_init(Object *obj)
300{
301 FDCtrlISABus *isa = ISA_FDC(obj);
302
303 device_add_bootindex_property(obj, &isa->bootindexA,
304 "bootindexA", "/floppy@0",
305 DEVICE(obj));
306 device_add_bootindex_property(obj, &isa->bootindexB,
307 "bootindexB", "/floppy@1",
308 DEVICE(obj));
309}
310
311static const TypeInfo isa_fdc_info = {
312 .name = TYPE_ISA_FDC,
313 .parent = TYPE_ISA_DEVICE,
314 .instance_size = sizeof(FDCtrlISABus),
315 .class_init = isabus_fdc_class_init,
316 .instance_init = isabus_fdc_instance_init,
317 .interfaces = (InterfaceInfo[]) {
318 { TYPE_ACPI_DEV_AML_IF },
319 { },
320 },
321};
322
323static void isa_fdc_register_types(void)
324{
325 type_register_static(&isa_fdc_info);
326}
327
328type_init(isa_fdc_register_types)
329