/** * Check the cmd register to see if we should start or stop * the DMA or FIS RX engines. * * @ad: Device to engage. * @allow_stop: Allow device to transition from started to stopped? * 'no' is useful for migration post_load, which does not expect a transition. * * @return 0 on success, -1 on error. */ static int ahci_cond_start_engines(AHCIDevice *ad, bool allow_stop) { AHCIPortRegs *pr = &ad->port_regs; if (pr->cmd & PORT_CMD_START) { if (ahci_map_clb_address(ad)) { pr->cmd |= PORT_CMD_LIST_ON; } else { error_report("AHCI: Failed to start DMA engine: " "bad command list buffer address"); return -1; } } else if (pr->cmd & PORT_CMD_LIST_ON) { if (allow_stop) { ahci_unmap_clb_address(ad); pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON); } else { error_report("AHCI: DMA engine should be off, " "but appears to still be running"); return -1; } } if (pr->cmd & PORT_CMD_FIS_RX) { if (ahci_map_fis_address(ad)) { pr->cmd |= PORT_CMD_FIS_ON; } else { error_report("AHCI: Failed to start FIS receive engine: " "bad FIS receive buffer address"); return -1; } } else if (pr->cmd & PORT_CMD_FIS_ON) { if (allow_stop) { ahci_unmap_fis_address(ad); pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON); } else { error_report("AHCI: FIS receive engine should be off, " "but appears to still be running"); return -1; } } return 0; }
static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) { AHCIPortRegs *pr = &s->dev[port].port_regs; DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val); switch (offset) { case PORT_LST_ADDR: pr->lst_addr = val; break; case PORT_LST_ADDR_HI: pr->lst_addr_hi = val; break; case PORT_FIS_ADDR: pr->fis_addr = val; break; case PORT_FIS_ADDR_HI: pr->fis_addr_hi = val; break; case PORT_IRQ_STAT: pr->irq_stat &= ~val; ahci_check_irq(s); break; case PORT_IRQ_MASK: pr->irq_mask = val & 0xfdc000ff; ahci_check_irq(s); break; case PORT_CMD: /* Block any Read-only fields from being set; * including LIST_ON and FIS_ON. */ pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | (val & ~PORT_CMD_RO_MASK); if (pr->cmd & PORT_CMD_START) { if (ahci_map_clb_address(&s->dev[port])) { pr->cmd |= PORT_CMD_LIST_ON; } else { error_report("AHCI: Failed to start DMA engine: " "bad command list buffer address"); } } else if (pr->cmd & PORT_CMD_LIST_ON) { ahci_unmap_clb_address(&s->dev[port]); pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON); } if (pr->cmd & PORT_CMD_FIS_RX) { if (ahci_map_fis_address(&s->dev[port])) { pr->cmd |= PORT_CMD_FIS_ON; } else { error_report("AHCI: Failed to start FIS receive engine: " "bad FIS receive buffer address"); } } else if (pr->cmd & PORT_CMD_FIS_ON) { ahci_unmap_fis_address(&s->dev[port]); pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON); } /* XXX usually the FIS would be pending on the bus here and issuing deferred until the OS enables FIS receival. Instead, we only submit it once - which works in most cases, but is a hack. */ if ((pr->cmd & PORT_CMD_FIS_ON) && !s->dev[port].init_d2h_sent) { ahci_init_d2h(&s->dev[port]); s->dev[port].init_d2h_sent = true; } check_cmd(s, port); break; case PORT_TFDATA: /* Read Only. */ break; case PORT_SIG: /* Read Only */ break; case PORT_SCR_STAT: /* Read Only */ break; case PORT_SCR_CTL: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && ((val & AHCI_SCR_SCTL_DET) == 0)) { ahci_reset_port(s, port); } pr->scr_ctl = val; break; case PORT_SCR_ERR: pr->scr_err &= ~val; break; case PORT_SCR_ACT: /* RW1 */ pr->scr_act |= val; break; case PORT_CMD_ISSUE: pr->cmd_issue |= val; check_cmd(s, port); break; default: break; } }