fdc-isa.c (10114B)
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 52OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC) 53 54struct 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 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 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 120FloppyDriveType 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 127static 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 150static 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 192int 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 217static 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 254static 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 264static 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 280static void isabus_fdc_class_init(ObjectClass *klass, void *data) 281{ 282 DeviceClass *dc = DEVICE_CLASS(klass); 283 ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); 284 285 dc->desc = "virtual floppy controller"; 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 295static 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 307static 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 315static void isa_fdc_register_types(void) 316{ 317 type_register_static(&isa_fdc_info); 318} 319 320type_init(isa_fdc_register_types)