int cylinders, heads, sectors;
int64_t nb_sectors;
int mult_sectors;
+ int identify_set;
+ uint16_t identify_data[256];
+ SetIRQFunc *set_irq;
+ void *irq_opaque;
int irq;
- openpic_t *openpic;
PCIDevice *pci_dev;
struct BMDMAState *bmdma;
int drive_serial;
uint8_t *data_ptr;
uint8_t *data_end;
uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
+ QEMUTimer *sector_write_timer; /* only used for win2k instal hack */
} IDEState;
#define BM_STATUS_DMAING 0x01
#define BM_CMD_START 0x01
#define BM_CMD_READ 0x08
+#define IDE_TYPE_PIIX3 0
+#define IDE_TYPE_CMD646 1
+
+/* CMD646 specific */
+#define MRDMODE 0x71
+#define MRDMODE_INTR_CH0 0x04
+#define MRDMODE_INTR_CH1 0x08
+#define MRDMODE_BLK_CH0 0x10
+#define MRDMODE_BLK_CH1 0x20
+#define UDIDETCR0 0x73
+#define UDIDETCR1 0x7B
+
typedef int IDEDMAFunc(IDEState *s,
target_phys_addr_t phys_addr,
int transfer_size1);
uint8_t cmd;
uint8_t status;
uint32_t addr;
+
+ struct PCIIDEState *pci_dev;
/* current transfer state */
IDEState *ide_if;
IDEDMAFunc *dma_cb;
PCIDevice dev;
IDEState ide_if[4];
BMDMAState bmdma[2];
+ int type; /* see IDE_TYPE_xxx */
} PCIIDEState;
static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb);
unsigned int oldsize;
char buf[20];
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
put_le16(p + 0, 0x0040);
put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
#endif
put_le16(p + 48, 1); /* dword I/O */
- put_le16(p + 49, 1 << 9 | 1 << 8); /* DMA and LBA supported */
+ put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
put_le16(p + 51, 0x200); /* PIO transfer cycle */
put_le16(p + 52, 0x200); /* DMA transfer cycle */
- put_le16(p + 53, 1 | 1 << 2); /* words 54-58,88 are valid */
+ put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */
put_le16(p + 54, s->cylinders);
put_le16(p + 55, s->heads);
put_le16(p + 56, s->sectors);
put_le16(p + 59, 0x100 | s->mult_sectors);
put_le16(p + 60, s->nb_sectors);
put_le16(p + 61, s->nb_sectors >> 16);
- put_le16(p + 80, (1 << 1) | (1 << 2));
+ put_le16(p + 63, 0x07); /* mdma0-2 supported */
+ put_le16(p + 65, 120);
+ put_le16(p + 66, 120);
+ put_le16(p + 67, 120);
+ put_le16(p + 68, 120);
+ put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+ put_le16(p + 81, 0x16); /* conforms to ata5 */
put_le16(p + 82, (1 << 14));
put_le16(p + 83, (1 << 14));
put_le16(p + 84, (1 << 14));
put_le16(p + 85, (1 << 14));
put_le16(p + 86, 0);
put_le16(p + 87, (1 << 14));
- put_le16(p + 88, 0x1f | (1 << 13));
- put_le16(p + 93, 1 | (1 << 14) | 0x2000 | 0x4000);
+ put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+ put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
}
static void ide_atapi_identify(IDEState *s)
uint16_t *p;
char buf[20];
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
/* Removable CDROM, 50us response, 12 byte packets */
put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
-
+
put_le16(p + 71, 30); /* in ns */
put_le16(p + 72, 30); /* in ns */
put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
}
static void ide_set_signature(IDEState *s)
{
BMDMAState *bm = s->bmdma;
if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) {
- if (bm)
+ if (bm) {
bm->status |= BM_STATUS_INT;
-#ifdef TARGET_PPC
- if (s->openpic)
- openpic_set_irq(s->openpic, s->irq, 1);
- else
-#endif
- if (s->irq == 16)
- pci_set_irq(s->pci_dev, 0, 1);
- else
- pic_set_irq(s->irq, 1);
+ }
+ s->set_irq(s->irq_opaque, s->irq, 1);
}
}
ide_dma_start(s, ide_read_dma_cb);
}
+static void ide_sector_write_timer_cb(void *opaque)
+{
+ IDEState *s = opaque;
+ ide_set_irq(s);
+}
+
static void ide_sector_write(IDEState *s)
{
int64_t sector_num;
ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write);
}
ide_set_sector(s, sector_num + n);
- ide_set_irq(s);
+
+#ifdef TARGET_I386
+ if (win2k_install_hack) {
+ /* It seems there is a bug in the Windows 2000 installer HDD
+ IDE driver which fills the disk with empty logs when the
+ IDE write IRQ comes too early. This hack tries to correct
+ that at the expense of slower write performances. Use this
+ option _only_ to install Windows 2000. You must disable it
+ for normal use. */
+ qemu_mod_timer(s->sector_write_timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 1000));
+ } else
+#endif
+ {
+ ide_set_irq(s);
+ }
}
static int ide_write_dma_cb(IDEState *s,
case 2352:
/* sync bytes */
buf[0] = 0x00;
- memset(buf + 1, 0xff, 11);
+ memset(buf + 1, 0xff, 10);
+ buf[11] = 0x00;
buf += 12;
/* MSF */
lba_to_msf(buf, lba);
transfer_size = transfer_size1;
while (transfer_size > 0) {
+#ifdef DEBUG_IDE_ATAPI
+ printf("transfer_size: %d phys_addr=%08x\n", transfer_size, phys_addr);
+#endif
if (s->packet_transfer_size <= 0)
break;
len = s->cd_sector_size - s->io_buffer_index;
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
- *q++ = 0; /* minute */
- *q++ = 2; /* second */
- *q++ = 0; /* frame */
+ lba_to_msf(q, 0);
+ q += 3;
} else {
/* sector 0 */
cpu_to_ube32(q, 0);
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
- *q++ = 0;
- *q++ = 0;
- *q++ = 0;
- *q++ = 0;
+ if (msf) {
+ *q++ = 0;
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
len = q - buf;
cpu_to_ube16(buf, len - 2);
/* XXX: valid for CDROM ? */
switch(s->feature) {
case 0x02: /* write cache enable */
- case 0x03: /* set transfer mode */
case 0x82: /* write cache disable */
case 0xaa: /* read look-ahead enable */
case 0x55: /* read look-ahead disable */
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s);
break;
+ case 0x03: { /* set transfer mode */
+ uint8_t val = s->nsector & 0x07;
+
+ switch (s->nsector >> 3) {
+ case 0x00: /* pio default */
+ case 0x01: /* pio mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x04: /* mdma mode */
+ put_le16(s->identify_data + 63,0x07 | (1 << (val + 8)));
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x08: /* udma mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f | (1 << (val + 8)));
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ }
default:
goto abort_cmd;
}
break;
case WIN_STANDBYNOW1:
case WIN_IDLEIMMEDIATE:
+ case WIN_FLUSH_CACHE:
s->status = READY_STAT;
ide_set_irq(s);
break;
case WIN_PIDENTIFY:
if (s->is_cdrom) {
ide_atapi_identify(s);
- s->status = READY_STAT;
+ s->status = READY_STAT | SEEK_STAT;
ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
} else {
ide_abort_command(s);
ret = 0;
else
ret = s->status;
-#ifdef TARGET_PPC
- if (s->openpic)
- openpic_set_irq(s->openpic, s->irq, 0);
- else
-#endif
- if (s->irq == 16)
- pci_set_irq(s->pci_dev, 0, 0);
- else
- pic_set_irq(s->irq, 0);
+ s->set_irq(s->irq_opaque, s->irq, 0);
break;
}
#ifdef DEBUG_IDE
return -1;
}
-static void ide_init2(IDEState *ide_state, int irq,
- BlockDriverState *hd0, BlockDriverState *hd1)
+static void ide_init2(IDEState *ide_state,
+ BlockDriverState *hd0, BlockDriverState *hd1,
+ SetIRQFunc *set_irq, void *irq_opaque, int irq)
{
IDEState *s;
static int drive_serial = 1;
}
}
s->drive_serial = drive_serial++;
+ s->set_irq = set_irq;
+ s->irq_opaque = irq_opaque;
s->irq = irq;
+ s->sector_write_timer = qemu_new_timer(vm_clock,
+ ide_sector_write_timer_cb, s);
ide_reset(s);
}
}
if (!ide_state)
return;
- ide_init2(ide_state, irq, hd0, hd1);
+ ide_init2(ide_state, hd0, hd1, pic_set_irq_new, isa_pic, irq);
ide_init_ioport(ide_state, iobase, iobase2);
}
/***********************************************************/
/* PCI IDE definitions */
+static void cmd646_update_irq(PCIIDEState *d);
+
static void ide_map(PCIDevice *pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
}
}
-static uint32_t bmdma_cmd_readb(void *opaque, uint32_t addr)
-{
- BMDMAState *bm = opaque;
- uint32_t val;
- val = bm->cmd;
-#ifdef DEBUG_IDE
- printf("%s: 0x%08x\n", __func__, val);
-#endif
- return val;
-}
-
static void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
{
BMDMAState *bm = opaque;
}
}
-static uint32_t bmdma_status_readb(void *opaque, uint32_t addr)
+static uint32_t bmdma_readb(void *opaque, uint32_t addr)
{
BMDMAState *bm = opaque;
+ PCIIDEState *pci_dev;
uint32_t val;
- val = bm->status;
+
+ switch(addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 1:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ val = pci_dev->dev.config[MRDMODE];
+ } else {
+ val = 0xff;
+ }
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ case 3:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ if (bm == &pci_dev->bmdma[0])
+ val = pci_dev->dev.config[UDIDETCR0];
+ else
+ val = pci_dev->dev.config[UDIDETCR1];
+ } else {
+ val = 0xff;
+ }
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
#ifdef DEBUG_IDE
- printf("%s: 0x%08x\n", __func__, val);
+ printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
#endif
return val;
}
-static void bmdma_status_writeb(void *opaque, uint32_t addr, uint32_t val)
+static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val)
{
BMDMAState *bm = opaque;
+ PCIIDEState *pci_dev;
#ifdef DEBUG_IDE
- printf("%s: 0x%08x\n", __func__, val);
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
#endif
- bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ switch(addr & 3) {
+ case 1:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ pci_dev->dev.config[MRDMODE] =
+ (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30);
+ cmd646_update_irq(pci_dev);
+ }
+ break;
+ case 2:
+ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ break;
+ case 3:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ if (bm == &pci_dev->bmdma[0])
+ pci_dev->dev.config[UDIDETCR0] = val;
+ else
+ pci_dev->dev.config[UDIDETCR1] = val;
+ }
+ break;
+ }
}
static uint32_t bmdma_addr_readl(void *opaque, uint32_t addr)
BMDMAState *bm = &d->bmdma[i];
d->ide_if[2 * i].bmdma = bm;
d->ide_if[2 * i + 1].bmdma = bm;
-
+ bm->pci_dev = (PCIIDEState *)pci_dev;
+
register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm);
- register_ioport_read(addr, 1, 1, bmdma_cmd_readb, bm);
- register_ioport_write(addr + 2, 1, 1, bmdma_status_writeb, bm);
- register_ioport_read(addr + 2, 1, 1, bmdma_status_readb, bm);
+ register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm);
+ register_ioport_read(addr, 4, 1, bmdma_readb, bm);
register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm);
register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm);
}
}
-/* hd_table must contain 4 block drivers */
-void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table)
+/* XXX: call it also when the MRDMODE is changed from the PCI config
+ registers */
+static void cmd646_update_irq(PCIIDEState *d)
+{
+ int pci_level;
+ pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) ||
+ ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1));
+ pci_set_irq((PCIDevice *)d, 0, pci_level);
+}
+
+/* the PCI irq level is the logical OR of the two channels */
+static void cmd646_set_irq(void *opaque, int channel, int level)
+{
+ PCIIDEState *d = opaque;
+ int irq_mask;
+
+ irq_mask = MRDMODE_INTR_CH0 << channel;
+ if (level)
+ d->dev.config[MRDMODE] |= irq_mask;
+ else
+ d->dev.config[MRDMODE] &= ~irq_mask;
+ cmd646_update_irq(d);
+}
+
+/* CMD646 PCI IDE controller */
+void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
+ int secondary_ide_enabled)
{
PCIIDEState *d;
uint8_t *pci_conf;
int i;
- d = (PCIIDEState *)pci_register_device(bus, "IDE", sizeof(PCIIDEState),
+ d = (PCIIDEState *)pci_register_device(bus, "CMD646 IDE",
+ sizeof(PCIIDEState),
-1,
NULL, NULL);
+ d->type = IDE_TYPE_CMD646;
pci_conf = d->dev.config;
- pci_conf[0x00] = 0x86; // Intel
- pci_conf[0x01] = 0x80;
- pci_conf[0x02] = 0x00; // fake
- pci_conf[0x03] = 0x01; // fake
+ pci_conf[0x00] = 0x95; // CMD646
+ pci_conf[0x01] = 0x10;
+ pci_conf[0x02] = 0x46;
+ pci_conf[0x03] = 0x06;
+
+ pci_conf[0x08] = 0x07; // IDE controller revision
+ pci_conf[0x09] = 0x8f;
+
pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
- pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic
-
- pci_conf[0x2c] = 0x86; // subsys vendor
- pci_conf[0x2d] = 0x80; // subsys vendor
- pci_conf[0x2e] = 0x00; // fake
- pci_conf[0x2f] = 0x01; // fake
+ pci_conf[0x0e] = 0x00; // header_type
+
+ if (secondary_ide_enabled) {
+ /* XXX: if not enabled, really disable the seconday IDE controller */
+ pci_conf[0x51] = 0x80; /* enable IDE1 */
+ }
pci_register_io_region((PCIDevice *)d, 0, 0x8,
PCI_ADDRESS_SPACE_IO, ide_map);
PCI_ADDRESS_SPACE_IO, bmdma_map);
pci_conf[0x3d] = 0x01; // interrupt on pin 1
-
+
for(i = 0; i < 4; i++)
d->ide_if[i].pci_dev = (PCIDevice *)d;
- ide_init2(&d->ide_if[0], 16, hd_table[0], hd_table[1]);
- ide_init2(&d->ide_if[2], 16, hd_table[2], hd_table[3]);
+ ide_init2(&d->ide_if[0], hd_table[0], hd_table[1],
+ cmd646_set_irq, d, 0);
+ ide_init2(&d->ide_if[2], hd_table[2], hd_table[3],
+ cmd646_set_irq, d, 1);
}
/* hd_table must contain 4 block drivers */
sizeof(PCIIDEState),
((PCIDevice *)piix3_state)->devfn + 1,
NULL, NULL);
+ d->type = IDE_TYPE_PIIX3;
+
pci_conf = d->dev.config;
pci_conf[0x00] = 0x86; // Intel
pci_conf[0x01] = 0x80;
pci_conf[0x02] = 0x10;
pci_conf[0x03] = 0x70;
+ pci_conf[0x09] = 0x80; // legacy ATA mode
pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
pci_conf[0x0e] = 0x00; // header_type
pci_register_io_region((PCIDevice *)d, 4, 0x10,
PCI_ADDRESS_SPACE_IO, bmdma_map);
- ide_init2(&d->ide_if[0], 14, hd_table[0], hd_table[1]);
- ide_init2(&d->ide_if[2], 15, hd_table[2], hd_table[3]);
+ ide_init2(&d->ide_if[0], hd_table[0], hd_table[1],
+ pic_set_irq_new, isa_pic, 14);
+ ide_init2(&d->ide_if[2], hd_table[2], hd_table[3],
+ pic_set_irq_new, isa_pic, 15);
ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6);
ide_init_ioport(&d->ide_if[2], 0x170, 0x376);
}
/* PowerMac uses memory mapped registers, not I/O. Return the memory
I/O index to access the ide. */
int pmac_ide_init (BlockDriverState **hd_table,
- openpic_t *openpic, int irq)
+ SetIRQFunc *set_irq, void *irq_opaque, int irq)
{
IDEState *ide_if;
int pmac_ide_memory;
ide_if = qemu_mallocz(sizeof(IDEState) * 2);
- ide_init2(&ide_if[0], irq, hd_table[0], hd_table[1]);
- ide_if[0].openpic = openpic;
- ide_if[1].openpic = openpic;
+ ide_init2(&ide_if[0], hd_table[0], hd_table[1],
+ set_irq, irq_opaque, irq);
pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
pmac_ide_write, &ide_if[0]);