Skip to content

Commit

Permalink
ahci/sata: improve wait for clear operations
Browse files Browse the repository at this point in the history
  • Loading branch information
rizlik authored and danielinux committed Oct 2, 2023
1 parent b3465b5 commit bef9d8c
Showing 1 changed file with 77 additions and 51 deletions.
128 changes: 77 additions & 51 deletions src/x86/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ __attribute__((aligned(HBA_TBL_ALIGN)));
#define PCI_REG_MAP_AHCI_MODE (0x1 << 6)
#define PCI_REG_MAP_ALL_PORTS (0x1 << 5)

#define SATA_MAX_TRIES (5)
#define SATA_DELAY (100)

#ifdef DEBUG_AHCI
#define AHCI_DEBUG_PRINTF(...) wolfBoot_printf(__VA_ARGS__)
#else
Expand Down Expand Up @@ -416,6 +419,40 @@ static int sata_unlock_disk(int drv)
return 0;
}
#endif /* WOLFBOOT_ATA_DISK_LOCK */

/**
* @brief Waits until a specific address is cleared by a given mask.
*
* This function waits until a specific 32-bit PCI memory address is cleared by a given
* mask. After SATA_DELAY * SATA_MAX_TRIES ms, if the address in memory is not
* cleared, the function returns -1. *
* @param[in] address The memory address to monitor.
* @param[in] mask The mask to apply to the value at the address.
*
* @return 0 if the masked bits are cleared within the specified number of
* tries, 1 otherwise.
*/
static int sata_wait_until_clear(uint32_t address, uint32_t mask)
{
int count = SATA_MAX_TRIES;
uint32_t reg;
int ret = -1;

while (1) {
if (count-- == 0)
break;

reg = mmio_read32(address);
if ((reg & mask) == 0) {
ret = 0;
break;
}
delay(SATA_DELAY);
}

return ret;
}

/**
* @brief Enables SATA ports and detects connected SATA disks.
*
Expand Down Expand Up @@ -451,12 +488,15 @@ void sata_enable(uint32_t base)
mmio_or32(AHCI_HBA_GHC(base), HBA_GHC_HR | HBA_GHC_IE);

/* Wait until reset is complete */
while ((mmio_read32(AHCI_HBA_GHC(base)) & HBA_GHC_HR) != 0)
;

r = sata_wait_until_clear(AHCI_HBA_GHC(base), HBA_GHC_HR);
if (r != 0) {
wolfBoot_printf("ACHI: timeout waiting reset\r\n");
panic();
}

/* Wait until enabled. */
if ((mmio_read32(AHCI_HBA_GHC(base)) & HBA_GHC_AE) == 0)
mmio_or32(AHCI_HBA_GHC(base), HBA_GHC_AE);;
mmio_or32(AHCI_HBA_GHC(base), HBA_GHC_AE);

AHCI_DEBUG_PRINTF("AHCI reset complete.\r\n");

Expand Down Expand Up @@ -502,20 +542,18 @@ void sata_enable(uint32_t base)
mmio_or32(AHCI_PxSCTL(base, i), (0x03 << 8));

/* Disable interrupt reporting to SW */
//mmio_write32(AHCI_PxIE(base, i), 0);

count = 0;
while (1) {
ssts = mmio_read32(AHCI_PxSSTS(base, i));
ipm = (ssts >> 8) & 0x0F;
ssts &= AHCI_SSTS_DET_MASK;
if (ssts == AHCI_PORT_SSTS_DET_PCE)
break;
if (count++ > 5) {
if (count++ > SATA_MAX_TRIES) {
AHCI_DEBUG_PRINTF("AHCI port %d: Timeout occurred.\r\n", i);
break;
}
delay(1000);
delay(SATA_DELAY);
};

if (ssts == 0) {
Expand Down Expand Up @@ -544,15 +582,12 @@ void sata_enable(uint32_t base)
AHCI_DEBUG_PRINTF("AHCI port: Sending STOP ...\r\n");

/* Wait for CR to be cleared */
count = 0;
do {
reg = mmio_read32(AHCI_PxCMD(base, i));
if (count++ > 5) {
AHCI_DEBUG_PRINTF("AHCI Error: Port did not clear CR!\r\n");
break;
}
delay(1000);
} while ((reg & AHCI_PORT_CMD_CR) != 0);
r = sata_wait_until_clear(AHCI_PxCMD(base, i), AHCI_PORT_CMD_CR);
if (r != 0) {
wolfBoot_printf("AHCI Error: Port did not clear CR!\r\n");
panic();
}

AHCI_DEBUG_PRINTF("AHCI port: Sent STOP.\r\n");

AHCI_DEBUG_PRINTF("AHCI port: Disabling FIS ...\r\n");
Expand All @@ -566,15 +601,12 @@ void sata_enable(uint32_t base)
}

/* Wait for FR to be cleared */
count = 0;
do {
reg = mmio_read32(AHCI_PxCMD(base, i));
if (count++ > 5) {
wolfBoot_printf("AHCI Error: Port did not clear FR!\r\n");
break;
}
delay(1000);
} while ((reg & AHCI_PORT_CMD_FR) != 0);
r = sata_wait_until_clear(AHCI_PxCMD(base, i), AHCI_PORT_CMD_FR);
if (r != 0 ) {
wolfBoot_printf("AHCI Error: Port did not clear FR!\r\n");
panic();
}

AHCI_DEBUG_PRINTF("AHCI port: FIS disabled.\r\n");

clb = (uint32_t)(uintptr_t)(ahci_hba_clb + i * HBA_CLB_SIZE);
Expand All @@ -594,9 +626,11 @@ void sata_enable(uint32_t base)
memset((uint8_t*)(uintptr_t)fis, 0, HBA_FIS_SIZE);

/* Wait until CR is cleared */
do {
reg = mmio_read32(AHCI_PxCMD(base, i));
} while(reg & AHCI_PORT_CMD_CR);
r = sata_wait_until_clear(AHCI_PxCMD(base, i), AHCI_PORT_CMD_CR);
if (r != 0) {
wolfBoot_printf("AHCI error: CR clear error\r\n");
panic();
}

reg |= AHCI_PORT_CMD_FRE | AHCI_PORT_CMD_START;
mmio_write32(AHCI_PxCMD(base, i), reg);
Expand Down Expand Up @@ -649,6 +683,8 @@ void sata_disable(uint32_t base)
uint32_t ports_impl;
uint32_t i, reg;
volatile uint32_t count;
int r;

AHCI_DEBUG_PRINTF("SATA: disabling sata controller at 0x%x\r\n", base);

ports_impl = mmio_read32(AHCI_HBA_PI(base));
Expand All @@ -674,15 +710,12 @@ void sata_disable(uint32_t base)
}

/* Wait for CR to be cleared */
count = 0;
do {
reg = mmio_read32(AHCI_PxCMD(base, i));
if (count++ > 5) {
break;
}
delay(1000);
} while ((reg & AHCI_PORT_CMD_CR) != 0);

r = sata_wait_until_clear(AHCI_PxCMD(base, i), AHCI_PORT_CMD_CR);
if (r != 0) {
wolfBoot_printf("AHCI error: CR clear error\r\n");
panic();
}

/* Disable FIS RX */
reg = mmio_read32(AHCI_PxCMD(base, i));
if (reg & (AHCI_PORT_CMD_CR | AHCI_PORT_CMD_START)) {
Expand All @@ -693,24 +726,17 @@ void sata_disable(uint32_t base)
}

/* Wait for FR to be cleared */
count = 0;
do {
reg = mmio_read32(AHCI_PxCMD(base, i));
if (count++ > 5) {
wolfBoot_printf("AHCI Error: Port did not clear FR!\r\n");
break;
}
delay(1000);
} while ((reg & AHCI_PORT_CMD_FR) != 0);
r = sata_wait_until_clear(AHCI_PxCMD(base, i), AHCI_PORT_CMD_FR);
if (r != 0) {
wolfBoot_printf("AHCI error: FR clear error\r\n");
panic();
}
reg = mmio_read32(AHCI_PxCMD(base, i));
mmio_write32(AHCI_PxCMD(base, i),
reg & (~AHCI_PORT_CMD_ICC_ACTIVE));

}
/* reg = mmio_read32(AHCI_HBA_GHC(base)); */
/* mmio_write32(AHCI_HBA_GHC(base), reg & (~HBA_GHC_AE)); */
/* mmio_or32(AHCI_HBA_GHC(base), HBA_GHC_HR | HBA_GHC_IE); */
/* memset((void *)SATA_BASE, 0, 0x1000000); */

}
#endif /* AHCI_H_ */

0 comments on commit bef9d8c

Please sign in to comment.