Poll interrupt status register to figure when the device has updated the
status and possibly finished the request, continue polling until BSY is
clear as we might see multiple status updates per request.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
void *buffer, u32 bsize)
{
static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
void *buffer, u32 bsize)
{
- u32 val, status, success, flags;
+ u32 val, status, success, flags, intbits;
struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl);
struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
struct ahci_fis_s *fis = GET_GLOBAL(port->fis);
struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl);
struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
struct ahci_fis_s *fis = GET_GLOBAL(port->fis);
SET_FLATPTR(cmd->prdt[0].baseu, 0);
SET_FLATPTR(cmd->prdt[0].flags, bsize-1);
SET_FLATPTR(cmd->prdt[0].baseu, 0);
SET_FLATPTR(cmd->prdt[0].flags, bsize-1);
- val = ahci_port_readl(ctrl, pnr, PORT_CMD);
- ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
-
- if (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE))
- return -1;
-
flags = ((1 << 16) | /* one prd entry */
(iswrite ? (1 << 6) : 0) |
(isatapi ? (1 << 5) : 0) |
flags = ((1 << 16) | /* one prd entry */
(iswrite ? (1 << 6) : 0) |
(isatapi ? (1 << 5) : 0) |
SET_FLATPTR(list[0].baseu, 0);
dprintf(2, "AHCI/%d: send cmd ...\n", pnr);
SET_FLATPTR(list[0].baseu, 0);
dprintf(2, "AHCI/%d: send cmd ...\n", pnr);
- SET_FLATPTR(fis->rfis[2], 0);
+ intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+ if (intbits)
+ ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
- while (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE)) {
- yield();
- }
- while ((status = GET_FLATPTR(fis->rfis[2])) == 0) {
- yield();
- }
+
+ do {
+ for (;;) {
+ intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+ if (intbits) {
+ ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
+ if (intbits & 0x02) {
+ status = GET_FLATPTR(fis->psfis[2]);
+ break;
+ }
+ if (intbits & 0x01) {
+ status = GET_FLATPTR(fis->rfis[2]);
+ break;
+ }
+ }
+ yield();
+ }
+ dprintf(2, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n",
+ pnr, intbits, status);
+ } while (status & ATA_CB_STAT_BSY);
success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF |
ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR)) &&
success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF |
ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR)) &&
ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
val = ahci_port_readl(ctrl, pnr, PORT_CMD);
ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
val = ahci_port_readl(ctrl, pnr, PORT_CMD);
- ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_FIS_RX);
+ val |= PORT_CMD_FIS_RX;
+ ahci_port_writel(ctrl, pnr, PORT_CMD, val);
+ val |= PORT_CMD_START;
+ ahci_port_writel(ctrl, pnr, PORT_CMD, val);
sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));