]>
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" | |
35 | #include "hw/acpi/aml-build.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 | ||
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); | |
89 | FDCtrlISABus *isa = ISA_FDC(dev); | |
90 | FDCtrl *fdctrl = &isa->state; | |
91 | Error *err = NULL; | |
92 | ||
93 | isa_register_portio_list(isadev, &fdctrl->portio_list, | |
94 | isa->iobase, fdc_portio_list, fdctrl, | |
95 | "fdc"); | |
96 | ||
97 | isa_init_irq(isadev, &fdctrl->irq, isa->irq); | |
98 | fdctrl->dma_chann = isa->dma; | |
99 | if (fdctrl->dma_chann != -1) { | |
100 | IsaDmaClass *k; | |
101 | fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); | |
102 | if (!fdctrl->dma) { | |
103 | error_setg(errp, "ISA controller does not support DMA"); | |
104 | return; | |
105 | } | |
106 | k = ISADMA_GET_CLASS(fdctrl->dma); | |
107 | k->register_channel(fdctrl->dma, fdctrl->dma_chann, | |
108 | &fdctrl_transfer_handler, fdctrl); | |
109 | } | |
110 | ||
111 | qdev_set_legacy_instance_id(dev, isa->iobase, 2); | |
112 | ||
113 | fdctrl_realize_common(dev, fdctrl, &err); | |
114 | if (err != NULL) { | |
115 | error_propagate(errp, err); | |
116 | return; | |
117 | } | |
118 | } | |
119 | ||
120 | FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) | |
121 | { | |
122 | FDCtrlISABus *isa = ISA_FDC(fdc); | |
123 | ||
124 | return isa->state.drives[i].drive; | |
125 | } | |
126 | ||
127 | static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc, | |
128 | uint8_t *maxh, uint8_t *maxs) | |
129 | { | |
130 | const FDFormat *fdf; | |
131 | ||
132 | *maxc = *maxh = *maxs = 0; | |
133 | for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) { | |
134 | if (fdf->drive != type) { | |
135 | continue; | |
136 | } | |
137 | if (*maxc < fdf->max_track) { | |
138 | *maxc = fdf->max_track; | |
139 | } | |
140 | if (*maxh < fdf->max_head) { | |
141 | *maxh = fdf->max_head; | |
142 | } | |
143 | if (*maxs < fdf->last_sect) { | |
144 | *maxs = fdf->last_sect; | |
145 | } | |
146 | } | |
147 | (*maxc)--; | |
148 | } | |
149 | ||
150 | static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) | |
151 | { | |
152 | Aml *dev, *fdi; | |
153 | uint8_t maxc, maxh, maxs; | |
154 | ||
155 | isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs); | |
156 | ||
157 | dev = aml_device("FLP%c", 'A' + idx); | |
158 | ||
159 | aml_append(dev, aml_name_decl("_ADR", aml_int(idx))); | |
160 | ||
161 | fdi = aml_package(16); | |
162 | aml_append(fdi, aml_int(idx)); /* Drive Number */ | |
163 | aml_append(fdi, | |
164 | aml_int(cmos_get_fd_drive_type(type))); /* Device Type */ | |
165 | /* | |
166 | * the values below are the limits of the drive, and are thus independent | |
167 | * of the inserted media | |
168 | */ | |
169 | aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */ | |
170 | aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */ | |
171 | aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */ | |
172 | /* | |
173 | * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of | |
174 | * the drive type, so shall we | |
175 | */ | |
176 | aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */ | |
177 | aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */ | |
178 | aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */ | |
179 | aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */ | |
180 | aml_append(fdi, aml_int(0x12)); /* disk_eot */ | |
181 | aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */ | |
182 | aml_append(fdi, aml_int(0xFF)); /* disk_dtl */ | |
183 | aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */ | |
184 | aml_append(fdi, aml_int(0xF6)); /* disk_fill */ | |
185 | aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */ | |
186 | aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */ | |
187 | ||
188 | aml_append(dev, aml_name_decl("_FDI", fdi)); | |
189 | return dev; | |
190 | } | |
191 | ||
192 | int cmos_get_fd_drive_type(FloppyDriveType fd0) | |
193 | { | |
194 | int val; | |
195 | ||
196 | switch (fd0) { | |
197 | case FLOPPY_DRIVE_TYPE_144: | |
198 | /* 1.44 Mb 3"5 drive */ | |
199 | val = 4; | |
200 | break; | |
201 | case FLOPPY_DRIVE_TYPE_288: | |
202 | /* 2.88 Mb 3"5 drive */ | |
203 | val = 5; | |
204 | break; | |
205 | case FLOPPY_DRIVE_TYPE_120: | |
206 | /* 1.2 Mb 5"5 drive */ | |
207 | val = 2; | |
208 | break; | |
209 | case FLOPPY_DRIVE_TYPE_NONE: | |
210 | default: | |
211 | val = 0; | |
212 | break; | |
213 | } | |
214 | return val; | |
215 | } | |
216 | ||
217 | static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope) | |
218 | { | |
219 | Aml *dev; | |
220 | Aml *crs; | |
221 | int i; | |
222 | ||
223 | #define ACPI_FDE_MAX_FD 4 | |
224 | uint32_t fde_buf[5] = { | |
225 | 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */ | |
226 | cpu_to_le32(2) /* tape presence (2 == never present) */ | |
227 | }; | |
228 | ||
229 | crs = aml_resource_template(); | |
230 | aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04)); | |
231 | aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01)); | |
232 | aml_append(crs, aml_irq_no_flags(6)); | |
233 | aml_append(crs, | |
234 | aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2)); | |
235 | ||
236 | dev = aml_device("FDC0"); | |
237 | aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); | |
238 | aml_append(dev, aml_name_decl("_CRS", crs)); | |
239 | ||
240 | for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { | |
241 | FloppyDriveType type = isa_fdc_get_drive_type(isadev, i); | |
242 | ||
243 | if (type < FLOPPY_DRIVE_TYPE_NONE) { | |
244 | fde_buf[i] = cpu_to_le32(1); /* drive present */ | |
245 | aml_append(dev, build_fdinfo_aml(i, type)); | |
246 | } | |
247 | } | |
248 | aml_append(dev, aml_name_decl("_FDE", | |
249 | aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf))); | |
250 | ||
251 | aml_append(scope, dev); | |
252 | } | |
253 | ||
254 | static const VMStateDescription vmstate_isa_fdc = { | |
255 | .name = "fdc", | |
256 | .version_id = 2, | |
257 | .minimum_version_id = 2, | |
258 | .fields = (VMStateField[]) { | |
259 | VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), | |
260 | VMSTATE_END_OF_LIST() | |
261 | } | |
262 | }; | |
263 | ||
264 | static Property isa_fdc_properties[] = { | |
265 | DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), | |
266 | DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), | |
267 | DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), | |
268 | DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, | |
269 | FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, | |
270 | FloppyDriveType), | |
271 | DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, | |
272 | FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, | |
273 | FloppyDriveType), | |
274 | DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, | |
275 | FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, | |
276 | FloppyDriveType), | |
277 | DEFINE_PROP_END_OF_LIST(), | |
278 | }; | |
279 | ||
280 | static void isabus_fdc_class_init(ObjectClass *klass, void *data) | |
281 | { | |
282 | DeviceClass *dc = DEVICE_CLASS(klass); | |
283 | ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); | |
284 | ||
9362984f | 285 | dc->desc = "virtual floppy controller"; |
72ea60e4 PMD |
286 | dc->realize = isabus_fdc_realize; |
287 | dc->fw_name = "fdc"; | |
288 | dc->reset = fdctrl_external_reset_isa; | |
289 | dc->vmsd = &vmstate_isa_fdc; | |
290 | isa->build_aml = fdc_isa_build_aml; | |
291 | device_class_set_props(dc, isa_fdc_properties); | |
292 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | |
293 | } | |
294 | ||
295 | static void isabus_fdc_instance_init(Object *obj) | |
296 | { | |
297 | FDCtrlISABus *isa = ISA_FDC(obj); | |
298 | ||
299 | device_add_bootindex_property(obj, &isa->bootindexA, | |
300 | "bootindexA", "/floppy@0", | |
301 | DEVICE(obj)); | |
302 | device_add_bootindex_property(obj, &isa->bootindexB, | |
303 | "bootindexB", "/floppy@1", | |
304 | DEVICE(obj)); | |
305 | } | |
306 | ||
307 | static const TypeInfo isa_fdc_info = { | |
308 | .name = TYPE_ISA_FDC, | |
309 | .parent = TYPE_ISA_DEVICE, | |
310 | .instance_size = sizeof(FDCtrlISABus), | |
311 | .class_init = isabus_fdc_class_init, | |
312 | .instance_init = isabus_fdc_instance_init, | |
313 | }; | |
314 | ||
315 | static void isa_fdc_register_types(void) | |
316 | { | |
317 | type_register_static(&isa_fdc_info); | |
318 | } | |
319 | ||
320 | type_init(isa_fdc_register_types) |