X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Ffdc.c;h=dcd1d46b4886ed6b1ca5a333a022b5be0146c068;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=d512b1ca98bbca5e6846712b4b881b19caaa4bfe;hpb=85571bc7415c3fa9390f5edc3720ec7975219a68;p=qemu diff --git a/hw/fdc.c b/hw/fdc.c index d512b1c..dcd1d46 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -1,8 +1,8 @@ /* * QEMU Floppy disk emulator (Intel 82078) - * - * Copyright (c) 2003 Jocelyn Mayer - * + * + * Copyright (c) 2003, 2007 Jocelyn Mayer + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +/* + * The controller is used in Sun4m systems in a slightly different + * way. There are changes in DOR register and DMA is not available. + */ #include "vl.h" /********************************************************/ @@ -62,7 +66,6 @@ typedef enum fdrive_type_t { typedef enum fdrive_flags_t { FDRIVE_MOTOR_ON = 0x01, /* motor on/off */ - FDRIVE_REVALIDATE = 0x02, /* Revalidated */ } fdrive_flags_t; typedef enum fdisk_flags_t { @@ -214,7 +217,7 @@ static fd_format_t fd_formats[] = { { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", }, - /* 320 kB 5"1/4 floppy disks */ + /* 320 kB 5"1/4 floppy disks */ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", }, /* 360 kB must match 5"1/4 better than 3"1/2... */ @@ -232,7 +235,6 @@ static void fd_revalidate (fdrive_t *drv) int nb_heads, max_track, last_sect, ro; FLOPPY_DPRINTF("revalidate\n"); - drv->drflags &= ~FDRIVE_REVALIDATE; if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ro = bdrv_is_read_only(drv->bs); bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); @@ -287,7 +289,6 @@ static void fd_revalidate (fdrive_t *drv) drv->max_track = 0; drv->flags &= ~FDISK_DBL_SIDES; } - drv->drflags |= FDRIVE_REVALIDATE; } /* Motor control */ @@ -367,9 +368,9 @@ struct fdctrl_t { /* Controller's identification */ uint8_t version; /* HW */ - int irq_lvl; + qemu_irq irq; int dma_chann; - uint32_t io_base; + target_phys_addr_t io_base; /* Controller state */ QEMUTimer *result_timer; uint8_t state; @@ -404,6 +405,12 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) uint32_t retval; switch (reg & 0x07) { +#ifdef TARGET_SPARC + case 0x00: + // Identify to Linux as S82078B + retval = fdctrl_read_statusB(fdctrl); + break; +#endif case 0x01: retval = fdctrl_read_statusB(fdctrl); break; @@ -455,36 +462,139 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) } } -static void fd_change_cb (void *opaque) +static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg) { - fdrive_t *drv = opaque; + return fdctrl_read(opaque, (uint32_t)reg); +} - FLOPPY_DPRINTF("disk change\n"); - fd_revalidate(drv); -#if 0 - fd_recalibrate(drv); - fdctrl_reset_fifo(drv->fdctrl); - fdctrl_raise_irq(drv->fdctrl, 0x20); -#endif +static void fdctrl_write_mem (void *opaque, + target_phys_addr_t reg, uint32_t value) +{ + fdctrl_write(opaque, (uint32_t)reg, value); } -fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, - uint32_t io_base, +static CPUReadMemoryFunc *fdctrl_mem_read[3] = { + fdctrl_read_mem, + fdctrl_read_mem, + fdctrl_read_mem, +}; + +static CPUWriteMemoryFunc *fdctrl_mem_write[3] = { + fdctrl_write_mem, + fdctrl_write_mem, + fdctrl_write_mem, +}; + +static void fd_save (QEMUFile *f, fdrive_t *fd) +{ + uint8_t tmp; + + tmp = fd->drflags; + qemu_put_8s(f, &tmp); + qemu_put_8s(f, &fd->head); + qemu_put_8s(f, &fd->track); + qemu_put_8s(f, &fd->sect); + qemu_put_8s(f, &fd->dir); + qemu_put_8s(f, &fd->rw); +} + +static void fdc_save (QEMUFile *f, void *opaque) +{ + fdctrl_t *s = opaque; + + qemu_put_8s(f, &s->state); + qemu_put_8s(f, &s->dma_en); + qemu_put_8s(f, &s->cur_drv); + qemu_put_8s(f, &s->bootsel); + qemu_put_buffer(f, s->fifo, FD_SECTOR_LEN); + qemu_put_be32s(f, &s->data_pos); + qemu_put_be32s(f, &s->data_len); + qemu_put_8s(f, &s->data_state); + qemu_put_8s(f, &s->data_dir); + qemu_put_8s(f, &s->int_status); + qemu_put_8s(f, &s->eot); + qemu_put_8s(f, &s->timer0); + qemu_put_8s(f, &s->timer1); + qemu_put_8s(f, &s->precomp_trk); + qemu_put_8s(f, &s->config); + qemu_put_8s(f, &s->lock); + qemu_put_8s(f, &s->pwrd); + fd_save(f, &s->drives[0]); + fd_save(f, &s->drives[1]); +} + +static int fd_load (QEMUFile *f, fdrive_t *fd) +{ + uint8_t tmp; + + qemu_get_8s(f, &tmp); + fd->drflags = tmp; + qemu_get_8s(f, &fd->head); + qemu_get_8s(f, &fd->track); + qemu_get_8s(f, &fd->sect); + qemu_get_8s(f, &fd->dir); + qemu_get_8s(f, &fd->rw); + + return 0; +} + +static int fdc_load (QEMUFile *f, void *opaque, int version_id) +{ + fdctrl_t *s = opaque; + int ret; + + if (version_id != 1) + return -EINVAL; + + qemu_get_8s(f, &s->state); + qemu_get_8s(f, &s->dma_en); + qemu_get_8s(f, &s->cur_drv); + qemu_get_8s(f, &s->bootsel); + qemu_get_buffer(f, s->fifo, FD_SECTOR_LEN); + qemu_get_be32s(f, &s->data_pos); + qemu_get_be32s(f, &s->data_len); + qemu_get_8s(f, &s->data_state); + qemu_get_8s(f, &s->data_dir); + qemu_get_8s(f, &s->int_status); + qemu_get_8s(f, &s->eot); + qemu_get_8s(f, &s->timer0); + qemu_get_8s(f, &s->timer1); + qemu_get_8s(f, &s->precomp_trk); + qemu_get_8s(f, &s->config); + qemu_get_8s(f, &s->lock); + qemu_get_8s(f, &s->pwrd); + + ret = fd_load(f, &s->drives[0]); + if (ret == 0) + ret = fd_load(f, &s->drives[1]); + + return ret; +} + +static void fdctrl_external_reset(void *opaque) +{ + fdctrl_t *s = opaque; + + fdctrl_reset(s, 0); +} + +fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped, + target_phys_addr_t io_base, BlockDriverState **fds) { fdctrl_t *fdctrl; -// int io_mem; + int io_mem; int i; FLOPPY_DPRINTF("init controller\n"); fdctrl = qemu_mallocz(sizeof(fdctrl_t)); if (!fdctrl) return NULL; - fdctrl->result_timer = qemu_new_timer(vm_clock, + fdctrl->result_timer = qemu_new_timer(vm_clock, fdctrl_result_timer, fdctrl); fdctrl->version = 0x90; /* Intel 82078 controller */ - fdctrl->irq_lvl = irq_lvl; + fdctrl->irq = irq; fdctrl->dma_chann = dma_chann; fdctrl->io_base = io_base; fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */ @@ -496,25 +606,24 @@ fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, } for (i = 0; i < 2; i++) { fd_init(&fdctrl->drives[i], fds[i]); - if (fds[i]) { - bdrv_set_change_cb(fds[i], - &fd_change_cb, &fdctrl->drives[i]); - } } fdctrl_reset(fdctrl, 0); fdctrl->state = FD_CTRL_ACTIVE; if (mem_mapped) { - FLOPPY_ERROR("memory mapped floppy not supported by now !\n"); -#if 0 - io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write); - cpu_register_physical_memory(base, 0x08, io_mem); -#endif + io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl); + cpu_register_physical_memory(io_base, 0x08, io_mem); } else { - register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl); - register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl); - register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl); - register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl); + register_ioport_read((uint32_t)io_base + 0x01, 5, 1, &fdctrl_read, + fdctrl); + register_ioport_read((uint32_t)io_base + 0x07, 1, 1, &fdctrl_read, + fdctrl); + register_ioport_write((uint32_t)io_base + 0x01, 5, 1, &fdctrl_write, + fdctrl); + register_ioport_write((uint32_t)io_base + 0x07, 1, 1, &fdctrl_write, + fdctrl); } + register_savevm("fdc", io_base, 1, fdc_save, fdc_load, fdctrl); + qemu_register_reset(fdctrl_external_reset, fdctrl); for (i = 0; i < 2; i++) { fd_revalidate(&fdctrl->drives[i]); } @@ -532,14 +641,22 @@ int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) static void fdctrl_reset_irq (fdctrl_t *fdctrl) { FLOPPY_DPRINTF("Reset interrupt\n"); - pic_set_irq(fdctrl->irq_lvl, 0); + qemu_set_irq(fdctrl->irq, 0); fdctrl->state &= ~FD_CTRL_INTR; } static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) { +#ifdef TARGET_SPARC + // Sparc mutation + if (!fdctrl->dma_en) { + fdctrl->state &= ~FD_CTRL_BUSY; + fdctrl->int_status = status; + return; + } +#endif if (~(fdctrl->state & FD_CTRL_INTR)) { - pic_set_irq(fdctrl->irq_lvl, 1); + qemu_set_irq(fdctrl->irq, 1); fdctrl->state |= FD_CTRL_INTR; } FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); @@ -722,18 +839,28 @@ static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) // fdctrl.precomp = (value >> 2) & 0x07; } +static int fdctrl_media_changed(fdrive_t *drv) +{ + int ret; + if (!drv->bs) + return 0; + ret = bdrv_media_changed(drv->bs); + if (ret) { + fd_revalidate(drv); + } + return ret; +} + /* Digital input register : 0x07 (read-only) */ static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) { uint32_t retval = 0; - if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE || - drv1(fdctrl)->drflags & FDRIVE_REVALIDATE) + if (fdctrl_media_changed(drv0(fdctrl)) || + fdctrl_media_changed(drv1(fdctrl))) retval |= 0x80; if (retval != 0) FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); - drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; - drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; return retval; } @@ -860,7 +987,7 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) fdctrl->data_len = fdctrl->fifo[8]; } else { int tmp; - fdctrl->data_len = 128 << fdctrl->fifo[5]; + fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); tmp = (cur_drv->last_sect - ks + 1); if (fdctrl->fifo[0] & 0x80) tmp += cur_drv->last_sect; @@ -941,11 +1068,11 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, len = dma_len - fdctrl->data_pos; if (len + rel_pos > FD_SECTOR_LEN) len = FD_SECTOR_LEN - rel_pos; - FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " - "(%d-0x%08x 0x%08x)\n", len, size, fdctrl->data_pos, + FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " + "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, fdctrl->data_len, fdctrl->cur_drv, cur_drv->head, cur_drv->track, cur_drv->sect, fd_sector(cur_drv), - fd_sector(cur_drv) * 512, addr); + fd_sector(cur_drv) * 512); if (fdctrl->data_dir != FD_DIR_WRITE || len < FD_SECTOR_LEN || rel_pos != 0) { /* READ & SCAN commands and realign to a sector for WRITE */ @@ -1006,7 +1133,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n", cur_drv->head, cur_drv->track, cur_drv->sect, fd_sector(cur_drv), - fdctrl->data_pos - size); + fdctrl->data_pos - len); /* XXX: cur_drv->sect >= cur_drv->last_sect should be an error in fact */ if (cur_drv->sect >= cur_drv->last_sect || @@ -1014,7 +1141,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, cur_drv->sect = 1; if (FD_MULTI_TRACK(fdctrl->data_state)) { if (cur_drv->head == 0 && - (cur_drv->flags & FDISK_DBL_SIDES) != 0) { + (cur_drv->flags & FDISK_DBL_SIDES) != 0) { cur_drv->head = 1; } else { cur_drv->head = 0; @@ -1141,7 +1268,7 @@ static void fdctrl_format_sector (fdctrl_t *fdctrl) memset(fdctrl->fifo, 0, FD_SECTOR_LEN); if (cur_drv->bs == NULL || bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv)); + FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); } else { if (cur_drv->sect == cur_drv->last_sect) { @@ -1605,7 +1732,7 @@ enqueue: FLOPPY_DPRINTF("treat READ_ID command\n"); /* XXX: should set main status register to busy */ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; - qemu_mod_timer(fdctrl->result_timer, + qemu_mod_timer(fdctrl->result_timer, qemu_get_clock(vm_clock) + (ticks_per_sec / 50)); break; case 0x4C: @@ -1645,8 +1772,9 @@ enqueue: #else cur_drv->last_sect = fdctrl->fifo[3]; #endif - /* Bochs BIOS is buggy and don't send format informations - * for each sector. So, pretend all's done right now... + /* TODO: implement format using DMA expected by the Bochs BIOS + * and Linux fdformat (read 3 bytes per sector via DMA and fill + * the sector with the specified fill byte */ fdctrl->data_state &= ~FD_STATE_FORMAT; fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); @@ -1715,5 +1843,13 @@ enqueue: static void fdctrl_result_timer(void *opaque) { fdctrl_t *fdctrl = opaque; + fdrive_t *cur_drv = get_cur_drv(fdctrl); + /* Pretend we are spinning. + * This is needed for Coherent, which uses READ ID to check for + * sector interleaving. + */ + if (cur_drv->last_sect != 0) { + cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; + } fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); }