summaryrefslogtreecommitdiff
path: root/hw/ide/ahci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ide/ahci.c')
-rw-r--r--hw/ide/ahci.c115
1 files changed, 73 insertions, 42 deletions
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 604152a823..4cda0d0075 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -584,7 +584,72 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
s->dev[port].finished |= finished;
*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished);
- ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_STAT_SDBS);
+ ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_SDB_FIS);
+}
+
+static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
+{
+ AHCIPortRegs *pr = &ad->port_regs;
+ uint8_t *pio_fis, *cmd_fis;
+ uint64_t tbl_addr;
+ dma_addr_t cmd_len = 0x80;
+
+ if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
+ return;
+ }
+
+ /* map cmd_fis */
+ tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr);
+ cmd_fis = dma_memory_map(ad->hba->as, tbl_addr, &cmd_len,
+ DMA_DIRECTION_TO_DEVICE);
+
+ if (cmd_fis == NULL) {
+ DPRINTF(ad->port_no, "dma_memory_map failed in ahci_write_fis_pio");
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR);
+ return;
+ }
+
+ if (cmd_len != 0x80) {
+ DPRINTF(ad->port_no,
+ "dma_memory_map mapped too few bytes in ahci_write_fis_pio");
+ dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
+ DMA_DIRECTION_TO_DEVICE, cmd_len);
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR);
+ return;
+ }
+
+ pio_fis = &ad->res_fis[RES_FIS_PSFIS];
+
+ pio_fis[0] = 0x5f;
+ pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+ pio_fis[2] = ad->port.ifs[0].status;
+ pio_fis[3] = ad->port.ifs[0].error;
+
+ pio_fis[4] = cmd_fis[4];
+ pio_fis[5] = cmd_fis[5];
+ pio_fis[6] = cmd_fis[6];
+ pio_fis[7] = cmd_fis[7];
+ pio_fis[8] = cmd_fis[8];
+ pio_fis[9] = cmd_fis[9];
+ pio_fis[10] = cmd_fis[10];
+ pio_fis[11] = cmd_fis[11];
+ pio_fis[12] = cmd_fis[12];
+ pio_fis[13] = cmd_fis[13];
+ pio_fis[14] = 0;
+ pio_fis[15] = ad->port.ifs[0].status;
+ pio_fis[16] = len & 255;
+ pio_fis[17] = len >> 8;
+ pio_fis[18] = 0;
+ pio_fis[19] = 0;
+
+ if (pio_fis[2] & ERR_STAT) {
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR);
+ }
+
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS);
+
+ dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
+ DMA_DIRECTION_TO_DEVICE, cmd_len);
}
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
@@ -629,7 +694,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
}
if (d2h_fis[2] & ERR_STAT) {
- ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_TFES);
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR);
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
@@ -969,11 +1034,6 @@ static int handle_cmd(AHCIState *s, int port, int slot)
/* We're ready to process the command in FIS byte 2. */
ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
-
- if ((s->dev[port].port.ifs[0].status & (READY_STAT|DRQ_STAT|BUSY_STAT)) ==
- READY_STAT) {
- ahci_write_fis_d2h(&s->dev[port], cmd_fis);
- }
}
out:
@@ -991,7 +1051,7 @@ out:
}
/* DMA dev <-> ram */
-static int ahci_start_transfer(IDEDMA *dma)
+static void ahci_start_transfer(IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
@@ -1038,11 +1098,9 @@ out:
s->end_transfer_func(s);
if (!(s->status & DRQ_STAT)) {
- /* done with DMA */
- ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS);
+ /* done with PIO send/receive */
+ ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status));
}
-
- return 0;
}
static void ahci_start_dma(IDEDMA *dma, IDEState *s,
@@ -1104,28 +1162,11 @@ static int ahci_dma_set_unit(IDEDMA *dma, int unit)
return 0;
}
-static int ahci_dma_add_status(IDEDMA *dma, int status)
-{
- AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
- DPRINTF(ad->port_no, "set status: %x\n", status);
-
- if (status & BM_STATUS_INT) {
- ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS);
- }
-
- return 0;
-}
-
-static int ahci_dma_set_inactive(IDEDMA *dma)
-{
- return 0;
-}
-
-static int ahci_async_cmd_done(IDEDMA *dma)
+static void ahci_cmd_done(IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
- DPRINTF(ad->port_no, "async cmd done\n");
+ DPRINTF(ad->port_no, "cmd done\n");
/* update d2h status */
ahci_write_fis_d2h(ad, NULL);
@@ -1135,8 +1176,6 @@ static int ahci_async_cmd_done(IDEDMA *dma)
ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
qemu_bh_schedule(ad->check_bh);
}
-
- return 0;
}
static void ahci_irq_set(void *opaque, int n, int level)
@@ -1147,22 +1186,14 @@ static void ahci_dma_restart_cb(void *opaque, int running, RunState state)
{
}
-static int ahci_dma_reset(IDEDMA *dma)
-{
- return 0;
-}
-
static const IDEDMAOps ahci_dma_ops = {
.start_dma = ahci_start_dma,
.start_transfer = ahci_start_transfer,
.prepare_buf = ahci_dma_prepare_buf,
.rw_buf = ahci_dma_rw_buf,
.set_unit = ahci_dma_set_unit,
- .add_status = ahci_dma_add_status,
- .set_inactive = ahci_dma_set_inactive,
- .async_cmd_done = ahci_async_cmd_done,
+ .cmd_done = ahci_cmd_done,
.restart_cb = ahci_dma_restart_cb,
- .reset = ahci_dma_reset,
};
void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)