]>
Commit | Line | Data |
---|---|---|
72ea60e4 PMD |
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" | |
e7c72a67 | 35 | #include "hw/acpi/acpi_aml_interface.h" |
72ea60e4 PMD |
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 | ||
52 | OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC) | |
53 | ||
54 | struct FDCtrlISABus { | |
55 | /*< private >*/ | |
56 | ISADevice parent_obj; | |
57 | /*< public >*/ | |
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 | ||
67 | static 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 | ||
75 | void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds) | |
76 | { | |
77 | fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds); | |
78 | } | |
79 | ||
80 | static 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 | ||
86 | static void isabus_fdc_realize(DeviceState *dev, Error **errp) | |
87 | { | |
88 | ISADevice *isadev = ISA_DEVICE(dev); | |
8e7db8ab | 89 | ISABus *bus = isa_bus_from_device(isadev); |
72ea60e4 PMD |
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 | ||
8e7db8ab | 98 | fdctrl->irq = isa_bus_get_irq(bus, isa->irq); |
72ea60e4 PMD |
99 | fdctrl->dma_chann = isa->dma; |
100 | if (fdctrl->dma_chann != -1) { | |
101 | IsaDmaClass *k; | |
8e7db8ab | 102 | fdctrl->dma = isa_bus_get_dma(bus, isa->dma); |
72ea60e4 PMD |
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 | ||
121 | FloppyDriveType 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 | ||
128 | static 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 | ||
151 | static 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)); /* Drive Number */ | |
164 | aml_append(fdi, | |
165 | aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ | |
166 | /* | |
167 | * the values below are the limits of the drive, and are thus independent | |
168 | * of the inserted media | |
169 | */ | |
170 | aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ | |
171 | aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ | |
172 | aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ | |
173 | /* | |
174 | * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of | |
175 | * the drive type, so shall we | |
176 | */ | |
177 | aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ | |
178 | aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ | |
179 | aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ | |
180 | aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ | |
181 | aml_append(fdi, aml_int(0x12)); /* disk_eot */ | |
182 | aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ | |
183 | aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ | |
184 | aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ | |
185 | aml_append(fdi, aml_int(0xF6)); /* disk_fill */ | |
186 | aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ | |
187 | aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ | |
188 | ||
189 | aml_append(dev, aml_name_decl("_FDI", fdi)); | |
190 | return dev; | |
191 | } | |
192 | ||
193 | int cmos_get_fd_drive_type(FloppyDriveType fd0) | |
194 | { | |
195 | int val; | |
196 | ||
197 | switch (fd0) { | |
198 | case FLOPPY_DRIVE_TYPE_144: | |
199 | /* 1.44 Mb 3"5 drive */ | |
200 | val = 4; | |
201 | break; | |
202 | case FLOPPY_DRIVE_TYPE_288: | |
203 | /* 2.88 Mb 3"5 drive */ | |
204 | val = 5; | |
205 | break; | |
206 | case FLOPPY_DRIVE_TYPE_120: | |
207 | /* 1.2 Mb 5"5 drive */ | |
208 | val = 2; | |
209 | break; | |
210 | case FLOPPY_DRIVE_TYPE_NONE: | |
211 | default: | |
212 | val = 0; | |
213 | break; | |
214 | } | |
215 | return val; | |
216 | } | |
217 | ||
e7c72a67 | 218 | static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope) |
72ea60e4 | 219 | { |
e7c72a67 | 220 | FDCtrlISABus *isa = ISA_FDC(adev); |
72ea60e4 PMD |
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, /* presence of floppy drives #0 - #3 */ | |
228 | cpu_to_le32(2) /* tape presence (2 == never present) */ | |
229 | }; | |
230 | ||
231 | crs = aml_resource_template(); | |
72ea60e4 | 232 | aml_append(crs, |
fdb8541b BB |
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)); | |
72ea60e4 PMD |
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++) { | |
e7c72a67 | 245 | FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i); |
72ea60e4 PMD |
246 | |
247 | if (type < FLOPPY_DRIVE_TYPE_NONE) { | |
248 | fde_buf[i] = cpu_to_le32(1); /* drive present */ | |
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 | ||
258 | static const VMStateDescription vmstate_isa_fdc = { | |
259 | .name = "fdc", | |
260 | .version_id = 2, | |
261 | .minimum_version_id = 2, | |
7d5dc0a3 | 262 | .fields = (const VMStateField[]) { |
72ea60e4 PMD |
263 | VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), |
264 | VMSTATE_END_OF_LIST() | |
265 | } | |
266 | }; | |
267 | ||
268 | static 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 | ||
284 | static void isabus_fdc_class_init(ObjectClass *klass, void *data) | |
285 | { | |
286 | DeviceClass *dc = DEVICE_CLASS(klass); | |
e7c72a67 | 287 | AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); |
72ea60e4 | 288 | |
9362984f | 289 | dc->desc = "virtual floppy controller"; |
72ea60e4 PMD |
290 | dc->realize = isabus_fdc_realize; |
291 | dc->fw_name = "fdc"; | |
292 | dc->reset = fdctrl_external_reset_isa; | |
293 | dc->vmsd = &vmstate_isa_fdc; | |
e7c72a67 | 294 | adevc->build_dev_aml = build_fdc_aml; |
72ea60e4 PMD |
295 | device_class_set_props(dc, isa_fdc_properties); |
296 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | |
297 | } | |
298 | ||
299 | static 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 | ||
311 | static 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, | |
e7c72a67 IM |
317 | .interfaces = (InterfaceInfo[]) { |
318 | { TYPE_ACPI_DEV_AML_IF }, | |
319 | { }, | |
320 | }, | |
72ea60e4 PMD |
321 | }; |
322 | ||
323 | static void isa_fdc_register_types(void) | |
324 | { | |
325 | type_register_static(&isa_fdc_info); | |
326 | } | |
327 | ||
328 | type_init(isa_fdc_register_types) |