diff options
Diffstat (limited to 'hw/ide/ahci.c')
-rw-r--r-- | hw/ide/ahci.c | 115 |
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) |